summaryrefslogtreecommitdiff
path: root/pkg_resources.py
diff options
context:
space:
mode:
Diffstat (limited to 'pkg_resources.py')
-rw-r--r--pkg_resources.py164
1 files changed, 123 insertions, 41 deletions
diff --git a/pkg_resources.py b/pkg_resources.py
index b5a334c2..0792adba 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -14,7 +14,7 @@ The package resource API is designed to work with normal filesystem packages,
method.
"""
__all__ = [
- 'register_loader_type', 'get_provider', 'IResourceProvider',
+ 'register_loader_type', 'get_provider', 'IResourceProvider',
'ResourceManager', 'AvailableDistributions', 'require', 'resource_string',
'resource_stream', 'resource_filename', 'set_extraction_path',
'cleanup_resources', 'parse_requirements', 'parse_version',
@@ -25,6 +25,7 @@ __all__ = [
]
import sys, os, zipimport, time, re
+from sets import ImmutableSet
class ResolutionError(Exception):
"""Abstract base for dependency resolution errors"""
@@ -38,7 +39,6 @@ class DistributionNotFound(ResolutionError):
class InvalidOption(ResolutionError):
"""Invalid or unrecognized option name for a distribution"""
-
_provider_factories = {}
def register_loader_type(loader_type, provider_factory):
@@ -418,7 +418,6 @@ def require(*requirements):
* get_distro_source() isn't implemented
* Distribution.install_on() isn't implemented
- * Requirement.options isn't implemented
* AvailableDistributions.resolve() is untested
* AvailableDistributions.scan() is untested
@@ -449,6 +448,7 @@ def require(*requirements):
+
class DefaultProvider:
"""Provides access to package resources in the filesystem"""
@@ -668,9 +668,11 @@ def yield_lines(strs):
LINE_END = re.compile(r"\s*(#.*)?$").match # whitespace and comment
CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match # line continuation
-DISTRO = re.compile(r"\s*(\w+)").match # Distribution name
+DISTRO = re.compile(r"\s*(\w+)").match # Distribution or option
VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|\.)+)").match # version info
COMMA = re.compile(r"\s*,").match # comma between items
+OBRACKET = re.compile(r"\s*\[").match
+CBRACKET = re.compile(r"\s*\]").match
EGG_NAME = re.compile(
r"(?P<name>[^-]+)"
@@ -693,8 +695,6 @@ def _parse_version_parts(s):
yield '*final' # ensure that alpha/beta/candidate are before final
-
-
def parse_version(s):
"""Convert a version string to a sortable key
@@ -867,15 +867,12 @@ def parse_requirements(strs):
"""
# create a steppable iterator, so we can handle \-continuations
lines = iter(yield_lines(strs))
- for line in lines:
- line = line.replace('-','_')
- match = DISTRO(line)
- if not match:
- raise ValueError("Missing distribution spec", line)
- distname = match.group(1)
- p = match.end()
- specs = []
- while not LINE_END(line,p):
+
+ def scan_list(ITEM,TERMINATOR,line,p,groups,item_name):
+
+ items = []
+
+ while not TERMINATOR(line,p):
if CONTINUE(line,p):
try:
line = lines.next().replace('-','_'); p = 0
@@ -883,64 +880,111 @@ def parse_requirements(strs):
raise ValueError(
"\\ must not appear on the last nonblank line"
)
- match = VERSION(line,p)
+
+ match = ITEM(line,p)
if not match:
- raise ValueError("Expected version spec in",line,"at",line[p:])
- op,val = match.group(1,2)
- specs.append((op,val.replace('_','-')))
+ raise ValueError("Expected "+item_name+" in",line,"at",line[p:])
+
+ items.append(match.group(*groups))
p = match.end()
+
match = COMMA(line,p)
if match:
p = match.end() # skip the comma
- elif not LINE_END(line,p):
- raise ValueError("Expected ',' or EOL in",line,"at",line[p:])
+ elif not TERMINATOR(line,p):
+ raise ValueError(
+ "Expected ',' or end-of-list in",line,"at",line[p:]
+ )
+
+ match = TERMINATOR(line,p)
+ if match: p = match.end() # skip the terminator, if any
+ return line, p, items
+
+ for line in lines:
+ line = line.replace('-','_')
+ match = DISTRO(line)
+ if not match:
+ raise ValueError("Missing distribution spec", line)
+ distname = match.group(1)
+ p = match.end()
+ options = []
+
+ match = OBRACKET(line,p)
+ if match:
+ p = match.end()
+ line, p, options = scan_list(DISTRO,CBRACKET,line,p,(1,),"option")
+
+ line, p, specs = scan_list(VERSION,LINE_END,line,p,(1,2),"version spec")
+ specs = [(op,val.replace('_','-')) for op,val in specs]
+ yield Requirement(distname.replace('_','-'), specs, options)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- yield Requirement(distname.replace('_','-'), specs)
class Requirement:
- def __init__(self, distname, specs=()):
+ def __init__(self, distname, specs=(), options=()):
self.distname = distname
self.key = distname.lower()
index = [(parse_version(v),state_machine[op],op,v) for op,v in specs]
index.sort()
self.specs = [(op,ver) for parsed,trans,op,ver in index]
- self.index = index
+ self.index, self.options = index, tuple(options)
+ self.hashCmp = (
+ self.key, tuple([(op,parsed) for parsed,trans,op,ver in index]),
+ ImmutableSet(map(str.lower,options))
+ )
+ self.__hash = hash(self.hashCmp)
def __str__(self):
return self.distname + ','.join([''.join(s) for s in self.specs])
def __repr__(self):
- return "Requirement(%r, %r)" % (self.distname, self.specs)
+ return "Requirement(%r, %r, %r)" % \
+ (self.distname,self.specs,self.options)
def __eq__(self,other):
- return isinstance(other,Requirement) \
- and self.key==other.key and self.specs==other.specs
+ return isinstance(other,Requirement) and self.hashCmp==other.hashCmp
def __contains__(self,item):
if isinstance(item,Distribution):
- if item.key <> self.key:
- return False
+ if item.key <> self.key: return False
item = item.parsed_version
elif isinstance(item,basestring):
item = parse_version(item)
last = True
for parsed,trans,op,ver in self.index:
action = trans[cmp(item,parsed)]
- if action=='F':
- return False
- elif action=='T':
- return True
- elif action=='+':
- last = True
- elif action=='-':
- last = False
+ if action=='F': return False
+ elif action=='T': return True
+ elif action=='+': last = True
+ elif action=='-': last = False
return last
+ def __hash__(self):
+ return self.__hash
+
#@staticmethod
def parse(s):
reqs = list(parse_requirements(s))
@@ -948,10 +992,11 @@ class Requirement:
if len(reqs)==1:
return reqs[0]
raise ValueError("Expected only one requirement", s)
- raise ValueError("No requirements found", s)
+ raise ValueError("No requirements found", s)
parse = staticmethod(parse)
+
state_machine = {
# =><
'<' : '--T',
@@ -962,6 +1007,7 @@ state_machine = {
'!=': 'F..',
}
+
def _get_mro(cls):
"""Get an mro for a type or classic class"""
if not isinstance(cls,type):
@@ -969,6 +1015,7 @@ def _get_mro(cls):
return cls.__mro__[1:]
return cls.__mro__
+
def _find_adapter(registry, ob):
"""Return an adapter factory for `ob` from `registry`"""
for t in _get_mro(getattr(ob, '__class__', type(ob))):
@@ -995,7 +1042,7 @@ def split_sections(s):
for line in yield_lines(s):
if line.startswith("["):
if line.endswith("]"):
- if content:
+ if section or content:
yield section, content
section = line[1:-1].strip().lower()
content = []
@@ -1005,8 +1052,16 @@ def split_sections(s):
content.append(line)
# wrap up last segment
- if content:
- yield section, content
+ yield section, content
+
+
+
+
+
+
+
+
+
# Set up global resource manager
@@ -1023,3 +1078,30 @@ _initialize(globals())
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+