compilerop.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. from __future__ import print_function
  24. # Stdlib imports
  25. import __future__
  26. from ast import PyCF_ONLY_AST
  27. import codeop
  28. import functools
  29. import hashlib
  30. import linecache
  31. import operator
  32. import time
  33. #-----------------------------------------------------------------------------
  34. # Constants
  35. #-----------------------------------------------------------------------------
  36. # Roughtly 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 cache(self, code, number=0):
  95. """Make a name for a block of code, and cache the code.
  96. Parameters
  97. ----------
  98. code : str
  99. The Python source code to cache.
  100. number : int
  101. A number which forms part of the code's name. Used for the execution
  102. counter.
  103. Returns
  104. -------
  105. The name of the cached code (as a string). Pass this as the filename
  106. argument to compilation, so that tracebacks are correctly hooked up.
  107. """
  108. name = code_name(code, number)
  109. entry = (len(code), time.time(),
  110. [line+'\n' for line in code.splitlines()], name)
  111. linecache.cache[name] = entry
  112. linecache._ipython_cache[name] = entry
  113. return name
  114. def check_linecache_ipython(*args):
  115. """Call linecache.checkcache() safely protecting our cached values.
  116. """
  117. # First call the orignal checkcache as intended
  118. linecache._checkcache_ori(*args)
  119. # Then, update back the cache with our data, so that tracebacks related
  120. # to our compiled codes can be produced.
  121. linecache.cache.update(linecache._ipython_cache)