Skip to content
Open
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions twine/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,36 @@
logger = logging.getLogger(__name__)


def _parse_file(path: str, **open_kwargs: Any) -> configparser.RawConfigParser:
"""Open and parse a configuration file.

This helper performs a single open/read operation so that if a
UnicodeDecodeError is raised it happens before the parser has been
partially populated.
"""
parser = configparser.RawConfigParser()
with open(path, **open_kwargs) as f:
parser.read_file(f)
return parser


def _parse_config(path: str) -> configparser.RawConfigParser:
"""Parse a config file with a UTF-8 fallback on decode errors.

Try to parse using the default system encoding first; if a
UnicodeDecodeError occurs, retry using UTF-8 and log that a fallback
was used.
"""
try:
parser = _parse_file(path)
logger.info(f"Using configuration from {path}")
return parser
except UnicodeDecodeError:
parser = _parse_file(path, encoding="utf-8")
logger.info(f"Using configuration from {path} (decoded with UTF-8 fallback)")
return parser


def get_config(path: str) -> Dict[str, RepositoryConfig]:
"""Read repository configuration from a file (i.e. ~/.pypirc).

Expand All @@ -59,12 +89,11 @@ def get_config(path: str) -> Dict[str, RepositoryConfig]:
pypyi and testpypi.
"""
realpath = os.path.realpath(os.path.expanduser(path))

parser = configparser.RawConfigParser()

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