Repository: pypa/setuptools

Issue:
upload command doesn't prompt for password; raises TypeError
# Problem statement

If the `~/.pypirc` file does not contain a password like so:

``` ini
[distutils]
index-servers = 
    pypitest

[pypitest]
repository = https://testpypi.python.org/pypi
username = my_username
; Note the lack of a password
```

Then uploading the package

```
python setup.py sdist upload -r pypitest
```

Fails to prompt the user for his password and instead raises a TypeError (output truncated)

```
running upload
Traceback (most recent call last):
  File "setup.py", line 16, in <module>
    keywords=["test", "hello"]
  File "/usr/lib/python2.7/distutils/core.py", line 151, in setup
    dist.run_commands()
  File "/usr/lib/python2.7/distutils/dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python2.7/distutils/dist.py", line 972, in run_command
    cmd_obj.run()
  File "/usr/lib/python2.7/distutils/command/upload.py", line 60, in run
    self.upload_file(command, pyversion, filename)
  File "/usr/lib/python2.7/distutils/command/upload.py", line 135, in upload_file
    self.password)
TypeError: cannot concatenate 'str' and 'NoneType' objects
```

**This is different** than the behavior of the `register` command, which prompts the user for a password before continuing.

```
python setup.py sdist register -r pypitest
```

(output truncated)

```
Creating tar archive
removing 'HelloPyPi-0.0.1.dev0' (and everything under it)
running register
Password: 
```

> Note that the `register` and the `upload` command exhibit the proper behavior **if you store your password in `~/.pypirc`**, but not if the password is omitted.
# Okay, so...?

I am aware that you can run

```
python setup.py sdist register -r pypitest upload -r pypitest
```

As a workaround, but it stands to reason that **if you can register a package without uploading it, then you should also be able to upload a package without registering it**, regardless of if a password has been specified in your `~/.pypirc` file.
# Steps to reproduce
1. Remove your pypi password from `~/.pypirc`
2. Find a project that you wish to upload to a pypi server (I used [my example repository](https://github.com/brookskindle/hellopypi) for this)
3. Run `python setup.py sdist upload -r target_pypi_server`
# Setuptools version

setuptools (20.9.0) -- from `pip list` in my virtualenv



Discussion:

Resolving patch:
diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py
--- a/setuptools/command/upload.py
+++ b/setuptools/command/upload.py
@@ -3,13 +3,18 @@
 
 class upload(orig.upload):
     """
-    Override default upload behavior to look up password
-    in the keyring if available.
+    Override default upload behavior to obtain password
+    in a variety of different ways.
     """
 
     def finalize_options(self):
         orig.upload.finalize_options(self)
-        self.password or self._load_password_from_keyring()
+        # Attempt to obtain password. Short circuit evaluation at the first
+        # sign of success.
+        self.password = (
+            self.password or self._load_password_from_keyring() or
+            self._prompt_for_password()
+        )
 
     def _load_password_from_keyring(self):
         """
@@ -17,7 +22,22 @@ def _load_password_from_keyring(self):
         """
         try:
             keyring = __import__('keyring')
-            self.password = keyring.get_password(self.repository,
-                self.username)
+            password = keyring.get_password(self.repository, self.username)
         except Exception:
-            pass
+            password = None
+        finally:
+            return password
+
+    def _prompt_for_password(self):
+        """
+        Prompt for a password on the tty. Suppress Exceptions.
+        """
+        password = None
+        try:
+            import getpass
+            while not password:
+                password = getpass.getpass()
+        except (Exception, KeyboardInterrupt):
+            password = None
+        finally:
+            return password


Test patch: