summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt9
-rw-r--r--CONTRIBUTORS.txt4
-rw-r--r--pkg_resources.py88
-rwxr-xr-xsetup.py3
-rw-r--r--setuptools/command/bdist_egg.py2
-rwxr-xr-xsetuptools/command/easy_install.py18
-rwxr-xr-xsetuptools/command/install_scripts.py5
-rw-r--r--setuptools/tests/test_bdist_egg.py69
-rw-r--r--setuptools/tests/test_dist_info.py65
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()
diff --git a/setup.py b/setup.py
index c4351b1e..4294f3f9 100755
--- a/setup.py
+++ b/setup.py
@@ -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)
+