mirror of
https://github.com/davidfraser/pyan.git
synced 2026-05-12 03:01:24 -04:00
fix #3
This commit is contained in:
@@ -573,28 +573,57 @@ class CallGraphVisitor(ast.NodeVisitor):
|
||||
|
||||
def visit_ListComp(self, node):
|
||||
self.logger.debug("ListComp")
|
||||
with ExecuteInInnerScope(self, "listcomp"):
|
||||
self.visit(node.elt)
|
||||
self.analyze_generators(node.generators)
|
||||
self.analyze_comprehension(node, "listcomp")
|
||||
|
||||
def visit_SetComp(self, node):
|
||||
self.logger.debug("SetComp")
|
||||
with ExecuteInInnerScope(self, "setcomp"):
|
||||
self.visit(node.elt)
|
||||
self.analyze_generators(node.generators)
|
||||
self.analyze_comprehension(node, "setcomp")
|
||||
|
||||
def visit_DictComp(self, node):
|
||||
self.logger.debug("DictComp")
|
||||
with ExecuteInInnerScope(self, "dictcomp"):
|
||||
self.visit(node.key)
|
||||
self.visit(node.value)
|
||||
self.analyze_generators(node.generators)
|
||||
self.analyze_comprehension(node, "dictcomp", field1="key", field2="value")
|
||||
|
||||
def visit_GeneratorExp(self, node):
|
||||
self.logger.debug("GeneratorExp")
|
||||
with ExecuteInInnerScope(self, "genexpr"):
|
||||
self.visit(node.elt)
|
||||
self.analyze_generators(node.generators)
|
||||
self.analyze_comprehension(node, "genexpr")
|
||||
|
||||
def analyze_comprehension(self, node, label, field1="elt", field2=None):
|
||||
# The outermost iterator is evaluated in the current scope;
|
||||
# everything else in the new inner scope.
|
||||
#
|
||||
# See function symtable_handle_comprehension() in
|
||||
# https://github.com/python/cpython/blob/master/Python/symtable.c
|
||||
# For how it works, see
|
||||
# https://stackoverflow.com/questions/48753060/what-are-these-extra-symbols-in-a-comprehensions-symtable
|
||||
# For related discussion, see
|
||||
# https://bugs.python.org/issue10544
|
||||
gens = node.generators # tuple of ast.comprehension
|
||||
outermost = gens[0]
|
||||
moregens = gens[1:] if len(gens) > 1 else []
|
||||
|
||||
outermost_iters = sanitize_exprs(outermost.iter)
|
||||
outermost_targets = sanitize_exprs(outermost.target)
|
||||
for expr in outermost_iters:
|
||||
self.visit(expr) # set self.last_value (to something and hope for the best)
|
||||
|
||||
with ExecuteInInnerScope(self, label):
|
||||
for expr in outermost_targets:
|
||||
self.visit(expr) # use self.last_value
|
||||
self.last_value = None
|
||||
for expr in outermost.ifs:
|
||||
self.visit(expr)
|
||||
|
||||
# TODO: there's also an is_async field we might want to use in a future version.
|
||||
for gen in moregens:
|
||||
targets = sanitize_exprs(gen.target)
|
||||
values = sanitize_exprs(gen.iter)
|
||||
self.analyze_binding(targets, values)
|
||||
for expr in gen.ifs:
|
||||
self.visit(expr)
|
||||
|
||||
self.visit(getattr(node, field1)) # e.g. node.elt
|
||||
if field2:
|
||||
self.visit(getattr(node, field2))
|
||||
|
||||
def visit_Call(self, node):
|
||||
self.logger.debug("Call %s" % (get_ast_node_name(node.func)))
|
||||
@@ -793,23 +822,6 @@ class CallGraphVisitor(ast.NodeVisitor):
|
||||
self.visit(tgt)
|
||||
self.last_value = None
|
||||
|
||||
def analyze_generators(self, generators):
|
||||
"""Analyze the generators in a comprehension form.
|
||||
|
||||
Analyzes the binding part, and visits the "if" expressions (if any).
|
||||
|
||||
generators: an iterable of ast.comprehension objects
|
||||
"""
|
||||
|
||||
for gen in generators:
|
||||
# TODO: there's also an is_async field we might want to use in a future version.
|
||||
targets = sanitize_exprs(gen.target)
|
||||
values = sanitize_exprs(gen.iter)
|
||||
self.analyze_binding(targets, values)
|
||||
|
||||
for expr in gen.ifs:
|
||||
self.visit(expr)
|
||||
|
||||
def resolve_builtins(self, ast_node):
|
||||
"""Resolve those calls to built-in functions whose return values
|
||||
can be determined in a simple manner.
|
||||
|
||||
Reference in New Issue
Block a user