Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions changelog/+24f6a8f8.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add ``--lenient`` option to allow certain errors to be emitted as warnings
instead of causing Twine to exit.

Note that use of this option represents acknowledgement that uploads may fail
through no fault of Twine!
14 changes: 13 additions & 1 deletion tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_settings_transforms_repository_config_non_pypi(write_config_file):
assert s.disable_progress_bar is False


def test_settings_verify_feature_compatibility() -> None:
def test_settings_verify_feature_compatibility(caplog) -> None:
s = settings.Settings(skip_existing=True)
s.repository_config = {"repository": repository.WAREHOUSE}
try:
Expand All @@ -105,6 +105,18 @@ def test_settings_verify_feature_compatibility() -> None:
with pytest.raises(exceptions.UnsupportedConfiguration):
s.verify_feature_capability()

s.lenient = True
try:
s.verify_feature_capability()
except exceptions.UnsupportedConfiguration as unexpected_exc:
pytest.fail(
"Expected exception to be logged instead of raised, but"
f" {unexpected_exc!r} was raised"
)
assert len(caplog.messages) == 1
assert "Unsupported configuration" in caplog.messages[0]
s.lenient = False

s.skip_existing = False
try:
s.verify_feature_capability()
Expand Down
18 changes: 12 additions & 6 deletions twine/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,21 @@ def with_feature(self, feature: str) -> "UnsupportedConfiguration.Builder":
return self

def finalize(self) -> "UnsupportedConfiguration":
return UnsupportedConfiguration(
f"The configured repository {self.repository_url!r} does not "
"have support for the following features: "
f"{', '.join(self.features)} and is an unsupported "
"configuration",
return UnsupportedConfiguration.from_args(
self.repository_url,
*self.features,
self.features,
)

@classmethod
def from_args(
cls, repository_url: str, features: t.List[str]
) -> "UnsupportedConfiguration":
return cls(
f"The configured repository {repository_url!r} may not have"
f" support for the following features: {', '.join(features)}"
" and is an unsupported configuration."
)


class UnreachableRepositoryURLDetected(TwineException):
"""An upload attempt was detected to a URL without a protocol prefix.
Expand Down
28 changes: 25 additions & 3 deletions twine/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from twine import repository
from twine import utils

logger = logging.getLogger(__name__)


class Settings:
"""Object that manages the configuration for Twine.
Expand Down Expand Up @@ -61,6 +63,7 @@ def __init__(
repository_url: Optional[str] = None,
verbose: bool = False,
disable_progress_bar: bool = False,
lenient: bool = False,
**ignored_kwargs: Any,
) -> None:
"""Initialize our settings instance.
Expand Down Expand Up @@ -107,11 +110,14 @@ def __init__(
Show verbose output.
:param disable_progress_bar:
Disable the progress bar.
:param lenient:
Emit some errors as warnings and attempt to keep going.
"""
self.config_file = config_file
self.comment = comment
self.verbose = verbose
self.disable_progress_bar = disable_progress_bar
self.lenient = lenient
self.skip_existing = skip_existing
self._handle_repository_options(
repository_name=repository_name,
Expand Down Expand Up @@ -278,6 +284,13 @@ def register_argparse_arguments(parser: argparse.ArgumentParser) -> None:
action="store_true",
help="Disable the progress bar.",
)
parser.add_argument(
"--lenient",
default=False,
required=False,
action="store_true",
help="Emit some errors as warnings and try to keep going.",
)

@classmethod
def from_argparse(cls, args: argparse.Namespace) -> "Settings":
Expand Down Expand Up @@ -325,12 +338,21 @@ def verify_feature_capability(self) -> None:
"""
repository_url = cast(str, self.repository_config["repository"])

exc = None
if self.skip_existing and not repository_url.startswith(
(repository.WAREHOUSE, repository.TEST_WAREHOUSE)
):
raise exceptions.UnsupportedConfiguration.Builder().with_feature(
"--skip-existing"
).with_repository_url(repository_url).finalize()
exc = (
exceptions.UnsupportedConfiguration.Builder()
.with_feature("--skip-existing")
.with_repository_url(repository_url)
.finalize()
)
if exc is not None:
if self.lenient:
logger.warning("Unsupported configuration: %s", exc)
else:
raise exc

def check_repository_url(self) -> None:
"""Verify we are not using legacy PyPI.
Expand Down
Loading