summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/distutils/packageindex.rst31
-rw-r--r--Doc/distutils/uploading.rst12
-rw-r--r--Lib/distutils/command/register.py97
-rw-r--r--Lib/distutils/command/upload.py41
-rw-r--r--Lib/distutils/core.py1
-rw-r--r--Lib/distutils/dist.py7
-rw-r--r--Lib/distutils/tests/test_config.py105
-rw-r--r--Lib/distutils/tests/test_dist.py46
-rw-r--r--Lib/distutils/tests/test_upload.py34
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS5
11 files changed, 296 insertions, 84 deletions
diff --git a/Doc/distutils/packageindex.rst b/Doc/distutils/packageindex.rst
index 8242012..ef81d64 100644
--- a/Doc/distutils/packageindex.rst
+++ b/Doc/distutils/packageindex.rst
@@ -55,11 +55,40 @@ The .pypirc file
The format of the :file:`.pypirc` file is as follows::
- [server-login]
+ [distutils]
+ index-servers =
+ pypi
+
+ [pypi]
repository: <repository-url>
username: <username>
password: <password>
*repository* can be omitted and defaults to ``http://www.python.org/pypi``.
+If you want to define another server a new section can be created::
+
+ [distutils]
+ index-servers =
+ pypi
+ other
+
+ [pypi]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
+
+ [other]
+ repository: http://example.com/pypi
+ username: <username>
+ password: <password>
+
+The command can then be called with the -r option::
+
+ python setup.py register -r http://example.com/pypi
+
+Or even with the section name::
+
+ python setup.py register -r other
+
diff --git a/Doc/distutils/uploading.rst b/Doc/distutils/uploading.rst
index 0b82184..52d6d5b 100644
--- a/Doc/distutils/uploading.rst
+++ b/Doc/distutils/uploading.rst
@@ -24,14 +24,20 @@ 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).
+You can specify another PyPI server with the :option:`--repository=*url*` option::
+
+ python setup.py sdist bdist_wininst upload -r http://example.com/pypi
+
+See section :ref:`pypirc` for more on defining several servers.
+
You can use the :option:`--sign` option to tell :command:`upload` to sign each
uploaded file using GPG (GNU Privacy Guard). The :program:`gpg` program must
be available for execution on the system :envvar:`PATH`. You can also specify
which key to use for signing using the :option:`--identity=*name*` option.
-Other :command:`upload` options include :option:`--repository=*url*` (which
-lets you override the repository setting from :file:`$HOME/.pypirc`), and
+Other :command:`upload` options include :option:`--repository=*url*`
+or :option:`--repository=*section*` where `url` is the url of the server
+and `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/Lib/distutils/command/register.py b/Lib/distutils/command/register.py
index 5c588ef..a9ab361 100644
--- a/Lib/distutils/command/register.py
+++ b/Lib/distutils/command/register.py
@@ -8,37 +8,29 @@ Implements the Distutils 'register' command (register with the repository).
__revision__ = "$Id$"
import os, string, urllib2, getpass, urlparse
-import StringIO, ConfigParser
+import StringIO
-from distutils.core import Command
+from distutils.core import PyPIRCCommand
from distutils.errors import *
+from distutils import log
-class register(Command):
+class register(PyPIRCCommand):
description = ("register the distribution with the Python package index")
-
- DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
-
- user_options = [
- ('repository=', 'r',
- "url of repository [default: %s]"%DEFAULT_REPOSITORY),
+ user_options = PyPIRCCommand.user_options + [
('list-classifiers', None,
'list the valid Trove classifiers'),
- ('show-response', None,
- 'display full response text from server'),
]
- boolean_options = ['verify', 'show-response', 'list-classifiers']
+ boolean_options = PyPIRCCommand.boolean_options + [
+ 'verify', 'list-classifiers']
def initialize_options(self):
- self.repository = None
- self.show_response = 0
+ PyPIRCCommand.initialize_options(self)
self.list_classifiers = 0
- def finalize_options(self):
- if self.repository is None:
- self.repository = self.DEFAULT_REPOSITORY
-
def run(self):
+ self.finalize_options()
+ self._set_config()
self.check_metadata()
if self.dry_run:
self.verify_metadata()
@@ -77,6 +69,23 @@ class register(Command):
"or (maintainer and maintainer_email) " +
"must be supplied")
+ def _set_config(self):
+ ''' Reads the configuration file and set attributes.
+ '''
+ config = self._read_pypirc()
+ if config != {}:
+ self.username = config['username']
+ self.password = config['password']
+ self.repository = config['repository']
+ self.realm = config['realm']
+ self.has_config = True
+ else:
+ if self.repository not in ('pypi', self.DEFAULT_REPOSITORY):
+ raise ValueError('%s not found in .pypirc' % self.repository)
+ if self.repository == 'pypi':
+ self.repository = self.DEFAULT_REPOSITORY
+ self.has_config = False
+
def classifiers(self):
''' Fetch the list of classifiers from the server.
'''
@@ -90,6 +99,7 @@ class register(Command):
(code, result) = self.post_to_server(self.build_post_data('verify'))
print 'Server response (%s): %s'%(code, result)
+
def send_metadata(self):
''' Send the metadata to the package index server.
@@ -99,10 +109,14 @@ class register(Command):
First we try to read the username/password from $HOME/.pypirc,
which is a ConfigParser-formatted file with a section
- [server-login] containing username and password entries (both
+ [distutils] containing username and password entries (both
in clear text). Eg:
- [server-login]
+ [distutils]
+ index-servers =
+ pypi
+
+ [pypi]
username: fred
password: sekrit
@@ -114,21 +128,15 @@ class register(Command):
3. set the password to a random string and email the user.
'''
- choice = 'x'
- username = password = ''
-
# see if we can short-cut and get the username/password from the
# config
- config = None
- if 'HOME' in os.environ:
- rc = os.path.join(os.environ['HOME'], '.pypirc')
- if os.path.exists(rc):
- print 'Using PyPI login from %s'%rc
- config = ConfigParser.ConfigParser()
- config.read(rc)
- username = config.get('server-login', 'username')
- password = config.get('server-login', 'password')
- choice = '1'
+ if self.has_config:
+ choice = '1'
+ username = self.username
+ password = self.password
+ else:
+ choice = 'x'
+ username = password = ''
# get the user's login info
choices = '1 2 3 4'.split()
@@ -155,32 +163,24 @@ Your selection [default 1]: ''',
# set up the authentication
auth = urllib2.HTTPPasswordMgr()
host = urlparse.urlparse(self.repository)[1]
- auth.add_password('pypi', host, username, password)
-
+ auth.add_password(self.realm, host, username, password)
# send the info to the server and report the result
code, result = self.post_to_server(self.build_post_data('submit'),
auth)
- print 'Server response (%s): %s'%(code, result)
+ print 'Server response (%s): %s' % (code, result)
# possibly save the login
- if 'HOME' in os.environ and config is None and code == 200:
- rc = os.path.join(os.environ['HOME'], '.pypirc')
+ if not self.has_config and code == 200:
print 'I can store your PyPI login so future submissions will be faster.'
- print '(the login will be stored in %s)'%rc
+ print '(the login will be stored in %s)' % self._get_rc_file()
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':
- f = open(rc, 'w')
- f.write('[server-login]\nusername:%s\npassword:%s\n'%(
- username, password))
- f.close()
- try:
- os.chmod(rc, 0600)
- except:
- pass
+ self._store_pypirc(username, password)
+
elif choice == '2':
data = {':action': 'user'}
data['name'] = data['password'] = data['email'] = ''
@@ -243,7 +243,8 @@ Your selection [default 1]: ''',
def post_to_server(self, data, auth=None):
''' Post a query to the server, and return a string response.
'''
-
+ self.announce('Registering %s to %s' % (data['name'],
+ self.repository), log.INFO)
# Build up the MIME payload for the urllib2 POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = '\n--' + boundary
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
index 301a159..daf6811 100644
--- a/Lib/distutils/command/upload.py
+++ b/Lib/distutils/command/upload.py
@@ -3,7 +3,7 @@
Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
from distutils.errors import *
-from distutils.core import Command
+from distutils.core import PyPIRCCommand
from distutils.spawn import spawn
from distutils import log
from hashlib import md5
@@ -16,53 +16,38 @@ import base64
import urlparse
import cStringIO as StringIO
-class upload(Command):
+class upload(PyPIRCCommand):
description = "upload binary package to PyPI"
- DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
-
- user_options = [
- ('repository=', 'r',
- "url of repository [default: %s]" % DEFAULT_REPOSITORY),
- ('show-response', None,
- 'display full response text from server'),
+ user_options = PyPIRCCommand.user_options + [
('sign', 's',
'sign files to upload using gpg'),
('identity=', 'i', 'GPG identity used to sign files'),
]
- boolean_options = ['show-response', 'sign']
+
+ boolean_options = PyPIRCCommand.boolean_options + ['sign']
def initialize_options(self):
+ PyPIRCCommand.initialize_options(self)
self.username = ''
self.password = ''
- self.repository = ''
self.show_response = 0
self.sign = False
self.identity = None
def finalize_options(self):
+ PyPIRCCommand.finalize_options(self)
if self.identity and not self.sign:
raise DistutilsOptionError(
"Must use --sign for --identity to have meaning"
)
- if 'HOME' in os.environ:
- rc = os.path.join(os.environ['HOME'], '.pypirc')
- if os.path.exists(rc):
- self.announce('Using PyPI login from %s' % rc)
- config = ConfigParser.ConfigParser({
- 'username':'',
- 'password':'',
- 'repository':''})
- config.read(rc)
- if not self.repository:
- self.repository = config.get('server-login', 'repository')
- if not self.username:
- self.username = config.get('server-login', 'username')
- if not self.password:
- self.password = config.get('server-login', 'password')
- if not self.repository:
- self.repository = self.DEFAULT_REPOSITORY
+ config = self._read_pypirc()
+ if config != {}:
+ self.username = config['username']
+ self.password = config['password']
+ self.repository = config['repository']
+ self.realm = config['realm']
def run(self):
if not self.distribution.dist_files:
diff --git a/Lib/distutils/core.py b/Lib/distutils/core.py
index 32b9026..de9ce7d 100644
--- a/Lib/distutils/core.py
+++ b/Lib/distutils/core.py
@@ -20,6 +20,7 @@ from distutils.util import grok_environment_error
# Mainly import these so setup scripts can "from distutils.core import" them.
from distutils.dist import Distribution
from distutils.cmd import Command
+from distutils.config import PyPIRCCommand
from distutils.extension import Extension
# This is a barebones help message generated displayed when the user
diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py
index d098cb9..0b13c1e 100644
--- a/Lib/distutils/dist.py
+++ b/Lib/distutils/dist.py
@@ -343,10 +343,9 @@ Common commands: (see '--help-commands' for more)
user_filename = "pydistutils.cfg"
# And look for the user config file
- if 'HOME' in os.environ:
- user_file = os.path.join(os.environ.get('HOME'), user_filename)
- if os.path.isfile(user_file):
- files.append(user_file)
+ user_file = os.path.join(os.path.expanduser('~'), user_filename)
+ if os.path.isfile(user_file):
+ files.append(user_file)
# All platforms support local setup.cfg
local_file = "setup.cfg"
diff --git a/Lib/distutils/tests/test_config.py b/Lib/distutils/tests/test_config.py
new file mode 100644
index 0000000..bfce3e3
--- /dev/null
+++ b/Lib/distutils/tests/test_config.py
@@ -0,0 +1,105 @@
+"""Tests for distutils.pypirc.pypirc."""
+import sys
+import os
+import unittest
+
+from distutils.core import PyPIRCCommand
+from distutils.core import Distribution
+
+from distutils.tests import support
+
+PYPIRC = """\
+[distutils]
+
+index-servers =
+ server1
+ server2
+
+[server1]
+username:me
+password:secret
+
+[server2]
+username:meagain
+password: secret
+realm:acme
+repository:http://another.pypi/
+"""
+
+PYPIRC_OLD = """\
+[server-login]
+username:tarek
+password:secret
+"""
+
+class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase):
+
+ def setUp(self):
+ """Patches the environment."""
+ if os.environ.has_key('HOME'):
+ self._old_home = os.environ['HOME']
+ else:
+ self._old_home = None
+ curdir = os.path.dirname(__file__)
+ os.environ['HOME'] = curdir
+ self.rc = os.path.join(curdir, '.pypirc')
+ self.dist = Distribution()
+
+ class command(PyPIRCCommand):
+ def __init__(self, dist):
+ PyPIRCCommand.__init__(self, dist)
+ def initialize_options(self):
+ pass
+ finalize_options = initialize_options
+
+ self._cmd = command
+
+ def tearDown(self):
+ """Removes the patch."""
+ if self._old_home is None:
+ del os.environ['HOME']
+ else:
+ os.environ['HOME'] = self._old_home
+ if os.path.exists(self.rc):
+ os.remove(self.rc)
+
+ def test_server_registration(self):
+ # This test makes sure PyPIRCCommand knows how to:
+ # 1. handle several sections in .pypirc
+ # 2. handle the old format
+
+ # new format
+ f = open(self.rc, 'w')
+ try:
+ f.write(PYPIRC)
+ finally:
+ f.close()
+
+ cmd = self._cmd(self.dist)
+ config = cmd._read_pypirc()
+
+ config = config.items()
+ config.sort()
+ waited = [('password', 'secret'), ('realm', 'pypi'),
+ ('repository', 'http://pypi.python.org/pypi'),
+ ('server', 'server1'), ('username', 'me')]
+ self.assertEquals(config, waited)
+
+ # old format
+ f = open(self.rc, 'w')
+ f.write(PYPIRC_OLD)
+ f.close()
+
+ config = cmd._read_pypirc()
+ config = config.items()
+ config.sort()
+ waited = [('password', 'secret'), ('realm', 'pypi'),
+ ('repository', 'http://pypi.python.org/pypi'),
+ ('server', 'server-login'), ('username', 'tarek')]
+ self.assertEquals(config, waited)
+
+def test_suite():
+ return unittest.makeSuite(PyPIRCCommandTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py
index 5ae7933..804c8a4 100644
--- a/Lib/distutils/tests/test_dist.py
+++ b/Lib/distutils/tests/test_dist.py
@@ -55,6 +55,7 @@ class DistributionTestCase(unittest.TestCase):
self.assertEqual(d.get_command_packages(), ["distutils.command"])
def test_command_packages_cmdline(self):
+ from distutils.tests.test_dist import test_dist
sys.argv.extend(["--command-packages",
"foo.bar,distutils.tests",
"test_dist",
@@ -179,9 +180,54 @@ class MetadataTestCase(unittest.TestCase):
dist.metadata.write_pkg_file(sio)
return sio.getvalue()
+ def test_custom_pydistutils(self):
+ # fixes #2166
+ # make sure pydistutils.cfg is found
+ old = {}
+ for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'):
+ value = os.environ.get(env)
+ old[env] = value
+ if value is not None:
+ del os.environ[env]
+
+ if os.name == 'posix':
+ user_filename = ".pydistutils.cfg"
+ else:
+ user_filename = "pydistutils.cfg"
+
+ curdir = os.path.dirname(__file__)
+ user_filename = os.path.join(curdir, user_filename)
+ f = open(user_filename, 'w')
+ f.write('.')
+ f.close()
+
+ try:
+ dist = distutils.dist.Distribution()
+
+ # linux-style
+ if sys.platform in ('linux', 'darwin'):
+ os.environ['HOME'] = curdir
+ files = dist.find_config_files()
+ self.assert_(user_filename in files)
+
+ # win32-style
+ if sys.platform == 'win32':
+ # home drive should be found
+ os.environ['HOMEPATH'] = curdir
+ files = dist.find_config_files()
+ self.assert_(user_filename in files)
+ finally:
+ for key, value in old.items():
+ if value is None:
+ continue
+ os.environ[key] = value
+ os.remove(user_filename)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(DistributionTestCase))
suite.addTest(unittest.makeSuite(MetadataTestCase))
return suite
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
new file mode 100644
index 0000000..b05ab1f
--- /dev/null
+++ b/Lib/distutils/tests/test_upload.py
@@ -0,0 +1,34 @@
+"""Tests for distutils.command.upload."""
+import sys
+import os
+import unittest
+
+from distutils.command.upload import upload
+from distutils.core import Distribution
+
+from distutils.tests import support
+from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase
+
+class uploadTestCase(PyPIRCCommandTestCase):
+
+ def test_finalize_options(self):
+
+ # new format
+ f = open(self.rc, 'w')
+ f.write(PYPIRC)
+ f.close()
+
+ dist = Distribution()
+ cmd = upload(dist)
+ cmd.finalize_options()
+ for attr, waited in (('username', 'me'), ('password', 'secret'),
+ ('realm', 'pypi'),
+ ('repository', 'http://pypi.python.org/pypi')):
+ self.assertEquals(getattr(cmd, attr), waited)
+
+
+def test_suite():
+ return unittest.makeSuite(uploadTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Misc/ACKS b/Misc/ACKS
index 00fcff1..d60e5cf 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -758,3 +758,4 @@ Siebren van der Zee
Uwe Zessin
Amaury Forgeot d'Arc
Peter Åstrand
+Tarek ZiadŽ
diff --git a/Misc/NEWS b/Misc/NEWS
index 9007462..3344fc9 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -381,6 +381,11 @@ Library
platforms when x is too large to fit into an IEEE 754 float; previously
it only raised OverflowError on non IEEE 754 platforms.
+- Issues #2166, #1741 and #1531505: now distutils deals with HOME
+ correctly under win32
+
+- #1858: distutils: added multiple server support in .pypirc
+
- Issue #1106316: pdb.post_mortem()'s parameter, "traceback", is now
optional: it defaults to the traceback of the exception that is currently
being handled (is mandatory to be in the middle of an exception, otherwise