Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/twinePublic

Commitc1c02d1

Browse files
committed
Remove --skip-existing support for non-PyPI indices
Closes#1251
1 parenta24d308 commitc1c02d1

File tree

7 files changed

+136
-59
lines changed

7 files changed

+136
-59
lines changed

‎changelog/1251.removal.rst‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Remove hacks that support ``--skip-existing`` for indexes other than PyPI and
2+
TestPyPI.
3+
4+
To date, these hacks continue to accrue and there have been numerous issues
5+
with them, not the least of which being that every time we update them, the
6+
paid index providers change things to break the compatibility we implement for
7+
them. Beyond that, these hacks do not work when text is internationalized in
8+
the response from the index provider.
9+
10+
For a sample of past issues, see:
11+
12+
- https://github.com/pypa/twine/issues/1251
13+
14+
- https://github.com/pypa/twine/issues/918
15+
16+
- https://github.com/pypa/twine/issues/856
17+
18+
- https://github.com/pypa/twine/issues/693
19+
20+
- https://github.com/pypa/twine/issues/332

‎tests/test_settings.py‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
importpytest
2020

2121
fromtwineimportexceptions
22+
fromtwineimportrepository
2223
fromtwineimportsettings
2324

2425

@@ -76,11 +77,44 @@ def test_settings_transforms_repository_config_non_pypi(write_config_file):
7677
asserts.identityisNone
7778
asserts.username=="someusername"
7879
asserts.password=="password"
79-
asserts.cacertisNone
8080
asserts.client_certisNone
8181
asserts.disable_progress_barisFalse
8282

8383

84+
deftest_settings_verify_feature_compatibility()->None:
85+
s=settings.Settings(skip_existing=True)
86+
s.repository_config= {"repository":repository.WAREHOUSE}
87+
try:
88+
s.verify_feature_capability()
89+
exceptexceptions.UnsupportedConfigurationasunexpected_exc:
90+
pytest.fail(
91+
"Expected feature capability to work with production PyPI"
92+
f" but got{unexpected_exc!r}"
93+
)
94+
95+
s.repository_config["repository"]=repository.TEST_WAREHOUSE
96+
try:
97+
s.verify_feature_capability()
98+
exceptexceptions.UnsupportedConfigurationasunexpected_exc:
99+
pytest.fail(
100+
"Expected feature capability to work with TestPyPI but got"
101+
f"{unexpected_exc!r}"
102+
)
103+
104+
s.repository_config["repository"]="https://not-really-pypi.example.com/legacy"
105+
withpytest.raises(exceptions.UnsupportedConfiguration):
106+
s.verify_feature_capability()
107+
108+
s.skip_existing=False
109+
try:
110+
s.verify_feature_capability()
111+
exceptexceptions.UnsupportedConfigurationasunexpected_exc:
112+
pytest.fail(
113+
"Expected an exception only when --skip-existing is provided"
114+
f" but got{unexpected_exc!r}"
115+
)
116+
117+
84118
@pytest.mark.parametrize(
85119
"verbose, log_level", [(True,logging.INFO), (False,logging.WARNING)]
86120
)

‎tests/test_upload.py‎

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -458,27 +458,6 @@ def test_prints_skip_message_for_response(
458458
),
459459
id="pypi",
460460
),
461-
pytest.param(
462-
dict(
463-
status_code=400,
464-
reason=(
465-
"Repository does not allow updating assets: pypi for url: "
466-
"http://www.foo.bar"
467-
),
468-
),
469-
id="nexus",
470-
),
471-
pytest.param(
472-
dict(
473-
status_code=400,
474-
text=(
475-
'<div class="content-section">\n'
476-
" Repository does not allow updating assets: pypi-local\n"
477-
"</div>\n"
478-
),
479-
),
480-
id="nexus_new",
481-
),
482461
pytest.param(
483462
dict(
484463
status_code=409,
@@ -489,37 +468,6 @@ def test_prints_skip_message_for_response(
489468
),
490469
id="pypiserver",
491470
),
492-
pytest.param(
493-
dict(
494-
status_code=403,
495-
text=(
496-
"Not enough permissions to overwrite artifact "
497-
"'pypi-local:twine/1.5.0/twine-1.5.0-py2.py3-none-any.whl'"
498-
"(user 'twine-deployer' needs DELETE permission)."
499-
),
500-
),
501-
id="artifactory_old",
502-
),
503-
pytest.param(
504-
dict(
505-
status_code=403,
506-
text=(
507-
"Not enough permissions to delete/overwrite artifact "
508-
"'pypi-local:twine/1.5.0/twine-1.5.0-py2.py3-none-any.whl'"
509-
"(user 'twine-deployer' needs DELETE permission)."
510-
),
511-
),
512-
id="artifactory_new",
513-
),
514-
pytest.param(
515-
dict(
516-
status_code=400,
517-
text=(
518-
'{"message":"validation failed: file name has already been taken"}'
519-
),
520-
),
521-
id="gitlab_enterprise",
522-
),
523471
],
524472
)
525473
deftest_skip_existing_skips_files_on_repository(response_kwargs):

‎twine/commands/upload.py‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,6 @@ def skip_upload(
6161
status==409
6262
# PyPI / TestPyPI / GCP Artifact Registry
6363
or (status==400andany("already exist"inxforxin [reason,text]))
64-
# Nexus Repository OSS (https://www.sonatype.com/nexus-repository-oss)
65-
or (status==400andany("updating asset"inxforxin [reason,text]))
66-
# Artifactory (https://jfrog.com/artifactory/)
67-
or (status==403and"overwrite artifact"intext)
68-
# Gitlab Enterprise Edition (https://about.gitlab.com)
69-
or (status==400and"already been taken"intext)
7064
)
7165

7266

@@ -131,6 +125,7 @@ def upload(upload_settings: settings.Settings, dists: List[str]) -> None:
131125
The repository responded with an error.
132126
"""
133127
upload_settings.check_repository_url()
128+
upload_settings.verify_feature_capability()
134129
repository_url=cast(str,upload_settings.repository_config["repository"])
135130

136131
# Attestations are only supported on PyPI and TestPyPI at the moment.

‎twine/exceptions.py‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
16+
importtypingast
1617

1718

1819
classTwineException(Exception):
@@ -77,6 +78,49 @@ def from_args(
7778
)
7879

7980

81+
classUnsupportedConfiguration(TwineException):
82+
"""An upload attempt was detected using features not supported by a repository.
83+
84+
The features specified either in configuration or on the command-line.
85+
"""
86+
87+
classBuilder:
88+
"""Build the parameters for an UnsupportedConfiguration exception.
89+
90+
In the event we add additional features we are not allowing with
91+
something other than PyPI or TestPyPI, we can use a builder to
92+
accumulate them all instead of requiring someone to run multiple times
93+
to discover all unsupported configuration options.
94+
"""
95+
96+
repository_url:str
97+
features:t.List[str]
98+
99+
def__init__(self)->None:
100+
self.repository_url=""
101+
self.features= []
102+
103+
defwith_repository_url(
104+
self,repository_url:str
105+
)->"UnsupportedConfiguration.Builder":
106+
self.repository_url=repository_url
107+
returnself
108+
109+
defwith_feature(self,feature:str)->"UnsupportedConfiguration.Builder":
110+
self.features.append(feature)
111+
returnself
112+
113+
deffinalize(self)->"UnsupportedConfiguration":
114+
returnUnsupportedConfiguration(
115+
f"The configured repository{self.repository_url!r} does not "
116+
"have support for the following features: "
117+
f"{', '.join(self.features)} and is an unsupported "
118+
"configuration",
119+
self.repository_url,
120+
*self.features,
121+
)
122+
123+
80124
classUnreachableRepositoryURLDetected(TwineException):
81125
"""An upload attempt was detected to a URL without a protocol prefix.
82126

‎twine/repository.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,23 @@ def upload(
181181
defpackage_is_uploaded(
182182
self,package:package_file.PackageFile,bypass_cache:bool=False
183183
)->bool:
184+
"""Determine if a package has been uploaded to PyPI already.
185+
186+
.. warning:: This does not support indexes other than PyPI or TestPyPI
187+
188+
:param package:
189+
The package file that will otherwise be uploaded.
190+
:type package:
191+
:class:`~twine.package.PackageFile`
192+
:param bypass_cache:
193+
Force a request to PyPI.
194+
:type bypass_cache:
195+
bool
196+
:returns:
197+
True if package has already been uploaded, False otherwise
198+
:rtype:
199+
bool
200+
"""
184201
# NOTE(sigmavirus24): Not all indices are PyPI and pypi.io doesn't
185202
# have a similar interface for finding the package versions.
186203
ifnotself.url.startswith((LEGACY_PYPI,WAREHOUSE,OLD_WAREHOUSE)):

‎twine/settings.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,25 @@ def _handle_certificates(
313313
self.cacert=utils.get_cacert(cacert,self.repository_config)
314314
self.client_cert=utils.get_clientcert(client_cert,self.repository_config)
315315

316+
defverify_feature_capability(self)->None:
317+
"""Verify configured settings are supported for the configured repository.
318+
319+
This presently checks:
320+
- ``--skip-existing`` was only provided for PyPI and TestPyPI
321+
322+
:raises twine.exceptions.UnsupportedConfiguration:
323+
The configured features are not available with the configured
324+
repository.
325+
"""
326+
repository_url=cast(str,self.repository_config["repository"])
327+
328+
ifself.skip_existingandnotrepository_url.startswith(
329+
(repository.WAREHOUSE,repository.TEST_WAREHOUSE)
330+
):
331+
raiseexceptions.UnsupportedConfiguration.Builder().with_feature(
332+
"--skip-existing"
333+
).with_repository_url(repository_url).finalize()
334+
316335
defcheck_repository_url(self)->None:
317336
"""Verify we are not using legacy PyPI.
318337

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp