Repository: pypa/twine
Pull request #452 (2019-02-09 17:57:49)

Restore prompts while retaining support for suppressing prompts
As discussed in #450, the work in #426 had some unintended side-effects, namely that it unconditionally suppressed prompts for username and password (and thus also keyring resolution).

This PR goes back in time, committing tests to the code prior to #426 plus some commits on top of #426, then merges the two, fixing the failing tests.

codecov[bot]: # [Codecov](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=h1) Report
> Merging [#452](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=desc) into [master](https://codecov.io/gh/pypa/twine/commit/61d01ac125c047ae6d6e5e4aab6ffa96cd3a6b95?src=pr&el=desc) will **increase** coverage by `0.9%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pypa/twine/pull/452/graphs/tree.svg?width=650&token=NrvN4iFbj6&height=150&src=pr)](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##           master     #452     +/-   ##
=========================================
+ Coverage   79.94%   80.85%   +0.9%     
=========================================
  Files          14       14             
  Lines         748      893    +145     
  Branches      108      141     +33     
=========================================
+ Hits          598      722    +124     
- Misses        114      132     +18     
- Partials       36       39      +3
```


| [Impacted Files](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [twine/utils.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvdXRpbHMucHk=) | `83.68% <100%> (ø)` | :arrow_up: |
| [twine/repository.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvcmVwb3NpdG9yeS5weQ==) | `68.84% <0%> (-4.5%)` | :arrow_down: |
| [twine/settings.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvc2V0dGluZ3MucHk=) | `92.63% <0%> (-0.71%)` | :arrow_down: |
| [twine/cli.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvY2xpLnB5) | `100% <0%> (ø)` | :arrow_up: |
| [twine/commands/check.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvY29tbWFuZHMvY2hlY2sucHk=) | `100% <0%> (ø)` | :arrow_up: |
| [twine/commands/upload.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvY29tbWFuZHMvdXBsb2FkLnB5) | `76.47% <0%> (+1.47%)` | :arrow_up: |
| [twine/wheel.py](https://codecov.io/gh/pypa/twine/pull/452/diff?src=pr&el=tree#diff-dHdpbmUvd2hlZWwucHk=) | `91.02% <0%> (+6.71%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=footer). Last update [61d01ac...0f6a1e1](https://codecov.io/gh/pypa/twine/pull/452?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/twine/utils.py b/twine/utils.py
index f5bbb21..c6c7821 100644
--- a/twine/utils.py
+++ b/twine/utils.py
@@ -57,7 +57,7 @@ TEST_REPOSITORY = "https://test.pypi.org/legacy/"
 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(allow_no_value=True)
+    parser = configparser.RawConfigParser()
 
     # this list will only be used if index-servers
     # is not defined in the config file
@@ -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 key in config:
+    elif config.get(key) is not None:
         return config[key]
     elif prompt_strategy:
         return prompt_strategy()


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 69b9e73..7e2d302 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -126,6 +126,26 @@ def test_get_config_missing(tmpdir):
     }
 
 
+def test_empty_userpass(tmpdir):
+    """
+    Empty username and password may be supplied to suppress
+    prompts. See #426.
+    """
+    pypirc = os.path.join(str(tmpdir), ".pypirc")
+
+    with open(pypirc, "w") as fp:
+        fp.write(textwrap.dedent("""
+            [pypi]
+            username=
+            password=
+        """))
+
+    config = utils.get_config(pypirc)
+    pypi = config['pypi']
+
+    assert pypi['username'] == pypi['password'] == ''
+
+
 def test_get_repository_config_missing(tmpdir):
     pypirc = os.path.join(str(tmpdir), ".pypirc")
 
@@ -229,6 +249,18 @@ def test_get_password_keyring_defers_to_prompt(monkeypatch):
     assert pw == 'entered pw'
 
 
+def test_no_password_defers_to_prompt(monkeypatch):
+    monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw')
+    pw = utils.get_password('system', 'user', None, {'password': None})
+    assert pw == 'entered pw'
+
+
+def test_empty_password_bypasses_prompt(monkeypatch):
+    monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw')
+    pw = utils.get_password('system', 'user', None, {'password': ''})
+    assert pw == ''
+
+
 def test_get_username_and_password_keyring_overrides_prompt(monkeypatch):
     import collections
     Credential = collections.namedtuple('Credential', 'username password')