diff options
-rwxr-xr-x | EasyInstall.txt | 14 | ||||
-rwxr-xr-x | ez_setup.py | 6 | ||||
-rwxr-xr-x | setup.py | 6 | ||||
-rw-r--r-- | setuptools/__init__.py | 2 | ||||
-rwxr-xr-x | setuptools/command/sdist.py | 41 | ||||
-rwxr-xr-x | setuptools/command/upload.py | 171 | ||||
-rwxr-xr-x | setuptools/package_index.py | 24 |
7 files changed, 239 insertions, 25 deletions
diff --git a/EasyInstall.txt b/EasyInstall.txt index 8acddc1b..31a2734a 100755 --- a/EasyInstall.txt +++ b/EasyInstall.txt @@ -23,13 +23,10 @@ Installing "Easy Install" ------------------------- Windows users can just download and run the `setuptools binary installer for -Windows <http://peak.telecommunity.com/dist/setuptools-0.5a6.win32.exe>`_. +Windows <http://peak.telecommunity.com/dist/setuptools-0.5a7.win32.exe>`_. All others should just download `ez_setup.py <http://peak.telecommunity.com/dist/ez_setup.py>`_, and run it; this will -download and install the `Python 2.3 egg`_ or `Python 2.4 egg`_ for you. - -.. _Python 2.3 egg: http://peak.telecommunity.com/dist/setuptools-0.5a6-py2.3.egg -.. _Python 2.4 egg: http://peak.telecommunity.com/dist/setuptools-0.5a6-py2.4.egg +download and install the appropriate egg for you. You may receive a message telling you about an obsolete version of setuptools being present; if so, you must be sure to delete it entirely, along @@ -66,7 +63,7 @@ version, and automatically downloading, building, and installing it:: **Example 2**. Install or upgrade a package by name and version by finding links on a given "download page":: - easy_install -f http://peak.telecommunity.com/dist "setuptools>=0.5a6" + easy_install -f http://peak.telecommunity.com/dist "setuptools>=0.5a7" **Example 3**. Download a source distribution from a specified URL, automatically building and installing it:: @@ -491,6 +488,11 @@ Known Issues in Exemaker. So, don't use Exemaker to wrap ``easy_install.py``, or at any rate don't expect it to work with all packages. +0.5a7 + * Added "upload" support for egg and source distributions, including a bug + fix for "upload" and a temporary workaround for lack of .egg support in + PyPI. + 0.5a6 * Beefed up the "sdist" command so that if you don't have a MANIFEST.in, it will include all files under revision control (CVS or Subversion) in the diff --git a/ez_setup.py b/ez_setup.py index a0367e22..674138d3 100755 --- a/ez_setup.py +++ b/ez_setup.py @@ -14,8 +14,8 @@ the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ -DEFAULT_VERSION = "0.5a6" -DEFAULT_URL = "http://peak.telecommunity.com/dist/" +DEFAULT_VERSION = "0.5a7" +DEFAULT_URL = "http://www.python.org/packages/source/s/setuptools/" import sys, os @@ -91,7 +91,7 @@ def download_setuptools( """ import urllib2, shutil egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) - url = download_base + egg_name + url = download_base + egg_name + '.zip' # XXX saveto = os.path.join(to_dir, egg_name) src = dst = None @@ -1,7 +1,7 @@ #!/usr/bin/env python """Distutils setup file, used to install or test 'setuptools'""" -VERSION = "0.5a6" +VERSION = "0.5a7" from setuptools import setup, find_packages, Require setup( @@ -26,8 +26,7 @@ setup( "close. See the home page and download page for details and docs.", keywords = "CPAN PyPI distutils eggs package management", - url = "http://peak.telecommunity.com/DevCenter/PythonEggs", - download_url = "http://peak.telecommunity.com/DevCenter/EasyInstall", + url = "http://peak.telecommunity.com/DevCenter/EasyInstall", test_suite = 'setuptools.tests.test_suite', requires = [ @@ -39,6 +38,7 @@ setup( + packages = find_packages(), py_modules = ['pkg_resources'], scripts = ['easy_install.py'], diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 5247f25b..89c4e7a9 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -8,7 +8,7 @@ from distutils.core import Command as _Command from distutils.util import convert_path import os.path -__version__ = '0.5a6' +__version__ = '0.5a7' __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index b39df36f..3891526d 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -63,8 +63,34 @@ def walk_revctrl(dirname='', memo=None): for item in walk_revctrl(path, memo): yield item + + + + + + + + + + + + + + + + + class sdist(_sdist): """Smart sdist that finds anything supported by revision control""" + + def run(self): + _sdist.run(self) + dist_files = getattr(self.distribution,'dist_files',[]) + for file in self.archive_files: + data = ('sdist', '', file) + if data not in dist_files: + dist_files.append(data) + def finalize_options(self): _sdist.finalize_options(self) if not os.path.isfile(self.template): @@ -80,3 +106,18 @@ class sdist(_sdist): + + + + + + + + + + + + + + + diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py new file mode 100755 index 00000000..bad01577 --- /dev/null +++ b/setuptools/command/upload.py @@ -0,0 +1,171 @@ +"""distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to PyPI).""" + +from distutils.errors import * +from distutils.core import Command +from distutils.spawn import spawn +from distutils import log +from md5 import md5 +import os +import socket +import platform +import ConfigParser +import httplib +import base64 +import urlparse +import cStringIO as StringIO + +class upload(Command): + + description = "upload binary package to PyPI" + + DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), + ('sign', 's', + 'sign files to upload using gpg'), + ] + boolean_options = ['show-response', 'sign'] + + def initialize_options(self): + self.username = '' + self.password = '' + self.repository = '' + self.show_response = 0 + self.sign = False + + def finalize_options(self): + if os.environ.has_key('HOME'): + 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 + + def run(self): + if not self.distribution.dist_files: + raise DistutilsOptionError("No dist file created in earlier command") + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): + # Sign if requested + if self.sign: + spawn(("gpg", "--detach-sign", "-a", filename), + dry_run=self.dry_run) + + # Fill in the data + content = open(filename,'rb').read() + basename = os.path.basename(filename) + if basename.endswith('.egg'): + basename += '.zip' + if command=='bdist_egg': + command='sdist' + data = { + ':action':'file_upload', + 'protcol_version':'1', + 'name':self.distribution.get_name(), + 'version':self.distribution.get_version(), + 'content':(basename,content), + 'filetype':command, + 'pyversion':pyversion, + 'md5_digest':md5(content).hexdigest(), + } + comment = '' + if command == 'bdist_rpm': + dist, version, id = platform.dist() + if dist: + comment = 'built for %s %s' % (dist, version) + elif command == 'bdist_dumb': + comment = 'built for %s' % platform.platform(terse=1) + data['comment'] = comment + + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc").read()) + + # set up the authentication + auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = StringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) != type([]): + value = [value] + for value in value: + if type(value) is tuple: + fn = ';filename="%s"' % value[0] + value = value[1] + else: + fn = "" + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write(fn) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue() + + self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) + + # build the Request + # We can't use urllib2 since we need to send the Basic + # auth right with the first request + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + http = httplib.HTTPConnection(netloc) + elif schema == 'https': + http = httplib.HTTPSConnection(netloc) + else: + raise AssertionError, "unsupported schema "+schema + + data = '' + loglevel = log.INFO + try: + http.connect() + http.putrequest("POST", url) + http.putheader('Content-type', + 'multipart/form-data; boundary=%s'%boundary) + http.putheader('Content-length', str(len(body))) + http.putheader('Authorization', auth) + http.endheaders() + http.send(body) + except socket.error, e: + self.announce(e.msg, log.ERROR) + return + + r = http.getresponse() + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (r.status, r.reason), + log.ERROR) + if self.show_response: + print '-'*75, r.read(), '-'*75 diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 78962eee..0a8ccddc 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -44,7 +44,9 @@ def distros_for_url(url, metadata=None): path = urlparse.urlparse(url)[2] base = urllib2.unquote(path.split('/')[-1]) - + if base.endswith('.egg.zip'): + base = base[:-4] # strip the .zip + if base.endswith('.egg'): dist = Distribution.from_filename(base, metadata) dist.path = url @@ -58,10 +60,10 @@ def distros_for_url(url, metadata=None): ) # Try source distro extensions (.zip, .tgz, etc.) - # + # for ext in EXTENSIONS: if base.endswith(ext): - base = base[:-len(ext)] + base = base[:-len(ext)] return interpret_distro_name(url, base, metadata) return [] # no extension matched @@ -78,8 +80,6 @@ def distros_for_url(url, metadata=None): - - def interpret_distro_name(url, base, metadata, py_version=None, distro_type=SOURCE_DIST, platform=None ): @@ -139,7 +139,7 @@ class PackageIndex(AvailableDistributions): dists = list(distros_for_url(url)) if dists: self.debug("Found link: %s", url) - if dists or not retrieve or url in self.fetched_urls: + if dists or not retrieve or url in self.fetched_urls: for dist in dists: self.add(dist) # don't need the actual page @@ -164,7 +164,7 @@ class PackageIndex(AvailableDistributions): def process_index(self,url,page): """Process the contents of a PyPI page""" - + def scan(link): # Process a URL to see if it's for a package page if link.startswith(self.index_url): @@ -226,7 +226,7 @@ class PackageIndex(AvailableDistributions): for dist in self.get(requirement.key, ()): if dist in requirement: return dist - self.debug("%s does not match %s", requirement, dist) + self.debug("%s does not match %s", requirement, dist) return super(PackageIndex, self).obtain(requirement,installer) @@ -388,6 +388,9 @@ class PackageIndex(AvailableDistributions): else: name = "__downloaded__" # default if URL has no path contents + if name.endswith('.egg.zip'): + name = name[:-4] # strip the extra .zip before download + filename = os.path.join(tmpdir,name) # Download the file @@ -405,9 +408,6 @@ class PackageIndex(AvailableDistributions): self.process_url(url, True) - - - def _download_html(self, url, headers, filename, tmpdir): # Check for a sourceforge URL sf_url = url.startswith('http://prdownloads.') @@ -445,7 +445,7 @@ class PackageIndex(AvailableDistributions): def info(self, msg, *args): log.info(msg, *args) - + def warn(self, msg, *args): log.warn(msg, *args) |