PageRenderTime 60ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/annotator/annrpython.py

https://bitbucket.org/kcr/pypy
Python | 682 lines | 670 code | 7 blank | 5 comment | 6 complexity | 815a019e6d4ea3a376fe405dabbffe78 MD5 | raw file
Possible License(s): Apache-2.0
  1. from __future__ import absolute_import
  2. import types
  3. from rpython.tool.ansi_print import ansi_log
  4. from rpython.tool.pairtype import pair
  5. from rpython.tool.error import (format_blocked_annotation_error,
  6. AnnotatorError, gather_error, ErrorWrapper)
  7. from rpython.flowspace.model import (Variable, Constant, FunctionGraph,
  8. c_last_exception, checkgraph)
  9. from rpython.translator import simplify, transform
  10. from rpython.annotator import model as annmodel, signature, unaryop, binaryop
  11. from rpython.annotator.bookkeeper import Bookkeeper
  12. import py
  13. log = py.log.Producer("annrpython")
  14. py.log.setconsumer("annrpython", ansi_log)
  15. FAIL = object()
  16. class RPythonAnnotator(object):
  17. """Block annotator for RPython.
  18. See description in doc/translation.txt."""
  19. def __init__(self, translator=None, policy=None, bookkeeper=None):
  20. import rpython.rtyper.ootypesystem.ooregistry # has side effects
  21. import rpython.rtyper.extfuncregistry # has side effects
  22. import rpython.rlib.nonconst # has side effects
  23. if translator is None:
  24. # interface for tests
  25. from rpython.translator.translator import TranslationContext
  26. translator = TranslationContext()
  27. translator.annotator = self
  28. self.translator = translator
  29. self.pendingblocks = {} # map {block: graph-containing-it}
  30. self.bindings = {} # map Variables to SomeValues
  31. self.annotated = {} # set of blocks already seen
  32. self.added_blocks = None # see processblock() below
  33. self.links_followed = {} # set of links that have ever been followed
  34. self.notify = {} # {block: {positions-to-reflow-from-when-done}}
  35. self.fixed_graphs = {} # set of graphs not to annotate again
  36. self.blocked_blocks = {} # set of {blocked_block: (graph, index)}
  37. # --- the following information is recorded for debugging ---
  38. self.blocked_graphs = {} # set of graphs that have blocked blocks
  39. # --- end of debugging information ---
  40. self.frozen = False
  41. if policy is None:
  42. from rpython.annotator.policy import AnnotatorPolicy
  43. self.policy = AnnotatorPolicy()
  44. else:
  45. self.policy = policy
  46. if bookkeeper is None:
  47. bookkeeper = Bookkeeper(self)
  48. self.bookkeeper = bookkeeper
  49. def __getstate__(self):
  50. attrs = """translator pendingblocks bindings annotated links_followed
  51. notify bookkeeper frozen policy added_blocks""".split()
  52. ret = self.__dict__.copy()
  53. for key, value in ret.items():
  54. if key not in attrs:
  55. assert type(value) is dict, (
  56. "%r is not dict. please update %s.__getstate__" %
  57. (key, self.__class__.__name__))
  58. ret[key] = {}
  59. return ret
  60. #___ convenience high-level interface __________________
  61. def build_types(self, function, input_arg_types, complete_now=True,
  62. main_entry_point=False):
  63. """Recursively build annotations about the specific entry point."""
  64. assert isinstance(function, types.FunctionType), "fix that!"
  65. from rpython.annotator.policy import AnnotatorPolicy
  66. policy = AnnotatorPolicy()
  67. # make input arguments and set their type
  68. args_s = [self.typeannotation(t) for t in input_arg_types]
  69. # XXX hack
  70. annmodel.TLS.check_str_without_nul = (
  71. self.translator.config.translation.check_str_without_nul)
  72. flowgraph, inputcells = self.get_call_parameters(function, args_s, policy)
  73. if not isinstance(flowgraph, FunctionGraph):
  74. assert isinstance(flowgraph, annmodel.SomeObject)
  75. return flowgraph
  76. if main_entry_point:
  77. self.translator.entry_point_graph = flowgraph
  78. return self.build_graph_types(flowgraph, inputcells, complete_now=complete_now)
  79. def get_call_parameters(self, function, args_s, policy):
  80. desc = self.bookkeeper.getdesc(function)
  81. args = self.bookkeeper.build_args("simple_call", args_s[:])
  82. result = []
  83. def schedule(graph, inputcells):
  84. result.append((graph, inputcells))
  85. return annmodel.s_ImpossibleValue
  86. prevpolicy = self.policy
  87. self.policy = policy
  88. self.bookkeeper.enter(None)
  89. try:
  90. desc.pycall(schedule, args, annmodel.s_ImpossibleValue)
  91. finally:
  92. self.bookkeeper.leave()
  93. self.policy = prevpolicy
  94. [(graph, inputcells)] = result
  95. return graph, inputcells
  96. def annotate_helper(self, function, args_s, policy=None):
  97. if policy is None:
  98. from rpython.annotator.policy import AnnotatorPolicy
  99. policy = AnnotatorPolicy()
  100. # XXX hack
  101. annmodel.TLS.check_str_without_nul = (
  102. self.translator.config.translation.check_str_without_nul)
  103. graph, inputcells = self.get_call_parameters(function, args_s, policy)
  104. self.build_graph_types(graph, inputcells, complete_now=False)
  105. self.complete_helpers(policy)
  106. return graph
  107. def complete_helpers(self, policy):
  108. saved = self.policy, self.added_blocks
  109. self.policy = policy
  110. try:
  111. self.added_blocks = {}
  112. self.complete()
  113. # invoke annotation simplifications for the new blocks
  114. self.simplify(block_subset=self.added_blocks)
  115. finally:
  116. self.policy, self.added_blocks = saved
  117. def build_graph_types(self, flowgraph, inputcells, complete_now=True):
  118. checkgraph(flowgraph)
  119. nbarg = len(flowgraph.getargs())
  120. if len(inputcells) != nbarg:
  121. raise TypeError("%s expects %d args, got %d" %(
  122. flowgraph, nbarg, len(inputcells)))
  123. # register the entry point
  124. self.addpendinggraph(flowgraph, inputcells)
  125. # recursively proceed until no more pending block is left
  126. if complete_now:
  127. self.complete()
  128. return self.binding(flowgraph.getreturnvar(), None)
  129. def gettype(self, variable):
  130. """Return the known type of a control flow graph variable,
  131. defaulting to 'object'."""
  132. if isinstance(variable, Constant):
  133. return type(variable.value)
  134. elif isinstance(variable, Variable):
  135. cell = self.bindings.get(variable)
  136. if cell:
  137. return cell.knowntype
  138. else:
  139. return object
  140. else:
  141. raise TypeError, ("Variable or Constant instance expected, "
  142. "got %r" % (variable,))
  143. def getuserclassdefinitions(self):
  144. """Return a list of ClassDefs."""
  145. return self.bookkeeper.classdefs
  146. #___ medium-level interface ____________________________
  147. def addpendinggraph(self, flowgraph, inputcells):
  148. self.addpendingblock(flowgraph, flowgraph.startblock, inputcells)
  149. def addpendingblock(self, graph, block, cells):
  150. """Register an entry point into block with the given input cells."""
  151. if graph in self.fixed_graphs:
  152. # special case for annotating/rtyping in several phases: calling
  153. # a graph that has already been rtyped. Safety-check the new
  154. # annotations that are passed in, and don't annotate the old
  155. # graph -- it's already low-level operations!
  156. for a, s_newarg in zip(graph.getargs(), cells):
  157. s_oldarg = self.binding(a)
  158. assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg
  159. else:
  160. assert not self.frozen
  161. for a in cells:
  162. assert isinstance(a, annmodel.SomeObject)
  163. if block not in self.annotated:
  164. self.bindinputargs(graph, block, cells)
  165. else:
  166. self.mergeinputargs(graph, block, cells)
  167. if not self.annotated[block]:
  168. self.pendingblocks[block] = graph
  169. def complete_pending_blocks(self):
  170. while self.pendingblocks:
  171. block, graph = self.pendingblocks.popitem()
  172. self.processblock(graph, block)
  173. def complete(self):
  174. """Process pending blocks until none is left."""
  175. while True:
  176. self.complete_pending_blocks()
  177. self.policy.no_more_blocks_to_annotate(self)
  178. if not self.pendingblocks:
  179. break # finished
  180. # make sure that the return variables of all graphs is annotated
  181. if self.added_blocks is not None:
  182. newgraphs = [self.annotated[block] for block in self.added_blocks]
  183. newgraphs = dict.fromkeys(newgraphs)
  184. got_blocked_blocks = False in newgraphs
  185. else:
  186. newgraphs = self.translator.graphs #all of them
  187. got_blocked_blocks = False in self.annotated.values()
  188. if got_blocked_blocks:
  189. for graph in self.blocked_graphs.values():
  190. self.blocked_graphs[graph] = True
  191. blocked_blocks = [block for block, done in self.annotated.items()
  192. if done is False]
  193. assert len(blocked_blocks) == len(self.blocked_blocks)
  194. text = format_blocked_annotation_error(self, self.blocked_blocks)
  195. #raise SystemExit()
  196. raise AnnotatorError(text)
  197. for graph in newgraphs:
  198. v = graph.getreturnvar()
  199. if v not in self.bindings:
  200. self.setbinding(v, annmodel.s_ImpossibleValue)
  201. # policy-dependent computation
  202. self.bookkeeper.compute_at_fixpoint()
  203. def binding(self, arg, default=FAIL):
  204. "Gives the SomeValue corresponding to the given Variable or Constant."
  205. if isinstance(arg, Variable):
  206. try:
  207. return self.bindings[arg]
  208. except KeyError:
  209. if default is not FAIL:
  210. return default
  211. else:
  212. raise
  213. elif isinstance(arg, Constant):
  214. #if arg.value is undefined_value: # undefined local variables
  215. # return annmodel.s_ImpossibleValue
  216. return self.bookkeeper.immutableconstant(arg)
  217. else:
  218. raise TypeError, 'Variable or Constant expected, got %r' % (arg,)
  219. def typeannotation(self, t):
  220. return signature.annotation(t, self.bookkeeper)
  221. def setbinding(self, arg, s_value):
  222. if arg in self.bindings:
  223. assert s_value.contains(self.bindings[arg])
  224. self.bindings[arg] = s_value
  225. def transfer_binding(self, v_target, v_source):
  226. assert v_source in self.bindings
  227. self.bindings[v_target] = self.bindings[v_source]
  228. def warning(self, msg, pos=None):
  229. if pos is None:
  230. try:
  231. pos = self.bookkeeper.position_key
  232. except AttributeError:
  233. pos = '?'
  234. if pos != '?':
  235. pos = self.whereami(pos)
  236. log.WARNING("%s/ %s" % (pos, msg))
  237. #___ interface for annotator.bookkeeper _______
  238. def recursivecall(self, graph, whence, inputcells):
  239. if isinstance(whence, tuple):
  240. parent_graph, parent_block, parent_index = whence
  241. tag = parent_block, parent_index
  242. self.translator.update_call_graph(parent_graph, graph, tag)
  243. # self.notify[graph.returnblock] is a dictionary of call
  244. # points to this func which triggers a reflow whenever the
  245. # return block of this graph has been analysed.
  246. callpositions = self.notify.setdefault(graph.returnblock, {})
  247. if whence is not None:
  248. if callable(whence):
  249. def callback():
  250. whence(self, graph)
  251. else:
  252. callback = whence
  253. callpositions[callback] = True
  254. # generalize the function's input arguments
  255. self.addpendingblock(graph, graph.startblock, inputcells)
  256. # get the (current) return value
  257. v = graph.getreturnvar()
  258. try:
  259. return self.bindings[v]
  260. except KeyError:
  261. # the function didn't reach any return statement so far.
  262. # (some functions actually never do, they always raise exceptions)
  263. return annmodel.s_ImpossibleValue
  264. def reflowfromposition(self, position_key):
  265. graph, block, index = position_key
  266. self.reflowpendingblock(graph, block)
  267. #___ simplification (should be moved elsewhere?) _______
  268. def simplify(self, block_subset=None, extra_passes=None):
  269. # Generic simplifications
  270. transform.transform_graph(self, block_subset=block_subset,
  271. extra_passes=extra_passes)
  272. if block_subset is None:
  273. graphs = self.translator.graphs
  274. else:
  275. graphs = {}
  276. for block in block_subset:
  277. graph = self.annotated.get(block)
  278. if graph:
  279. graphs[graph] = True
  280. for graph in graphs:
  281. simplify.eliminate_empty_blocks(graph)
  282. #___ flowing annotations in blocks _____________________
  283. def processblock(self, graph, block):
  284. # Important: this is not called recursively.
  285. # self.flowin() can only issue calls to self.addpendingblock().
  286. # The analysis of a block can be in three states:
  287. # * block not in self.annotated:
  288. # never seen the block.
  289. # * self.annotated[block] == False:
  290. # the input variables of the block are in self.bindings but we
  291. # still have to consider all the operations in the block.
  292. # * self.annotated[block] == graph-containing-block:
  293. # analysis done (at least until we find we must generalize the
  294. # input variables).
  295. #print '* processblock', block, cells
  296. self.annotated[block] = graph
  297. if block in self.blocked_blocks:
  298. del self.blocked_blocks[block]
  299. try:
  300. self.flowin(graph, block)
  301. except BlockedInference, e:
  302. self.annotated[block] = False # failed, hopefully temporarily
  303. self.blocked_blocks[block] = (graph, e.opindex)
  304. except Exception, e:
  305. # hack for debug tools only
  306. if not hasattr(e, '__annotator_block'):
  307. setattr(e, '__annotator_block', block)
  308. raise
  309. # The dict 'added_blocks' is used by rpython.annlowlevel to
  310. # detect which are the new blocks that annotating an additional
  311. # small helper creates.
  312. if self.added_blocks is not None:
  313. self.added_blocks[block] = True
  314. def reflowpendingblock(self, graph, block):
  315. assert not self.frozen
  316. assert graph not in self.fixed_graphs
  317. self.pendingblocks[block] = graph
  318. assert block in self.annotated
  319. self.annotated[block] = False # must re-flow
  320. self.blocked_blocks[block] = (graph, None)
  321. def bindinputargs(self, graph, block, inputcells):
  322. # Create the initial bindings for the input args of a block.
  323. assert len(block.inputargs) == len(inputcells)
  324. for a, cell in zip(block.inputargs, inputcells):
  325. self.setbinding(a, cell)
  326. self.annotated[block] = False # must flowin.
  327. self.blocked_blocks[block] = (graph, None)
  328. def mergeinputargs(self, graph, block, inputcells):
  329. # Merge the new 'cells' with each of the block's existing input
  330. # variables.
  331. oldcells = [self.binding(a) for a in block.inputargs]
  332. try:
  333. unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)]
  334. except annmodel.UnionError, e:
  335. e.args = e.args + (
  336. ErrorWrapper(gather_error(self, graph, block, None)),)
  337. raise
  338. # if the merged cells changed, we must redo the analysis
  339. if unions != oldcells:
  340. self.bindinputargs(graph, block, unions)
  341. def whereami(self, position_key):
  342. graph, block, i = position_key
  343. blk = ""
  344. if block:
  345. at = block.at()
  346. if at:
  347. blk = " block"+at
  348. opid=""
  349. if i is not None:
  350. opid = " op=%d" % i
  351. return repr(graph) + blk + opid
  352. def flowin(self, graph, block):
  353. #print 'Flowing', block, [self.binding(a) for a in block.inputargs]
  354. try:
  355. for i in range(len(block.operations)):
  356. try:
  357. self.bookkeeper.enter((graph, block, i))
  358. self.consider_op(block, i)
  359. finally:
  360. self.bookkeeper.leave()
  361. except BlockedInference, e:
  362. if (e.op is block.operations[-1] and
  363. block.exitswitch == c_last_exception):
  364. # this is the case where the last operation of the block will
  365. # always raise an exception which is immediately caught by
  366. # an exception handler. We then only follow the exceptional
  367. # branches.
  368. exits = [link for link in block.exits
  369. if link.exitcase is not None]
  370. elif e.op.opname in ('simple_call', 'call_args', 'next'):
  371. # XXX warning, keep the name of the call operations in sync
  372. # with the flow object space. These are the operations for
  373. # which it is fine to always raise an exception. We then
  374. # swallow the BlockedInference and that's it.
  375. # About 'next': see test_annotate_iter_empty_container().
  376. return
  377. else:
  378. # other cases are problematic (but will hopefully be solved
  379. # later by reflowing). Throw the BlockedInference up to
  380. # processblock().
  381. raise
  382. except annmodel.HarmlesslyBlocked:
  383. return
  384. else:
  385. # dead code removal: don't follow all exits if the exitswitch
  386. # is known
  387. exits = block.exits
  388. if isinstance(block.exitswitch, Variable):
  389. s_exitswitch = self.bindings[block.exitswitch]
  390. if s_exitswitch.is_constant():
  391. exits = [link for link in exits
  392. if link.exitcase == s_exitswitch.const]
  393. # mapping (exitcase, variable) -> s_annotation
  394. # that can be attached to booleans, exitswitches
  395. knowntypedata = getattr(self.bindings.get(block.exitswitch),
  396. "knowntypedata", {})
  397. # filter out those exceptions which cannot
  398. # occour for this specific, typed operation.
  399. if block.exitswitch == c_last_exception:
  400. op = block.operations[-1]
  401. if op.opname in binaryop.BINARY_OPERATIONS:
  402. arg1 = self.binding(op.args[0])
  403. arg2 = self.binding(op.args[1])
  404. binop = getattr(pair(arg1, arg2), op.opname, None)
  405. can_only_throw = annmodel.read_can_only_throw(binop, arg1, arg2)
  406. elif op.opname in unaryop.UNARY_OPERATIONS:
  407. arg1 = self.binding(op.args[0])
  408. opname = op.opname
  409. if opname == 'contains': opname = 'op_contains'
  410. unop = getattr(arg1, opname, None)
  411. can_only_throw = annmodel.read_can_only_throw(unop, arg1)
  412. else:
  413. can_only_throw = None
  414. if can_only_throw is not None:
  415. candidates = can_only_throw
  416. candidate_exits = exits
  417. exits = []
  418. for link in candidate_exits:
  419. case = link.exitcase
  420. if case is None:
  421. exits.append(link)
  422. continue
  423. covered = [c for c in candidates if issubclass(c, case)]
  424. if covered:
  425. exits.append(link)
  426. candidates = [c for c in candidates if c not in covered]
  427. for link in exits:
  428. in_except_block = False
  429. last_exception_var = link.last_exception # may be None for non-exception link
  430. last_exc_value_var = link.last_exc_value # may be None for non-exception link
  431. if isinstance(link.exitcase, (types.ClassType, type)) \
  432. and issubclass(link.exitcase, py.builtin.BaseException):
  433. assert last_exception_var and last_exc_value_var
  434. last_exc_value_object = self.bookkeeper.valueoftype(link.exitcase)
  435. last_exception_object = annmodel.SomeType()
  436. if isinstance(last_exception_var, Constant):
  437. last_exception_object.const = last_exception_var.value
  438. last_exception_object.is_type_of = [last_exc_value_var]
  439. if isinstance(last_exception_var, Variable):
  440. self.setbinding(last_exception_var, last_exception_object)
  441. if isinstance(last_exc_value_var, Variable):
  442. self.setbinding(last_exc_value_var, last_exc_value_object)
  443. last_exception_object = annmodel.SomeType()
  444. if isinstance(last_exception_var, Constant):
  445. last_exception_object.const = last_exception_var.value
  446. #if link.exitcase is Exception:
  447. # last_exc_value_object = annmodel.SomeObject()
  448. #else:
  449. last_exc_value_vars = []
  450. in_except_block = True
  451. ignore_link = False
  452. cells = []
  453. renaming = {}
  454. for a,v in zip(link.args,link.target.inputargs):
  455. renaming.setdefault(a, []).append(v)
  456. for a,v in zip(link.args,link.target.inputargs):
  457. if a == last_exception_var:
  458. assert in_except_block
  459. cells.append(last_exception_object)
  460. elif a == last_exc_value_var:
  461. assert in_except_block
  462. cells.append(last_exc_value_object)
  463. last_exc_value_vars.append(v)
  464. else:
  465. cell = self.binding(a)
  466. if (link.exitcase, a) in knowntypedata:
  467. knownvarvalue = knowntypedata[(link.exitcase, a)]
  468. cell = pair(cell, knownvarvalue).improve()
  469. # ignore links that try to pass impossible values
  470. if cell == annmodel.s_ImpossibleValue:
  471. ignore_link = True
  472. if hasattr(cell,'is_type_of'):
  473. renamed_is_type_of = []
  474. for v in cell.is_type_of:
  475. new_vs = renaming.get(v,[])
  476. renamed_is_type_of += new_vs
  477. assert cell.knowntype is type
  478. newcell = annmodel.SomeType()
  479. if cell.is_constant():
  480. newcell.const = cell.const
  481. cell = newcell
  482. cell.is_type_of = renamed_is_type_of
  483. if hasattr(cell, 'knowntypedata'):
  484. renamed_knowntypedata = {}
  485. for (value, v), s in cell.knowntypedata.items():
  486. new_vs = renaming.get(v, [])
  487. for new_v in new_vs:
  488. renamed_knowntypedata[value, new_v] = s
  489. assert isinstance(cell, annmodel.SomeBool)
  490. newcell = annmodel.SomeBool()
  491. if cell.is_constant():
  492. newcell.const = cell.const
  493. cell = newcell
  494. cell.set_knowntypedata(renamed_knowntypedata)
  495. cells.append(cell)
  496. if ignore_link:
  497. continue
  498. if in_except_block:
  499. last_exception_object.is_type_of = last_exc_value_vars
  500. self.links_followed[link] = True
  501. self.addpendingblock(graph, link.target, cells)
  502. if block in self.notify:
  503. # reflow from certain positions when this block is done
  504. for callback in self.notify[block]:
  505. if isinstance(callback, tuple):
  506. self.reflowfromposition(callback) # callback is a position
  507. else:
  508. callback()
  509. #___ creating the annotations based on operations ______
  510. def consider_op(self, block, opindex):
  511. op = block.operations[opindex]
  512. argcells = [self.binding(a) for a in op.args]
  513. consider_meth = getattr(self,'consider_op_'+op.opname,
  514. None)
  515. if not consider_meth:
  516. raise Exception,"unknown op: %r" % op
  517. # let's be careful about avoiding propagated SomeImpossibleValues
  518. # to enter an op; the latter can result in violations of the
  519. # more general results invariant: e.g. if SomeImpossibleValue enters is_
  520. # is_(SomeImpossibleValue, None) -> SomeBool
  521. # is_(SomeInstance(not None), None) -> SomeBool(const=False) ...
  522. # boom -- in the assert of setbinding()
  523. for arg in argcells:
  524. if isinstance(arg, annmodel.SomeImpossibleValue):
  525. raise BlockedInference(self, op, opindex)
  526. try:
  527. resultcell = consider_meth(*argcells)
  528. except Exception, e:
  529. graph = self.bookkeeper.position_key[0]
  530. e.args = e.args + (
  531. ErrorWrapper(gather_error(self, graph, block, opindex)),)
  532. raise
  533. if resultcell is None:
  534. resultcell = self.noreturnvalue(op)
  535. elif resultcell == annmodel.s_ImpossibleValue:
  536. raise BlockedInference(self, op, opindex) # the operation cannot succeed
  537. assert isinstance(resultcell, annmodel.SomeObject)
  538. assert isinstance(op.result, Variable)
  539. self.setbinding(op.result, resultcell) # bind resultcell to op.result
  540. def noreturnvalue(self, op):
  541. return annmodel.s_ImpossibleValue # no return value (hook method)
  542. # XXX "contains" clash with SomeObject method
  543. def consider_op_contains(self, seq, elem):
  544. self.bookkeeper.count("contains", seq)
  545. return seq.op_contains(elem)
  546. def consider_op_newtuple(self, *args):
  547. return annmodel.SomeTuple(items = args)
  548. def consider_op_newlist(self, *args):
  549. return self.bookkeeper.newlist(*args)
  550. def consider_op_newdict(self):
  551. return self.bookkeeper.newdict()
  552. def _registeroperations(cls, unary_ops, binary_ops):
  553. # All unary operations
  554. d = {}
  555. for opname in unary_ops:
  556. fnname = 'consider_op_' + opname
  557. exec py.code.Source("""
  558. def consider_op_%s(self, arg, *args):
  559. return arg.%s(*args)
  560. """ % (opname, opname)).compile() in globals(), d
  561. setattr(cls, fnname, d[fnname])
  562. # All binary operations
  563. for opname in binary_ops:
  564. fnname = 'consider_op_' + opname
  565. exec py.code.Source("""
  566. def consider_op_%s(self, arg1, arg2, *args):
  567. return pair(arg1,arg2).%s(*args)
  568. """ % (opname, opname)).compile() in globals(), d
  569. setattr(cls, fnname, d[fnname])
  570. _registeroperations = classmethod(_registeroperations)
  571. # register simple operations handling
  572. RPythonAnnotator._registeroperations(unaryop.UNARY_OPERATIONS, binaryop.BINARY_OPERATIONS)
  573. class BlockedInference(Exception):
  574. """This exception signals the type inference engine that the situation
  575. is currently blocked, and that it should try to progress elsewhere."""
  576. def __init__(self, annotator, op, opindex):
  577. self.annotator = annotator
  578. try:
  579. self.break_at = annotator.bookkeeper.position_key
  580. except AttributeError:
  581. self.break_at = None
  582. self.op = op
  583. self.opindex = opindex
  584. def __repr__(self):
  585. if not self.break_at:
  586. break_at = "?"
  587. else:
  588. break_at = self.annotator.whereami(self.break_at)
  589. return "<BlockedInference break_at %s [%s]>" %(break_at, self.op)
  590. __str__ = __repr__