You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

markers.py 3.9 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. # -*- coding: utf-8 -*-
  2. """Interpret PEP 345 environment markers.
  3. EXPR [in|==|!=|not in] EXPR [or|and] ...
  4. where EXPR belongs to any of those:
  5. python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1])
  6. python_full_version = sys.version.split()[0]
  7. os.name = os.name
  8. sys.platform = sys.platform
  9. platform.version = platform.version()
  10. platform.machine = platform.machine()
  11. platform.python_implementation = platform.python_implementation()
  12. a free string, like '2.6', or 'win32'
  13. """
  14. __all__ = ['default_environment', 'compile', 'interpret']
  15. import ast
  16. import os
  17. import platform
  18. import sys
  19. import weakref
  20. _builtin_compile = compile
  21. try:
  22. from platform import python_implementation
  23. except ImportError:
  24. if os.name == "java":
  25. # Jython 2.5 has ast module, but not platform.python_implementation() function.
  26. def python_implementation():
  27. return "Jython"
  28. else:
  29. raise
  30. # restricted set of variables
  31. _VARS = {'sys.platform': sys.platform,
  32. 'python_version': '%s.%s' % sys.version_info[:2],
  33. # FIXME parsing sys.platform is not reliable, but there is no other
  34. # way to get e.g. 2.7.2+, and the PEP is defined with sys.version
  35. 'python_full_version': sys.version.split(' ', 1)[0],
  36. 'os.name': os.name,
  37. 'platform.version': platform.version(),
  38. 'platform.machine': platform.machine(),
  39. 'platform.python_implementation': python_implementation(),
  40. 'extra': None # wheel extension
  41. }
  42. for var in list(_VARS.keys()):
  43. if '.' in var:
  44. _VARS[var.replace('.', '_')] = _VARS[var]
  45. def default_environment():
  46. """Return copy of default PEP 385 globals dictionary."""
  47. return dict(_VARS)
  48. class ASTWhitelist(ast.NodeTransformer):
  49. def __init__(self, statement):
  50. self.statement = statement # for error messages
  51. ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str)
  52. # Bool operations
  53. ALLOWED += (ast.And, ast.Or)
  54. # Comparison operations
  55. ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn)
  56. def visit(self, node):
  57. """Ensure statement only contains allowed nodes."""
  58. if not isinstance(node, self.ALLOWED):
  59. raise SyntaxError('Not allowed in environment markers.\n%s\n%s' %
  60. (self.statement,
  61. (' ' * node.col_offset) + '^'))
  62. return ast.NodeTransformer.visit(self, node)
  63. def visit_Attribute(self, node):
  64. """Flatten one level of attribute access."""
  65. new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx)
  66. return ast.copy_location(new_node, node)
  67. def parse_marker(marker):
  68. tree = ast.parse(marker, mode='eval')
  69. new_tree = ASTWhitelist(marker).generic_visit(tree)
  70. return new_tree
  71. def compile_marker(parsed_marker):
  72. return _builtin_compile(parsed_marker, '<environment marker>', 'eval',
  73. dont_inherit=True)
  74. _cache = weakref.WeakValueDictionary()
  75. def compile(marker):
  76. """Return compiled marker as a function accepting an environment dict."""
  77. try:
  78. return _cache[marker]
  79. except KeyError:
  80. pass
  81. if not marker.strip():
  82. def marker_fn(environment=None, override=None):
  83. """"""
  84. return True
  85. else:
  86. compiled_marker = compile_marker(parse_marker(marker))
  87. def marker_fn(environment=None, override=None):
  88. """override updates environment"""
  89. if override is None:
  90. override = {}
  91. if environment is None:
  92. environment = default_environment()
  93. environment.update(override)
  94. return eval(compiled_marker, environment)
  95. marker_fn.__doc__ = marker
  96. _cache[marker] = marker_fn
  97. return _cache[marker]
  98. def interpret(marker, environment=None):
  99. return compile(marker)(environment)