Visitor.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. # cython: infer_types=True
  2. #
  3. # Tree visitor and transform framework
  4. #
  5. from __future__ import absolute_import, print_function
  6. import sys
  7. import inspect
  8. from . import TypeSlots
  9. from . import Builtin
  10. from . import Nodes
  11. from . import ExprNodes
  12. from . import Errors
  13. from . import DebugFlags
  14. from . import Future
  15. import cython
  16. cython.declare(_PRINTABLE=tuple)
  17. if sys.version_info[0] >= 3:
  18. _PRINTABLE = (bytes, str, int, float)
  19. else:
  20. _PRINTABLE = (str, unicode, long, int, float)
  21. class TreeVisitor(object):
  22. """
  23. Base class for writing visitors for a Cython tree, contains utilities for
  24. recursing such trees using visitors. Each node is
  25. expected to have a child_attrs iterable containing the names of attributes
  26. containing child nodes or lists of child nodes. Lists are not considered
  27. part of the tree structure (i.e. contained nodes are considered direct
  28. children of the parent node).
  29. visit_children visits each of the children of a given node (see the visit_children
  30. documentation). When recursing the tree using visit_children, an attribute
  31. access_path is maintained which gives information about the current location
  32. in the tree as a stack of tuples: (parent_node, attrname, index), representing
  33. the node, attribute and optional list index that was taken in each step in the path to
  34. the current node.
  35. Example:
  36. >>> class SampleNode(object):
  37. ... child_attrs = ["head", "body"]
  38. ... def __init__(self, value, head=None, body=None):
  39. ... self.value = value
  40. ... self.head = head
  41. ... self.body = body
  42. ... def __repr__(self): return "SampleNode(%s)" % self.value
  43. ...
  44. >>> tree = SampleNode(0, SampleNode(1), [SampleNode(2), SampleNode(3)])
  45. >>> class MyVisitor(TreeVisitor):
  46. ... def visit_SampleNode(self, node):
  47. ... print("in %s %s" % (node.value, self.access_path))
  48. ... self.visitchildren(node)
  49. ... print("out %s" % node.value)
  50. ...
  51. >>> MyVisitor().visit(tree)
  52. in 0 []
  53. in 1 [(SampleNode(0), 'head', None)]
  54. out 1
  55. in 2 [(SampleNode(0), 'body', 0)]
  56. out 2
  57. in 3 [(SampleNode(0), 'body', 1)]
  58. out 3
  59. out 0
  60. """
  61. def __init__(self):
  62. super(TreeVisitor, self).__init__()
  63. self.dispatch_table = {}
  64. self.access_path = []
  65. def dump_node(self, node, indent=0):
  66. ignored = list(node.child_attrs or []) + [
  67. u'child_attrs', u'pos', u'gil_message', u'cpp_message', u'subexprs']
  68. values = []
  69. pos = getattr(node, 'pos', None)
  70. if pos:
  71. source = pos[0]
  72. if source:
  73. import os.path
  74. source = os.path.basename(source.get_description())
  75. values.append(u'%s:%s:%s' % (source, pos[1], pos[2]))
  76. attribute_names = dir(node)
  77. attribute_names.sort()
  78. for attr in attribute_names:
  79. if attr in ignored:
  80. continue
  81. if attr.startswith('_') or attr.endswith('_'):
  82. continue
  83. try:
  84. value = getattr(node, attr)
  85. except AttributeError:
  86. continue
  87. if value is None or value == 0:
  88. continue
  89. elif isinstance(value, list):
  90. value = u'[...]/%d' % len(value)
  91. elif not isinstance(value, _PRINTABLE):
  92. continue
  93. else:
  94. value = repr(value)
  95. values.append(u'%s = %s' % (attr, value))
  96. return u'%s(%s)' % (node.__class__.__name__, u',\n '.join(values))
  97. def _find_node_path(self, stacktrace):
  98. import os.path
  99. last_traceback = stacktrace
  100. nodes = []
  101. while hasattr(stacktrace, 'tb_frame'):
  102. frame = stacktrace.tb_frame
  103. node = frame.f_locals.get(u'self')
  104. if isinstance(node, Nodes.Node):
  105. code = frame.f_code
  106. method_name = code.co_name
  107. pos = (os.path.basename(code.co_filename),
  108. frame.f_lineno)
  109. nodes.append((node, method_name, pos))
  110. last_traceback = stacktrace
  111. stacktrace = stacktrace.tb_next
  112. return (last_traceback, nodes)
  113. def _raise_compiler_error(self, child, e):
  114. trace = ['']
  115. for parent, attribute, index in self.access_path:
  116. node = getattr(parent, attribute)
  117. if index is None:
  118. index = ''
  119. else:
  120. node = node[index]
  121. index = u'[%d]' % index
  122. trace.append(u'%s.%s%s = %s' % (
  123. parent.__class__.__name__, attribute, index,
  124. self.dump_node(node)))
  125. stacktrace, called_nodes = self._find_node_path(sys.exc_info()[2])
  126. last_node = child
  127. for node, method_name, pos in called_nodes:
  128. last_node = node
  129. trace.append(u"File '%s', line %d, in %s: %s" % (
  130. pos[0], pos[1], method_name, self.dump_node(node)))
  131. raise Errors.CompilerCrash(
  132. getattr(last_node, 'pos', None), self.__class__.__name__,
  133. u'\n'.join(trace), e, stacktrace)
  134. @cython.final
  135. def find_handler(self, obj):
  136. # to resolve, try entire hierarchy
  137. cls = type(obj)
  138. pattern = "visit_%s"
  139. mro = inspect.getmro(cls)
  140. handler_method = None
  141. for mro_cls in mro:
  142. handler_method = getattr(self, pattern % mro_cls.__name__, None)
  143. if handler_method is not None:
  144. return handler_method
  145. print(type(self), cls)
  146. if self.access_path:
  147. print(self.access_path)
  148. print(self.access_path[-1][0].pos)
  149. print(self.access_path[-1][0].__dict__)
  150. raise RuntimeError("Visitor %r does not accept object: %s" % (self, obj))
  151. def visit(self, obj):
  152. return self._visit(obj)
  153. @cython.final
  154. def _visit(self, obj):
  155. try:
  156. try:
  157. handler_method = self.dispatch_table[type(obj)]
  158. except KeyError:
  159. handler_method = self.find_handler(obj)
  160. self.dispatch_table[type(obj)] = handler_method
  161. return handler_method(obj)
  162. except Errors.CompileError:
  163. raise
  164. except Errors.AbortError:
  165. raise
  166. except Exception as e:
  167. if DebugFlags.debug_no_exception_intercept:
  168. raise
  169. self._raise_compiler_error(obj, e)
  170. @cython.final
  171. def _visitchild(self, child, parent, attrname, idx):
  172. self.access_path.append((parent, attrname, idx))
  173. result = self._visit(child)
  174. self.access_path.pop()
  175. return result
  176. def visitchildren(self, parent, attrs=None):
  177. return self._visitchildren(parent, attrs)
  178. @cython.final
  179. @cython.locals(idx=int)
  180. def _visitchildren(self, parent, attrs):
  181. """
  182. Visits the children of the given parent. If parent is None, returns
  183. immediately (returning None).
  184. The return value is a dictionary giving the results for each
  185. child (mapping the attribute name to either the return value
  186. or a list of return values (in the case of multiple children
  187. in an attribute)).
  188. """
  189. if parent is None: return None
  190. result = {}
  191. for attr in parent.child_attrs:
  192. if attrs is not None and attr not in attrs: continue
  193. child = getattr(parent, attr)
  194. if child is not None:
  195. if type(child) is list:
  196. childretval = [self._visitchild(x, parent, attr, idx) for idx, x in enumerate(child)]
  197. else:
  198. childretval = self._visitchild(child, parent, attr, None)
  199. assert not isinstance(childretval, list), 'Cannot insert list here: %s in %r' % (attr, parent)
  200. result[attr] = childretval
  201. return result
  202. class VisitorTransform(TreeVisitor):
  203. """
  204. A tree transform is a base class for visitors that wants to do stream
  205. processing of the structure (rather than attributes etc.) of a tree.
  206. It implements __call__ to simply visit the argument node.
  207. It requires the visitor methods to return the nodes which should take
  208. the place of the visited node in the result tree (which can be the same
  209. or one or more replacement). Specifically, if the return value from
  210. a visitor method is:
  211. - [] or None; the visited node will be removed (set to None if an attribute and
  212. removed if in a list)
  213. - A single node; the visited node will be replaced by the returned node.
  214. - A list of nodes; the visited nodes will be replaced by all the nodes in the
  215. list. This will only work if the node was already a member of a list; if it
  216. was not, an exception will be raised. (Typically you want to ensure that you
  217. are within a StatListNode or similar before doing this.)
  218. """
  219. def visitchildren(self, parent, attrs=None):
  220. result = self._visitchildren(parent, attrs)
  221. for attr, newnode in result.items():
  222. if type(newnode) is not list:
  223. setattr(parent, attr, newnode)
  224. else:
  225. # Flatten the list one level and remove any None
  226. newlist = []
  227. for x in newnode:
  228. if x is not None:
  229. if type(x) is list:
  230. newlist += x
  231. else:
  232. newlist.append(x)
  233. setattr(parent, attr, newlist)
  234. return result
  235. def recurse_to_children(self, node):
  236. self.visitchildren(node)
  237. return node
  238. def __call__(self, root):
  239. return self._visit(root)
  240. class CythonTransform(VisitorTransform):
  241. """
  242. Certain common conventions and utilities for Cython transforms.
  243. - Sets up the context of the pipeline in self.context
  244. - Tracks directives in effect in self.current_directives
  245. """
  246. def __init__(self, context):
  247. super(CythonTransform, self).__init__()
  248. self.context = context
  249. def __call__(self, node):
  250. from . import ModuleNode
  251. if isinstance(node, ModuleNode.ModuleNode):
  252. self.current_directives = node.directives
  253. return super(CythonTransform, self).__call__(node)
  254. def visit_CompilerDirectivesNode(self, node):
  255. old = self.current_directives
  256. self.current_directives = node.directives
  257. self.visitchildren(node)
  258. self.current_directives = old
  259. return node
  260. def visit_Node(self, node):
  261. self.visitchildren(node)
  262. return node
  263. class ScopeTrackingTransform(CythonTransform):
  264. # Keeps track of type of scopes
  265. #scope_type: can be either of 'module', 'function', 'cclass', 'pyclass', 'struct'
  266. #scope_node: the node that owns the current scope
  267. def visit_ModuleNode(self, node):
  268. self.scope_type = 'module'
  269. self.scope_node = node
  270. self.visitchildren(node)
  271. return node
  272. def visit_scope(self, node, scope_type):
  273. prev = self.scope_type, self.scope_node
  274. self.scope_type = scope_type
  275. self.scope_node = node
  276. self.visitchildren(node)
  277. self.scope_type, self.scope_node = prev
  278. return node
  279. def visit_CClassDefNode(self, node):
  280. return self.visit_scope(node, 'cclass')
  281. def visit_PyClassDefNode(self, node):
  282. return self.visit_scope(node, 'pyclass')
  283. def visit_FuncDefNode(self, node):
  284. return self.visit_scope(node, 'function')
  285. def visit_CStructOrUnionDefNode(self, node):
  286. return self.visit_scope(node, 'struct')
  287. class EnvTransform(CythonTransform):
  288. """
  289. This transformation keeps a stack of the environments.
  290. """
  291. def __call__(self, root):
  292. self.env_stack = []
  293. self.enter_scope(root, root.scope)
  294. return super(EnvTransform, self).__call__(root)
  295. def current_env(self):
  296. return self.env_stack[-1][1]
  297. def current_scope_node(self):
  298. return self.env_stack[-1][0]
  299. def global_scope(self):
  300. return self.current_env().global_scope()
  301. def enter_scope(self, node, scope):
  302. self.env_stack.append((node, scope))
  303. def exit_scope(self):
  304. self.env_stack.pop()
  305. def visit_FuncDefNode(self, node):
  306. self.enter_scope(node, node.local_scope)
  307. self.visitchildren(node)
  308. self.exit_scope()
  309. return node
  310. def visit_GeneratorBodyDefNode(self, node):
  311. self.visitchildren(node)
  312. return node
  313. def visit_ClassDefNode(self, node):
  314. self.enter_scope(node, node.scope)
  315. self.visitchildren(node)
  316. self.exit_scope()
  317. return node
  318. def visit_CStructOrUnionDefNode(self, node):
  319. self.enter_scope(node, node.scope)
  320. self.visitchildren(node)
  321. self.exit_scope()
  322. return node
  323. def visit_ScopedExprNode(self, node):
  324. if node.expr_scope:
  325. self.enter_scope(node, node.expr_scope)
  326. self.visitchildren(node)
  327. self.exit_scope()
  328. else:
  329. self.visitchildren(node)
  330. return node
  331. def visit_CArgDeclNode(self, node):
  332. # default arguments are evaluated in the outer scope
  333. if node.default:
  334. attrs = [attr for attr in node.child_attrs if attr != 'default']
  335. self.visitchildren(node, attrs)
  336. self.enter_scope(node, self.current_env().outer_scope)
  337. self.visitchildren(node, ('default',))
  338. self.exit_scope()
  339. else:
  340. self.visitchildren(node)
  341. return node
  342. class NodeRefCleanupMixin(object):
  343. """
  344. Clean up references to nodes that were replaced.
  345. NOTE: this implementation assumes that the replacement is
  346. done first, before hitting any further references during
  347. normal tree traversal. This needs to be arranged by calling
  348. "self.visitchildren()" at a proper place in the transform
  349. and by ordering the "child_attrs" of nodes appropriately.
  350. """
  351. def __init__(self, *args):
  352. super(NodeRefCleanupMixin, self).__init__(*args)
  353. self._replacements = {}
  354. def visit_CloneNode(self, node):
  355. arg = node.arg
  356. if arg not in self._replacements:
  357. self.visitchildren(arg)
  358. node.arg = self._replacements.get(arg, arg)
  359. return node
  360. def visit_ResultRefNode(self, node):
  361. expr = node.expression
  362. if expr is None or expr not in self._replacements:
  363. self.visitchildren(node)
  364. expr = node.expression
  365. if expr is not None:
  366. node.expression = self._replacements.get(expr, expr)
  367. return node
  368. def replace(self, node, replacement):
  369. self._replacements[node] = replacement
  370. return replacement
  371. find_special_method_for_binary_operator = {
  372. '<': '__lt__',
  373. '<=': '__le__',
  374. '==': '__eq__',
  375. '!=': '__ne__',
  376. '>=': '__ge__',
  377. '>': '__gt__',
  378. '+': '__add__',
  379. '&': '__and__',
  380. '/': '__div__',
  381. '//': '__floordiv__',
  382. '<<': '__lshift__',
  383. '%': '__mod__',
  384. '*': '__mul__',
  385. '|': '__or__',
  386. '**': '__pow__',
  387. '>>': '__rshift__',
  388. '-': '__sub__',
  389. '^': '__xor__',
  390. 'in': '__contains__',
  391. }.get
  392. find_special_method_for_unary_operator = {
  393. 'not': '__not__',
  394. '~': '__inv__',
  395. '-': '__neg__',
  396. '+': '__pos__',
  397. }.get
  398. class MethodDispatcherTransform(EnvTransform):
  399. """
  400. Base class for transformations that want to intercept on specific
  401. builtin functions or methods of builtin types, including special
  402. methods triggered by Python operators. Must run after declaration
  403. analysis when entries were assigned.
  404. Naming pattern for handler methods is as follows:
  405. * builtin functions: _handle_(general|simple|any)_function_NAME
  406. * builtin methods: _handle_(general|simple|any)_method_TYPENAME_METHODNAME
  407. """
  408. # only visit call nodes and Python operations
  409. def visit_GeneralCallNode(self, node):
  410. self.visitchildren(node)
  411. function = node.function
  412. if not function.type.is_pyobject:
  413. return node
  414. arg_tuple = node.positional_args
  415. if not isinstance(arg_tuple, ExprNodes.TupleNode):
  416. return node
  417. keyword_args = node.keyword_args
  418. if keyword_args and not isinstance(keyword_args, ExprNodes.DictNode):
  419. # can't handle **kwargs
  420. return node
  421. args = arg_tuple.args
  422. return self._dispatch_to_handler(node, function, args, keyword_args)
  423. def visit_SimpleCallNode(self, node):
  424. self.visitchildren(node)
  425. function = node.function
  426. if function.type.is_pyobject:
  427. arg_tuple = node.arg_tuple
  428. if not isinstance(arg_tuple, ExprNodes.TupleNode):
  429. return node
  430. args = arg_tuple.args
  431. else:
  432. args = node.args
  433. return self._dispatch_to_handler(node, function, args, None)
  434. def visit_PrimaryCmpNode(self, node):
  435. if node.cascade:
  436. # not currently handled below
  437. self.visitchildren(node)
  438. return node
  439. return self._visit_binop_node(node)
  440. def visit_BinopNode(self, node):
  441. return self._visit_binop_node(node)
  442. def _visit_binop_node(self, node):
  443. self.visitchildren(node)
  444. # FIXME: could special case 'not_in'
  445. special_method_name = find_special_method_for_binary_operator(node.operator)
  446. if special_method_name:
  447. operand1, operand2 = node.operand1, node.operand2
  448. if special_method_name == '__contains__':
  449. operand1, operand2 = operand2, operand1
  450. elif special_method_name == '__div__':
  451. if Future.division in self.current_env().global_scope().context.future_directives:
  452. special_method_name = '__truediv__'
  453. obj_type = operand1.type
  454. if obj_type.is_builtin_type:
  455. type_name = obj_type.name
  456. else:
  457. type_name = "object" # safety measure
  458. node = self._dispatch_to_method_handler(
  459. special_method_name, None, False, type_name,
  460. node, None, [operand1, operand2], None)
  461. return node
  462. def visit_UnopNode(self, node):
  463. self.visitchildren(node)
  464. special_method_name = find_special_method_for_unary_operator(node.operator)
  465. if special_method_name:
  466. operand = node.operand
  467. obj_type = operand.type
  468. if obj_type.is_builtin_type:
  469. type_name = obj_type.name
  470. else:
  471. type_name = "object" # safety measure
  472. node = self._dispatch_to_method_handler(
  473. special_method_name, None, False, type_name,
  474. node, None, [operand], None)
  475. return node
  476. ### dispatch to specific handlers
  477. def _find_handler(self, match_name, has_kwargs):
  478. call_type = has_kwargs and 'general' or 'simple'
  479. handler = getattr(self, '_handle_%s_%s' % (call_type, match_name), None)
  480. if handler is None:
  481. handler = getattr(self, '_handle_any_%s' % match_name, None)
  482. return handler
  483. def _delegate_to_assigned_value(self, node, function, arg_list, kwargs):
  484. assignment = function.cf_state[0]
  485. value = assignment.rhs
  486. if value.is_name:
  487. if not value.entry or len(value.entry.cf_assignments) > 1:
  488. # the variable might have been reassigned => play safe
  489. return node
  490. elif value.is_attribute and value.obj.is_name:
  491. if not value.obj.entry or len(value.obj.entry.cf_assignments) > 1:
  492. # the underlying variable might have been reassigned => play safe
  493. return node
  494. else:
  495. return node
  496. return self._dispatch_to_handler(
  497. node, value, arg_list, kwargs)
  498. def _dispatch_to_handler(self, node, function, arg_list, kwargs):
  499. if function.is_name:
  500. # we only consider functions that are either builtin
  501. # Python functions or builtins that were already replaced
  502. # into a C function call (defined in the builtin scope)
  503. if not function.entry:
  504. return node
  505. entry = function.entry
  506. is_builtin = (
  507. entry.is_builtin or
  508. entry is self.current_env().builtin_scope().lookup_here(function.name))
  509. if not is_builtin:
  510. if function.cf_state and function.cf_state.is_single:
  511. # we know the value of the variable
  512. # => see if it's usable instead
  513. return self._delegate_to_assigned_value(
  514. node, function, arg_list, kwargs)
  515. if arg_list and entry.is_cmethod and entry.scope and entry.scope.parent_type.is_builtin_type:
  516. if entry.scope.parent_type is arg_list[0].type:
  517. # Optimised (unbound) method of a builtin type => try to "de-optimise".
  518. return self._dispatch_to_method_handler(
  519. entry.name, self_arg=None, is_unbound_method=True,
  520. type_name=entry.scope.parent_type.name,
  521. node=node, function=function, arg_list=arg_list, kwargs=kwargs)
  522. return node
  523. function_handler = self._find_handler(
  524. "function_%s" % function.name, kwargs)
  525. if function_handler is None:
  526. return self._handle_function(node, function.name, function, arg_list, kwargs)
  527. if kwargs:
  528. return function_handler(node, function, arg_list, kwargs)
  529. else:
  530. return function_handler(node, function, arg_list)
  531. elif function.is_attribute:
  532. attr_name = function.attribute
  533. if function.type.is_pyobject:
  534. self_arg = function.obj
  535. elif node.self and function.entry:
  536. entry = function.entry.as_variable
  537. if not entry or not entry.is_builtin:
  538. return node
  539. # C implementation of a Python builtin method - see if we find further matches
  540. self_arg = node.self
  541. arg_list = arg_list[1:] # drop CloneNode of self argument
  542. else:
  543. return node
  544. obj_type = self_arg.type
  545. is_unbound_method = False
  546. if obj_type.is_builtin_type:
  547. if obj_type is Builtin.type_type and self_arg.is_name and arg_list and arg_list[0].type.is_pyobject:
  548. # calling an unbound method like 'list.append(L,x)'
  549. # (ignoring 'type.mro()' here ...)
  550. type_name = self_arg.name
  551. self_arg = None
  552. is_unbound_method = True
  553. else:
  554. type_name = obj_type.name
  555. else:
  556. type_name = "object" # safety measure
  557. return self._dispatch_to_method_handler(
  558. attr_name, self_arg, is_unbound_method, type_name,
  559. node, function, arg_list, kwargs)
  560. else:
  561. return node
  562. def _dispatch_to_method_handler(self, attr_name, self_arg,
  563. is_unbound_method, type_name,
  564. node, function, arg_list, kwargs):
  565. method_handler = self._find_handler(
  566. "method_%s_%s" % (type_name, attr_name), kwargs)
  567. if method_handler is None:
  568. if (attr_name in TypeSlots.method_name_to_slot
  569. or attr_name == '__new__'):
  570. method_handler = self._find_handler(
  571. "slot%s" % attr_name, kwargs)
  572. if method_handler is None:
  573. return self._handle_method(
  574. node, type_name, attr_name, function,
  575. arg_list, is_unbound_method, kwargs)
  576. if self_arg is not None:
  577. arg_list = [self_arg] + list(arg_list)
  578. if kwargs:
  579. result = method_handler(
  580. node, function, arg_list, is_unbound_method, kwargs)
  581. else:
  582. result = method_handler(
  583. node, function, arg_list, is_unbound_method)
  584. return result
  585. def _handle_function(self, node, function_name, function, arg_list, kwargs):
  586. """Fallback handler"""
  587. return node
  588. def _handle_method(self, node, type_name, attr_name, function,
  589. arg_list, is_unbound_method, kwargs):
  590. """Fallback handler"""
  591. return node
  592. class RecursiveNodeReplacer(VisitorTransform):
  593. """
  594. Recursively replace all occurrences of a node in a subtree by
  595. another node.
  596. """
  597. def __init__(self, orig_node, new_node):
  598. super(RecursiveNodeReplacer, self).__init__()
  599. self.orig_node, self.new_node = orig_node, new_node
  600. def visit_CloneNode(self, node):
  601. if node is self.orig_node:
  602. return self.new_node
  603. if node.arg is self.orig_node:
  604. node.arg = self.new_node
  605. return node
  606. def visit_Node(self, node):
  607. self.visitchildren(node)
  608. if node is self.orig_node:
  609. return self.new_node
  610. else:
  611. return node
  612. def recursively_replace_node(tree, old_node, new_node):
  613. replace_in = RecursiveNodeReplacer(old_node, new_node)
  614. replace_in(tree)
  615. class NodeFinder(TreeVisitor):
  616. """
  617. Find out if a node appears in a subtree.
  618. """
  619. def __init__(self, node):
  620. super(NodeFinder, self).__init__()
  621. self.node = node
  622. self.found = False
  623. def visit_Node(self, node):
  624. if self.found:
  625. pass # short-circuit
  626. elif node is self.node:
  627. self.found = True
  628. else:
  629. self._visitchildren(node, None)
  630. def tree_contains(tree, node):
  631. finder = NodeFinder(node)
  632. finder.visit(tree)
  633. return finder.found
  634. # Utils
  635. def replace_node(ptr, value):
  636. """Replaces a node. ptr is of the form used on the access path stack
  637. (parent, attrname, listidx|None)
  638. """
  639. parent, attrname, listidx = ptr
  640. if listidx is None:
  641. setattr(parent, attrname, value)
  642. else:
  643. getattr(parent, attrname)[listidx] = value
  644. class PrintTree(TreeVisitor):
  645. """Prints a representation of the tree to standard output.
  646. Subclass and override repr_of to provide more information
  647. about nodes. """
  648. def __init__(self, start=None, end=None):
  649. TreeVisitor.__init__(self)
  650. self._indent = ""
  651. if start is not None or end is not None:
  652. self._line_range = (start or 0, end or 2**30)
  653. else:
  654. self._line_range = None
  655. def indent(self):
  656. self._indent += " "
  657. def unindent(self):
  658. self._indent = self._indent[:-2]
  659. def __call__(self, tree, phase=None):
  660. print("Parse tree dump at phase '%s'" % phase)
  661. self.visit(tree)
  662. return tree
  663. # Don't do anything about process_list, the defaults gives
  664. # nice-looking name[idx] nodes which will visually appear
  665. # under the parent-node, not displaying the list itself in
  666. # the hierarchy.
  667. def visit_Node(self, node):
  668. self._print_node(node)
  669. self.indent()
  670. self.visitchildren(node)
  671. self.unindent()
  672. return node
  673. def visit_CloneNode(self, node):
  674. self._print_node(node)
  675. self.indent()
  676. line = node.pos[1]
  677. if self._line_range is None or self._line_range[0] <= line <= self._line_range[1]:
  678. print("%s- %s: %s" % (self._indent, 'arg', self.repr_of(node.arg)))
  679. self.indent()
  680. self.visitchildren(node.arg)
  681. self.unindent()
  682. self.unindent()
  683. return node
  684. def _print_node(self, node):
  685. line = node.pos[1]
  686. if self._line_range is None or self._line_range[0] <= line <= self._line_range[1]:
  687. if len(self.access_path) == 0:
  688. name = "(root)"
  689. else:
  690. parent, attr, idx = self.access_path[-1]
  691. if idx is not None:
  692. name = "%s[%d]" % (attr, idx)
  693. else:
  694. name = attr
  695. print("%s- %s: %s" % (self._indent, name, self.repr_of(node)))
  696. def repr_of(self, node):
  697. if node is None:
  698. return "(none)"
  699. else:
  700. result = node.__class__.__name__
  701. if isinstance(node, ExprNodes.NameNode):
  702. result += "(type=%s, name=\"%s\")" % (repr(node.type), node.name)
  703. elif isinstance(node, Nodes.DefNode):
  704. result += "(name=\"%s\")" % node.name
  705. elif isinstance(node, ExprNodes.ExprNode):
  706. t = node.type
  707. result += "(type=%s)" % repr(t)
  708. elif node.pos:
  709. pos = node.pos
  710. path = pos[0].get_description()
  711. if '/' in path:
  712. path = path.split('/')[-1]
  713. if '\\' in path:
  714. path = path.split('\\')[-1]
  715. result += "(pos=(%s:%s:%s))" % (path, pos[1], pos[2])
  716. return result
  717. if __name__ == "__main__":
  718. import doctest
  719. doctest.testmod()