summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt3
-rw-r--r--CONTRIBUTORS.txt3
-rw-r--r--pkg_resources.py88
-rwxr-xr-xsetup.py3
-rwxr-xr-xsetuptools/command/easy_install.py18
-rwxr-xr-xsetuptools/command/install_scripts.py5
-rw-r--r--setuptools/tests/test_dist_info.py65
7 files changed, 177 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index a58c1ce1..149b1fed 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,6 +6,9 @@ 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
------
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 9ef062c7..2ec6b3e6 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -7,8 +7,11 @@ Contributors
* Arfrever Frehtes Taifersar Arahesis
* Christophe Combelles
* Daniel Stutzbach
+* David Hoth
* Hanno Schlichting
* Jannis Leidel
+* Jason R. Coombs
+* 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/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_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)
+