##// END OF EJS Templates
Removed another of the tweaks, and added a note in that it is largely untested.
Doug Blank -
Show More
@@ -1,147 +1,144 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 * Thomas Kluyver
10 * Thomas Kluyver
11 """
11 """
12
12
13 # Note: though it might be more natural to name this module 'compiler', that
13 # Note: though it might be more natural to name this module 'compiler', that
14 # name is in the stdlib and name collisions with the stdlib tend to produce
14 # name is in the stdlib and name collisions with the stdlib tend to produce
15 # weird problems (often with third-party tools).
15 # weird problems (often with third-party tools).
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2010-2011 The IPython Development Team.
18 # Copyright (C) 2010-2011 The IPython Development Team.
19 #
19 #
20 # Distributed under the terms of the BSD License.
20 # Distributed under the terms of the BSD License.
21 #
21 #
22 # The full license is in the file COPYING.txt, distributed with this software.
22 # The full license is in the file COPYING.txt, distributed with this software.
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Imports
26 # Imports
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 from __future__ import print_function
28 from __future__ import print_function
29
29
30 # Stdlib imports
30 # Stdlib imports
31 import __future__
31 import __future__
32 from ast import PyCF_ONLY_AST
32 from ast import PyCF_ONLY_AST
33 import codeop
33 import codeop
34 import functools
34 import functools
35 import hashlib
35 import hashlib
36 import linecache
36 import linecache
37 import operator
37 import operator
38 import time
38 import time
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Constants
41 # Constants
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Roughtly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
44 # Roughtly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
45 # this is used as a bitmask to extract future-related code flags.
45 # this is used as a bitmask to extract future-related code flags.
46 try:
46 PyCF_MASK = functools.reduce(operator.or_,
47 PyCF_MASK = functools.reduce(operator.or_,
47 (getattr(__future__, fname).compiler_flag
48 (getattr(__future__, fname).compiler_flag
48 for fname in __future__.all_feature_names))
49 for fname in __future__.all_feature_names))
50 except AttributeError: # IronPython __future__'s are non-standard, 2/8/2014
51 PyCF_MASK = 0
52
49
53 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
54 # Local utilities
51 # Local utilities
55 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
56
53
57 def code_name(code, number=0):
54 def code_name(code, number=0):
58 """ Compute a (probably) unique name for code for caching.
55 """ Compute a (probably) unique name for code for caching.
59
56
60 This now expects code to be unicode.
57 This now expects code to be unicode.
61 """
58 """
62 hash_digest = hashlib.md5(code.encode("utf-8")).hexdigest()
59 hash_digest = hashlib.md5(code.encode("utf-8")).hexdigest()
63 # Include the number and 12 characters of the hash in the name. It's
60 # Include the number and 12 characters of the hash in the name. It's
64 # pretty much impossible that in a single session we'll have collisions
61 # pretty much impossible that in a single session we'll have collisions
65 # even with truncated hashes, and the full one makes tracebacks too long
62 # even with truncated hashes, and the full one makes tracebacks too long
66 return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
63 return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
67
64
68 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
69 # Classes and functions
66 # Classes and functions
70 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
71
68
72 class CachingCompiler(codeop.Compile):
69 class CachingCompiler(codeop.Compile):
73 """A compiler that caches code compiled from interactive statements.
70 """A compiler that caches code compiled from interactive statements.
74 """
71 """
75
72
76 def __init__(self):
73 def __init__(self):
77 codeop.Compile.__init__(self)
74 codeop.Compile.__init__(self)
78
75
79 # This is ugly, but it must be done this way to allow multiple
76 # This is ugly, but it must be done this way to allow multiple
80 # simultaneous ipython instances to coexist. Since Python itself
77 # simultaneous ipython instances to coexist. Since Python itself
81 # directly accesses the data structures in the linecache module, and
78 # directly accesses the data structures in the linecache module, and
82 # the cache therein is global, we must work with that data structure.
79 # the cache therein is global, we must work with that data structure.
83 # We must hold a reference to the original checkcache routine and call
80 # We must hold a reference to the original checkcache routine and call
84 # that in our own check_cache() below, but the special IPython cache
81 # that in our own check_cache() below, but the special IPython cache
85 # must also be shared by all IPython instances. If we were to hold
82 # must also be shared by all IPython instances. If we were to hold
86 # separate caches (one in each CachingCompiler instance), any call made
83 # separate caches (one in each CachingCompiler instance), any call made
87 # by Python itself to linecache.checkcache() would obliterate the
84 # by Python itself to linecache.checkcache() would obliterate the
88 # cached data from the other IPython instances.
85 # cached data from the other IPython instances.
89 if not hasattr(linecache, '_ipython_cache'):
86 if not hasattr(linecache, '_ipython_cache'):
90 linecache._ipython_cache = {}
87 linecache._ipython_cache = {}
91 if not hasattr(linecache, '_checkcache_ori'):
88 if not hasattr(linecache, '_checkcache_ori'):
92 linecache._checkcache_ori = linecache.checkcache
89 linecache._checkcache_ori = linecache.checkcache
93 # Now, we must monkeypatch the linecache directly so that parts of the
90 # Now, we must monkeypatch the linecache directly so that parts of the
94 # stdlib that call it outside our control go through our codepath
91 # stdlib that call it outside our control go through our codepath
95 # (otherwise we'd lose our tracebacks).
92 # (otherwise we'd lose our tracebacks).
96 linecache.checkcache = check_linecache_ipython
93 linecache.checkcache = check_linecache_ipython
97
94
98 def ast_parse(self, source, filename='<unknown>', symbol='exec'):
95 def ast_parse(self, source, filename='<unknown>', symbol='exec'):
99 """Parse code to an AST with the current compiler flags active.
96 """Parse code to an AST with the current compiler flags active.
100
97
101 Arguments are exactly the same as ast.parse (in the standard library),
98 Arguments are exactly the same as ast.parse (in the standard library),
102 and are passed to the built-in compile function."""
99 and are passed to the built-in compile function."""
103 return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
100 return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
104
101
105 def reset_compiler_flags(self):
102 def reset_compiler_flags(self):
106 """Reset compiler flags to default state."""
103 """Reset compiler flags to default state."""
107 # This value is copied from codeop.Compile.__init__, so if that ever
104 # This value is copied from codeop.Compile.__init__, so if that ever
108 # changes, it will need to be updated.
105 # changes, it will need to be updated.
109 self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
106 self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
110
107
111 @property
108 @property
112 def compiler_flags(self):
109 def compiler_flags(self):
113 """Flags currently active in the compilation process.
110 """Flags currently active in the compilation process.
114 """
111 """
115 return self.flags
112 return self.flags
116
113
117 def cache(self, code, number=0):
114 def cache(self, code, number=0):
118 """Make a name for a block of code, and cache the code.
115 """Make a name for a block of code, and cache the code.
119
116
120 Parameters
117 Parameters
121 ----------
118 ----------
122 code : str
119 code : str
123 The Python source code to cache.
120 The Python source code to cache.
124 number : int
121 number : int
125 A number which forms part of the code's name. Used for the execution
122 A number which forms part of the code's name. Used for the execution
126 counter.
123 counter.
127
124
128 Returns
125 Returns
129 -------
126 -------
130 The name of the cached code (as a string). Pass this as the filename
127 The name of the cached code (as a string). Pass this as the filename
131 argument to compilation, so that tracebacks are correctly hooked up.
128 argument to compilation, so that tracebacks are correctly hooked up.
132 """
129 """
133 name = code_name(code, number)
130 name = code_name(code, number)
134 entry = (len(code), time.time(),
131 entry = (len(code), time.time(),
135 [line+'\n' for line in code.splitlines()], name)
132 [line+'\n' for line in code.splitlines()], name)
136 linecache.cache[name] = entry
133 linecache.cache[name] = entry
137 linecache._ipython_cache[name] = entry
134 linecache._ipython_cache[name] = entry
138 return name
135 return name
139
136
140 def check_linecache_ipython(*args):
137 def check_linecache_ipython(*args):
141 """Call linecache.checkcache() safely protecting our cached values.
138 """Call linecache.checkcache() safely protecting our cached values.
142 """
139 """
143 # First call the orignal checkcache as intended
140 # First call the orignal checkcache as intended
144 linecache._checkcache_ori(*args)
141 linecache._checkcache_ori(*args)
145 # Then, update back the cache with our data, so that tracebacks related
142 # Then, update back the cache with our data, so that tracebacks related
146 # to our compiled codes can be produced.
143 # to our compiled codes can be produced.
147 linecache.cache.update(linecache._ipython_cache)
144 linecache.cache.update(linecache._ipython_cache)
@@ -1,60 +1,63 b''
1 """
1 """cli-specific implementation of process utilities.
2 cli-specific implementation of process utilities.
3
2
4 cli - Common Language Infrastructure for IronPython. Code
3 cli - Common Language Infrastructure for IronPython. Code
5 can run on any operating system. Check os.name for os-
4 can run on any operating system. Check os.name for os-
6 specific settings.
5 specific settings.
7
6
8 This file is only meant to be imported by process.py, not by end-users.
7 This file is only meant to be imported by process.py, not by end-users.
8
9 This file is largely untested. To become a full drop-in process
10 interface for IronPython will probably require you to help fill
11 in the details.
9 """
12 """
10
13
11 # Import cli libraries:
14 # Import cli libraries:
12 import clr
15 import clr
13 import System
16 import System
14
17
15 # Import Python libraries:
18 # Import Python libraries:
16 import os
19 import os
17
20
18 # Import IPython libraries:
21 # Import IPython libraries:
19 from IPython.utils import py3compat
22 from IPython.utils import py3compat
20 from ._process_common import arg_split
23 from ._process_common import arg_split
21
24
22 def _find_cmd(cmd):
25 def _find_cmd(cmd):
23 """Find the full path to a command using which."""
26 """Find the full path to a command using which."""
24 paths = System.Environment.GetEnvironmentVariable("PATH").Split(os.pathsep)
27 paths = System.Environment.GetEnvironmentVariable("PATH").Split(os.pathsep)
25 for path in paths:
28 for path in paths:
26 filename = os.path.join(path, cmd)
29 filename = os.path.join(path, cmd)
27 if System.IO.File.Exists(filename):
30 if System.IO.File.Exists(filename):
28 return py3compat.bytes_to_str(filename)
31 return py3compat.bytes_to_str(filename)
29 raise OSError("command %r not found" % cmd)
32 raise OSError("command %r not found" % cmd)
30
33
31 def system(cmd):
34 def system(cmd):
32 """
35 """
33 system(cmd) should work in a cli environment on Mac OSX, Linux,
36 system(cmd) should work in a cli environment on Mac OSX, Linux,
34 and Windows
37 and Windows
35 """
38 """
36 psi = System.Diagnostics.ProcessStartInfo(cmd)
39 psi = System.Diagnostics.ProcessStartInfo(cmd)
37 psi.RedirectStandardOutput = True
40 psi.RedirectStandardOutput = True
38 psi.RedirectStandardError = True
41 psi.RedirectStandardError = True
39 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal
42 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal
40 psi.UseShellExecute = False
43 psi.UseShellExecute = False
41 # Start up process:
44 # Start up process:
42 reg = System.Diagnostics.Process.Start(psi)
45 reg = System.Diagnostics.Process.Start(psi)
43
46
44 def getoutput(cmd):
47 def getoutput(cmd):
45 """
48 """
46 getoutput(cmd) should work in a cli environment on Mac OSX, Linux,
49 getoutput(cmd) should work in a cli environment on Mac OSX, Linux,
47 and Windows
50 and Windows
48 """
51 """
49 psi = System.Diagnostics.ProcessStartInfo(cmd)
52 psi = System.Diagnostics.ProcessStartInfo(cmd)
50 psi.RedirectStandardOutput = True
53 psi.RedirectStandardOutput = True
51 psi.RedirectStandardError = True
54 psi.RedirectStandardError = True
52 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal
55 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal
53 psi.UseShellExecute = False
56 psi.UseShellExecute = False
54 # Start up process:
57 # Start up process:
55 reg = System.Diagnostics.Process.Start(psi)
58 reg = System.Diagnostics.Process.Start(psi)
56 myOutput = reg.StandardOutput
59 myOutput = reg.StandardOutput
57 output = myOutput.ReadToEnd()
60 output = myOutput.ReadToEnd()
58 myError = reg.StandardError
61 myError = reg.StandardError
59 error = myError.ReadToEnd()
62 error = myError.ReadToEnd()
60 return output
63 return output
General Comments 0
You need to be logged in to leave comments. Login now