Permalink
Browse files

Merge pull request #887 from pnasrat/update-distlib

Update vendored distlib to 0.1.1
  • Loading branch information...
2 parents 5256d57 + ee41f83 commit 4ac9406eb85fe7dda76defb6754df85577d1410f @pfmoore pfmoore committed Apr 7, 2013
@@ -6,7 +6,7 @@
#
import logging
-__version__ = '0.1.0'
+__version__ = '0.1.1'
class DistlibException(Exception):
pass
@@ -16,10 +16,11 @@
import zipimport
from . import DistlibException
-from .compat import StringIO, configparser
+from .compat import StringIO, configparser, string_types
from .version import get_scheme, UnsupportedVersionError
+from .markers import interpret
from .metadata import Metadata
-from .util import (parse_requires, cached_property, get_export_entry,
+from .util import (parse_requirement, cached_property, get_export_entry,
CSVReader, CSVWriter)
@@ -116,10 +117,10 @@ def _yield_distributions(self):
for dir in os.listdir(realpath):
dist_path = os.path.join(realpath, dir)
if self._include_dist and dir.endswith(DISTINFO_EXT):
- yield InstalledDistribution(dist_path, env=self)
+ yield new_dist_class(dist_path, env=self)
elif self._include_egg and dir.endswith(('.egg-info',
'.egg')):
- yield EggInfoDistribution(dist_path, self)
+ yield old_dist_class(dist_path, self)
def _generate_cache(self):
"""
@@ -299,11 +300,6 @@ class Distribution(object):
present (in other words, whether the package was installed by user
request or it was installed as a dependency)."""
- extras = None
- """
- Set to a list of requested extras if specified in a requirement.
- """
-
def __init__(self, metadata):
"""
Initialise an instance.
@@ -316,6 +312,7 @@ def __init__(self, metadata):
self.version = metadata.version
self.locator = None
self.md5_digest = None
+ self.extras = None # additional features requested during installation
@property
def download_url(self):
@@ -337,45 +334,55 @@ def provides(self):
A set of distribution names and versions provided by this distribution.
:return: A set of "name (version)" strings.
"""
- return set(self.metadata['Provides-Dist']
- + self.metadata['Provides']
- + ['%s (%s)' % (self.name, self.version)]
- )
+ plist = self.metadata['Provides-Dist']
+ s = '%s (%s)' % (self.name, self.version)
+ if s not in plist:
+ plist.append(s)
+ return self.filter_requirements(plist)
- @cached_property
+ @property
def requires(self):
- """
- A set of requirements (dependencies of this distribution).
- :return: A set of strings like "foo (>= 1.0, < 2.0, != 1.3)" where
- ``foo`` is the name of the distribution depended on, and
- the constraints indicate which versions of ``foo`` are
- acceptable to fulfill the requirement.
- """
- return set(self.metadata['Requires-Dist']
- + self.get_requirements('install')
- + self.get_requirements('setup')
- + self.get_requirements('test')
- )
+ rlist = self.metadata['Requires-Dist']
+ return self.filter_requirements(rlist)
- def get_requirements(self, key):
- """
- Get the requirements of a particular type
- ('setup', 'install', 'test', 'extra:key').
- """
- result = []
- parts = key.split(':', 1)
- if len(parts) == 2:
- key, extra = parts
- else:
- extra = None
- d = self.metadata.dependencies
- if key in d:
- if extra is None:
- result = d[key]
+ @property
+ def setup_requires(self):
+ rlist = self.metadata['Setup-Requires-Dist']
+ return self.filter_requirements(rlist)
+
+ @property
+ def test_requires(self):
+ rlist = self.metadata['Requires-Dist']
+ return self.filter_requirements(rlist, extras=['test'])
+
+ @property
+ def doc_requires(self):
+ rlist = self.metadata['Requires-Dist']
+ return self.filter_requirements(rlist, extras=['doc'])
+
+ def filter_requirements(self, rlist, context=None, extras=None):
+ result = set()
+ marked = []
+ for req in rlist:
+ if ';' not in req:
+ result.add(req)
+ else:
+ marked.append(req.split(';', 1))
+ if marked:
+ if context is None:
+ context = {}
+ if extras is None:
+ extras = self.extras
+ if not extras:
+ extras = [None]
else:
- ed = d[key]
- assert isinstance(ed, dict)
- result = ed.get(extra, [])
+ extras = list(extras) # leave original alone
+ extras.append(None)
+ for extra in extras:
+ context['extra'] = extra
+ for r, marker in marked:
+ if interpret(marker, context):
+ result.add(r.strip())
return result
def matches_requirement(self, req):
@@ -852,6 +859,39 @@ def set_name_and_version(s, n, v):
def _get_metadata(self, path):
requires = None
+ def parse_requires(req_path):
+ """Create a list of dependencies from a requires.txt file.
+
+ *req_path* must be the path to a setuptools-produced requires.txt file.
+ """
+
+ reqs = []
+ try:
+ with open(req_path, 'r') as fp:
+ lines = fp.read().splitlines()
+ except IOError:
+ return reqs
+
+ for line in lines:
+ line = line.strip()
+ if line.startswith('['):
+ logger.warning('Unexpected line: quitting requirement scan: %r',
+ line)
+ break
+ r = parse_requirement(line)
+ if not r:
+ logger.warning('Not recognised as a requirement: %r', line)
+ continue
+ if r.extras:
+ logger.warning('extra requirements in requires.txt are '
+ 'not supported')
+ if not r.constraints:
+ reqs.append(r.name)
+ else:
+ cons = ', '.join('%s%s' % c for c in r.constraints)
+ reqs.append('%s (%s)' % (r.name, cons))
+ return reqs
+
if path.endswith('.egg'):
if os.path.isdir(path):
meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
@@ -986,6 +1026,10 @@ def __eq__(self, other):
# See http://docs.python.org/reference/datamodel#object.__hash__
__hash__ = object.__hash__
+new_dist_class = InstalledDistribution
+old_dist_class = EggInfoDistribution
+
+
class DependencyGraph(object):
"""
Represents a dependency graph between distributions.
@@ -1168,11 +1212,7 @@ def make_graph(dists, scheme='default'):
# now make the edges
for dist in dists:
- # need to leave this in because tests currently rely on it ...
- requires = dist.metadata['Requires-Dist']
- if not requires:
- requires = (dist.get_requirements('install') +
- dist.get_requirements('setup'))
+ requires = (dist.requires | dist.setup_requires)
for req in requires:
try:
matcher = scheme.matcher(req)
@@ -107,6 +107,9 @@ def __init__(self, scheme='default'):
# we use our own opener rather than just using urlopen.
self.opener = build_opener(RedirectHandler())
+ def clear_cache(self):
+ self._cache.clear()
+
def _get_scheme(self):
return self._scheme
@@ -283,13 +286,16 @@ def _update_version_data(self, result, info):
dist.locator = self
result[version] = dist
- def locate(self, requirement):
+ def locate(self, requirement, prereleases=False):
"""
Find the most recent distribution which matches the given
requirement.
:param requirement: A requirement of the form 'foo (1.0)' or perhaps
'foo (>= 1.0, < 2.0, != 1.3)'
+ :param prereleases: If ``True``, allow pre-release versions
+ to be located. Otherwise, pre-release versions
+ are not returned.
:return: A :class:`Distribution` instance, or ``None`` if no such
distribution could be located.
"""
@@ -300,10 +306,9 @@ def locate(self, requirement):
raise DistlibException('Not a valid requirement: %r' % requirement)
if r.extras:
# lose the extras part of the requirement
- i = requirement.find('[')
- assert i > 0, 'expected to find a [ in a requirement with extras'
- requirement = requirement[:i]
+ requirement = r.requirement
matcher = scheme.matcher(requirement)
+ vcls = matcher.version_class
logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__)
versions = self.get_project(matcher.name)
if versions:
@@ -314,7 +319,10 @@ def locate(self, requirement):
if not matcher.match(k):
logger.debug('%s did not match %r', matcher, k)
else:
- slist.append(k)
+ if prereleases or not vcls(k).is_prerelease:
+ slist.append(k)
+ else:
+ logger.debug('skipping pre-release version %s', k)
except Exception:
logger.warning('error matching %s with %r', matcher, k)
pass # slist.append(k)
@@ -837,6 +845,11 @@ def __init__(self, *locators, **kwargs):
self.locators = locators
super(AggregatingLocator, self).__init__(**kwargs)
+ def clear_cache(self):
+ super(AggregatingLocator, self).clear_cache()
+ for locator in self.locators:
+ locator.clear_cache()
+
def _set_scheme(self, value):
self._scheme = value
for locator in self.locators:
@@ -1020,13 +1033,15 @@ def try_to_replace(self, provider, other, problems):
result = True
return result
- def find(self, requirement, tests=False):
+ def find(self, requirement, tests=False, prereleases=False):
"""
Find a distribution matching requirement and all distributions
it depends on. Use the ``tests`` argument to determine whether
distributions used only for testing should be included in the
results. Allow ``requirement`` to be either a :class:`Distribution`
- instance or a string expressing a requirement.
+ instance or a string expressing a requirement. If ``prereleases``
+ is True, allow pre-release versions to be returned - otherwise,
+ don't.
Return a set of :class:`Distribution` instances and a set of
problems.
@@ -1051,7 +1066,8 @@ def find(self, requirement, tests=False):
dist = odist = requirement
logger.debug('passed %s as requirement', odist)
else:
- dist = odist = self.locator.locate(requirement)
+ dist = odist = self.locator.locate(requirement,
+ prereleases=prereleases)
if dist is None:
raise DistlibException('Unable to locate %r' % requirement)
logger.debug('located %s', odist)
@@ -1070,32 +1086,19 @@ def find(self, requirement, tests=False):
if other != dist:
self.try_to_replace(dist, other, problems)
- ireqts = set(dist.get_requirements('install'))
- sreqts = set(dist.get_requirements('setup'))
+ ireqts = dist.requires
+ sreqts = dist.setup_requires
ereqts = set()
- extras = dist.extras
- if extras:
- d = dist.get_requirements('extras')
- for extra in extras:
- if extra not in d:
- logger.warning('Requested extra not known: %r', extra)
- else:
- s = set(d[extra])
- ereqts |= s
- if extra not in ('doc', 'test'):
- ireqts |= s
if not tests or dist not in install_dists:
treqts = set()
else:
- treqts = set(dist.get_requirements('test'))
- if extras and 'test' in d:
- treqts |= set(d['test'])
+ treqts = dist.test_requires
all_reqts = ireqts | sreqts | treqts | ereqts
for r in all_reqts:
providers = self.find_providers(r)
if not providers:
logger.debug('No providers found for %r', r)
- provider = self.locator.locate(r)
+ provider = self.locator.locate(r, prereleases=prereleases)
if provider is None:
logger.debug('Cannot satisfy %r', r)
problems.add(('unsatisfied', r))
@@ -105,14 +105,14 @@ def add_dir(dirs, d):
assert parent not in ('', '/')
add_dir(dirs, parent)
- files = self.files
+ result = set(self.files) # make a copy!
if wantdirs:
dirs = set()
- for f in files:
+ for f in result:
add_dir(dirs, os.path.dirname(f))
- files |= dirs
+ result |= dirs
return [os.path.join(*path_tuple) for path_tuple in
- sorted(os.path.split(path) for path in files)]
+ sorted(os.path.split(path) for path in result)]
def clear(self):
"""Clear all collected files."""
Oops, something went wrong.

0 comments on commit 4ac9406

Please sign in to comment.