Skip to content

Commit

Permalink
Raise exception for missing config file
Browse files Browse the repository at this point in the history
  • Loading branch information
bhrutledge committed Jun 20, 2021
1 parent 1794790 commit fd51ed1
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 12 deletions.
8 changes: 6 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
import pytest

from twine import settings
from twine import utils


@pytest.fixture()
def config_file(tmpdir):
return tmpdir / ".pypirc"
def config_file(tmpdir, monkeypatch):
path = tmpdir / ".pypirc"
# Mimic common case of .pypirc in home directory
monkeypatch.setattr(utils, "DEFAULT_CONFIG_FILE", path)
return path


@pytest.fixture
Expand Down
22 changes: 18 additions & 4 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,24 @@ def test_get_repository_config_with_invalid_url(config_file, repo_url, message):
utils.get_repository_from_config(config_file, "pypi", repo_url)


def test_get_repository_config_missing_config(config_file):
"""Raise an exception when a repository isn't defined in .pypirc."""
with pytest.raises(exceptions.InvalidConfiguration):
utils.get_repository_from_config(config_file, "foobar")
def test_get_repository_config_missing_repository(write_config_file):
"""Raise an exception when a custom repository isn't defined in .pypirc."""
config_file = write_config_file("")
with pytest.raises(
exceptions.InvalidConfiguration,
match="Missing 'missing-repository'",
):
utils.get_repository_from_config(config_file, "missing-repository")


@pytest.mark.parametrize("repository", ["pypi", "missing-repository"])
def test_get_repository_config_missing_file(repository):
"""Raise an exception when a custom config file doesn't exist."""
with pytest.raises(
exceptions.InvalidConfiguration,
match=r"No such file.*missing-file",
):
utils.get_repository_from_config("missing-file", repository)


def test_get_config_deprecated_pypirc():
Expand Down
4 changes: 2 additions & 2 deletions twine/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(
password: Optional[str] = None,
non_interactive: bool = False,
comment: Optional[str] = None,
config_file: str = "~/.pypirc",
config_file: str = utils.DEFAULT_CONFIG_FILE,
skip_existing: bool = False,
cacert: Optional[str] = None,
client_cert: Optional[str] = None,
Expand Down Expand Up @@ -244,7 +244,7 @@ def register_argparse_arguments(parser: argparse.ArgumentParser) -> None:
)
parser.add_argument(
"--config-file",
default="~/.pypirc",
default=utils.DEFAULT_CONFIG_FILE,
help="The .pypirc config file to use.",
)
parser.add_argument(
Expand Down
20 changes: 16 additions & 4 deletions twine/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
DEFAULT_REPOSITORY = "https://upload.pypi.org/legacy/"
TEST_REPOSITORY = "https://test.pypi.org/legacy/"

DEFAULT_CONFIG_FILE = "~/.pypirc"

# TODO: In general, it seems to be assumed that the values retrieved from
# instances of this type aren't None, except for username and password.
# Type annotations would be cleaner if this were Dict[str, str], but that
Expand All @@ -48,13 +50,20 @@ def get_config(path: str) -> Dict[str, RepositoryConfig]:
Format: https://packaging.python.org/specifications/pypirc/
If the file doesn't exist, return a default configuration for pypyi and testpypi.
If the default config file doesn't exist, return a default configuration for
pypyi and testpypi.
"""
realpath = os.path.realpath(os.path.expanduser(path))
parser = configparser.RawConfigParser()

path = os.path.expanduser(path)
if parser.read(path):
logger.info(f"Using configuration from {path}")
try:
with open(realpath) as f:
parser.read_file(f)
logger.info(f"Using configuration from {realpath}")
except FileNotFoundError:
# User probably set --config-file, but the file can't be read
if path != DEFAULT_CONFIG_FILE:
raise

# server-login is obsolete, but retained for backwards compatibility
defaults: RepositoryConfig = {
Expand Down Expand Up @@ -121,8 +130,11 @@ def get_repository_from_config(
"username": None,
"password": None,
}

try:
return get_config(config_file)[repository]
except OSError as exc:
raise exceptions.InvalidConfiguration(str(exc))
except KeyError:
raise exceptions.InvalidConfiguration(
f"Missing '{repository}' section from {config_file}.\n"
Expand Down

0 comments on commit fd51ed1

Please sign in to comment.