def __init__(self, expr, context):
self.expr = expr
self.context = context
self.expr_table = {
LLLnode: self.get_expr,
ast.Num: self.number,
ast.Str: self.string,
ast.NameConstant: self.constants,
ast.Name: self.variables,
ast.Attribute: self.attribute,
ast.Subscript: self.subscript,
ast.BinOp: self.arithmetic,
ast.Compare: self.compare,
ast.BoolOp: self.boolean_operations,
ast.UnaryOp: self.unary_operations,
ast.Call: self.call,
ast.List: self.list_literals,
ast.Dict: self.struct_literals,
ast.Tuple: self.tuple_literals,
}
expr_type = self.expr.__class__
if expr_type in self.expr_table:
self.lll_node = self.expr_table[expr_type]()
else:
raise Exception("Unsupported operator: %r" % ast.dump(self.expr))
python类Dict()的实例源码
def pop_format_context(self, expl_expr):
"""Format the %-formatted string with current format context.
The expl_expr should be an ast.Str instance constructed from
the %-placeholders created by .explanation_param(). This will
add the required code to format said string to .on_failure and
return the ast.Name instance of the formatted string.
"""
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
return ast.Name(name, ast.Load())
def pop_format_context(self, expl_expr):
"""Format the %-formatted string with current format context.
The expl_expr should be an ast.Str instance constructed from
the %-placeholders created by .explanation_param(). This will
add the required code to format said string to .on_failure and
return the ast.Name instance of the formatted string.
"""
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
return ast.Name(name, ast.Load())
def pop_format_context(self, expl_expr):
"""Format the %-formatted string with current format context.
The expl_expr should be an ast.Str instance constructed from
the %-placeholders created by .explanation_param(). This will
add the required code to format said string to .on_failure and
return the ast.Name instance of the formatted string.
"""
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
return ast.Name(name, ast.Load())
def pop_format_context(self, expl_expr):
"""Format the %-formatted string with current format context.
The expl_expr should be an ast.Str instance constructed from
the %-placeholders created by .explanation_param(). This will
add the required code to format said string to .on_failure and
return the ast.Name instance of the formatted string.
"""
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
return ast.Name(name, ast.Load())
def pop_format_context(self, expl_expr):
"""Format the %-formatted string with current format context.
The expl_expr should be an ast.Str instance constructed from
the %-placeholders created by .explanation_param(). This will
add the required code to format said string to .on_failure and
return the ast.Name instance of the formatted string.
"""
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
return ast.Name(name, ast.Load())
def _process_function_signature(stmt, arg_names, static_env):
return_type = Ellipsis
if isinstance(stmt, ast.Expr):
value = stmt.value
if isinstance(value, ast.BinOp):
left, op, right = value.left, value.op, value.right
if isinstance(op, ast.RShift):
arg_types = fn._process_argument_signature(left, arg_names,
static_env)
return_type = static_env.eval_expr_ast(right)
else:
return None
elif isinstance(value, ast.Dict) or isinstance(value, ast.Set):
arg_types = fn._process_argument_signature(value, arg_names,
static_env)
else:
return None
else:
return None
if arg_types is None:
return None
return (arg_types, return_type)
def astFormat(x, gid=None):
"""Given a value, turn it into an AST if it's a constant; otherwise, leave it alone."""
if type(x) in [int, float, complex]:
return ast.Num(x)
elif type(x) == bool or x == None:
return ast.NameConstant(x)
elif type(x) == type:
types = { bool : "bool", int : "int", float : "float",
complex : "complex", str : "str", bytes : "bytes", unicode : "unicode",
list : "list", tuple : "tuple", dict : "dict" }
return ast.Name(types[x], ast.Load())
elif type(x) == str: # str or unicode
return ast.Str(x)
elif type(x) == bytes:
return ast.Bytes(x)
elif type(x) == list:
elts = [astFormat(val) for val in x]
return ast.List(elts, ast.Load())
elif type(x) == dict:
keys = []
vals = []
for key in x:
keys.append(astFormat(key))
vals.append(astFormat(x[key]))
return ast.Dict(keys, vals)
elif type(x) == tuple:
elts = [astFormat(val) for val in x]
return ast.Tuple(elts, ast.Load())
elif type(x) == set:
elts = [astFormat(val) for val in x]
if len(elts) == 0: # needs to be a call instead
return ast.Call(ast.Name("set", ast.Load()), [], [])
else:
return ast.Set(elts)
elif type(x) == slice:
return ast.Slice(astFormat(x.start), astFormat(x.stop), astFormat(x.step))
elif isinstance(x, ast.AST):
return x # Do not change if it's not constant!
else:
log("astTools\tastFormat\t" + str(type(x)) + "," + str(x),"bug")
return None
def visit_Dict(self, node):
"""
Dict(expr* keys, expr* values)
"""
els = []
for k, v in zip(node.keys, node.values):
if isinstance(k, ast.Name):
els.append('"%s" => %s' % (self.visit(k), self.visit(v)))
else: # ast.Str, ast.Num
if self._dict_format == True: # ast.Str
els.append("%s: %s" % (self.visit(k), self.visit(v)))
else: # ast.Str, ast.Num
els.append("%s => %s" % (self.visit(k), self.visit(v)))
return "{%s}" % (", ".join(els))
def test_dict(self):
d = ast.Dict([], [ast.Name("x", ast.Load())])
self.expr(d, "same number of keys as values")
d = ast.Dict([None], [ast.Name("x", ast.Load())])
self.expr(d, "None disallowed")
d = ast.Dict([ast.Name("x", ast.Load())], [None])
self.expr(d, "None disallowed")
def _get_assign_names(targets, load_names, store_names):
for target in targets:
orig_target = target
target = _get_ast_name_node(target)
if (isinstance(target, ast.Name)
and isinstance(target.ctx, ast.Store)):
# 'x = value': store name 'x'
store_names.add(target.id)
elif (isinstance(target, ast.Name)
and isinstance(target.ctx, ast.Load)):
# 'obj.attr = value': load name 'obj'
load_names.add(target.id)
elif isinstance(target, ast.Tuple):
# x, y = ...
_get_assign_names(target.elts, load_names, store_names)
elif isinstance(target, ast.Constant):
# '(1).__class__ = MyInt': it raises a TypeError
raise ComplexAssignment(orig_target)
elif isinstance(target, (ast.Dict, ast.List)):
# '{...}[key] = ...', '[...][index] = ...'
pass
elif isinstance(target, ast.Call):
# 'globals()[key] = value'
# 'type(mock)._mock_check_sig = checksig'
raise ComplexAssignment(orig_target)
else:
raise Exception("unsupported assign target: %s"
% ast.dump(target))
def optimize_iterable(self, node):
# it's already a constant, nothing to do
if isinstance(node, ast.Constant):
return
# remplace empty dict (create at runtime) with an empty tuple
# (constant)
if isinstance(node, ast.Dict) and not node.keys:
return self.new_constant(node, ())
# FIXME: optimize dict?
value = get_literal(node, types=(list, set), constant_items=True)
if value is UNSET:
return
if not value:
# replace empty iterable with an empty tuple
return self.new_constant(node, ())
if len(value) > self.config.max_seq_len:
return
if isinstance(value, list):
return self.new_constant(node, tuple(value))
if isinstance(value, set):
return self.new_constant(node, frozenset(value))
def _new_constant(node, value):
if isinstance(value, ast.AST):
# convenient shortcut: return the AST object unchanged
return value
# FIXME: test the config directly here?
if value is None:
new_node = ast.Constant(value=None)
elif isinstance(value, (bool, int, float, complex, str, bytes)):
new_node = ast.Constant(value=value)
elif isinstance(value, (tuple, frozenset)):
if not _is_constant(value):
raise TypeError("container items are not constant: %r" % (value,))
new_node = ast.Constant(value=value)
elif isinstance(value, list):
elts = [_new_constant(node, elt) for elt in value]
new_node = ast.List(elts=elts, ctx=ast.Load())
elif isinstance(value, dict):
keys = []
values = []
for key, value in value.items():
keys.append(_new_constant(node, key))
values.append(_new_constant(node, value))
new_node = ast.Dict(keys=keys, values=values, ctx=ast.Load())
elif isinstance(value, set):
elts = [_new_constant(node, elt) for elt in value]
new_node = ast.Set(elts=elts, ctx=ast.Load())
else:
raise TypeError("unknown type: %s" % type(value).__name__)
copy_lineno(node, new_node)
return new_node
# FIXME: use functools.singledispatch?
def check_empty_dict(node):
if not isinstance(node, ast.Dict):
return
if len(node.keys) == 0 and len(node.values) == 0:
yield (node.lineno, 'init by {}')
def _check_string_formatting(self, node):
[lhs, rhs] = [node.left, node.right]
if isinstance(lhs, ast.Str):
lhs = lhs.s
else:
return
if isinstance(rhs, ast.Tuple):
if sys.version_info >= (3, 5):
if any(isinstance(elt, ast.Starred) for elt in rhs.elts): # pylint: disable=no-member
return
rhs = tuple(
elt.s if isinstance(elt, ast.Str) else 0
for elt in rhs.elts
)
elif isinstance(rhs, ast.Dict):
new_rhs = {}
for key, value in zip(rhs.keys, rhs.values):
if isinstance(key, ast.Str):
key = key.s
else:
return
if isinstance(value, ast.Str):
value = value.s
else:
value = 0
new_rhs[key] = value
rhs = new_rhs
elif isinstance(rhs, ast.Str):
rhs = rhs.s
elif isinstance(rhs, ast.Num):
rhs = 0
else:
return
try:
lhs % rhs
except KeyError as exc:
yield self.tag(node, 'string-formatting-error', 'missing key', str(exc))
except Exception as exc: # pylint: disable=broad-except
yield self.tag(node, 'string-formatting-error', str(exc))
def test_dict(self):
d = ast.Dict([], [ast.Name("x", ast.Load())])
self.expr(d, "same number of keys as values")
d = ast.Dict([None], [ast.Name("x", ast.Load())])
self.expr(d, "None disallowed")
d = ast.Dict([ast.Name("x", ast.Load())], [None])
self.expr(d, "None disallowed")
def __init__(self, operators=None, functions=None, names=None):
super(EvalWithCompoundTypes, self).__init__(operators, functions, names)
self.functions.update(
list=list,
tuple=tuple,
dict=dict,
set=set)
self.nodes.update({
ast.Dict: self._eval_dict,
ast.Tuple: self._eval_tuple,
ast.List: self._eval_list,
ast.Set: self._eval_set
})
def find_dict_index(dict_node, key):
"""Given a key, find the index of the corresponding dict entry."""
assert dict_node.__class__ is ast.Dict, ast_err_msg(dict_node)
indices = [i for i, n in enumerate(dict_node.keys) if
n.__class__ is ast.Str and n.s == key]
assert len(indices) < 2, (
'Found redundant dict entries for key "%s"' % key)
return indices[0] if indices else None
def ast_dict_index(dnode, key):
"""Search an ast.Dict for the argument key, and return its index."""
idx = [i for i in range(len(dnode.keys)) if (
type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
if not idx:
return -1
elif len(idx) > 1:
raise gclient_utils.Error('Multiple dict entries with same key in AST')
return idx[-1]
def ast2str(node, indent=0):
"""Return a pretty-printed rendition of an ast.Node."""
t = type(node)
if t is ast.Module:
return '\n'.join([ast2str(x, indent) for x in node.body])
elif t is ast.Assign:
return ((' ' * indent) +
' = '.join([ast2str(x) for x in node.targets] +
[ast2str(node.value, indent)]) + '\n')
elif t is ast.Name:
return node.id
elif t is ast.List:
if not node.elts:
return '[]'
elif len(node.elts) == 1:
return '[' + ast2str(node.elts[0], indent) + ']'
return ('[\n' + (' ' * (indent + 1)) +
(',\n' + (' ' * (indent + 1))).join(
[ast2str(x, indent + 1) for x in node.elts]) +
'\n' + (' ' * indent) + ']')
elif t is ast.Dict:
if not node.keys:
return '{}'
elif len(node.keys) == 1:
return '{%s: %s}' % (ast2str(node.keys[0]),
ast2str(node.values[0], indent + 1))
return ('{\n' + (' ' * (indent + 1)) +
(',\n' + (' ' * (indent + 1))).join(
['%s: %s' % (ast2str(node.keys[i]),
ast2str(node.values[i], indent + 1))
for i in range(len(node.keys))]) +
'\n' + (' ' * indent) + '}')
elif t is ast.Str:
return "'%s'" % node.s
else:
raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
% (node.lineno, node.col_offset, t))
def _get_ordered_child_nodes(node):
if isinstance(node, ast.Dict):
children = []
for i in range(len(node.keys)):
children.append(node.keys[i])
children.append(node.values[i])
return children
elif isinstance(node, ast.Call):
children = [node.func] + node.args
for kw in node.keywords:
children.append(kw.value)
# TODO: take care of Python 3.5 updates (eg. args=[Starred] and keywords)
if hasattr(node, "starargs") and node.starargs is not None:
children.append(node.starargs)
if hasattr(node, "kwargs") and node.kwargs is not None:
children.append(node.kwargs)
children.sort(key=lambda x: (x.lineno, x.col_offset))
return children
elif isinstance(node, ast.arguments):
children = node.args + node.kwonlyargs + node.kw_defaults + node.defaults
if node.vararg is not None:
children.append(node.vararg)
if node.kwarg is not None:
children.append(node.kwarg)
children.sort(key=lambda x: (x.lineno, x.col_offset))
return children
else:
return ast.iter_child_nodes(node)
def find_dict_index(dict_node, key):
"""Given a key, find the index of the corresponding dict entry."""
assert dict_node.__class__ is ast.Dict, ast_err_msg(dict_node)
indices = [i for i, n in enumerate(dict_node.keys) if
n.__class__ is ast.Str and n.s == key]
assert len(indices) < 2, (
'Found redundant dict entries for key "%s"' % key)
return indices[0] if indices else None
def ast_dict_index(dnode, key):
"""Search an ast.Dict for the argument key, and return its index."""
idx = [i for i in range(len(dnode.keys)) if (
type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
if not idx:
return -1
elif len(idx) > 1:
raise gclient_utils.Error('Multiple dict entries with same key in AST')
return idx[-1]
def ast2str(node, indent=0):
"""Return a pretty-printed rendition of an ast.Node."""
t = type(node)
if t is ast.Module:
return '\n'.join([ast2str(x, indent) for x in node.body])
elif t is ast.Assign:
return ((' ' * indent) +
' = '.join([ast2str(x) for x in node.targets] +
[ast2str(node.value, indent)]) + '\n')
elif t is ast.Name:
return node.id
elif t is ast.List:
if not node.elts:
return '[]'
elif len(node.elts) == 1:
return '[' + ast2str(node.elts[0], indent) + ']'
return ('[\n' + (' ' * (indent + 1)) +
(',\n' + (' ' * (indent + 1))).join(
[ast2str(x, indent + 1) for x in node.elts]) +
'\n' + (' ' * indent) + ']')
elif t is ast.Dict:
if not node.keys:
return '{}'
elif len(node.keys) == 1:
return '{%s: %s}' % (ast2str(node.keys[0]),
ast2str(node.values[0], indent + 1))
return ('{\n' + (' ' * (indent + 1)) +
(',\n' + (' ' * (indent + 1))).join(
['%s: %s' % (ast2str(node.keys[i]),
ast2str(node.values[i], indent + 1))
for i in range(len(node.keys))]) +
'\n' + (' ' * indent) + '}')
elif t is ast.Str:
return "'%s'" % node.s
else:
raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
% (node.lineno, node.col_offset, t))
def resolve_attr_id(node):
if isinstance(node, (ast.Attribute, ast.Subscript)):
value_id = None
if isinstance(node.value, (ast.Name, ast.Attribute, ast.Subscript)):
value_id = resolve_attr_id(node.value)
elif isinstance(node.value, ast.Call):
value_id = resolve_attr_id(node.value)
elif isinstance(node.value, ast.Str):
value_id = 'str'
elif isinstance(node.value, ast.Bytes):
value_id = 'bytes'
elif isinstance(node.value, (ast.List, ast.ListComp)):
value_id = 'list'
elif isinstance(node.value, ast.Tuple):
value_id = 'tuple'
elif isinstance(node.value, (ast.Set, ast.SetComp)):
value_id = 'set'
elif isinstance(node.value, (ast.Dict, ast.DictComp)):
value_id = 'dict'
else:
raise SyntaxError(
'unsupport type: {}'.format(ast.dump(node.value))
)
if isinstance(node, ast.Attribute):
return '{}.{}'.format(value_id, node.attr)
elif isinstance(node, ast.Subscript):
slice = None
if isinstance(node.slice.value, ast.Str):
slice = node.slice.value.s
elif isinstance(node.slice.value, ast.Num):
slice = node.slice.value.n
elif isinstance(node.slice.value, ast.Name):
slice = resolve_attr_id(node.slice.value)
return '{}[{}]'.format(value_id, slice)
elif isinstance(node, ast.Call):
return '{}()'.format(resolve_attr_id(node.func))
return node.id
def test_dict(self):
d = ast.Dict([], [ast.Name("x", ast.Load())])
self.expr(d, "same number of keys as values")
d = ast.Dict([None], [ast.Name("x", ast.Load())])
self.expr(d, "None disallowed")
d = ast.Dict([ast.Name("x", ast.Load())], [None])
self.expr(d, "None disallowed")
def find_dict_index(dict_node, key):
"""Given a key, find the index of the corresponding dict entry."""
assert dict_node.__class__ is ast.Dict, ast_err_msg(dict_node)
indices = [i for i, n in enumerate(dict_node.keys) if
n.__class__ is ast.Str and n.s == key]
assert len(indices) < 2, (
'Found redundant dict entries for key "%s"' % key)
return indices[0] if indices else None
def _process_argument_signature(value, arg_names, static_env):
arg_types = []
if isinstance(value, ast.Dict):
keys, values = value.keys, value.values
n_keys = len(keys)
n_args = len(arg_names)
if n_keys != n_args:
raise _errors.TyError(
"Function specifies {0} arguments, "
"but function signature specifies {1} arguments."
.format(n_args, n_keys), value)
for key, value, arg_name in zip(keys, values, arg_names):
if not isinstance(key, ast.Name):
raise _errors.TyError(
"Argument name must be an identiifer.", key)
sig_arg_name = key.id
if sig_arg_name != arg_name:
raise _errors.TyError(
"Function specifies argument name {0}, but function "
"signature specifies argument name {1}."
.format(arg_name, key), key)
arg_types.append(static_env.eval_expr_ast(value))
elif isinstance(value, ast.Set):
elts = value.elts
n_elts = len(elts)
n_args = len(arg_names)
if n_elts != n_args:
raise _errors.TyError(
"Function specifies {0} arguments, but function"
"signature specifies {1} arguments."
.format(n_args, n_elts), value)
for elt in elts:
arg_types.append(static_env.eval_expr_ast(elt))
else:
raise _errors.TyError(
"Argument signature must have the form of either a set "
"or dict literal.", value)
return tuple(arg_types)
def safe_eval(node_or_string, env):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
and None.
"""
_safe_names = {'None': None, 'True': True, 'False': False}
_safe_names.update(env)
if isinstance(node_or_string, basestring):
node_or_string = ast.parse(node_or_string, mode='eval')
if isinstance(node_or_string, ast.Expression):
node_or_string = node_or_string.body
def _convert(node):
if isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, ast.List):
return list(map(_convert, node.elts))
elif isinstance(node, ast.Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, ast.Name):
if node.id in _safe_names:
return _safe_names[node.id]
elif isinstance(node, ast.BinOp) and \
isinstance(node.op, (ast.Add, ast.Sub)) and \
isinstance(node.right, ast.Num) and \
isinstance(node.right.n, complex) and \
isinstance(node.left, ast.Num) and \
isinstance(node.left.n, (int, long, float)):
left = node.left.n
right = node.right.n
if isinstance(node.op, ast.Add):
return left + right
else:
return left - right
raise ValueError('malformed string')
return _convert(node_or_string)
def safe_eval(node_or_string, env):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
and None.
"""
_safe_names = {'None': None, 'True': True, 'False': False}
_safe_names.update(env)
if isinstance(node_or_string, basestring):
node_or_string = ast_parse(node_or_string, mode='eval')
if isinstance(node_or_string, ast.Expression):
node_or_string = node_or_string.body
def _convert(node):
if isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, ast.List):
return list(map(_convert, node.elts))
elif isinstance(node, ast.Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, ast.Name):
if node.id in _safe_names:
return _safe_names[node.id]
elif isinstance(node, ast.BinOp) and \
isinstance(node.op, (Add, Sub)) and \
isinstance(node.right, Num) and \
isinstance(node.right.n, complex) and \
isinstance(node.left, Num) and \
isinstance(node.left.n, (int, long, float)):
left = node.left.n
right = node.right.n
if isinstance(node.op, Add):
return left + right
else:
return left - right
raise ValueError('malformed string')
return _convert(node_or_string)