##// END OF EJS Templates
Fix compilerop to handle unicode input.
Thomas Kluyver -
Show More
@@ -1,119 +1,121
1 1 """Compiler tools with improved interactive support.
2 2
3 3 Provides compilation machinery similar to codeop, but with caching support so
4 4 we can provide interactive tracebacks.
5 5
6 6 Authors
7 7 -------
8 8 * Robert Kern
9 9 * Fernando Perez
10 10 """
11 11
12 12 # Note: though it might be more natural to name this module 'compiler', that
13 13 # name is in the stdlib and name collisions with the stdlib tend to produce
14 14 # weird problems (often with third-party tools).
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Copyright (C) 2010 The IPython Development Team.
18 18 #
19 19 # Distributed under the terms of the BSD License.
20 20 #
21 21 # The full license is in the file COPYING.txt, distributed with this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib imports
30 30 import codeop
31 31 import hashlib
32 32 import linecache
33 33 import time
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Local utilities
37 37 #-----------------------------------------------------------------------------
38 38
39 39 def code_name(code, number=0):
40 40 """ Compute a (probably) unique name for code for caching.
41
42 This now expects code to be unicode.
41 43 """
42 hash_digest = hashlib.md5(code).hexdigest()
44 hash_digest = hashlib.md5(code.encode("utf-8")).hexdigest()
43 45 # Include the number and 12 characters of the hash in the name. It's
44 46 # pretty much impossible that in a single session we'll have collisions
45 47 # even with truncated hashes, and the full one makes tracebacks too long
46 48 return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
47 49
48 50 #-----------------------------------------------------------------------------
49 51 # Classes and functions
50 52 #-----------------------------------------------------------------------------
51 53
52 54 class CachingCompiler(object):
53 55 """A compiler that caches code compiled from interactive statements.
54 56 """
55 57
56 58 def __init__(self):
57 59 self._compiler = codeop.CommandCompiler()
58 60
59 61 # This is ugly, but it must be done this way to allow multiple
60 62 # simultaneous ipython instances to coexist. Since Python itself
61 63 # directly accesses the data structures in the linecache module, and
62 64 # the cache therein is global, we must work with that data structure.
63 65 # We must hold a reference to the original checkcache routine and call
64 66 # that in our own check_cache() below, but the special IPython cache
65 67 # must also be shared by all IPython instances. If we were to hold
66 68 # separate caches (one in each CachingCompiler instance), any call made
67 69 # by Python itself to linecache.checkcache() would obliterate the
68 70 # cached data from the other IPython instances.
69 71 if not hasattr(linecache, '_ipython_cache'):
70 72 linecache._ipython_cache = {}
71 73 if not hasattr(linecache, '_checkcache_ori'):
72 74 linecache._checkcache_ori = linecache.checkcache
73 75 # Now, we must monkeypatch the linecache directly so that parts of the
74 76 # stdlib that call it outside our control go through our codepath
75 77 # (otherwise we'd lose our tracebacks).
76 78 linecache.checkcache = self.check_cache
77 79
78 80 @property
79 81 def compiler_flags(self):
80 82 """Flags currently active in the compilation process.
81 83 """
82 84 return self._compiler.compiler.flags
83 85
84 86 def __call__(self, code, symbol, number=0):
85 87 """Compile some code while caching its contents such that the inspect
86 88 module can find it later.
87 89
88 90 Parameters
89 91 ----------
90 92 code : str
91 93 Source code to be compiled, one or more lines.
92 94
93 95 symbol : str
94 96 One of 'single', 'exec' or 'eval' (see the builtin ``compile``
95 97 documentation for further details on these fields).
96 98
97 99 number : int, optional
98 100 An integer argument identifying the code, useful for informational
99 101 purposes in tracebacks (typically it will be the IPython prompt
100 102 number).
101 103 """
102 104 name = code_name(code, number)
103 105 code_obj = self._compiler(code, name, symbol)
104 106 entry = (len(code), time.time(),
105 107 [line+'\n' for line in code.splitlines()], name)
106 108 # Cache the info both in the linecache (a global cache used internally
107 109 # by most of Python's inspect/traceback machinery), and in our cache
108 110 linecache.cache[name] = entry
109 111 linecache._ipython_cache[name] = entry
110 112 return code_obj
111 113
112 114 def check_cache(self, *args):
113 115 """Call linecache.checkcache() safely protecting our cached values.
114 116 """
115 117 # First call the orignal checkcache as intended
116 118 linecache._checkcache_ori(*args)
117 119 # Then, update back the cache with our data, so that tracebacks related
118 120 # to our compiled codes can be produced.
119 121 linecache.cache.update(linecache._ipython_cache)
General Comments 0
You need to be logged in to leave comments. Login now