Merged revisions 68415 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r68415 | tarek.ziade | 2009-01-09 00:56:31 +0100 (Fri, 09 Jan 2009) | 1 line

  fixed #4394 make the storage of the password optional in .pypirc
........
This commit is contained in:
Tarek Ziadé 2009-01-09 00:15:45 +00:00
parent bcd5cbe01e
commit 13f7c3b6ca
9 changed files with 175 additions and 77 deletions

View File

@ -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

View File

@ -11,7 +11,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
@ -20,11 +20,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.
@ -38,4 +41,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).

View File

@ -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

View File

@ -174,19 +174,23 @@ def send_metadata(self):
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 = 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 = 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'}

View File

@ -48,6 +48,11 @@ def finalize_options(self):
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")

View File

@ -82,12 +82,12 @@ def _read_pypirc(self):
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:

View File

@ -199,6 +199,7 @@ def __init__ (self, attrs=None):
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

View File

@ -2,6 +2,7 @@
import sys
import os
import unittest
import getpass
from distutils.command.register import register
from distutils.core import Distribution
@ -9,7 +10,27 @@
from distutils.tests import support
from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase
class RawInputs(object):
PYPIRC_NOPASSWORD = """\
[distutils]
index-servers =
server1
[server1]
username:me
"""
WANTED_PYPIRC = """\
[distutils]
index-servers =
pypi
[pypi]
username:tarek
password:password
"""
class Inputs(object):
"""Fakes user inputs."""
def __init__(self, *answers):
self.answers = answers
@ -21,18 +42,33 @@ def __call__(self, prompt=''):
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 = list(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.
@ -50,30 +86,17 @@ def test_create_pypirc(self):
# we shouldn't have a .pypirc file yet
self.assert_(not os.path.exists(self.rc))
# patching raw_input and getpass.getpass
# patching input and getpass.getpass
# so register gets happy
#
# 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')
inputs = Inputs('1', 'tarek', 'y')
from distutils.command import register as register_module
register_module.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 = sorted(args[0].items())
self.calls.append(tuple(els))
return 200, 'OK'
cmd.post_to_server = pypi_server = FakeServer()
@ -101,6 +124,24 @@ def _no_way(prompt=''):
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)

View File

@ -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 @@ def test_finalize_options(self):
('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)