aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg_resources.py152
-rwxr-xr-xsetuptools.egg-info/entry_points.txt3
-rw-r--r--setuptools/dist.py8
3 files changed, 143 insertions, 20 deletions
diff --git a/pkg_resources.py b/pkg_resources.py
index 79db00b8..3192f200 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -144,7 +144,7 @@ __all__ = [
# Parsing functions and string utilities
'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
- 'safe_extra', 'to_filename',
+ 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',
# filesystem utilities
'ensure_directory', 'normalize_path',
@@ -1146,6 +1146,129 @@ def to_filename(name):
+_marker_names = {
+ 'os': ['name'], 'sys': ['platform'],
+ 'platform': ['version','machine','python_implementation'],
+ 'python_version': [], 'python_full_version': [], 'extra':[],
+}
+
+_marker_values = {
+ 'os_name': lambda: os.name,
+ 'sys_platform': lambda: sys.platform,
+ 'python_full_version': lambda: sys.version.split()[0],
+ 'python_version': lambda:'%s.%s' % (sys.version_info[0], sys.version_info[1]),
+ 'platform_version': lambda: _platinfo('version'),
+ 'platform_machine': lambda: _platinfo('machine'),
+ 'python_implementation': lambda: _platinfo('python_implementation') or _pyimp(),
+}
+
+def _platinfo(attr):
+ try:
+ import platform
+ except ImportError:
+ return ''
+ return getattr(platform, attr, lambda:'')()
+
+def _pyimp():
+ if sys.platform=='cli':
+ return 'IronPython'
+ elif sys.platform.startswith('java'):
+ return 'Jython'
+ elif '__pypy__' in sys.builtin_module_names:
+ return 'PyPy'
+ else:
+ return 'CPython'
+
+def invalid_marker(text):
+ """Validate text as a PEP 426 environment marker; return exception or False"""
+ try:
+ evaluate_marker(text)
+ except SyntaxError:
+ return sys.exc_info()[1]
+ return False
+
+def evaluate_marker(text, extra=None, _ops={}):
+ """Evaluate a PEP 426 environment marker; SyntaxError if marker is invalid"""
+
+ if not _ops:
+
+ from token import NAME, STRING
+ import token, symbol, operator
+
+ def and_test(nodelist):
+ # MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
+ return reduce(operator.and_, [interpret(nodelist[i]) for i in range(1,len(nodelist),2)])
+
+ def test(nodelist):
+ # MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
+ return reduce(operator.or_, [interpret(nodelist[i]) for i in range(1,len(nodelist),2)])
+
+ def atom(nodelist):
+ t = nodelist[1][0]
+ if t == token.LPAR:
+ if nodelist[2][0] == token.RPAR:
+ raise SyntaxError("Empty parentheses")
+ return interpret(nodelist[2])
+ raise SyntaxError("Language feature not supported in environment markers")
+
+ def comparison(nodelist):
+ if len(nodelist)>4:
+ raise SyntaxError("Chained comparison not allowed in environment markers")
+ comp = nodelist[2][1]
+ cop = comp[1]
+ if comp[0] == NAME:
+ if len(nodelist[2]) == 3:
+ if cop == 'not':
+ cop = 'not in'
+ else:
+ cop = 'is not'
+ try:
+ cop = _ops[cop]
+ except KeyError:
+ raise SyntaxError(repr(cop)+" operator not allowed in environment markers")
+ return cop(evaluate(nodelist[1]), evaluate(nodelist[3]))
+
+ _ops.update({
+ symbol.test: test, symbol.and_test: and_test, symbol.atom: atom,
+ symbol.comparison: comparison, 'not in': lambda x,y: x not in y,
+ 'in': lambda x,y: x in y, '==': operator.eq, '!=': operator.ne,
+ })
+ if hasattr(symbol,'or_test'):
+ _ops[symbol.or_test] = test
+
+ def interpret(nodelist):
+ while len(nodelist)==2: nodelist = nodelist[1]
+ try:
+ op = _ops[nodelist[0]]
+ except KeyError:
+ raise SyntaxError("Comparison or logical expression expected")
+ raise SyntaxError("Language feature not supported in environment markers: "+symbol.sym_name[nodelist[0]])
+ return op(nodelist)
+
+ def evaluate(nodelist):
+ while len(nodelist)==2: nodelist = nodelist[1]
+ kind = nodelist[0]
+ name = nodelist[1]
+ #while len(name)==2: name = name[1]
+ if kind==NAME:
+ try:
+ op = _marker_values[name]
+ except KeyError:
+ raise SyntaxError("Unknown name %r" % name)
+ return op()
+ if kind==STRING:
+ s = nodelist[1]
+ if s[:1] not in "'\"" or s.startswith('"""') or s.startswith("'''") \
+ or '\\' in s:
+ raise SyntaxError(
+ "Only plain strings allowed in environment markers")
+ return s[1:-1]
+ raise SyntaxError("Language feature not supported in environment markers")
+
+ import parser
+ return interpret(parser.expr(text).totuple(1)[1])
+
+
class NullProvider:
"""Try to implement resources and metadata for arbitrary PEP 302 loaders"""
@@ -1925,7 +2048,6 @@ def parse_version(s):
parts.pop()
parts.append(part)
return tuple(parts)
-
class EntryPoint(object):
"""Object representing an advertised importable object"""
@@ -2139,7 +2261,14 @@ class Distribution(object):
dm = self.__dep_map = {None: []}
for name in 'requires.txt', 'depends.txt':
for extra,reqs in split_sections(self._get_metadata(name)):
- if extra: extra = safe_extra(extra)
+ if extra:
+ if ':' in extra:
+ extra, marker = extra.split(':',1)
+ if invalid_marker(marker):
+ reqs=[] # XXX warn
+ elif not evaluate_marker(marker):
+ reqs=[]
+ extra = safe_extra(extra) or None
dm.setdefault(extra,[]).extend(parse_requirements(reqs))
return dm
_dep_map = property(_dep_map)
@@ -2163,6 +2292,8 @@ class Distribution(object):
for line in self.get_metadata_lines(name):
yield line
+
+
def activate(self,path=None):
"""Ensure distribution is importable on `path` (default=sys.path)"""
if path is None: path = sys.path
@@ -2201,6 +2332,9 @@ class Distribution(object):
raise AttributeError,attr
return getattr(self._provider, attr)
+
+
+
#@classmethod
def from_filename(cls,filename,metadata=None, **kw):
return cls.from_location(
@@ -2242,18 +2376,6 @@ class Distribution(object):
-
-
-
-
-
-
-
-
-
-
-
-
def insert_on(self, path, loc = None):
"""Insert self.location in path before its nearest parent directory"""
diff --git a/setuptools.egg-info/entry_points.txt b/setuptools.egg-info/entry_points.txt
index 2642c316..04fbe4be 100755
--- a/setuptools.egg-info/entry_points.txt
+++ b/setuptools.egg-info/entry_points.txt
@@ -7,6 +7,7 @@ build_py = setuptools.command.build_py:build_py
saveopts = setuptools.command.saveopts:saveopts
egg_info = setuptools.command.egg_info:egg_info
register = setuptools.command.register:register
+upload = setuptools.command.upload:upload
install_egg_info = setuptools.command.install_egg_info:install_egg_info
alias = setuptools.command.alias:alias
easy_install = setuptools.command.easy_install:easy_install
@@ -31,7 +32,7 @@ depends.txt = setuptools.command.egg_info:warn_depends_obsolete
[console_scripts]
easy_install = setuptools.command.easy_install:main
-easy_install-2.6 = setuptools.command.easy_install:main
+easy_install-2.3 = setuptools.command.easy_install:main
[setuptools.file_finders]
svn_cvs = setuptools.command.sdist:_default_revctrl
diff --git a/setuptools/dist.py b/setuptools/dist.py
index c1218ef2..582cc557 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -47,7 +47,6 @@ def assert_string_list(dist, attr, value):
raise DistutilsSetupError(
"%r must be a list of strings (got %r)" % (attr,value)
)
-
def check_nsp(dist, attr, value):
"""Verify that namespace packages are valid"""
assert_string_list(dist,attr,value)
@@ -69,6 +68,10 @@ def check_extras(dist, attr, value):
"""Verify that extras_require mapping is valid"""
try:
for k,v in value.items():
+ if ':' in k:
+ k,m = k.split(':',1)
+ if pkg_resources.invalid_marker(m):
+ raise DistutilsSetupError("Invalid environment marker: "+m)
list(pkg_resources.parse_requirements(v))
except (TypeError,ValueError,AttributeError):
raise DistutilsSetupError(
@@ -77,9 +80,6 @@ def check_extras(dist, attr, value):
"requirement specifiers."
)
-
-
-
def assert_bool(dist, attr, value):
"""Verify that value is True, False, 0, or 1"""
if bool(value) != value: