Commit e7d341af authored by pje's avatar pje

Backport experimental environment marker support from the trunk

--HG--
branch : setuptools-0.6
parent 46d1c4ed
...@@ -144,7 +144,7 @@ __all__ = [ ...@@ -144,7 +144,7 @@ __all__ = [
# Parsing functions and string utilities # Parsing functions and string utilities
'parse_requirements', 'parse_version', 'safe_name', 'safe_version', 'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
'safe_extra', 'to_filename', 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',
# filesystem utilities # filesystem utilities
'ensure_directory', 'normalize_path', 'ensure_directory', 'normalize_path',
...@@ -1146,6 +1146,129 @@ def to_filename(name): ...@@ -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: class NullProvider:
"""Try to implement resources and metadata for arbitrary PEP 302 loaders""" """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
...@@ -1925,7 +2048,6 @@ def parse_version(s): ...@@ -1925,7 +2048,6 @@ def parse_version(s):
parts.pop() parts.pop()
parts.append(part) parts.append(part)
return tuple(parts) return tuple(parts)
class EntryPoint(object): class EntryPoint(object):
"""Object representing an advertised importable object""" """Object representing an advertised importable object"""
...@@ -2139,7 +2261,14 @@ class Distribution(object): ...@@ -2139,7 +2261,14 @@ class Distribution(object):
dm = self.__dep_map = {None: []} dm = self.__dep_map = {None: []}
for name in 'requires.txt', 'depends.txt': for name in 'requires.txt', 'depends.txt':
for extra,reqs in split_sections(self._get_metadata(name)): 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)) dm.setdefault(extra,[]).extend(parse_requirements(reqs))
return dm return dm
_dep_map = property(_dep_map) _dep_map = property(_dep_map)
...@@ -2163,6 +2292,8 @@ class Distribution(object): ...@@ -2163,6 +2292,8 @@ class Distribution(object):
for line in self.get_metadata_lines(name): for line in self.get_metadata_lines(name):
yield line yield line
def activate(self,path=None): def activate(self,path=None):
"""Ensure distribution is importable on `path` (default=sys.path)""" """Ensure distribution is importable on `path` (default=sys.path)"""
if path is None: path = sys.path if path is None: path = sys.path
...@@ -2201,6 +2332,9 @@ class Distribution(object): ...@@ -2201,6 +2332,9 @@ class Distribution(object):
raise AttributeError,attr raise AttributeError,attr
return getattr(self._provider, attr) return getattr(self._provider, attr)
#@classmethod #@classmethod
def from_filename(cls,filename,metadata=None, **kw): def from_filename(cls,filename,metadata=None, **kw):
return cls.from_location( return cls.from_location(
...@@ -2242,18 +2376,6 @@ class Distribution(object): ...@@ -2242,18 +2376,6 @@ class Distribution(object):
def insert_on(self, path, loc = None): def insert_on(self, path, loc = None):
"""Insert self.location in path before its nearest parent directory""" """Insert self.location in path before its nearest parent directory"""
......
...@@ -7,6 +7,7 @@ build_py = setuptools.command.build_py:build_py ...@@ -7,6 +7,7 @@ build_py = setuptools.command.build_py:build_py
saveopts = setuptools.command.saveopts:saveopts saveopts = setuptools.command.saveopts:saveopts
egg_info = setuptools.command.egg_info:egg_info egg_info = setuptools.command.egg_info:egg_info
register = setuptools.command.register:register register = setuptools.command.register:register
upload = setuptools.command.upload:upload
install_egg_info = setuptools.command.install_egg_info:install_egg_info install_egg_info = setuptools.command.install_egg_info:install_egg_info
alias = setuptools.command.alias:alias alias = setuptools.command.alias:alias
easy_install = setuptools.command.easy_install:easy_install easy_install = setuptools.command.easy_install:easy_install
...@@ -31,7 +32,7 @@ depends.txt = setuptools.command.egg_info:warn_depends_obsolete ...@@ -31,7 +32,7 @@ depends.txt = setuptools.command.egg_info:warn_depends_obsolete
[console_scripts] [console_scripts]
easy_install = setuptools.command.easy_install:main 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] [setuptools.file_finders]
svn_cvs = setuptools.command.sdist:_default_revctrl svn_cvs = setuptools.command.sdist:_default_revctrl
......
...@@ -47,7 +47,6 @@ def assert_string_list(dist, attr, value): ...@@ -47,7 +47,6 @@ def assert_string_list(dist, attr, value):
raise DistutilsSetupError( raise DistutilsSetupError(
"%r must be a list of strings (got %r)" % (attr,value) "%r must be a list of strings (got %r)" % (attr,value)
) )
def check_nsp(dist, attr, value): def check_nsp(dist, attr, value):
"""Verify that namespace packages are valid""" """Verify that namespace packages are valid"""
assert_string_list(dist,attr,value) assert_string_list(dist,attr,value)
...@@ -69,6 +68,10 @@ def check_extras(dist, attr, value): ...@@ -69,6 +68,10 @@ def check_extras(dist, attr, value):
"""Verify that extras_require mapping is valid""" """Verify that extras_require mapping is valid"""
try: try:
for k,v in value.items(): 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)) list(pkg_resources.parse_requirements(v))
except (TypeError,ValueError,AttributeError): except (TypeError,ValueError,AttributeError):
raise DistutilsSetupError( raise DistutilsSetupError(
...@@ -77,9 +80,6 @@ def check_extras(dist, attr, value): ...@@ -77,9 +80,6 @@ def check_extras(dist, attr, value):
"requirement specifiers." "requirement specifiers."
) )
def assert_bool(dist, attr, value): def assert_bool(dist, attr, value):
"""Verify that value is True, False, 0, or 1""" """Verify that value is True, False, 0, or 1"""
if bool(value) != value: if bool(value) != value:
......
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