Skip to content

Commit 03bcad6

Browse files
authored
Restore test isolation by allowing username override for PyPI (#1131)
Closes #1121
1 parent dd61356 commit 03bcad6

File tree

4 files changed

+20
-21
lines changed

4 files changed

+20
-21
lines changed

changelog/1121.removal.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Username for PyPI and Test PyPI now defaults to __token__ but no longer overrides a username configured in the environment or supplied on the command line. Workflows still supplying anything other than __token__ for the username when uploading to PyPI or Test PyPI will now fail. Either supply __token__ or do not supply a username at all.

tests/test_register.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,15 @@ def none_register(*args, **settings_kwargs):
9090
monkeypatch.setattr(register, "register", replaced_register)
9191
testenv = {
9292
"TWINE_REPOSITORY": repo,
93-
# Ignored because the TWINE_REPOSITORY is PyPI/TestPyPI
94-
"TWINE_USERNAME": "this-is-ignored",
93+
"TWINE_USERNAME": "pypiuser",
9594
"TWINE_PASSWORD": "pypipassword",
9695
"TWINE_CERT": "/foo/bar.crt",
9796
}
9897
with helpers.set_env(**testenv):
9998
cli.dispatch(["register", helpers.WHEEL_FIXTURE])
10099
register_settings = replaced_register.calls[0].args[0]
101100
assert "pypipassword" == register_settings.password
102-
assert "__token__" == register_settings.username
101+
assert "pypiuser" == register_settings.username
103102
assert "/foo/bar.crt" == register_settings.cacert
104103

105104

tests/test_upload.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,16 +564,15 @@ def none_upload(*args, **settings_kwargs):
564564
monkeypatch.setattr(upload, "upload", replaced_upload)
565565
testenv = {
566566
"TWINE_REPOSITORY": repo,
567-
# Ignored because TWINE_REPOSITORY is PyPI/TestPyPI
568-
"TWINE_USERNAME": "this-is-ignored",
567+
"TWINE_USERNAME": "pypiuser",
569568
"TWINE_PASSWORD": "pypipassword",
570569
"TWINE_CERT": "/foo/bar.crt",
571570
}
572571
with helpers.set_env(**testenv):
573572
cli.dispatch(["upload", "path/to/file"])
574573
upload_settings = replaced_upload.calls[0].args[0]
575574
assert "pypipassword" == upload_settings.password
576-
assert "__token__" == upload_settings.username
575+
assert "pypiuser" == upload_settings.username
577576
assert "/foo/bar.crt" == upload_settings.cacert
578577

579578

twine/auth.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,9 @@ def choose(cls, interactive: bool) -> Type["Resolver"]:
3939
@property
4040
@functools.lru_cache()
4141
def username(self) -> Optional[str]:
42-
if cast(str, self.config["repository"]).startswith(
43-
(utils.DEFAULT_REPOSITORY, utils.TEST_REPOSITORY)
44-
):
45-
# As of 2024-01-01, PyPI requires API tokens for uploads, meaning
46-
# that the username is invariant.
47-
return "__token__"
42+
if self.is_pypi() and not self.input.username:
43+
# Default username.
44+
self.input.username = "__token__"
4845

4946
return utils.get_userpass_value(
5047
self.input.username,
@@ -111,20 +108,23 @@ def password_from_keyring_or_prompt(self) -> str:
111108
logger.info("password set from keyring")
112109
return password
113110

114-
# As of 2024-01-01, PyPI requires API tokens for uploads;
115-
# specialize the prompt to clarify that an API token must be provided.
116-
if cast(str, self.config["repository"]).startswith(
117-
(utils.DEFAULT_REPOSITORY, utils.TEST_REPOSITORY)
118-
):
119-
prompt = "API token"
120-
else:
121-
prompt = "password"
111+
# Prompt for API token when required.
112+
what = "API token" if self.is_pypi() else "password"
122113

123-
return self.prompt(prompt, getpass.getpass)
114+
return self.prompt(what, getpass.getpass)
124115

125116
def prompt(self, what: str, how: Callable[..., str]) -> str:
126117
return how(f"Enter your {what}: ")
127118

119+
def is_pypi(self) -> bool:
120+
"""As of 2024-01-01, PyPI requires API tokens for uploads."""
121+
return cast(str, self.config["repository"]).startswith(
122+
(
123+
utils.DEFAULT_REPOSITORY,
124+
utils.TEST_REPOSITORY,
125+
)
126+
)
127+
128128

129129
class Private(Resolver):
130130
def prompt(self, what: str, how: Optional[Callable[..., str]] = None) -> str:

0 commit comments

Comments
 (0)