def _get_offsets(func_ast):
for arg in func_ast.args:
start_line, start_col = arg.lineno - 2, arg.col_offset - 1
# horrible hack for http://bugs.python.org/issue31241
if isinstance(arg, (ast.ListComp, ast.GeneratorExp)):
start_col -= 1
yield start_line, start_col
for kw in func_ast.keywords:
yield kw.value.lineno - 2, kw.value.col_offset - len(kw.arg) - 2
python类GeneratorExp()的实例源码
def visit_Call(self, node):
if (
isinstance(node.func, ast.Name) and
node.func.id == 'dict' and
len(node.args) == 1 and
not _has_kwargs(node) and
isinstance(node.args[0], (ast.ListComp, ast.GeneratorExp)) and
isinstance(node.args[0].elt, (ast.Tuple, ast.List)) and
len(node.args[0].elt.elts) == 2
):
arg, = node.args
key = Offset(node.func.lineno, node.func.col_offset)
self.dicts[key] = arg
self.generic_visit(node)
def visit_GeneratorExp(self, node):
# type: (ast.GeneratorExp) -> None
# Generator expressions are an interesting case.
# They create a new sub scope, but they're not
# explicitly named. Python just creates a table
# with the name "genexpr".
self._handle_comprehension(node, 'genexpr')
def visit_Call(self, node):
argnodes = node.args + node.keywords
py2_starargs = getattr(node, 'starargs', None)
if py2_starargs: # pragma: no cover (<PY35)
argnodes.append(py2_starargs)
py2_kwargs = getattr(node, 'kwargs', None)
if py2_kwargs: # pragma: no cover (<PY35)
argnodes.append(py2_kwargs)
arg_offsets = set()
has_starargs = bool(py2_starargs or py2_kwargs)
for argnode in argnodes:
if (
_is_star_arg(argnode) or
_is_star_star_kwarg(argnode)
): # pragma: no cover (PY35+)
has_starargs = True
offset = _to_offset(argnode)
# multiline strings have invalid position, ignore them
if offset.utf8_byte_offset != -1: # pragma: no branch (cpy bug)
arg_offsets.add(offset)
# If the sole argument is a generator, don't add a trailing comma as
# this breaks lib2to3 based tools
only_a_generator = (
len(argnodes) == 1 and isinstance(argnodes[0], ast.GeneratorExp)
)
if arg_offsets and not only_a_generator:
key = Offset(node.lineno, node.col_offset)
self.calls[key].append(Call(node, has_starargs, arg_offsets))
self.generic_visit(node)
def test_generatorexp(self):
self._simple_comp(ast.GeneratorExp)
def test_generatorexp(self):
self._simple_comp(ast.GeneratorExp)
def test_generatorexp(self):
self._simple_comp(ast.GeneratorExp)
def checkCompare(self, compare, modelName = None, script = None):
"""Check an AST Compare node - iterate for Attribute nodes and match
against modelName argument. Recurse for script's model defs."""
if modelName is None and script is None:
return
if modelName is not None:
valueCallArgs = []
generatorExps = []
for node in ast.walk(compare):
if isinstance(node, ast.Attribute):
if isinstance(node.value, ast.Name):
if node.value.id == modelName:
wrapped = self.checkWrapped(node, valueCallArgs, generatorExps)
if not wrapped:
self.problem("Comparison on attribute {0}.{1} not wrapped in value()".format(modelName, node.attr), lineno=compare.lineno)
elif isinstance(node, ast.Call):
if isinstance(node.func, ast.Name):
if node.func.id == 'value':
valueCallArgs.append(node.args)
elif isinstance(node, ast.GeneratorExp):
generatorExps.append(node)
if script is not None:
for name in script.modelVars:
self.checkCompare(compare, modelName = name)
def node_defines_name(node, name):
"""
Check if the specified statement node defines symbol *name*.
:param node: The node to check.
:param name: The symbol name to check.
:return: Whether or not the node defines the symbole specified.
:rtype: bool
"""
if isinstance(name, ast.Name):
name = name.id
if isinstance(node, ast.Assign):
if node_targets_name(node, name):
return True
if isinstance(node.value, (ast.DictComp, ast.ListComp, ast.SetComp)):
return node_defines_name(node.value, name)
elif isinstance(node, ast.ClassDef):
return node.name == name
# these ones all assume the iterable will be executed at least once
elif isinstance(node, (ast.DictComp, ast.GeneratorExp, ast.ListComp, ast.SetComp)):
for generator in node.generators:
target = generator.target
if isinstance(target, ast.Name):
if target.id == name:
return True
continue
for child_node in iter_child_expr_nodes(target):
if isinstance(child_node, ast.Name) and child_node.id == name:
return True
return False
elif isinstance(node, ast.ExceptHandler):
if isinstance(node.name, ast.Name):
return node.name.id == name
elif isinstance(node.name, str):
return node.name == name
elif isinstance(node, ast.Expr):
if isinstance(node.value, (ast.DictComp, ast.GeneratorExp, ast.ListComp, ast.SetComp)):
return node_defines_name(node.value, name)
elif isinstance(node, ast.For):
return isinstance(node.target, ast.Name) and node.target.id == name
elif isinstance(node, ast.FunctionDef):
return node.name == name
elif isinstance(node, (ast.Import, ast.ImportFrom)):
return next((alias for alias in node.names if (alias.asname or alias.name) == name), None) is not None
return False
def peval_comprehension(state, node, ctx):
accum_cls = {
ast.ListComp: ListAccumulator,
ast.GeneratorExp: GeneratorExpAccumulator,
ast.SetComp: SetAccumulator,
ast.DictComp: DictAccumulator,
}
# variables from generators temporary mask bindings
target_names = set()
for generator in node.generators:
if type(generator.target) == ast.Name:
target_names.add(generator.target.id)
else:
target_names.update([elt.id for elt in generator.target.elts])
# pre-evaluate the expression
elt_bindings = dict(ctx.bindings)
for name in target_names:
if name in elt_bindings:
del elt_bindings[name]
elt_ctx = ctx.update(bindings=elt_bindings)
if type(node) == ast.DictComp:
elt = ast.Tuple(elts=[node.key, node.value])
else:
elt = node.elt
state, new_elt = _peval_expression(state, elt, elt_ctx)
try:
state, container = _peval_comprehension(
state, accum_cls[type(node)], new_elt, node.generators, ctx)
evaluated = True
except CannotEvaluateComprehension:
evaluated = False
if evaluated:
return state, KnownValue(value=container)
else:
state, new_elt = map_reify(state, new_elt)
state, new_generators = _peval_comprehension_generators(state, node.generators, ctx)
if type(node) == ast.DictComp:
key, value = new_elt.elts
return state, replace_fields(node, key=key, value=value, generators=new_generators)
else:
return state, replace_fields(node, elt=new_elt, generators=new_generators)