compilerop.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. """Compiler tools with improved interactive support.
  2. Provides compilation machinery similar to codeop, but with caching support so
  3. we can provide interactive tracebacks.
  4. Authors
  5. -------
  6. * Robert Kern
  7. * Fernando Perez
  8. * Thomas Kluyver
  9. """
  10. # Note: though it might be more natural to name this module 'compiler', that
  11. # name is in the stdlib and name collisions with the stdlib tend to produce
  12. # weird problems (often with third-party tools).
  13. #-----------------------------------------------------------------------------
  14. # Copyright (C) 2010-2011 The IPython Development Team.
  15. #
  16. # Distributed under the terms of the BSD License.
  17. #
  18. # The full license is in the file COPYING.txt, distributed with this software.
  19. #-----------------------------------------------------------------------------
  20. #-----------------------------------------------------------------------------
  21. # Imports
  22. #-----------------------------------------------------------------------------
  23. # Stdlib imports
  24. import __future__
  25. from ast import PyCF_ONLY_AST
  26. import codeop
  27. import functools
  28. import hashlib
  29. import linecache
  30. import operator
  31. import time
  32. from contextlib import contextmanager
  33. #-----------------------------------------------------------------------------
  34. # Constants
  35. #-----------------------------------------------------------------------------
  36. # Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
  37. # this is used as a bitmask to extract future-related code flags.
  38. PyCF_MASK = functools.reduce(operator.or_,
  39. (getattr(__future__, fname).compiler_flag
  40. for fname in __future__.all_feature_names))
  41. #-----------------------------------------------------------------------------
  42. # Local utilities
  43. #-----------------------------------------------------------------------------
  44. def code_name(code, number=0):
  45. """ Compute a (probably) unique name for code for caching.
  46. This now expects code to be unicode.
  47. """
  48. hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
  49. # Include the number and 12 characters of the hash in the name. It's
  50. # pretty much impossible that in a single session we'll have collisions
  51. # even with truncated hashes, and the full one makes tracebacks too long
  52. return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
  53. #-----------------------------------------------------------------------------
  54. # Classes and functions
  55. #-----------------------------------------------------------------------------
  56. class CachingCompiler(codeop.Compile):
  57. """A compiler that caches code compiled from interactive statements.
  58. """
  59. def __init__(self):
  60. codeop.Compile.__init__(self)
  61. # This is ugly, but it must be done this way to allow multiple
  62. # simultaneous ipython instances to coexist. Since Python itself
  63. # directly accesses the data structures in the linecache module, and
  64. # the cache therein is global, we must work with that data structure.
  65. # We must hold a reference to the original checkcache routine and call
  66. # that in our own check_cache() below, but the special IPython cache
  67. # must also be shared by all IPython instances. If we were to hold
  68. # separate caches (one in each CachingCompiler instance), any call made
  69. # by Python itself to linecache.checkcache() would obliterate the
  70. # cached data from the other IPython instances.
  71. if not hasattr(linecache, '_ipython_cache'):
  72. linecache._ipython_cache = {}
  73. if not hasattr(linecache, '_checkcache_ori'):
  74. linecache._checkcache_ori = linecache.checkcache
  75. # Now, we must monkeypatch the linecache directly so that parts of the
  76. # stdlib that call it outside our control go through our codepath
  77. # (otherwise we'd lose our tracebacks).
  78. linecache.checkcache = check_linecache_ipython
  79. def ast_parse(self, source, filename='<unknown>', symbol='exec'):
  80. """Parse code to an AST with the current compiler flags active.
  81. Arguments are exactly the same as ast.parse (in the standard library),
  82. and are passed to the built-in compile function."""
  83. return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
  84. def reset_compiler_flags(self):
  85. """Reset compiler flags to default state."""
  86. # This value is copied from codeop.Compile.__init__, so if that ever
  87. # changes, it will need to be updated.
  88. self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
  89. @property
  90. def compiler_flags(self):
  91. """Flags currently active in the compilation process.
  92. """
  93. return self.flags
  94. def get_code_name(self, raw_code, transformed_code, number):
  95. """Compute filename given the code, and the cell number.
  96. Parameters
  97. ----------
  98. raw_code : str
  99. The raw cell code.
  100. transformed_code : str
  101. The executable Python source code to cache and compile.
  102. number : int
  103. A number which forms part of the code's name. Used for the execution
  104. counter.
  105. Returns
  106. -------
  107. The computed filename.
  108. """
  109. return code_name(transformed_code, number)
  110. def cache(self, transformed_code, number=0, raw_code=None):
  111. """Make a name for a block of code, and cache the code.
  112. Parameters
  113. ----------
  114. transformed_code : str
  115. The executable Python source code to cache and compile.
  116. number : int
  117. A number which forms part of the code's name. Used for the execution
  118. counter.
  119. raw_code : str
  120. The raw code before transformation, if None, set to `transformed_code`.
  121. Returns
  122. -------
  123. The name of the cached code (as a string). Pass this as the filename
  124. argument to compilation, so that tracebacks are correctly hooked up.
  125. """
  126. if raw_code is None:
  127. raw_code = transformed_code
  128. name = self.get_code_name(raw_code, transformed_code, number)
  129. entry = (
  130. len(transformed_code),
  131. time.time(),
  132. [line + "\n" for line in transformed_code.splitlines()],
  133. name,
  134. )
  135. linecache.cache[name] = entry
  136. linecache._ipython_cache[name] = entry
  137. return name
  138. @contextmanager
  139. def extra_flags(self, flags):
  140. ## bits that we'll set to 1
  141. turn_on_bits = ~self.flags & flags
  142. self.flags = self.flags | flags
  143. try:
  144. yield
  145. finally:
  146. # turn off only the bits we turned on so that something like
  147. # __future__ that set flags stays.
  148. self.flags &= ~turn_on_bits
  149. def check_linecache_ipython(*args):
  150. """Call linecache.checkcache() safely protecting our cached values.
  151. """
  152. # First call the original checkcache as intended
  153. linecache._checkcache_ori(*args)
  154. # Then, update back the cache with our data, so that tracebacks related
  155. # to our compiled codes can be produced.
  156. linecache.cache.update(linecache._ipython_cache)