Commit bfe37ff8 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge Pull Request #144

parents e99e20ce df551652
...@@ -2473,6 +2473,18 @@ def _remove_md5_fragment(location): ...@@ -2473,6 +2473,18 @@ def _remove_md5_fragment(location):
return location return location
def _version_from_file(lines):
"""
Given an iterable of lines from a Metadata file, return
the value of the Version field, if present, or None otherwise.
"""
is_version_line = lambda line: line.lower().startswith('version:')
version_lines = filter(is_version_line, lines)
line = next(iter(version_lines), '')
_, _, value = line.partition(':')
return safe_version(value.strip()) or None
class Distribution(object): class Distribution(object):
"""Wrap an actual or potential sys.path entry w/metadata""" """Wrap an actual or potential sys.path entry w/metadata"""
PKG_INFO = 'PKG-INFO' PKG_INFO = 'PKG-INFO'
...@@ -2490,22 +2502,29 @@ class Distribution(object): ...@@ -2490,22 +2502,29 @@ class Distribution(object):
self._provider = metadata or empty_provider self._provider = metadata or empty_provider
@classmethod @classmethod
def from_location(cls, location, basename, metadata=None,**kw): def from_location(cls, location, basename, metadata=None, **kw):
project_name, version, py_version, platform = [None]*4 project_name, version, py_version, platform = [None]*4
dist_path = os.path.join(location, basename)
basename, ext = os.path.splitext(basename) basename, ext = os.path.splitext(basename)
if ext.lower() in _distributionImpl: if ext.lower() in _distributionImpl:
# .dist-info gets much metadata differently cls = _distributionImpl[ext.lower()]
match = EGG_NAME(basename) match = EGG_NAME(basename)
if match: if match:
project_name, version, py_version, platform = match.group( project_name, version, py_version, platform = match.group(
'name','ver','pyver','plat' 'name', 'ver', 'pyver', 'plat'
) )
cls = _distributionImpl[ext.lower()]
version = cls._version_from_metadata(dist_path) or version
return cls( return cls(
location, metadata, project_name=project_name, version=version, location, metadata, project_name=project_name, version=version,
py_version=py_version, platform=platform, **kw py_version=py_version, platform=platform, **kw
) )
@staticmethod
def _version_from_metadata(dist_path):
pass
@property @property
def hashcmp(self): def hashcmp(self):
return ( return (
...@@ -2591,13 +2610,11 @@ class Distribution(object): ...@@ -2591,13 +2610,11 @@ class Distribution(object):
try: try:
return self._version return self._version
except AttributeError: except AttributeError:
for line in self._get_metadata(self.PKG_INFO): version = _version_from_file(self._get_metadata(self.PKG_INFO))
if line.lower().startswith('version:'): if version is None:
self._version = safe_version(line.split(':',1)[1].strip())
return self._version
else:
tmpl = "Missing 'Version:' header and/or %s file" tmpl = "Missing 'Version:' header and/or %s file"
raise ValueError(tmpl % self.PKG_INFO, self) raise ValueError(tmpl % self.PKG_INFO, self)
return version
@property @property
def _dep_map(self): def _dep_map(self):
...@@ -2802,6 +2819,30 @@ class Distribution(object): ...@@ -2802,6 +2819,30 @@ class Distribution(object):
return [dep for dep in self._dep_map if dep] return [dep for dep in self._dep_map if dep]
class EggInfoDistribution(Distribution):
@staticmethod
def _version_from_metadata(dist_path):
"""
Packages installed by distutils (e.g. numpy or scipy),
which uses an old safe_version, and so
their version numbers can get mangled when
converted to filenames (e.g., 1.11.0.dev0+2329eae to
1.11.0.dev0_2329eae). These distributions will not be
parsed properly
downstream by Distribution and safe_version, so
take an extra step and try to get the version number from
the metadata file itself instead of the filename.
"""
if not os.path.isfile(dist_path):
return
try:
with open(dist_path) as strm:
return _version_from_file(strm)
except IOError:
pass
class DistInfoDistribution(Distribution): class DistInfoDistribution(Distribution):
"""Wrap an actual or potential sys.path entry w/metadata, .dist-info style""" """Wrap an actual or potential sys.path entry w/metadata, .dist-info style"""
PKG_INFO = 'METADATA' PKG_INFO = 'METADATA'
...@@ -2867,7 +2908,7 @@ class DistInfoDistribution(Distribution): ...@@ -2867,7 +2908,7 @@ class DistInfoDistribution(Distribution):
_distributionImpl = { _distributionImpl = {
'.egg': Distribution, '.egg': Distribution,
'.egg-info': Distribution, '.egg-info': EggInfoDistribution,
'.dist-info': DistInfoDistribution, '.dist-info': DistInfoDistribution,
} }
......
...@@ -5,9 +5,15 @@ import zipfile ...@@ -5,9 +5,15 @@ import zipfile
import datetime import datetime
import time import time
import subprocess import subprocess
import stat
import distutils.dist
import distutils.command.install_egg_info
import pytest
import pkg_resources import pkg_resources
try: try:
unicode unicode
except NameError: except NameError:
...@@ -109,3 +115,49 @@ class TestIndependence: ...@@ -109,3 +115,49 @@ class TestIndependence:
) )
cmd = [sys.executable, '-c', '; '.join(lines)] cmd = [sys.executable, '-c', '; '.join(lines)]
subprocess.check_call(cmd) subprocess.check_call(cmd)
class TestDeepVersionLookupDistutils(object):
@pytest.fixture
def env(self, tmpdir):
"""
Create a package environment, similar to a virtualenv,
in which packages are installed.
"""
class Environment(str):
pass
env = Environment(tmpdir)
tmpdir.chmod(stat.S_IRWXU)
subs = 'home', 'lib', 'scripts', 'data', 'egg-base'
env.paths = dict(
(dirname, str(tmpdir / dirname))
for dirname in subs
)
list(map(os.mkdir, env.paths.values()))
return env
def create_foo_pkg(self, env, version):
"""
Create a foo package installed (distutils-style) to env.paths['lib']
as version.
"""
attrs = dict(name='foo', version=version)
dist = distutils.dist.Distribution(attrs)
iei_cmd = distutils.command.install_egg_info.install_egg_info(dist)
iei_cmd.initialize_options()
iei_cmd.install_dir = env.paths['lib']
iei_cmd.finalize_options()
iei_cmd.run()
def test_version_resolved_from_egg_info(self, env):
version = '1.11.0.dev0+2329eae'
self.create_foo_pkg(env, version)
# this requirement parsing will raise a VersionConflict unless the
# .egg-info file is parsed (see #419 on BitBucket)
req = pkg_resources.Requirement.parse('foo>=1.9')
dist = pkg_resources.WorkingSet([env.paths['lib']]).find(req)
assert dist.version == version
...@@ -8,7 +8,11 @@ from .textwrap import DALS ...@@ -8,7 +8,11 @@ from .textwrap import DALS
from . import contexts from . import contexts
class TestEggInfo: class Environment(str):
pass
class TestEggInfo(object):
setup_script = DALS(""" setup_script = DALS("""
from setuptools import setup from setuptools import setup
...@@ -33,8 +37,6 @@ class TestEggInfo: ...@@ -33,8 +37,6 @@ class TestEggInfo:
@pytest.yield_fixture @pytest.yield_fixture
def env(self): def env(self):
class Environment(str): pass
with contexts.tempdir(prefix='setuptools-test.') as env_dir: with contexts.tempdir(prefix='setuptools-test.') as env_dir:
env = Environment(env_dir) env = Environment(env_dir)
os.chmod(env_dir, stat.S_IRWXU) os.chmod(env_dir, stat.S_IRWXU)
...@@ -49,8 +51,7 @@ class TestEggInfo: ...@@ -49,8 +51,7 @@ class TestEggInfo:
f.write(DALS(""" f.write(DALS("""
[egg_info] [egg_info]
egg-base = %(egg-base)s egg-base = %(egg-base)s
""" % env.paths """ % env.paths))
))
yield env yield env
def test_egg_base_installed_egg_info(self, tmpdir_cwd, env): def test_egg_base_installed_egg_info(self, tmpdir_cwd, env):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment