summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/distutils/packageindex.rst76
-rw-r--r--Doc/distutils/uploading.rst10
-rw-r--r--Doc/whatsnew/2.7.rst6
-rw-r--r--Lib/distutils/command/register.py30
-rw-r--r--Lib/distutils/command/upload.py5
-rw-r--r--Lib/distutils/config.py4
-rw-r--r--Lib/distutils/dist.py1
-rw-r--r--Lib/distutils/tests/test_register.py86
-rw-r--r--Lib/distutils/tests/test_upload.py29
9 files changed, 172 insertions, 75 deletions
diff --git a/Doc/distutils/packageindex.rst b/Doc/distutils/packageindex.rst
index 3715c82..c4cbf88 100644
--- a/Doc/distutils/packageindex.rst
+++ b/Doc/distutils/packageindex.rst
@@ -8,17 +8,17 @@ The Python Package Index (PyPI) holds meta-data describing distributions
packaged with distutils. The distutils command :command:`register` is used to
submit your distribution's meta-data to the index. It is invoked as follows::
- python setup.py register
+ python setup.py register
Distutils will respond with the following prompt::
- running register
- We need to know who you are, so please choose either:
- 1. use your existing login,
- 2. register as a new user,
- 3. have the server generate a new password for you (and email it to you), or
- 4. quit
- Your selection [default 1]:
+ running register
+ We need to know who you are, so please choose either:
+ 1. use your existing login,
+ 2. register as a new user,
+ 3. have the server generate a new password for you (and email it to you), or
+ 4. quit
+ Your selection [default 1]:
Note: if your username and password are saved locally, you will not see this
menu.
@@ -55,40 +55,50 @@ The .pypirc file
The format of the :file:`.pypirc` file is as follows::
- [distutils]
- index-servers =
- pypi
+ [distutils]
+ index-servers =
+ pypi
- [pypi]
- repository: <repository-url>
- username: <username>
- password: <password>
+ [pypi]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
-*repository* can be omitted and defaults to ``http://www.python.org/pypi``.
+The *distutils* section defines a *index-servers* variable that lists the
+name of all sections describing a repository.
-If you want to define another server a new section can be created::
+Each section describing a repository defines three variables:
- [distutils]
- index-servers =
- pypi
- other
+- *repository*, that defines the url of the PyPI server. Defaults to
+ ``http://www.python.org/pypi``.
+- *username*, which is the registered username on the PyPI server.
+- *password*, that will be used to authenticate. If omitted the user
+ will be prompt to type it when needed.
- [pypi]
- repository: <repository-url>
- username: <username>
- password: <password>
+If you want to define another server a new section can be created and
+listed in the *index-servers* variable::
- [other]
- repository: http://example.com/pypi
- username: <username>
- password: <password>
+ [distutils]
+ index-servers =
+ pypi
+ other
-The command can then be called with the -r option::
+ [pypi]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
- python setup.py register -r http://example.com/pypi
+ [other]
+ repository: http://example.com/pypi
+ username: <username>
+ password: <password>
-Or even with the section name::
+:command:`register` can then be called with the -r option to point the
+repository to work with::
- python setup.py register -r other
+ python setup.py register -r http://example.com/pypi
+The name of the section that describes the repository may also be used
+for conveniency::
+ python setup.py register -r other
diff --git a/Doc/distutils/uploading.rst b/Doc/distutils/uploading.rst
index 66f712b..71b3ca1 100644
--- a/Doc/distutils/uploading.rst
+++ b/Doc/distutils/uploading.rst
@@ -13,7 +13,7 @@ package data if the author of the package wishes to. The distutils command
The command is invoked immediately after building one or more distribution
files. For example, the command ::
- python setup.py sdist bdist_wininst upload
+ python setup.py sdist bdist_wininst upload
will cause the source distribution and the Windows installer to be uploaded to
PyPI. Note that these will be uploaded even if they are built using an earlier
@@ -22,11 +22,14 @@ line for the invocation including the :command:`upload` command are uploaded.
The :command:`upload` command uses the username, password, and repository URL
from the :file:`$HOME/.pypirc` file (see section :ref:`pypirc` for more on this
-file).
+file). If a :command:`register` command was previously called in the same command,
+and if the password was entered in the prompt, :command:`upload` will reuse the
+entered password. This is useful if you do not want to store a clear text
+password in the :file:`$HOME/.pypirc` file.
You can specify another PyPI server with the :option:`--repository=*url*` option::
- python setup.py sdist bdist_wininst upload -r http://example.com/pypi
+ python setup.py sdist bdist_wininst upload -r http://example.com/pypi
See section :ref:`pypirc` for more on defining several servers.
@@ -40,4 +43,3 @@ Other :command:`upload` options include :option:`--repository=<url>` or
*section* the name of the section in :file:`$HOME/.pypirc`, and
:option:`--show-response` (which displays the full response text from the PyPI
server for help in debugging upload problems).
-
diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst
index da17620..24d3a86 100644
--- a/Doc/whatsnew/2.7.rst
+++ b/Doc/whatsnew/2.7.rst
@@ -120,6 +120,12 @@ changes, or look through the Subversion logs for all the details.
(Contributed by Gregory P. Smith.)
+* It is not mandatory anymore to store clear text passwords in the
+ :file:`.pypirc` file when registering and uploading packages to PyPI. As
+ long as the username is present in that file, the :mod:`distutils` package
+ will prompt for the password if not present.
+ (Added by tarek, with the initial contribution of Nathan Van Gheem;
+ :issue:`4394`.)
.. ======================================================================
.. whole new modules get described in subsections here
diff --git a/Lib/distutils/command/register.py b/Lib/distutils/command/register.py
index bf7be96..40661d8 100644
--- a/Lib/distutils/command/register.py
+++ b/Lib/distutils/command/register.py
@@ -173,19 +173,23 @@ Your selection [default 1]: ''', log.INFO)
log.INFO)
# possibly save the login
- if not self.has_config and code == 200:
- self.announce(('I can store your PyPI login so future '
- 'submissions will be faster.'), log.INFO)
- self.announce('(the login will be stored in %s)' % \
- self._get_rc_file(), log.INFO)
-
- choice = 'X'
- while choice.lower() not in 'yn':
- choice = raw_input('Save your login (y/N)?')
- if not choice:
- choice = 'n'
- if choice.lower() == 'y':
- self._store_pypirc(username, password)
+ if code == 200:
+ if self.has_config:
+ # sharing the password in the distribution instance
+ # so the upload command can reuse it
+ self.distribution.password = password
+ else:
+ self.announce(('I can store your PyPI login so future '
+ 'submissions will be faster.'), log.INFO)
+ self.announce('(the login will be stored in %s)' % \
+ self._get_rc_file(), log.INFO)
+ choice = 'X'
+ while choice.lower() not in 'yn':
+ choice = raw_input('Save your login (y/N)?')
+ if not choice:
+ choice = 'n'
+ if choice.lower() == 'y':
+ self._store_pypirc(username, password)
elif choice == '2':
data = {':action': 'user'}
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
index 8805d41..e30347e 100644
--- a/Lib/distutils/command/upload.py
+++ b/Lib/distutils/command/upload.py
@@ -50,6 +50,11 @@ class upload(PyPIRCCommand):
self.repository = config['repository']
self.realm = config['realm']
+ # getting the password from the distribution
+ # if previously set by the register command
+ if not self.password and self.distribution.password:
+ self.password = self.distribution.password
+
def run(self):
if not self.distribution.dist_files:
raise DistutilsOptionError("No dist file created in earlier command")
diff --git a/Lib/distutils/config.py b/Lib/distutils/config.py
index 4186c9b..9166199 100644
--- a/Lib/distutils/config.py
+++ b/Lib/distutils/config.py
@@ -82,12 +82,12 @@ class PyPIRCCommand(Command):
for server in _servers:
current = {'server': server}
current['username'] = config.get(server, 'username')
- current['password'] = config.get(server, 'password')
# optional params
for key, default in (('repository',
self.DEFAULT_REPOSITORY),
- ('realm', self.DEFAULT_REALM)):
+ ('realm', self.DEFAULT_REALM),
+ ('password', None)):
if config.has_option(server, key):
current[key] = config.get(server, key)
else:
diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py
index c15ca97..18cc910 100644
--- a/Lib/distutils/dist.py
+++ b/Lib/distutils/dist.py
@@ -206,6 +206,7 @@ Common commands: (see '--help-commands' for more)
self.extra_path = None
self.scripts = None
self.data_files = None
+ self.password = ''
# And now initialize bookkeeping stuff that can't be supplied by
# the caller at all. 'command_obj' maps command names to
diff --git a/Lib/distutils/tests/test_register.py b/Lib/distutils/tests/test_register.py
index 3a3a3b7..b3543f5 100644
--- a/Lib/distutils/tests/test_register.py
+++ b/Lib/distutils/tests/test_register.py
@@ -2,6 +2,7 @@
import sys
import os
import unittest
+import getpass
from distutils.command.register import register
from distutils.core import Distribution
@@ -9,6 +10,26 @@ from distutils.core import Distribution
from distutils.tests import support
from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+ server1
+
+[server1]
+username:me
+"""
+
+WANTED_PYPIRC = """\
+[distutils]
+index-servers =
+ pypi
+
+[pypi]
+username:tarek
+password:password
+"""
+
class RawInputs(object):
"""Fakes user inputs."""
def __init__(self, *answers):
@@ -21,18 +42,33 @@ class RawInputs(object):
finally:
self.index += 1
-WANTED_PYPIRC = """\
-[distutils]
-index-servers =
- pypi
+class FakeServer(object):
+ """Fakes a PyPI server"""
+ def __init__(self):
+ self.calls = []
-[pypi]
-username:tarek
-password:xxx
-"""
+ def __call__(self, *args):
+ # we want to compare them, so let's store
+ # something comparable
+ els = args[0].items()
+ els.sort()
+ self.calls.append(tuple(els))
+ return 200, 'OK'
class registerTestCase(PyPIRCCommandTestCase):
+ def setUp(self):
+ PyPIRCCommandTestCase.setUp(self)
+ # patching the password prompt
+ self._old_getpass = getpass.getpass
+ def _getpass(prompt):
+ return 'password'
+ getpass.getpass = _getpass
+
+ def tearDown(self):
+ getpass.getpass = self._old_getpass
+ PyPIRCCommandTestCase.tearDown(self)
+
def test_create_pypirc(self):
# this test makes sure a .pypirc file
# is created when requested.
@@ -56,25 +92,11 @@ class registerTestCase(PyPIRCCommandTestCase):
# Here's what we are faking :
# use your existing login (choice 1.)
# Username : 'tarek'
- # Password : 'xxx'
+ # Password : 'password'
# Save your login (y/N)? : 'y'
inputs = RawInputs('1', 'tarek', 'y')
from distutils.command import register as register_module
register_module.raw_input = inputs.__call__
- def _getpass(prompt):
- return 'xxx'
- register_module.getpass.getpass = _getpass
- class FakeServer(object):
- def __init__(self):
- self.calls = []
-
- def __call__(self, *args):
- # we want to compare them, so let's store
- # something comparable
- els = args[0].items()
- els.sort()
- self.calls.append(tuple(els))
- return 200, 'OK'
cmd.post_to_server = pypi_server = FakeServer()
@@ -102,6 +124,24 @@ class registerTestCase(PyPIRCCommandTestCase):
self.assert_(len(pypi_server.calls), 2)
self.assert_(pypi_server.calls[0], pypi_server.calls[1])
+ def test_password_not_in_file(self):
+
+ f = open(self.rc, 'w')
+ f.write(PYPIRC_NOPASSWORD)
+ f.close()
+
+ dist = Distribution()
+ cmd = register(dist)
+ cmd.post_to_server = FakeServer()
+
+ cmd._set_config()
+ cmd.finalize_options()
+ cmd.send_metadata()
+
+ # dist.password should be set
+ # therefore used afterwards by other commands
+ self.assertEquals(dist.password, 'password')
+
def test_suite():
return unittest.makeSuite(registerTestCase)
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
index b05ab1f..3f8ca6d 100644
--- a/Lib/distutils/tests/test_upload.py
+++ b/Lib/distutils/tests/test_upload.py
@@ -9,6 +9,17 @@ from distutils.core import Distribution
from distutils.tests import support
from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+ server1
+
+[server1]
+username:me
+"""
+
+
class uploadTestCase(PyPIRCCommandTestCase):
def test_finalize_options(self):
@@ -26,6 +37,24 @@ class uploadTestCase(PyPIRCCommandTestCase):
('repository', 'http://pypi.python.org/pypi')):
self.assertEquals(getattr(cmd, attr), waited)
+ def test_saved_password(self):
+ # file with no password
+ f = open(self.rc, 'w')
+ f.write(PYPIRC_NOPASSWORD)
+ f.close()
+
+ # make sure it passes
+ dist = Distribution()
+ cmd = upload(dist)
+ cmd.finalize_options()
+ self.assertEquals(cmd.password, None)
+
+ # make sure we get it as well, if another command
+ # initialized it at the dist level
+ dist.password = 'xxx'
+ cmd = upload(dist)
+ cmd.finalize_options()
+ self.assertEquals(cmd.password, 'xxx')
def test_suite():
return unittest.makeSuite(uploadTestCase)