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):
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):
"""Wrap an actual or potential sys.path entry w/metadata"""
PKG_INFO = 'PKG-INFO'
......@@ -2490,22 +2502,29 @@ class Distribution(object):
self._provider = metadata or empty_provider
@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
dist_path = os.path.join(location, basename)
basename, ext = os.path.splitext(basename)
if ext.lower() in _distributionImpl:
# .dist-info gets much metadata differently
cls = _distributionImpl[ext.lower()]
match = EGG_NAME(basename)
if match:
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(
location, metadata, project_name=project_name, version=version,
py_version=py_version, platform=platform, **kw
)
@staticmethod
def _version_from_metadata(dist_path):
pass
@property
def hashcmp(self):
return (
......@@ -2591,13 +2610,11 @@ class Distribution(object):
try:
return self._version
except AttributeError:
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:
version = _version_from_file(self._get_metadata(self.PKG_INFO))
if version is None:
tmpl = "Missing 'Version:' header and/or %s file"
raise ValueError(tmpl % self.PKG_INFO, self)
return version
@property
def _dep_map(self):
......@@ -2802,6 +2819,30 @@ class Distribution(object):
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):
"""Wrap an actual or potential sys.path entry w/metadata, .dist-info style"""
PKG_INFO = 'METADATA'
......@@ -2867,7 +2908,7 @@ class DistInfoDistribution(Distribution):
_distributionImpl = {
'.egg': Distribution,
'.egg-info': Distribution,
'.egg-info': EggInfoDistribution,
'.dist-info': DistInfoDistribution,
}
......
......@@ -5,9 +5,15 @@ import zipfile
import datetime
import time
import subprocess
import stat
import distutils.dist
import distutils.command.install_egg_info
import pytest
import pkg_resources
try:
unicode
except NameError:
......@@ -109,3 +115,49 @@ class TestIndependence:
)
cmd = [sys.executable, '-c', '; '.join(lines)]
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
from . import contexts
class TestEggInfo:
class Environment(str):
pass
class TestEggInfo(object):
setup_script = DALS("""
from setuptools import setup
......@@ -33,8 +37,6 @@ class TestEggInfo:
@pytest.yield_fixture
def env(self):
class Environment(str): pass
with contexts.tempdir(prefix='setuptools-test.') as env_dir:
env = Environment(env_dir)
os.chmod(env_dir, stat.S_IRWXU)
......@@ -49,8 +51,7 @@ class TestEggInfo:
f.write(DALS("""
[egg_info]
egg-base = %(egg-base)s
""" % env.paths
))
""" % env.paths))
yield 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