1
# -*- coding: utf-8 -*-
3
# Copyright (C) 2012-2013 Vinay Sajip.
4
# Licensed to the Python Software Foundation under a contributor agreement.
5
# See LICENSE.txt and CONTRIBUTORS.txt.
7
"""Parser for the environment markers micro-language defined in PEP 345."""
14
from .compat import python_implementation, string_types
15
from .util import in_venv
17
__all__ = ['interpret']
19
class Evaluator(object):
21
A limited evaluator for Python expressions.
25
'eq': lambda x, y: x == y,
26
'gt': lambda x, y: x > y,
27
'gte': lambda x, y: x >= y,
28
'in': lambda x, y: x in y,
29
'lt': lambda x, y: x < y,
30
'lte': lambda x, y: x <= y,
31
'not': lambda x: not x,
32
'noteq': lambda x, y: x != y,
33
'notin': lambda x, y: x not in y,
37
'sys.platform': sys.platform,
38
'python_version': '%s.%s' % sys.version_info[:2],
39
# parsing sys.platform is not reliable, but there is no other
40
# way to get e.g. 2.7.2+, and the PEP is defined with sys.version
41
'python_full_version': sys.version.split(' ', 1)[0],
43
'platform.in_venv': str(in_venv()),
44
'platform.version': platform.version(),
45
'platform.machine': platform.machine(),
46
'platform.python_implementation': platform.python_implementation(),
49
def __init__(self, context=None):
51
Initialise an instance.
53
:param context: If specified, names are looked up in this mapping.
55
self.context = context or {}
58
def get_fragment(self, offset):
60
Get the part of the source which is causing a problem.
63
s = '%r' % (self.source[offset:offset + fragment_len])
64
if offset + fragment_len < len(self.source):
68
def get_handler(self, node_type):
70
Get a handler for the specified AST node type.
72
return getattr(self, 'do_%s' % node_type, None)
74
def evaluate(self, node, filename=None):
76
Evaluate a source string or node, using ``filename`` when
79
if isinstance(node, string_types):
81
kwargs = {'mode': 'eval'}
83
kwargs['filename'] = filename
85
node = ast.parse(node, **kwargs)
86
except SyntaxError as e:
87
s = self.get_fragment(e.offset)
88
raise SyntaxError('syntax error %s' % s)
89
node_type = node.__class__.__name__.lower()
90
handler = self.get_handler(node_type)
92
if self.source is None:
93
s = '(source not available)'
95
s = self.get_fragment(node.col_offset)
96
raise SyntaxError("don't know how to evaluate %r %s" % (
100
def get_attr_key(self, node):
101
assert isinstance(node, ast.Attribute), 'attribute node expected'
102
return '%s.%s' % (node.value.id, node.attr)
104
def do_attribute(self, node):
106
if not isinstance(node.value, ast.Name):
109
key = self.get_attr_key(node)
110
valid = key in self.context or key in self.allowed_values
112
raise SyntaxError('invalid expression: %s' % key)
113
if key in self.context:
114
result = self.context[key]
116
result = self.allowed_values[key]
119
def do_boolop(self, node):
120
result = self.evaluate(node.values[0])
121
is_or = node.op.__class__ is ast.Or
122
is_and = node.op.__class__ is ast.And
123
assert is_or or is_and
124
if (is_and and result) or (is_or and not result):
125
for n in node.values[1:]:
126
result = self.evaluate(n)
127
if (is_or and result) or (is_and and not result):
131
def do_compare(self, node):
132
def sanity_check(lhsnode, rhsnode):
134
if isinstance(lhsnode, ast.Str) and isinstance(rhsnode, ast.Str):
136
elif (isinstance(lhsnode, ast.Attribute)
137
and isinstance(rhsnode, ast.Attribute)):
138
klhs = self.get_attr_key(lhsnode)
139
krhs = self.get_attr_key(rhsnode)
142
s = self.get_fragment(node.col_offset)
143
raise SyntaxError('Invalid comparison: %s' % s)
146
lhs = self.evaluate(lhsnode)
148
for op, rhsnode in zip(node.ops, node.comparators):
149
sanity_check(lhsnode, rhsnode)
150
op = op.__class__.__name__.lower()
151
if op not in self.operators:
152
raise SyntaxError('unsupported operation: %r' % op)
153
rhs = self.evaluate(rhsnode)
154
result = self.operators[op](lhs, rhs)
161
def do_expression(self, node):
162
return self.evaluate(node.body)
164
def do_name(self, node):
166
if node.id in self.context:
168
result = self.context[node.id]
169
elif node.id in self.allowed_values:
171
result = self.allowed_values[node.id]
173
raise SyntaxError('invalid expression: %s' % node.id)
176
def do_str(self, node):
180
def interpret(marker, execution_context=None):
182
Interpret a marker and return a result depending on environment.
184
:param marker: The marker to interpret.
186
:param execution_context: The context used for name lookup.
187
:type execution_context: mapping
189
return Evaluator(execution_context).evaluate(marker.strip())