fixed #4394 make the storage of the password optional in .pypirc
This commit is contained in:
parent
5b913e31a1
commit
1a240fb9f0
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue