Kaydet (Commit) 1a240fb9 authored tarafından Tarek Ziadé's avatar Tarek Ziadé

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

üst 5b913e31
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment