[Python-checkins] r68415 - in python/trunk: Doc/distutils/packageindex.rst Doc/distutils/uploading.rst Doc/whatsnew/2.7.rst Lib/distutils/command/register.py Lib/distutils/command/upload.py Lib/distutils/config.py Lib/distutils/dist.py Lib/distutils/tests/test_register.py Lib/distutils/tests/test_upload.py

tarek.ziade python-checkins at python.org
Fri Jan 9 00:56:31 CET 2009


Author: tarek.ziade
Date: Fri Jan  9 00:56:31 2009
New Revision: 68415

Log:
fixed #4394 make the storage of the password optional in .pypirc

Modified:
   python/trunk/Doc/distutils/packageindex.rst
   python/trunk/Doc/distutils/uploading.rst
   python/trunk/Doc/whatsnew/2.7.rst
   python/trunk/Lib/distutils/command/register.py
   python/trunk/Lib/distutils/command/upload.py
   python/trunk/Lib/distutils/config.py
   python/trunk/Lib/distutils/dist.py
   python/trunk/Lib/distutils/tests/test_register.py
   python/trunk/Lib/distutils/tests/test_upload.py

Modified: python/trunk/Doc/distutils/packageindex.rst
==============================================================================
--- python/trunk/Doc/distutils/packageindex.rst	(original)
+++ python/trunk/Doc/distutils/packageindex.rst	Fri Jan  9 00:56:31 2009
@@ -8,17 +8,17 @@
 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 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

Modified: python/trunk/Doc/distutils/uploading.rst
==============================================================================
--- python/trunk/Doc/distutils/uploading.rst	(original)
+++ python/trunk/Doc/distutils/uploading.rst	Fri Jan  9 00:56:31 2009
@@ -13,7 +13,7 @@
 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 @@
 
 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 @@
 *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).
-

Modified: python/trunk/Doc/whatsnew/2.7.rst
==============================================================================
--- python/trunk/Doc/whatsnew/2.7.rst	(original)
+++ python/trunk/Doc/whatsnew/2.7.rst	Fri Jan  9 00:56:31 2009
@@ -120,6 +120,12 @@
 
   (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

Modified: python/trunk/Lib/distutils/command/register.py
==============================================================================
--- python/trunk/Lib/distutils/command/register.py	(original)
+++ python/trunk/Lib/distutils/command/register.py	Fri Jan  9 00:56:31 2009
@@ -173,19 +173,23 @@
                           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'}

Modified: python/trunk/Lib/distutils/command/upload.py
==============================================================================
--- python/trunk/Lib/distutils/command/upload.py	(original)
+++ python/trunk/Lib/distutils/command/upload.py	Fri Jan  9 00:56:31 2009
@@ -50,6 +50,11 @@
             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")

Modified: python/trunk/Lib/distutils/config.py
==============================================================================
--- python/trunk/Lib/distutils/config.py	(original)
+++ python/trunk/Lib/distutils/config.py	Fri Jan  9 00:56:31 2009
@@ -82,12 +82,12 @@
                 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:

Modified: python/trunk/Lib/distutils/dist.py
==============================================================================
--- python/trunk/Lib/distutils/dist.py	(original)
+++ python/trunk/Lib/distutils/dist.py	Fri Jan  9 00:56:31 2009
@@ -206,6 +206,7 @@
         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

Modified: python/trunk/Lib/distutils/tests/test_register.py
==============================================================================
--- python/trunk/Lib/distutils/tests/test_register.py	(original)
+++ python/trunk/Lib/distutils/tests/test_register.py	Fri Jan  9 00:56:31 2009
@@ -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.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 @@
         finally:
             self.index += 1
 
-WANTED_PYPIRC = """\
-[distutils]
-index-servers =
-    pypi
-
-[pypi]
-username:tarek
-password:xxx
-"""
+class FakeServer(object):
+    """Fakes a PyPI server"""
+    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'
 
 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 @@
         # 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 @@
         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)
 

Modified: python/trunk/Lib/distutils/tests/test_upload.py
==============================================================================
--- python/trunk/Lib/distutils/tests/test_upload.py	(original)
+++ python/trunk/Lib/distutils/tests/test_upload.py	Fri Jan  9 00:56:31 2009
@@ -9,6 +9,17 @@
 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 @@
                              ('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)


More information about the Python-checkins mailing list