##// END OF EJS Templates
Merge pull request #8960 from sieben/simplify_comparison...
Min RK -
r21784:c0f6be23 merge
parent child Browse files
Show More
@@ -1,360 +1,360
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Provides a reload() function that acts recursively.
4 4
5 5 Python's normal :func:`python:reload` function only reloads the module that it's
6 6 passed. The :func:`reload` function in this module also reloads everything
7 7 imported from that module, which is useful when you're changing files deep
8 8 inside a package.
9 9
10 10 To use this as your default reload function, type this for Python 2::
11 11
12 12 import __builtin__
13 13 from IPython.lib import deepreload
14 14 __builtin__.reload = deepreload.reload
15 15
16 16 Or this for Python 3::
17 17
18 18 import builtins
19 19 from IPython.lib import deepreload
20 20 builtins.reload = deepreload.reload
21 21
22 22 A reference to the original :func:`python:reload` is stored in this module as
23 23 :data:`original_reload`, so you can restore it later.
24 24
25 25 This code is almost entirely based on knee.py, which is a Python
26 26 re-implementation of hierarchical module import.
27 27 """
28 28 from __future__ import print_function
29 29 #*****************************************************************************
30 30 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
31 31 #
32 32 # Distributed under the terms of the BSD License. The full license is in
33 33 # the file COPYING, distributed as part of this software.
34 34 #*****************************************************************************
35 35
36 36 from contextlib import contextmanager
37 37 import imp
38 38 import sys
39 39
40 40 from types import ModuleType
41 41 from warnings import warn
42 42
43 43 from IPython.utils.py3compat import builtin_mod, builtin_mod_name
44 44
45 45 original_import = builtin_mod.__import__
46 46
47 47 @contextmanager
48 48 def replace_import_hook(new_import):
49 49 saved_import = builtin_mod.__import__
50 50 builtin_mod.__import__ = new_import
51 51 try:
52 52 yield
53 53 finally:
54 54 builtin_mod.__import__ = saved_import
55 55
56 56 def get_parent(globals, level):
57 57 """
58 58 parent, name = get_parent(globals, level)
59 59
60 60 Return the package that an import is being performed in. If globals comes
61 61 from the module foo.bar.bat (not itself a package), this returns the
62 62 sys.modules entry for foo.bar. If globals is from a package's __init__.py,
63 63 the package's entry in sys.modules is returned.
64 64
65 65 If globals doesn't come from a package or a module in a package, or a
66 66 corresponding entry is not found in sys.modules, None is returned.
67 67 """
68 68 orig_level = level
69 69
70 70 if not level or not isinstance(globals, dict):
71 71 return None, ''
72 72
73 73 pkgname = globals.get('__package__', None)
74 74
75 75 if pkgname is not None:
76 76 # __package__ is set, so use it
77 77 if not hasattr(pkgname, 'rindex'):
78 78 raise ValueError('__package__ set to non-string')
79 79 if len(pkgname) == 0:
80 80 if level > 0:
81 81 raise ValueError('Attempted relative import in non-package')
82 82 return None, ''
83 83 name = pkgname
84 84 else:
85 85 # __package__ not set, so figure it out and set it
86 86 if '__name__' not in globals:
87 87 return None, ''
88 88 modname = globals['__name__']
89 89
90 90 if '__path__' in globals:
91 91 # __path__ is set, so modname is already the package name
92 92 globals['__package__'] = name = modname
93 93 else:
94 94 # Normal module, so work out the package name if any
95 95 lastdot = modname.rfind('.')
96 if lastdot < 0 and level > 0:
96 if lastdot < 0 < level:
97 97 raise ValueError("Attempted relative import in non-package")
98 98 if lastdot < 0:
99 99 globals['__package__'] = None
100 100 return None, ''
101 101 globals['__package__'] = name = modname[:lastdot]
102 102
103 103 dot = len(name)
104 104 for x in range(level, 1, -1):
105 105 try:
106 106 dot = name.rindex('.', 0, dot)
107 107 except ValueError:
108 108 raise ValueError("attempted relative import beyond top-level "
109 109 "package")
110 110 name = name[:dot]
111 111
112 112 try:
113 113 parent = sys.modules[name]
114 114 except:
115 115 if orig_level < 1:
116 116 warn("Parent module '%.200s' not found while handling absolute "
117 117 "import" % name)
118 118 parent = None
119 119 else:
120 120 raise SystemError("Parent module '%.200s' not loaded, cannot "
121 121 "perform relative import" % name)
122 122
123 123 # We expect, but can't guarantee, if parent != None, that:
124 124 # - parent.__name__ == name
125 125 # - parent.__dict__ is globals
126 126 # If this is violated... Who cares?
127 127 return parent, name
128 128
129 129 def load_next(mod, altmod, name, buf):
130 130 """
131 131 mod, name, buf = load_next(mod, altmod, name, buf)
132 132
133 133 altmod is either None or same as mod
134 134 """
135 135
136 136 if len(name) == 0:
137 137 # completely empty module name should only happen in
138 138 # 'from . import' (or '__import__("")')
139 139 return mod, None, buf
140 140
141 141 dot = name.find('.')
142 142 if dot == 0:
143 143 raise ValueError('Empty module name')
144 144
145 145 if dot < 0:
146 146 subname = name
147 147 next = None
148 148 else:
149 149 subname = name[:dot]
150 150 next = name[dot+1:]
151 151
152 152 if buf != '':
153 153 buf += '.'
154 154 buf += subname
155 155
156 156 result = import_submodule(mod, subname, buf)
157 157 if result is None and mod != altmod:
158 158 result = import_submodule(altmod, subname, subname)
159 159 if result is not None:
160 160 buf = subname
161 161
162 162 if result is None:
163 163 raise ImportError("No module named %.200s" % name)
164 164
165 165 return result, next, buf
166 166
167 167 # Need to keep track of what we've already reloaded to prevent cyclic evil
168 168 found_now = {}
169 169
170 170 def import_submodule(mod, subname, fullname):
171 171 """m = import_submodule(mod, subname, fullname)"""
172 172 # Require:
173 173 # if mod == None: subname == fullname
174 174 # else: mod.__name__ + "." + subname == fullname
175 175
176 176 global found_now
177 177 if fullname in found_now and fullname in sys.modules:
178 178 m = sys.modules[fullname]
179 179 else:
180 180 print('Reloading', fullname)
181 181 found_now[fullname] = 1
182 182 oldm = sys.modules.get(fullname, None)
183 183
184 184 if mod is None:
185 185 path = None
186 186 elif hasattr(mod, '__path__'):
187 187 path = mod.__path__
188 188 else:
189 189 return None
190 190
191 191 try:
192 192 # This appears to be necessary on Python 3, because imp.find_module()
193 193 # tries to import standard libraries (like io) itself, and we don't
194 194 # want them to be processed by our deep_import_hook.
195 195 with replace_import_hook(original_import):
196 196 fp, filename, stuff = imp.find_module(subname, path)
197 197 except ImportError:
198 198 return None
199 199
200 200 try:
201 201 m = imp.load_module(fullname, fp, filename, stuff)
202 202 except:
203 203 # load_module probably removed name from modules because of
204 204 # the error. Put back the original module object.
205 205 if oldm:
206 206 sys.modules[fullname] = oldm
207 207 raise
208 208 finally:
209 209 if fp: fp.close()
210 210
211 211 add_submodule(mod, m, fullname, subname)
212 212
213 213 return m
214 214
215 215 def add_submodule(mod, submod, fullname, subname):
216 216 """mod.{subname} = submod"""
217 217 if mod is None:
218 218 return #Nothing to do here.
219 219
220 220 if submod is None:
221 221 submod = sys.modules[fullname]
222 222
223 223 setattr(mod, subname, submod)
224 224
225 225 return
226 226
227 227 def ensure_fromlist(mod, fromlist, buf, recursive):
228 228 """Handle 'from module import a, b, c' imports."""
229 229 if not hasattr(mod, '__path__'):
230 230 return
231 231 for item in fromlist:
232 232 if not hasattr(item, 'rindex'):
233 233 raise TypeError("Item in ``from list'' not a string")
234 234 if item == '*':
235 235 if recursive:
236 236 continue # avoid endless recursion
237 237 try:
238 238 all = mod.__all__
239 239 except AttributeError:
240 240 pass
241 241 else:
242 242 ret = ensure_fromlist(mod, all, buf, 1)
243 243 if not ret:
244 244 return 0
245 245 elif not hasattr(mod, item):
246 246 import_submodule(mod, item, buf + '.' + item)
247 247
248 248 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
249 249 """Replacement for __import__()"""
250 250 parent, buf = get_parent(globals, level)
251 251
252 252 head, name, buf = load_next(parent, None if level < 0 else parent, name, buf)
253 253
254 254 tail = head
255 255 while name:
256 256 tail, name, buf = load_next(tail, tail, name, buf)
257 257
258 258 # If tail is None, both get_parent and load_next found
259 259 # an empty module name: someone called __import__("") or
260 260 # doctored faulty bytecode
261 261 if tail is None:
262 262 raise ValueError('Empty module name')
263 263
264 264 if not fromlist:
265 265 return head
266 266
267 267 ensure_fromlist(tail, fromlist, buf, 0)
268 268 return tail
269 269
270 270 modules_reloading = {}
271 271
272 272 def deep_reload_hook(m):
273 273 """Replacement for reload()."""
274 274 if not isinstance(m, ModuleType):
275 275 raise TypeError("reload() argument must be module")
276 276
277 277 name = m.__name__
278 278
279 279 if name not in sys.modules:
280 280 raise ImportError("reload(): module %.200s not in sys.modules" % name)
281 281
282 282 global modules_reloading
283 283 try:
284 284 return modules_reloading[name]
285 285 except:
286 286 modules_reloading[name] = m
287 287
288 288 dot = name.rfind('.')
289 289 if dot < 0:
290 290 subname = name
291 291 path = None
292 292 else:
293 293 try:
294 294 parent = sys.modules[name[:dot]]
295 295 except KeyError:
296 296 modules_reloading.clear()
297 297 raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot])
298 298 subname = name[dot+1:]
299 299 path = getattr(parent, "__path__", None)
300 300
301 301 try:
302 302 # This appears to be necessary on Python 3, because imp.find_module()
303 303 # tries to import standard libraries (like io) itself, and we don't
304 304 # want them to be processed by our deep_import_hook.
305 305 with replace_import_hook(original_import):
306 306 fp, filename, stuff = imp.find_module(subname, path)
307 307 finally:
308 308 modules_reloading.clear()
309 309
310 310 try:
311 311 newm = imp.load_module(name, fp, filename, stuff)
312 312 except:
313 313 # load_module probably removed name from modules because of
314 314 # the error. Put back the original module object.
315 315 sys.modules[name] = m
316 316 raise
317 317 finally:
318 318 if fp: fp.close()
319 319
320 320 modules_reloading.clear()
321 321 return newm
322 322
323 323 # Save the original hooks
324 324 try:
325 325 original_reload = builtin_mod.reload
326 326 except AttributeError:
327 327 original_reload = imp.reload # Python 3
328 328
329 329 # Replacement for reload()
330 330 def reload(module, exclude=('sys', 'os.path', builtin_mod_name, '__main__')):
331 331 """Recursively reload all modules used in the given module. Optionally
332 332 takes a list of modules to exclude from reloading. The default exclude
333 333 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
334 334 display, exception, and io hooks.
335 335 """
336 336 global found_now
337 337 for i in exclude:
338 338 found_now[i] = 1
339 339 try:
340 340 with replace_import_hook(deep_import_hook):
341 341 return deep_reload_hook(module)
342 342 finally:
343 343 found_now = {}
344 344
345 345
346 346 def _dreload(module, **kwargs):
347 347 """
348 348 **deprecated**
349 349
350 350 import reload explicitly from `IPython.lib.deepreload` to use it
351 351
352 352 """
353 353 warn("""
354 354 injecting `dreload` in interactive namespace is deprecated, please import `reload` explicitly from `IPython.lib.deepreload`
355 355 """, DeprecationWarning, stacklevel=2)
356 356 reload(module, **kwargs)
357 357
358 358 # Uncomment the following to automatically activate deep reloading whenever
359 359 # this module is imported
360 360 #builtin_mod.reload = reload
@@ -1,315 +1,315
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Class and program to colorize python source code for ANSI terminals.
4 4
5 5 Based on an HTML code highlighter by Jurgen Hermann found at:
6 6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
7 7
8 8 Modifications by Fernando Perez (fperez@colorado.edu).
9 9
10 10 Information on the original HTML highlighter follows:
11 11
12 12 MoinMoin - Python Source Parser
13 13
14 14 Title: Colorize Python source using the built-in tokenizer
15 15
16 16 Submitter: Jurgen Hermann
17 17 Last Updated:2001/04/06
18 18
19 19 Version no:1.2
20 20
21 21 Description:
22 22
23 23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
24 24 Python source code to HTML markup, rendering comments, keywords,
25 25 operators, numeric and string literals in different colors.
26 26
27 27 It shows how to use the built-in keyword, token and tokenize modules to
28 28 scan Python source code and re-emit it with no changes to its original
29 29 formatting (which is the hard part).
30 30 """
31 31 from __future__ import print_function
32 32 from __future__ import absolute_import
33 33 from __future__ import unicode_literals
34 34
35 35 __all__ = ['ANSICodeColors','Parser']
36 36
37 37 _scheme_default = 'Linux'
38 38
39 39
40 40 # Imports
41 41 import keyword
42 42 import os
43 43 import sys
44 44 import token
45 45 import tokenize
46 46
47 47 try:
48 48 generate_tokens = tokenize.generate_tokens
49 49 except AttributeError:
50 50 # Python 3. Note that we use the undocumented _tokenize because it expects
51 51 # strings, not bytes. See also Python issue #9969.
52 52 generate_tokens = tokenize._tokenize
53 53
54 54 from IPython.utils.coloransi import *
55 55 from IPython.utils.py3compat import PY3
56 56
57 57 if PY3:
58 58 from io import StringIO
59 59 else:
60 60 from StringIO import StringIO
61 61
62 62 #############################################################################
63 63 ### Python Source Parser (does Hilighting)
64 64 #############################################################################
65 65
66 66 _KEYWORD = token.NT_OFFSET + 1
67 67 _TEXT = token.NT_OFFSET + 2
68 68
69 69 #****************************************************************************
70 70 # Builtin color schemes
71 71
72 72 Colors = TermColors # just a shorthand
73 73
74 74 # Build a few color schemes
75 75 NoColor = ColorScheme(
76 76 'NoColor',{
77 77 token.NUMBER : Colors.NoColor,
78 78 token.OP : Colors.NoColor,
79 79 token.STRING : Colors.NoColor,
80 80 tokenize.COMMENT : Colors.NoColor,
81 81 token.NAME : Colors.NoColor,
82 82 token.ERRORTOKEN : Colors.NoColor,
83 83
84 84 _KEYWORD : Colors.NoColor,
85 85 _TEXT : Colors.NoColor,
86 86
87 87 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
88 88 } )
89 89
90 90 LinuxColors = ColorScheme(
91 91 'Linux',{
92 92 token.NUMBER : Colors.LightCyan,
93 93 token.OP : Colors.Yellow,
94 94 token.STRING : Colors.LightBlue,
95 95 tokenize.COMMENT : Colors.LightRed,
96 96 token.NAME : Colors.Normal,
97 97 token.ERRORTOKEN : Colors.Red,
98 98
99 99 _KEYWORD : Colors.LightGreen,
100 100 _TEXT : Colors.Yellow,
101 101
102 102 'normal' : Colors.Normal # color off (usu. Colors.Normal)
103 103 } )
104 104
105 105 LightBGColors = ColorScheme(
106 106 'LightBG',{
107 107 token.NUMBER : Colors.Cyan,
108 108 token.OP : Colors.Blue,
109 109 token.STRING : Colors.Blue,
110 110 tokenize.COMMENT : Colors.Red,
111 111 token.NAME : Colors.Normal,
112 112 token.ERRORTOKEN : Colors.Red,
113 113
114 114 _KEYWORD : Colors.Green,
115 115 _TEXT : Colors.Blue,
116 116
117 117 'normal' : Colors.Normal # color off (usu. Colors.Normal)
118 118 } )
119 119
120 120 # Build table of color schemes (needed by the parser)
121 121 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
122 122 _scheme_default)
123 123
124 124 class Parser:
125 125 """ Format colored Python source.
126 126 """
127 127
128 128 def __init__(self, color_table=None,out = sys.stdout):
129 129 """ Create a parser with a specified color table and output channel.
130 130
131 131 Call format() to process code.
132 132 """
133 133 self.color_table = color_table and color_table or ANSICodeColors
134 134 self.out = out
135 135
136 136 def format(self, raw, out = None, scheme = ''):
137 137 return self.format2(raw, out, scheme)[0]
138 138
139 139 def format2(self, raw, out = None, scheme = ''):
140 140 """ Parse and send the colored source.
141 141
142 142 If out and scheme are not specified, the defaults (given to
143 143 constructor) are used.
144 144
145 145 out should be a file-type object. Optionally, out can be given as the
146 146 string 'str' and the parser will automatically return the output in a
147 147 string."""
148 148
149 149 string_output = 0
150 150 if out == 'str' or self.out == 'str' or \
151 151 isinstance(self.out,StringIO):
152 152 # XXX - I don't really like this state handling logic, but at this
153 153 # point I don't want to make major changes, so adding the
154 154 # isinstance() check is the simplest I can do to ensure correct
155 155 # behavior.
156 156 out_old = self.out
157 157 self.out = StringIO()
158 158 string_output = 1
159 159 elif out is not None:
160 160 self.out = out
161 161
162 162 # Fast return of the unmodified input for NoColor scheme
163 163 if scheme == 'NoColor':
164 164 error = False
165 165 self.out.write(raw)
166 166 if string_output:
167 167 return raw,error
168 168 else:
169 169 return None,error
170 170
171 171 # local shorthands
172 172 colors = self.color_table[scheme].colors
173 173 self.colors = colors # put in object so __call__ sees it
174 174
175 175 # Remove trailing whitespace and normalize tabs
176 176 self.raw = raw.expandtabs().rstrip()
177 177
178 178 # store line offsets in self.lines
179 179 self.lines = [0, 0]
180 180 pos = 0
181 181 raw_find = self.raw.find
182 182 lines_append = self.lines.append
183 183 while 1:
184 184 pos = raw_find('\n', pos) + 1
185 185 if not pos: break
186 186 lines_append(pos)
187 187 lines_append(len(self.raw))
188 188
189 189 # parse the source and write it
190 190 self.pos = 0
191 191 text = StringIO(self.raw)
192 192
193 193 error = False
194 194 try:
195 195 for atoken in generate_tokens(text.readline):
196 196 self(*atoken)
197 197 except tokenize.TokenError as ex:
198 198 msg = ex.args[0]
199 199 line = ex.args[1][0]
200 200 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
201 201 (colors[token.ERRORTOKEN],
202 202 msg, self.raw[self.lines[line]:],
203 203 colors.normal)
204 204 )
205 205 error = True
206 206 self.out.write(colors.normal+'\n')
207 207 if string_output:
208 208 output = self.out.getvalue()
209 209 self.out = out_old
210 210 return (output, error)
211 211 return (None, error)
212 212
213 213 def __call__(self, toktype, toktext, start_pos, end_pos, line):
214 214 """ Token handler, with syntax highlighting."""
215 215 (srow,scol) = start_pos
216 216 (erow,ecol) = end_pos
217 217 colors = self.colors
218 218 owrite = self.out.write
219 219
220 220 # line separator, so this works across platforms
221 221 linesep = os.linesep
222 222
223 223 # calculate new positions
224 224 oldpos = self.pos
225 225 newpos = self.lines[srow] + scol
226 226 self.pos = newpos + len(toktext)
227 227
228 228 # send the original whitespace, if needed
229 229 if newpos > oldpos:
230 230 owrite(self.raw[oldpos:newpos])
231 231
232 232 # skip indenting tokens
233 233 if toktype in [token.INDENT, token.DEDENT]:
234 234 self.pos = newpos
235 235 return
236 236
237 237 # map token type to a color group
238 if token.LPAR <= toktype and toktype <= token.OP:
238 if token.LPAR <= toktype <= token.OP:
239 239 toktype = token.OP
240 240 elif toktype == token.NAME and keyword.iskeyword(toktext):
241 241 toktype = _KEYWORD
242 242 color = colors.get(toktype, colors[_TEXT])
243 243
244 244 #print '<%s>' % toktext, # dbg
245 245
246 246 # Triple quoted strings must be handled carefully so that backtracking
247 247 # in pagers works correctly. We need color terminators on _each_ line.
248 248 if linesep in toktext:
249 249 toktext = toktext.replace(linesep, '%s%s%s' %
250 250 (colors.normal,linesep,color))
251 251
252 252 # send text
253 253 owrite('%s%s%s' % (color,toktext,colors.normal))
254 254
255 255 def main(argv=None):
256 256 """Run as a command-line script: colorize a python file or stdin using ANSI
257 257 color escapes and print to stdout.
258 258
259 259 Inputs:
260 260
261 261 - argv(None): a list of strings like sys.argv[1:] giving the command-line
262 262 arguments. If None, use sys.argv[1:].
263 263 """
264 264
265 265 usage_msg = """%prog [options] [filename]
266 266
267 267 Colorize a python file or stdin using ANSI color escapes and print to stdout.
268 268 If no filename is given, or if filename is -, read standard input."""
269 269
270 270 import optparse
271 271 parser = optparse.OptionParser(usage=usage_msg)
272 272 newopt = parser.add_option
273 273 newopt('-s','--scheme',metavar='NAME',dest='scheme_name',action='store',
274 274 choices=['Linux','LightBG','NoColor'],default=_scheme_default,
275 275 help="give the color scheme to use. Currently only 'Linux'\
276 276 (default) and 'LightBG' and 'NoColor' are implemented (give without\
277 277 quotes)")
278 278
279 279 opts,args = parser.parse_args(argv)
280 280
281 281 if len(args) > 1:
282 282 parser.error("you must give at most one filename.")
283 283
284 284 if len(args) == 0:
285 285 fname = '-' # no filename given; setup to read from stdin
286 286 else:
287 287 fname = args[0]
288 288
289 289 if fname == '-':
290 290 stream = sys.stdin
291 291 else:
292 292 try:
293 293 stream = open(fname)
294 294 except IOError as msg:
295 295 print(msg, file=sys.stderr)
296 296 sys.exit(1)
297 297
298 298 parser = Parser()
299 299
300 300 # we need nested try blocks because pre-2.5 python doesn't support unified
301 301 # try-except-finally
302 302 try:
303 303 try:
304 304 # write colorized version to stdout
305 305 parser.format(stream.read(),scheme=opts.scheme_name)
306 306 except IOError as msg:
307 307 # if user reads through a pager and quits, don't print traceback
308 308 if msg.args != (32,'Broken pipe'):
309 309 raise
310 310 finally:
311 311 if stream is not sys.stdin:
312 312 stream.close() # in case a non-handled exception happened above
313 313
314 314 if __name__ == "__main__":
315 315 main()
General Comments 0
You need to be logged in to leave comments. Login now