diff options
| -rw-r--r-- | CHANGES.txt | 9 | ||||
| -rw-r--r-- | CONTRIBUTORS.txt | 4 | ||||
| -rw-r--r-- | pkg_resources.py | 88 | ||||
| -rwxr-xr-x | setup.py | 3 | ||||
| -rw-r--r-- | setuptools/command/bdist_egg.py | 2 | ||||
| -rwxr-xr-x | setuptools/command/easy_install.py | 18 | ||||
| -rwxr-xr-x | setuptools/command/install_scripts.py | 5 | ||||
| -rw-r--r-- | setuptools/tests/test_bdist_egg.py | 69 | ||||
| -rw-r--r-- | setuptools/tests/test_dist_info.py | 65 |
9 files changed, 255 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 2707f01c..149b1fed 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,15 @@ CHANGES ======= ------ +0.6.28 +------ + +* Issue 294: setup.py can now be invoked from any directory. +* Scripts are now installed honoring the umask. +* Added support for .dist-info directories. +* Issue #283 bdist_egg issues with python 3.3.0aX + +------ 0.6.27 ------ diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 9ef062c7..65dd0039 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -7,8 +7,12 @@ Contributors * Arfrever Frehtes Taifersar Arahesis * Christophe Combelles * Daniel Stutzbach +* David Hoth * Hanno Schlichting * Jannis Leidel +* Jason R. Coombs +* Jim Fulton +* Justin Azoff * Lennart Regebro * Martin von Löwis * Noufal Ibrahim diff --git a/pkg_resources.py b/pkg_resources.py index a61c0efe..27b9f834 100644 --- a/pkg_resources.py +++ b/pkg_resources.py @@ -1746,7 +1746,7 @@ def find_on_path(importer, path_item, only=False): # scan for .egg and .egg-info in directory for entry in os.listdir(path_item): lower = entry.lower() - if lower.endswith('.egg-info'): + if lower.endswith('.egg-info') or lower.endswith('.dist-info'): fullpath = os.path.join(path_item, entry) if os.path.isdir(fullpath): # egg-info directory, allow getting metadata @@ -2119,6 +2119,8 @@ def _remove_md5_fragment(location): class Distribution(object): """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' + def __init__(self, location=None, metadata=None, project_name=None, version=None, py_version=PY_MAJOR, platform=None, precedence = EGG_DIST @@ -2136,12 +2138,14 @@ class Distribution(object): def from_location(cls,location,basename,metadata=None,**kw): project_name, version, py_version, platform = [None]*4 basename, ext = os.path.splitext(basename) - if ext.lower() in (".egg",".egg-info"): + if ext.lower() in _distributionImpl: + # .dist-info gets much metadata differently match = EGG_NAME(basename) if match: project_name, version, py_version, platform = match.group( 'name','ver','pyver','plat' ) + cls = _distributionImpl[ext.lower()] return cls( location, metadata, project_name=project_name, version=version, py_version=py_version, platform=platform, **kw @@ -2204,13 +2208,13 @@ class Distribution(object): try: return self._version except AttributeError: - for line in self._get_metadata('PKG-INFO'): + for line in self._get_metadata(self.PKG_INFO): if line.lower().startswith('version:'): self._version = safe_version(line.split(':',1)[1].strip()) return self._version else: raise ValueError( - "Missing 'Version:' header and/or PKG-INFO file", self + "Missing 'Version:' header and/or %s file" % self.PKG_INFO, self ) version = property(version) @@ -2441,6 +2445,82 @@ class Distribution(object): extras = property(extras) +class DistInfoDistribution(Distribution): + """Wrap an actual or potential sys.path entry w/metadata, .dist-info style""" + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + from email.parser import Parser + self._pkg_info = Parser().parsestr(self.get_metadata(self.PKG_INFO)) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _preparse_requirement(self, requires_dist): + """Convert 'Foobar (1); baz' to ('Foobar ==1', 'baz') + Split environment marker, add == prefix to version specifiers as + necessary, and remove parenthesis. + """ + parts = requires_dist.split(';', 1) + [''] + distvers = parts[0].strip() + mark = parts[1].strip() + distvers = re.sub(self.EQEQ, r"\1==\2\3", distvers) + distvers = distvers.replace('(', '').replace(')', '') + return (distvers, mark) + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + def dummy_marker(marker): + def marker_fn(environment=None, override=None): + return True + marker_fn.__doc__ = marker + return marker_fn + try: + from markerlib import as_function + except ImportError: + as_function = dummy_marker + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + distvers, mark = self._preparse_requirement(req) + parsed = parse_requirements(distvers).next() + parsed.marker_fn = as_function(mark) + reqs.append(parsed) + + def reqs_for_extra(extra): + for req in reqs: + if req.marker_fn(override={'extra':extra}): + yield req + + common = set(reqs_for_extra(None)) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + extra = safe_extra(extra.strip()) + dm[extra] = list(set(reqs_for_extra(extra)) - common) + + return dm + + +_distributionImpl = {'.egg': Distribution, + '.egg-info': Distribution, + '.dist-info': DistInfoDistribution } + + def issue_warning(*args,**kw): level = 1 g = globals() @@ -4,6 +4,9 @@ import sys import os import textwrap +# Allow to run setup.py from another directory. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + src_root = None if sys.version_info >= (3,): tmp_src = os.path.join("build", "src") diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 0ee9c55b..cf2d75e4 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -463,6 +463,8 @@ def iter_symbols(code): yield name def can_scan(): + if sys.version_info > (3, 3): + return False # Can't scan recent formats if not sys.platform.startswith('java') and sys.platform != 'cli': # CPython, PyPy, etc. return True diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index dfd9f7ff..f2260236 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -10,7 +10,15 @@ file, or visit the `EasyInstall home page`__. __ http://packages.python.org/distribute/easy_install.html """ -import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random +import sys +import os +import zipimport +import shutil +import tempfile +import zipfile +import re +import stat +import random from glob import glob from setuptools import Command, _dont_write_bytecode from setuptools.sandbox import run_setup @@ -762,12 +770,13 @@ Please make the appropriate changes for your system and try again. target = os.path.join(self.script_dir, script_name) self.add_output(target) + mask = current_umask() if not self.dry_run: ensure_directory(target) f = open(target,"w"+mode) f.write(contents) f.close() - chmod(target,0755) + chmod(target, 0777-mask) @@ -1870,6 +1879,11 @@ def rmtree(path, ignore_errors=False, onerror=auto_chmod): except os.error: onerror(os.rmdir, path, sys.exc_info()) +def current_umask(): + tmp = os.umask(022) + os.umask(tmp) + return tmp + def bootstrap(): # This function is called when setuptools*.egg is run using /bin/sh import setuptools; argv0 = os.path.dirname(setuptools.__path__[0]) diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index 6ce1b993..82456035 100755 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -39,15 +39,16 @@ class install_scripts(_install_scripts): def write_script(self, script_name, contents, mode="t", *ignored): """Write an executable file to the scripts directory""" - from setuptools.command.easy_install import chmod + from setuptools.command.easy_install import chmod, current_umask log.info("Installing %s script to %s", script_name, self.install_dir) target = os.path.join(self.install_dir, script_name) self.outfiles.append(target) + mask = current_umask() if not self.dry_run: ensure_directory(target) f = open(target,"w"+mode) f.write(contents) f.close() - chmod(target,0755) + chmod(target, 0777-mask) diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py new file mode 100644 index 00000000..7da122cc --- /dev/null +++ b/setuptools/tests/test_bdist_egg.py @@ -0,0 +1,69 @@ +"""develop tests +""" +import sys +import os, re, shutil, tempfile, unittest +import tempfile +import site +from StringIO import StringIO + +from distutils.errors import DistutilsError +from setuptools.command.bdist_egg import bdist_egg +from setuptools.command import easy_install as easy_install_pkg +from setuptools.dist import Distribution + +SETUP_PY = """\ +from setuptools import setup + +setup(name='foo', py_modules=['hi']) +""" + +class TestDevelopTest(unittest.TestCase): + + def setUp(self): + self.dir = tempfile.mkdtemp() + self.old_cwd = os.getcwd() + os.chdir(self.dir) + f = open('setup.py', 'w') + f.write(SETUP_PY) + f.close() + f = open('hi.py', 'w') + f.write('1\n') + f.close() + if sys.version >= "2.6": + self.old_base = site.USER_BASE + site.USER_BASE = tempfile.mkdtemp() + self.old_site = site.USER_SITE + site.USER_SITE = tempfile.mkdtemp() + + def tearDown(self): + os.chdir(self.old_cwd) + shutil.rmtree(self.dir) + if sys.version >= "2.6": + shutil.rmtree(site.USER_BASE) + shutil.rmtree(site.USER_SITE) + site.USER_BASE = self.old_base + site.USER_SITE = self.old_site + + def test_bdist_egg(self): + dist = Distribution(dict( + script_name='setup.py', + script_args=['bdist_egg'], + name='foo', + py_modules=['hi'] + )) + os.makedirs(os.path.join('build', 'src')) + old_stdout = sys.stdout + sys.stdout = o = StringIO() + try: + dist.parse_command_line() + dist.run_commands() + finally: + sys.stdout = old_stdout + + # let's see if we got our egg link at the right place + [content] = os.listdir('dist') + self.assertTrue(re.match('foo-0.0.0-py[23].\d.egg$', content)) + +def test_suite(): + return unittest.makeSuite(TestDevelopTest) + diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py new file mode 100644 index 00000000..119cc68b --- /dev/null +++ b/setuptools/tests/test_dist_info.py @@ -0,0 +1,65 @@ +"""Test .dist-info style distributions. +""" +import os, shutil, tempfile, unittest +import pkg_resources +from pkg_resources import Requirement +try: + import markerlib + has_markerlib = True +except: + has_markerlib = False + +class TestDistInfo(unittest.TestCase): + + def test_distinfo(self): + dists = {} + for d in pkg_resources.find_distributions(self.tmpdir): + dists[d.project_name] = d + + assert len(dists) == 2, dists + + unversioned = dists['UnversionedDistribution'] + versioned = dists['VersionedDistribution'] + + assert versioned.version == '2.718' # from filename + assert unversioned.version == '0.3' # from METADATA + + @unittest.skipIf(not has_markerlib, + "install markerlib to test conditional dependencies") + def test_conditional_dependencies(self): + requires = [Requirement.parse('splort==4'), + Requirement.parse('quux>=1.1')] + + for d in pkg_resources.find_distributions(self.tmpdir): + self.assertEquals(d.requires(), requires[:1]) + self.assertEquals(d.requires(extras=('baz',)), requires) + self.assertEquals(d.extras, ['baz']) + + def setUp(self): + self.tmpdir = tempfile.mkdtemp() + versioned = os.path.join(self.tmpdir, + 'VersionedDistribution-2.718.dist-info') + os.mkdir(versioned) + open(os.path.join(versioned, 'METADATA'), 'w+').write( +"""Metadata-Version: 1.2 +Name: VersionedDistribution +Requires-Dist: splort (4) +Provides-Extra: baz +Requires-Dist: quux (>=1.1); extra == 'baz' +""") + + unversioned = os.path.join(self.tmpdir, + 'UnversionedDistribution.dist-info') + os.mkdir(unversioned) + open(os.path.join(unversioned, 'METADATA'), 'w+').write( +"""Metadata-Version: 1.2 +Name: UnversionedDistribution +Version: 0.3 +Requires-Dist: splort (==4) +Provides-Extra: baz +Requires-Dist: quux (>=1.1); extra == 'baz' +""") + + def tearDown(self): + shutil.rmtree(self.tmpdir) + |
