PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/annotator/annrpython.py

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