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