Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow defining empty username and password in .pypirc #426

Merged
merged 1 commit into from
Jan 12, 2019

Conversation

marob
Copy link
Contributor

@marob marob commented Nov 19, 2018

In the case of a private repository (Nexus in my case) without any authentication, I haven't found a way not to have a prompt asking for username and password.

This PR allows to have the following configuration:

username:
password:

Note that I haven't tested for side effects.

In the case of a private repository (Nexus in my case) without any authentication, I haven't found a way not to have a prompt asking for username and password.

This PR allows to have the following configuration:
```ini
username:
password:
```
Note that I haven't tested for side effects.
@codecov
Copy link

codecov bot commented Nov 19, 2018

Codecov Report

Merging #426 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #426   +/-   ##
=======================================
  Coverage   78.29%   78.29%           
=======================================
  Files          14       14           
  Lines         751      751           
  Branches      108      108           
=======================================
  Hits          588      588           
  Misses        130      130           
  Partials       33       33
Impacted Files Coverage Δ
twine/utils.py 83.68% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 89a35dc...92602d8. Read the comment docs.

@sigmavirus24 sigmavirus24 merged commit f4505d6 into pypa:master Jan 12, 2019
@marob marob deleted the patch-1 branch January 12, 2019 15:19
@jaraco
Copy link
Member

jaraco commented Feb 6, 2019

This change breaks twine for me. I have no .pypirc. Following this change, I get an error when trying to upload:

Uploading treehouse-0.1.0-cp37-cp37m-macosx_10_9_x86_64.whl
HTTPError: 403 Client Error: Invalid or non-existent authentication information. for url: https://upload.pypi.org/legacy/

@jaraco
Copy link
Member

jaraco commented Feb 6, 2019

Before this change, twine would resolve my username from the TWINE_USERNAME environment variable. Now I suspect it's being loaded as ''.

@jaraco
Copy link
Member

jaraco commented Feb 7, 2019

Should I open a new ticket on this issue?

@sigmavirus24
Copy link
Member

Yes, please. It's disappointing that our CI didn't catch this

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

@marob Thinking about the code prior to this change, if you had put username: unused and password: unused in the .pypirc file, would that satisfy your use-case?

@marob
Copy link
Contributor Author

marob commented Feb 9, 2019

I'm not for using a special value to indicate an absence of value.

I don't understand how the changes introduced in this PR can have the side effect of not taking env vars into account.

On my understanding, the ArgumentParser parses arguments on the command line and defaults to env var if the argument is not present.
Son as the code first checks cli_value coming from the the ArgumentParser before testing the value from the configuration file (so before my modifications), this should not be related.

@@ -57,7 +57,7 @@
def get_config(path="~/.pypirc"):
# even if the config file does not exist, set up the parser
# variable to reduce the number of if/else statements
parser = configparser.RawConfigParser()
parser = configparser.RawConfigParser(allow_no_value=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today I learned that allow_no_value=True allows for:

[pypi]
username
password

But with allow_no_value=False, username= and password= are still valid and still result in empty strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With allow_no_value=False, if the config file is of the form:

[pypi]
username
password

the username and password key won't be available in the parsed config.
Or am I mistaking?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, the parser will raise an Exception, but naked username or password was not the prescribed syntax; rather username: (which I assume is equivalent to username=) was.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, now I remember why I had to add allow_no_value=True in order to allow empty value.

What's the difference between adding : (or =), and using naked keys?
Is it parsed differently?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Adding the separator causes the value to be parsed as the empty string. Not including the separator creates a ParseException unless allow_no_value=True in which case the value is None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. The intent of my PR was to be able to provide a None value in the configuration file in order to be anonymous.
Without this PR, you wouldn't be able to provide such value, so the CLI would systematically ask for a login/password (which I didn't want).

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

I don't understand how the changes introduced in this PR can have the side effect of not taking env vars into account.

You're right, and I've learned more in #450.

@@ -195,7 +195,7 @@ def get_userpass_value(cli_value, config, key, prompt_strategy=None):
"""
if cli_value is not None:
return cli_value
elif config.get(key):
elif key in config:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that config always defaults with {'username': None, 'password': None}, I now believe this change unconditionally disables prompts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't add username nor password keys in your configuration file, the keys won't be available in the parsed config object, so it should continue to the prompt_strategy part.

Copy link
Member

@jaraco jaraco Feb 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's where you're mistaken. Keys are set unconditionally to default to None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if I don't have a server-login section (which is my case):

[distutils]
index-servers =
    internal

[internal]
repository: ******
username
password

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

if you had put username: unused and password: unused in the .pypirc file, would that satisfy your use-case?

I'm not for using a special value to indicate an absence of value.

I'll take that as a yes.

And I agree, using 'unused' to have special meaning would be undesirable. But if all you're seeking to do is supply a value that's supplied but ignored, '' is hardly better and is in fact worse because it doesn't convey its purpose and can be conflated with other special values like None.

jaraco added a commit that referenced this pull request Feb 9, 2019
…allowed, as added in #426. Note that the tests pass even without 'allow_no_value'.
jaraco added a commit that referenced this pull request Feb 9, 2019
@marob
Copy link
Contributor Author

marob commented Feb 9, 2019

I'll take that as a yes.

It was a no.

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

In cad2ccf, I add a regression test that would have blocked this change... and in d1123b3, I merge that passing test with this change to illustrate the failure, seen here.

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

It was a no.

Does that mean your private PyPI server is rejecting unused:unused as the credentials but accepts : as the credentials? Or maybe there's something in the requests that filters out double-blank authentication.

@marob
Copy link
Contributor Author

marob commented Feb 9, 2019

I don't know if it accepts unused:unused (but I suspect it won't, except if I created a special user for it).

The goal is to provide no username nor password (no authentication) at all.

jaraco added a commit that referenced this pull request Feb 9, 2019
@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

I don't know if it accepts unused:unused (but I suspect it won't, except if I created a special user for it).

I would not expect you to have to create a special user. My suspicion was that your server was not requiring authentication at all, so was ignoring the empty authentication that was being passed.

In 3c0ad12, I've added another test, a test capturing the intention that you're intending to express in this PR, prior to the PR, which should fail first and pass after merging with this commit.

jaraco added a commit that referenced this pull request Feb 9, 2019
jaraco added a commit that referenced this pull request Feb 9, 2019
…, suppressing prompts and keyring resolution. Ref #426. Fixes #450.
@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

Or maybe there's something in the requests that filters out double-blank authentication.

I've confirmed that requests does not suppress double-blanks for auth:

$ requests.get('http://httpbin.org/headers', auth=('', '')).json()                                                                                       
{'headers': {'Accept': '*/*',
  'Accept-Encoding': 'gzip, deflate',
  'Authorization': 'Basic Og==',
  'Connection': 'close',
  'Host': 'httpbin.org',
  'User-Agent': 'python-requests/2.21.0'}}

@marob
Copy link
Contributor Author

marob commented Feb 9, 2019

The use case is not to provide empty string values for the username and password (which will indeed generate a : basic authentication), but to provide None values to prevent the CLI to ask for login/password.

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

@marob: Can you confirm then that with username= and password= in your config and the latest code in #452 that your Nexus server rejects the request because of the auth?

Regardless of that outcome, I tend to agree - one shouldn't have to supply special values, even empty ones, to avoid prompts. Unfortunately, due to the current behavior and expectations, the None value is already used to mean "unspecified, so prompt (maybe keyring)". A more involved refactoring is going to be required to get the behavior you're seeking.

theacodes pushed a commit that referenced this pull request Feb 9, 2019
* Add test capturing expectation that an empty username or password is allowed, as added in #426. Note that the tests pass even without 'allow_no_value'.

* Add test capturing expectation that the password prompt occurs if no password was indicated. Ref #426 #450.

* Add test capturing new expectation that an empty password found in the config should bypass the prompt. Ref #426.

* Remove 'allow_no_value' on config parser. It was unneeded and adds ambiguity. Ref #426.

* Allow empty strings in config to satisfy username and password values, suppressing prompts and keyring resolution. Ref #426. Fixes #450.
@marob
Copy link
Contributor Author

marob commented Feb 9, 2019

It does work with 95ecde2 and the following configuration:

[distutils]
index-servers =
    internal

[internal]
repository: ******
username:
password:

@jaraco
Copy link
Member

jaraco commented Feb 9, 2019

That's great news. We'll acknowledge that it's a hack - that a better design might be had, but at least there's a suitable workaround in the meantime. Thanks @marob for the extra effort in making this product better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants