Show More
The requested changes are too big and content was truncated. Show full diff
@@ -1,580 +1,581 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | A base class for a configurable application. |
|
4 | 4 | |
|
5 | 5 | Authors: |
|
6 | 6 | |
|
7 | 7 | * Brian Granger |
|
8 | 8 | * Min RK |
|
9 | 9 | """ |
|
10 | from __future__ import print_function | |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Copyright (C) 2008-2011 The IPython Development Team |
|
13 | 14 | # |
|
14 | 15 | # Distributed under the terms of the BSD License. The full license is in |
|
15 | 16 | # the file COPYING, distributed as part of this software. |
|
16 | 17 | #----------------------------------------------------------------------------- |
|
17 | 18 | |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | # Imports |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | |
|
22 | 23 | import logging |
|
23 | 24 | import os |
|
24 | 25 | import re |
|
25 | 26 | import sys |
|
26 | 27 | from copy import deepcopy |
|
27 | 28 | from collections import defaultdict |
|
28 | 29 | |
|
29 | 30 | from IPython.external.decorator import decorator |
|
30 | 31 | |
|
31 | 32 | from IPython.config.configurable import SingletonConfigurable |
|
32 | 33 | from IPython.config.loader import ( |
|
33 | 34 | KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, |
|
34 | 35 | ) |
|
35 | 36 | |
|
36 | 37 | from IPython.utils.traitlets import ( |
|
37 | 38 | Unicode, List, Enum, Dict, Instance, TraitError |
|
38 | 39 | ) |
|
39 | 40 | from IPython.utils.importstring import import_item |
|
40 | 41 | from IPython.utils.text import indent, wrap_paragraphs, dedent |
|
41 | 42 | from IPython.utils import py3compat |
|
42 | 43 | |
|
43 | 44 | #----------------------------------------------------------------------------- |
|
44 | 45 | # function for re-wrapping a helpstring |
|
45 | 46 | #----------------------------------------------------------------------------- |
|
46 | 47 | |
|
47 | 48 | #----------------------------------------------------------------------------- |
|
48 | 49 | # Descriptions for the various sections |
|
49 | 50 | #----------------------------------------------------------------------------- |
|
50 | 51 | |
|
51 | 52 | # merge flags&aliases into options |
|
52 | 53 | option_description = """ |
|
53 | 54 | Arguments that take values are actually convenience aliases to full |
|
54 | 55 | Configurables, whose aliases are listed on the help line. For more information |
|
55 | 56 | on full configurables, see '--help-all'. |
|
56 | 57 | """.strip() # trim newlines of front and back |
|
57 | 58 | |
|
58 | 59 | keyvalue_description = """ |
|
59 | 60 | Parameters are set from command-line arguments of the form: |
|
60 | 61 | `--Class.trait=value`. |
|
61 | 62 | This line is evaluated in Python, so simple expressions are allowed, e.g.:: |
|
62 | 63 | `--C.a='range(3)'` For setting C.a=[0,1,2]. |
|
63 | 64 | """.strip() # trim newlines of front and back |
|
64 | 65 | |
|
65 | 66 | # sys.argv can be missing, for example when python is embedded. See the docs |
|
66 | 67 | # for details: http://docs.python.org/2/c-api/intro.html#embedding-python |
|
67 | 68 | if not hasattr(sys, "argv"): |
|
68 | 69 | sys.argv = [""] |
|
69 | 70 | |
|
70 | 71 | subcommand_description = """ |
|
71 | 72 | Subcommands are launched as `{app} cmd [args]`. For information on using |
|
72 | 73 | subcommand 'cmd', do: `{app} cmd -h`. |
|
73 | 74 | """.strip().format(app=os.path.basename(sys.argv[0])) |
|
74 | 75 | # get running program name |
|
75 | 76 | |
|
76 | 77 | #----------------------------------------------------------------------------- |
|
77 | 78 | # Application class |
|
78 | 79 | #----------------------------------------------------------------------------- |
|
79 | 80 | |
|
80 | 81 | @decorator |
|
81 | 82 | def catch_config_error(method, app, *args, **kwargs): |
|
82 | 83 | """Method decorator for catching invalid config (Trait/ArgumentErrors) during init. |
|
83 | 84 | |
|
84 | 85 | On a TraitError (generally caused by bad config), this will print the trait's |
|
85 | 86 | message, and exit the app. |
|
86 | 87 | |
|
87 | 88 | For use on init methods, to prevent invoking excepthook on invalid input. |
|
88 | 89 | """ |
|
89 | 90 | try: |
|
90 | 91 | return method(app, *args, **kwargs) |
|
91 | 92 | except (TraitError, ArgumentError) as e: |
|
92 | 93 | app.print_help() |
|
93 | 94 | app.log.fatal("Bad config encountered during initialization:") |
|
94 | 95 | app.log.fatal(str(e)) |
|
95 | 96 | app.log.debug("Config at the time: %s", app.config) |
|
96 | 97 | app.exit(1) |
|
97 | 98 | |
|
98 | 99 | |
|
99 | 100 | class ApplicationError(Exception): |
|
100 | 101 | pass |
|
101 | 102 | |
|
102 | 103 | class LevelFormatter(logging.Formatter): |
|
103 | 104 | """Formatter with additional `highlevel` record |
|
104 | 105 | |
|
105 | 106 | This field is empty if log level is less than highlevel_limit, |
|
106 | 107 | otherwise it is formatted with self.highlevel_format. |
|
107 | 108 | |
|
108 | 109 | Useful for adding 'WARNING' to warning messages, |
|
109 | 110 | without adding 'INFO' to info, etc. |
|
110 | 111 | """ |
|
111 | 112 | highlevel_limit = logging.WARN |
|
112 | 113 | highlevel_format = " %(levelname)s |" |
|
113 | 114 | |
|
114 | 115 | def format(self, record): |
|
115 | 116 | if record.levelno >= self.highlevel_limit: |
|
116 | 117 | record.highlevel = self.highlevel_format % record.__dict__ |
|
117 | 118 | else: |
|
118 | 119 | record.highlevel = "" |
|
119 | 120 | return super(LevelFormatter, self).format(record) |
|
120 | 121 | |
|
121 | 122 | |
|
122 | 123 | class Application(SingletonConfigurable): |
|
123 | 124 | """A singleton application with full configuration support.""" |
|
124 | 125 | |
|
125 | 126 | # The name of the application, will usually match the name of the command |
|
126 | 127 | # line application |
|
127 | 128 | name = Unicode(u'application') |
|
128 | 129 | |
|
129 | 130 | # The description of the application that is printed at the beginning |
|
130 | 131 | # of the help. |
|
131 | 132 | description = Unicode(u'This is an application.') |
|
132 | 133 | # default section descriptions |
|
133 | 134 | option_description = Unicode(option_description) |
|
134 | 135 | keyvalue_description = Unicode(keyvalue_description) |
|
135 | 136 | subcommand_description = Unicode(subcommand_description) |
|
136 | 137 | |
|
137 | 138 | # The usage and example string that goes at the end of the help string. |
|
138 | 139 | examples = Unicode() |
|
139 | 140 | |
|
140 | 141 | # A sequence of Configurable subclasses whose config=True attributes will |
|
141 | 142 | # be exposed at the command line. |
|
142 | 143 | classes = List([]) |
|
143 | 144 | |
|
144 | 145 | # The version string of this application. |
|
145 | 146 | version = Unicode(u'0.0') |
|
146 | 147 | |
|
147 | 148 | # the argv used to initialize the application |
|
148 | 149 | argv = List() |
|
149 | 150 | |
|
150 | 151 | # The log level for the application |
|
151 | 152 | log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'), |
|
152 | 153 | default_value=logging.WARN, |
|
153 | 154 | config=True, |
|
154 | 155 | help="Set the log level by value or name.") |
|
155 | 156 | def _log_level_changed(self, name, old, new): |
|
156 | 157 | """Adjust the log level when log_level is set.""" |
|
157 | 158 | if isinstance(new, basestring): |
|
158 | 159 | new = getattr(logging, new) |
|
159 | 160 | self.log_level = new |
|
160 | 161 | self.log.setLevel(new) |
|
161 | 162 | |
|
162 | 163 | log_datefmt = Unicode("%Y-%m-%d %H:%M:%S", config=True, |
|
163 | 164 | help="The date format used by logging formatters for %(asctime)s" |
|
164 | 165 | ) |
|
165 | 166 | def _log_datefmt_changed(self, name, old, new): |
|
166 | 167 | self._log_format_changed() |
|
167 | 168 | |
|
168 | 169 | log_format = Unicode("[%(name)s]%(highlevel)s %(message)s", config=True, |
|
169 | 170 | help="The Logging format template", |
|
170 | 171 | ) |
|
171 | 172 | def _log_format_changed(self, name, old, new): |
|
172 | 173 | """Change the log formatter when log_format is set.""" |
|
173 | 174 | _log_handler = self.log.handlers[0] |
|
174 | 175 | _log_formatter = LevelFormatter(new, datefmt=self.log_datefmt) |
|
175 | 176 | _log_handler.setFormatter(_log_formatter) |
|
176 | 177 | |
|
177 | 178 | log = Instance(logging.Logger) |
|
178 | 179 | def _log_default(self): |
|
179 | 180 | """Start logging for this application. |
|
180 | 181 | |
|
181 | 182 | The default is to log to stderr using a StreamHandler, if no default |
|
182 | 183 | handler already exists. The log level starts at logging.WARN, but this |
|
183 | 184 | can be adjusted by setting the ``log_level`` attribute. |
|
184 | 185 | """ |
|
185 | 186 | log = logging.getLogger(self.__class__.__name__) |
|
186 | 187 | log.setLevel(self.log_level) |
|
187 | 188 | log.propagate = False |
|
188 | 189 | _log = log # copied from Logger.hasHandlers() (new in Python 3.2) |
|
189 | 190 | while _log: |
|
190 | 191 | if _log.handlers: |
|
191 | 192 | return log |
|
192 | 193 | if not _log.propagate: |
|
193 | 194 | break |
|
194 | 195 | else: |
|
195 | 196 | _log = _log.parent |
|
196 | 197 | if sys.executable.endswith('pythonw.exe'): |
|
197 | 198 | # this should really go to a file, but file-logging is only |
|
198 | 199 | # hooked up in parallel applications |
|
199 | 200 | _log_handler = logging.StreamHandler(open(os.devnull, 'w')) |
|
200 | 201 | else: |
|
201 | 202 | _log_handler = logging.StreamHandler() |
|
202 | 203 | _log_formatter = LevelFormatter(self.log_format, datefmt=self.log_datefmt) |
|
203 | 204 | _log_handler.setFormatter(_log_formatter) |
|
204 | 205 | log.addHandler(_log_handler) |
|
205 | 206 | return log |
|
206 | 207 | |
|
207 | 208 | # the alias map for configurables |
|
208 | 209 | aliases = Dict({'log-level' : 'Application.log_level'}) |
|
209 | 210 | |
|
210 | 211 | # flags for loading Configurables or store_const style flags |
|
211 | 212 | # flags are loaded from this dict by '--key' flags |
|
212 | 213 | # this must be a dict of two-tuples, the first element being the Config/dict |
|
213 | 214 | # and the second being the help string for the flag |
|
214 | 215 | flags = Dict() |
|
215 | 216 | def _flags_changed(self, name, old, new): |
|
216 | 217 | """ensure flags dict is valid""" |
|
217 | 218 | for key,value in new.iteritems(): |
|
218 | 219 | assert len(value) == 2, "Bad flag: %r:%s"%(key,value) |
|
219 | 220 | assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value) |
|
220 | 221 | assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value) |
|
221 | 222 | |
|
222 | 223 | |
|
223 | 224 | # subcommands for launching other applications |
|
224 | 225 | # if this is not empty, this will be a parent Application |
|
225 | 226 | # this must be a dict of two-tuples, |
|
226 | 227 | # the first element being the application class/import string |
|
227 | 228 | # and the second being the help string for the subcommand |
|
228 | 229 | subcommands = Dict() |
|
229 | 230 | # parse_command_line will initialize a subapp, if requested |
|
230 | 231 | subapp = Instance('IPython.config.application.Application', allow_none=True) |
|
231 | 232 | |
|
232 | 233 | # extra command-line arguments that don't set config values |
|
233 | 234 | extra_args = List(Unicode) |
|
234 | 235 | |
|
235 | 236 | |
|
236 | 237 | def __init__(self, **kwargs): |
|
237 | 238 | SingletonConfigurable.__init__(self, **kwargs) |
|
238 | 239 | # Ensure my class is in self.classes, so my attributes appear in command line |
|
239 | 240 | # options and config files. |
|
240 | 241 | if self.__class__ not in self.classes: |
|
241 | 242 | self.classes.insert(0, self.__class__) |
|
242 | 243 | |
|
243 | 244 | def _config_changed(self, name, old, new): |
|
244 | 245 | SingletonConfigurable._config_changed(self, name, old, new) |
|
245 | 246 | self.log.debug('Config changed:') |
|
246 | 247 | self.log.debug(repr(new)) |
|
247 | 248 | |
|
248 | 249 | @catch_config_error |
|
249 | 250 | def initialize(self, argv=None): |
|
250 | 251 | """Do the basic steps to configure me. |
|
251 | 252 | |
|
252 | 253 | Override in subclasses. |
|
253 | 254 | """ |
|
254 | 255 | self.parse_command_line(argv) |
|
255 | 256 | |
|
256 | 257 | |
|
257 | 258 | def start(self): |
|
258 | 259 | """Start the app mainloop. |
|
259 | 260 | |
|
260 | 261 | Override in subclasses. |
|
261 | 262 | """ |
|
262 | 263 | if self.subapp is not None: |
|
263 | 264 | return self.subapp.start() |
|
264 | 265 | |
|
265 | 266 | def print_alias_help(self): |
|
266 | 267 | """Print the alias part of the help.""" |
|
267 | 268 | if not self.aliases: |
|
268 | 269 | return |
|
269 | 270 | |
|
270 | 271 | lines = [] |
|
271 | 272 | classdict = {} |
|
272 | 273 | for cls in self.classes: |
|
273 | 274 | # include all parents (up to, but excluding Configurable) in available names |
|
274 | 275 | for c in cls.mro()[:-3]: |
|
275 | 276 | classdict[c.__name__] = c |
|
276 | 277 | |
|
277 | 278 | for alias, longname in self.aliases.iteritems(): |
|
278 | 279 | classname, traitname = longname.split('.',1) |
|
279 | 280 | cls = classdict[classname] |
|
280 | 281 | |
|
281 | 282 | trait = cls.class_traits(config=True)[traitname] |
|
282 | 283 | help = cls.class_get_trait_help(trait).splitlines() |
|
283 | 284 | # reformat first line |
|
284 | 285 | help[0] = help[0].replace(longname, alias) + ' (%s)'%longname |
|
285 | 286 | if len(alias) == 1: |
|
286 | 287 | help[0] = help[0].replace('--%s='%alias, '-%s '%alias) |
|
287 | 288 | lines.extend(help) |
|
288 | 289 | # lines.append('') |
|
289 |
print |
|
|
290 | print(os.linesep.join(lines)) | |
|
290 | 291 | |
|
291 | 292 | def print_flag_help(self): |
|
292 | 293 | """Print the flag part of the help.""" |
|
293 | 294 | if not self.flags: |
|
294 | 295 | return |
|
295 | 296 | |
|
296 | 297 | lines = [] |
|
297 | 298 | for m, (cfg,help) in self.flags.iteritems(): |
|
298 | 299 | prefix = '--' if len(m) > 1 else '-' |
|
299 | 300 | lines.append(prefix+m) |
|
300 | 301 | lines.append(indent(dedent(help.strip()))) |
|
301 | 302 | # lines.append('') |
|
302 |
print |
|
|
303 | print(os.linesep.join(lines)) | |
|
303 | 304 | |
|
304 | 305 | def print_options(self): |
|
305 | 306 | if not self.flags and not self.aliases: |
|
306 | 307 | return |
|
307 | 308 | lines = ['Options'] |
|
308 | 309 | lines.append('-'*len(lines[0])) |
|
309 | 310 | lines.append('') |
|
310 | 311 | for p in wrap_paragraphs(self.option_description): |
|
311 | 312 | lines.append(p) |
|
312 | 313 | lines.append('') |
|
313 |
print |
|
|
314 | print(os.linesep.join(lines)) | |
|
314 | 315 | self.print_flag_help() |
|
315 | 316 | self.print_alias_help() |
|
316 | ||
|
317 | print() | |
|
317 | 318 | |
|
318 | 319 | def print_subcommands(self): |
|
319 | 320 | """Print the subcommand part of the help.""" |
|
320 | 321 | if not self.subcommands: |
|
321 | 322 | return |
|
322 | 323 | |
|
323 | 324 | lines = ["Subcommands"] |
|
324 | 325 | lines.append('-'*len(lines[0])) |
|
325 | 326 | lines.append('') |
|
326 | 327 | for p in wrap_paragraphs(self.subcommand_description): |
|
327 | 328 | lines.append(p) |
|
328 | 329 | lines.append('') |
|
329 | 330 | for subc, (cls, help) in self.subcommands.iteritems(): |
|
330 | 331 | lines.append(subc) |
|
331 | 332 | if help: |
|
332 | 333 | lines.append(indent(dedent(help.strip()))) |
|
333 | 334 | lines.append('') |
|
334 |
print |
|
|
335 | print(os.linesep.join(lines)) | |
|
335 | 336 | |
|
336 | 337 | def print_help(self, classes=False): |
|
337 | 338 | """Print the help for each Configurable class in self.classes. |
|
338 | 339 | |
|
339 | 340 | If classes=False (the default), only flags and aliases are printed. |
|
340 | 341 | """ |
|
341 | 342 | self.print_description() |
|
342 | 343 | self.print_subcommands() |
|
343 | 344 | self.print_options() |
|
344 | 345 | |
|
345 | 346 | if classes: |
|
346 | 347 | if self.classes: |
|
347 |
print |
|
|
348 |
print |
|
|
349 | ||
|
348 | print("Class parameters") | |
|
349 | print("----------------") | |
|
350 | print() | |
|
350 | 351 | for p in wrap_paragraphs(self.keyvalue_description): |
|
351 |
print |
|
|
352 | ||
|
352 | print(p) | |
|
353 | print() | |
|
353 | 354 | |
|
354 | 355 | for cls in self.classes: |
|
355 | 356 | cls.class_print_help() |
|
356 | ||
|
357 | print() | |
|
357 | 358 | else: |
|
358 |
print |
|
|
359 | ||
|
359 | print("To see all available configurables, use `--help-all`") | |
|
360 | print() | |
|
360 | 361 | |
|
361 | 362 | self.print_examples() |
|
362 | 363 | |
|
363 | 364 | |
|
364 | 365 | def print_description(self): |
|
365 | 366 | """Print the application description.""" |
|
366 | 367 | for p in wrap_paragraphs(self.description): |
|
367 |
print |
|
|
368 | ||
|
368 | print(p) | |
|
369 | print() | |
|
369 | 370 | |
|
370 | 371 | def print_examples(self): |
|
371 | 372 | """Print usage and examples. |
|
372 | 373 | |
|
373 | 374 | This usage string goes at the end of the command line help string |
|
374 | 375 | and should contain examples of the application's usage. |
|
375 | 376 | """ |
|
376 | 377 | if self.examples: |
|
377 |
print |
|
|
378 |
print |
|
|
379 | ||
|
380 |
print |
|
|
381 | ||
|
378 | print("Examples") | |
|
379 | print("--------") | |
|
380 | print() | |
|
381 | print(indent(dedent(self.examples.strip()))) | |
|
382 | print() | |
|
382 | 383 | |
|
383 | 384 | def print_version(self): |
|
384 | 385 | """Print the version string.""" |
|
385 |
print |
|
|
386 | print(self.version) | |
|
386 | 387 | |
|
387 | 388 | def update_config(self, config): |
|
388 | 389 | """Fire the traits events when the config is updated.""" |
|
389 | 390 | # Save a copy of the current config. |
|
390 | 391 | newconfig = deepcopy(self.config) |
|
391 | 392 | # Merge the new config into the current one. |
|
392 | 393 | newconfig.merge(config) |
|
393 | 394 | # Save the combined config as self.config, which triggers the traits |
|
394 | 395 | # events. |
|
395 | 396 | self.config = newconfig |
|
396 | 397 | |
|
397 | 398 | @catch_config_error |
|
398 | 399 | def initialize_subcommand(self, subc, argv=None): |
|
399 | 400 | """Initialize a subcommand with argv.""" |
|
400 | 401 | subapp,help = self.subcommands.get(subc) |
|
401 | 402 | |
|
402 | 403 | if isinstance(subapp, basestring): |
|
403 | 404 | subapp = import_item(subapp) |
|
404 | 405 | |
|
405 | 406 | # clear existing instances |
|
406 | 407 | self.__class__.clear_instance() |
|
407 | 408 | # instantiate |
|
408 | 409 | self.subapp = subapp.instance(config=self.config) |
|
409 | 410 | # and initialize subapp |
|
410 | 411 | self.subapp.initialize(argv) |
|
411 | 412 | |
|
412 | 413 | def flatten_flags(self): |
|
413 | 414 | """flatten flags and aliases, so cl-args override as expected. |
|
414 | 415 | |
|
415 | 416 | This prevents issues such as an alias pointing to InteractiveShell, |
|
416 | 417 | but a config file setting the same trait in TerminalInteraciveShell |
|
417 | 418 | getting inappropriate priority over the command-line arg. |
|
418 | 419 | |
|
419 | 420 | Only aliases with exactly one descendent in the class list |
|
420 | 421 | will be promoted. |
|
421 | 422 | |
|
422 | 423 | """ |
|
423 | 424 | # build a tree of classes in our list that inherit from a particular |
|
424 | 425 | # it will be a dict by parent classname of classes in our list |
|
425 | 426 | # that are descendents |
|
426 | 427 | mro_tree = defaultdict(list) |
|
427 | 428 | for cls in self.classes: |
|
428 | 429 | clsname = cls.__name__ |
|
429 | 430 | for parent in cls.mro()[1:-3]: |
|
430 | 431 | # exclude cls itself and Configurable,HasTraits,object |
|
431 | 432 | mro_tree[parent.__name__].append(clsname) |
|
432 | 433 | # flatten aliases, which have the form: |
|
433 | 434 | # { 'alias' : 'Class.trait' } |
|
434 | 435 | aliases = {} |
|
435 | 436 | for alias, cls_trait in self.aliases.iteritems(): |
|
436 | 437 | cls,trait = cls_trait.split('.',1) |
|
437 | 438 | children = mro_tree[cls] |
|
438 | 439 | if len(children) == 1: |
|
439 | 440 | # exactly one descendent, promote alias |
|
440 | 441 | cls = children[0] |
|
441 | 442 | aliases[alias] = '.'.join([cls,trait]) |
|
442 | 443 | |
|
443 | 444 | # flatten flags, which are of the form: |
|
444 | 445 | # { 'key' : ({'Cls' : {'trait' : value}}, 'help')} |
|
445 | 446 | flags = {} |
|
446 | 447 | for key, (flagdict, help) in self.flags.iteritems(): |
|
447 | 448 | newflag = {} |
|
448 | 449 | for cls, subdict in flagdict.iteritems(): |
|
449 | 450 | children = mro_tree[cls] |
|
450 | 451 | # exactly one descendent, promote flag section |
|
451 | 452 | if len(children) == 1: |
|
452 | 453 | cls = children[0] |
|
453 | 454 | newflag[cls] = subdict |
|
454 | 455 | flags[key] = (newflag, help) |
|
455 | 456 | return flags, aliases |
|
456 | 457 | |
|
457 | 458 | @catch_config_error |
|
458 | 459 | def parse_command_line(self, argv=None): |
|
459 | 460 | """Parse the command line arguments.""" |
|
460 | 461 | argv = sys.argv[1:] if argv is None else argv |
|
461 | 462 | self.argv = [ py3compat.cast_unicode(arg) for arg in argv ] |
|
462 | 463 | |
|
463 | 464 | if argv and argv[0] == 'help': |
|
464 | 465 | # turn `ipython help notebook` into `ipython notebook -h` |
|
465 | 466 | argv = argv[1:] + ['-h'] |
|
466 | 467 | |
|
467 | 468 | if self.subcommands and len(argv) > 0: |
|
468 | 469 | # we have subcommands, and one may have been specified |
|
469 | 470 | subc, subargv = argv[0], argv[1:] |
|
470 | 471 | if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands: |
|
471 | 472 | # it's a subcommand, and *not* a flag or class parameter |
|
472 | 473 | return self.initialize_subcommand(subc, subargv) |
|
473 | 474 | |
|
474 | 475 | # Arguments after a '--' argument are for the script IPython may be |
|
475 | 476 | # about to run, not IPython iteslf. For arguments parsed here (help and |
|
476 | 477 | # version), we want to only search the arguments up to the first |
|
477 | 478 | # occurrence of '--', which we're calling interpreted_argv. |
|
478 | 479 | try: |
|
479 | 480 | interpreted_argv = argv[:argv.index('--')] |
|
480 | 481 | except ValueError: |
|
481 | 482 | interpreted_argv = argv |
|
482 | 483 | |
|
483 | 484 | if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')): |
|
484 | 485 | self.print_help('--help-all' in interpreted_argv) |
|
485 | 486 | self.exit(0) |
|
486 | 487 | |
|
487 | 488 | if '--version' in interpreted_argv or '-V' in interpreted_argv: |
|
488 | 489 | self.print_version() |
|
489 | 490 | self.exit(0) |
|
490 | 491 | |
|
491 | 492 | # flatten flags&aliases, so cl-args get appropriate priority: |
|
492 | 493 | flags,aliases = self.flatten_flags() |
|
493 | 494 | |
|
494 | 495 | loader = KVArgParseConfigLoader(argv=argv, aliases=aliases, |
|
495 | 496 | flags=flags) |
|
496 | 497 | config = loader.load_config() |
|
497 | 498 | self.update_config(config) |
|
498 | 499 | # store unparsed args in extra_args |
|
499 | 500 | self.extra_args = loader.extra_args |
|
500 | 501 | |
|
501 | 502 | @catch_config_error |
|
502 | 503 | def load_config_file(self, filename, path=None): |
|
503 | 504 | """Load a .py based config file by filename and path.""" |
|
504 | 505 | loader = PyFileConfigLoader(filename, path=path) |
|
505 | 506 | try: |
|
506 | 507 | config = loader.load_config() |
|
507 | 508 | except ConfigFileNotFound: |
|
508 | 509 | # problem finding the file, raise |
|
509 | 510 | raise |
|
510 | 511 | except Exception: |
|
511 | 512 | # try to get the full filename, but it will be empty in the |
|
512 | 513 | # unlikely event that the error raised before filefind finished |
|
513 | 514 | filename = loader.full_filename or filename |
|
514 | 515 | # problem while running the file |
|
515 | 516 | self.log.error("Exception while loading config file %s", |
|
516 | 517 | filename, exc_info=True) |
|
517 | 518 | else: |
|
518 | 519 | self.log.debug("Loaded config file: %s", loader.full_filename) |
|
519 | 520 | self.update_config(config) |
|
520 | 521 | |
|
521 | 522 | def generate_config_file(self): |
|
522 | 523 | """generate default config file from Configurables""" |
|
523 | 524 | lines = ["# Configuration file for %s."%self.name] |
|
524 | 525 | lines.append('') |
|
525 | 526 | lines.append('c = get_config()') |
|
526 | 527 | lines.append('') |
|
527 | 528 | for cls in self.classes: |
|
528 | 529 | lines.append(cls.class_config_section()) |
|
529 | 530 | return '\n'.join(lines) |
|
530 | 531 | |
|
531 | 532 | def exit(self, exit_status=0): |
|
532 | 533 | self.log.debug("Exiting application: %s" % self.name) |
|
533 | 534 | sys.exit(exit_status) |
|
534 | 535 | |
|
535 | 536 | @classmethod |
|
536 | 537 | def launch_instance(cls, argv=None, **kwargs): |
|
537 | 538 | """Launch a global instance of this Application |
|
538 | 539 | |
|
539 | 540 | If a global instance already exists, this reinitializes and starts it |
|
540 | 541 | """ |
|
541 | 542 | app = cls.instance(**kwargs) |
|
542 | 543 | app.initialize(argv) |
|
543 | 544 | app.start() |
|
544 | 545 | |
|
545 | 546 | #----------------------------------------------------------------------------- |
|
546 | 547 | # utility functions, for convenience |
|
547 | 548 | #----------------------------------------------------------------------------- |
|
548 | 549 | |
|
549 | 550 | def boolean_flag(name, configurable, set_help='', unset_help=''): |
|
550 | 551 | """Helper for building basic --trait, --no-trait flags. |
|
551 | 552 | |
|
552 | 553 | Parameters |
|
553 | 554 | ---------- |
|
554 | 555 | |
|
555 | 556 | name : str |
|
556 | 557 | The name of the flag. |
|
557 | 558 | configurable : str |
|
558 | 559 | The 'Class.trait' string of the trait to be set/unset with the flag |
|
559 | 560 | set_help : unicode |
|
560 | 561 | help string for --name flag |
|
561 | 562 | unset_help : unicode |
|
562 | 563 | help string for --no-name flag |
|
563 | 564 | |
|
564 | 565 | Returns |
|
565 | 566 | ------- |
|
566 | 567 | |
|
567 | 568 | cfg : dict |
|
568 | 569 | A dict with two keys: 'name', and 'no-name', for setting and unsetting |
|
569 | 570 | the trait, respectively. |
|
570 | 571 | """ |
|
571 | 572 | # default helpstrings |
|
572 | 573 | set_help = set_help or "set %s=True"%configurable |
|
573 | 574 | unset_help = unset_help or "set %s=False"%configurable |
|
574 | 575 | |
|
575 | 576 | cls,trait = configurable.split('.') |
|
576 | 577 | |
|
577 | 578 | setter = {cls : {trait : True}} |
|
578 | 579 | unsetter = {cls : {trait : False}} |
|
579 | 580 | return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)} |
|
580 | 581 |
@@ -1,387 +1,388 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | A base class for objects that are configurable. |
|
4 | 4 | |
|
5 | 5 | Inheritance diagram: |
|
6 | 6 | |
|
7 | 7 | .. inheritance-diagram:: IPython.config.configurable |
|
8 | 8 | :parts: 3 |
|
9 | 9 | |
|
10 | 10 | Authors: |
|
11 | 11 | |
|
12 | 12 | * Brian Granger |
|
13 | 13 | * Fernando Perez |
|
14 | 14 | * Min RK |
|
15 | 15 | """ |
|
16 | from __future__ import print_function | |
|
16 | 17 | |
|
17 | 18 | #----------------------------------------------------------------------------- |
|
18 | 19 | # Copyright (C) 2008-2011 The IPython Development Team |
|
19 | 20 | # |
|
20 | 21 | # Distributed under the terms of the BSD License. The full license is in |
|
21 | 22 | # the file COPYING, distributed as part of this software. |
|
22 | 23 | #----------------------------------------------------------------------------- |
|
23 | 24 | |
|
24 | 25 | #----------------------------------------------------------------------------- |
|
25 | 26 | # Imports |
|
26 | 27 | #----------------------------------------------------------------------------- |
|
27 | 28 | |
|
28 | 29 | import datetime |
|
29 | 30 | from copy import deepcopy |
|
30 | 31 | |
|
31 | 32 | from .loader import Config, LazyConfigValue |
|
32 | 33 | from IPython.utils.traitlets import HasTraits, Instance |
|
33 | 34 | from IPython.utils.text import indent, wrap_paragraphs |
|
34 | 35 | |
|
35 | 36 | |
|
36 | 37 | #----------------------------------------------------------------------------- |
|
37 | 38 | # Helper classes for Configurables |
|
38 | 39 | #----------------------------------------------------------------------------- |
|
39 | 40 | |
|
40 | 41 | |
|
41 | 42 | class ConfigurableError(Exception): |
|
42 | 43 | pass |
|
43 | 44 | |
|
44 | 45 | |
|
45 | 46 | class MultipleInstanceError(ConfigurableError): |
|
46 | 47 | pass |
|
47 | 48 | |
|
48 | 49 | #----------------------------------------------------------------------------- |
|
49 | 50 | # Configurable implementation |
|
50 | 51 | #----------------------------------------------------------------------------- |
|
51 | 52 | |
|
52 | 53 | class Configurable(HasTraits): |
|
53 | 54 | |
|
54 | 55 | config = Instance(Config, (), {}) |
|
55 | 56 | parent = Instance('IPython.config.configurable.Configurable') |
|
56 | 57 | created = None |
|
57 | 58 | |
|
58 | 59 | def __init__(self, **kwargs): |
|
59 | 60 | """Create a configurable given a config config. |
|
60 | 61 | |
|
61 | 62 | Parameters |
|
62 | 63 | ---------- |
|
63 | 64 | config : Config |
|
64 | 65 | If this is empty, default values are used. If config is a |
|
65 | 66 | :class:`Config` instance, it will be used to configure the |
|
66 | 67 | instance. |
|
67 | 68 | parent : Configurable instance, optional |
|
68 | 69 | The parent Configurable instance of this object. |
|
69 | 70 | |
|
70 | 71 | Notes |
|
71 | 72 | ----- |
|
72 | 73 | Subclasses of Configurable must call the :meth:`__init__` method of |
|
73 | 74 | :class:`Configurable` *before* doing anything else and using |
|
74 | 75 | :func:`super`:: |
|
75 | 76 | |
|
76 | 77 | class MyConfigurable(Configurable): |
|
77 | 78 | def __init__(self, config=None): |
|
78 | 79 | super(MyConfigurable, self).__init__(config=config) |
|
79 | 80 | # Then any other code you need to finish initialization. |
|
80 | 81 | |
|
81 | 82 | This ensures that instances will be configured properly. |
|
82 | 83 | """ |
|
83 | 84 | parent = kwargs.pop('parent', None) |
|
84 | 85 | if parent is not None: |
|
85 | 86 | # config is implied from parent |
|
86 | 87 | if kwargs.get('config', None) is None: |
|
87 | 88 | kwargs['config'] = parent.config |
|
88 | 89 | self.parent = parent |
|
89 | 90 | |
|
90 | 91 | config = kwargs.pop('config', None) |
|
91 | 92 | if config is not None: |
|
92 | 93 | # We used to deepcopy, but for now we are trying to just save |
|
93 | 94 | # by reference. This *could* have side effects as all components |
|
94 | 95 | # will share config. In fact, I did find such a side effect in |
|
95 | 96 | # _config_changed below. If a config attribute value was a mutable type |
|
96 | 97 | # all instances of a component were getting the same copy, effectively |
|
97 | 98 | # making that a class attribute. |
|
98 | 99 | # self.config = deepcopy(config) |
|
99 | 100 | self.config = config |
|
100 | 101 | # This should go second so individual keyword arguments override |
|
101 | 102 | # the values in config. |
|
102 | 103 | super(Configurable, self).__init__(**kwargs) |
|
103 | 104 | self.created = datetime.datetime.now() |
|
104 | 105 | |
|
105 | 106 | #------------------------------------------------------------------------- |
|
106 | 107 | # Static trait notifiations |
|
107 | 108 | #------------------------------------------------------------------------- |
|
108 | 109 | |
|
109 | 110 | @classmethod |
|
110 | 111 | def section_names(cls): |
|
111 | 112 | """return section names as a list""" |
|
112 | 113 | return [c.__name__ for c in reversed(cls.__mro__) if |
|
113 | 114 | issubclass(c, Configurable) and issubclass(cls, c) |
|
114 | 115 | ] |
|
115 | 116 | |
|
116 | 117 | def _find_my_config(self, cfg): |
|
117 | 118 | """extract my config from a global Config object |
|
118 | 119 | |
|
119 | 120 | will construct a Config object of only the config values that apply to me |
|
120 | 121 | based on my mro(), as well as those of my parent(s) if they exist. |
|
121 | 122 | |
|
122 | 123 | If I am Bar and my parent is Foo, and their parent is Tim, |
|
123 | 124 | this will return merge following config sections, in this order:: |
|
124 | 125 | |
|
125 | 126 | [Bar, Foo.bar, Tim.Foo.Bar] |
|
126 | 127 | |
|
127 | 128 | With the last item being the highest priority. |
|
128 | 129 | """ |
|
129 | 130 | cfgs = [cfg] |
|
130 | 131 | if self.parent: |
|
131 | 132 | cfgs.append(self.parent._find_my_config(cfg)) |
|
132 | 133 | my_config = Config() |
|
133 | 134 | for c in cfgs: |
|
134 | 135 | for sname in self.section_names(): |
|
135 | 136 | # Don't do a blind getattr as that would cause the config to |
|
136 | 137 | # dynamically create the section with name Class.__name__. |
|
137 | 138 | if c._has_section(sname): |
|
138 | 139 | my_config.merge(c[sname]) |
|
139 | 140 | return my_config |
|
140 | 141 | |
|
141 | 142 | def _load_config(self, cfg, section_names=None, traits=None): |
|
142 | 143 | """load traits from a Config object""" |
|
143 | 144 | |
|
144 | 145 | if traits is None: |
|
145 | 146 | traits = self.traits(config=True) |
|
146 | 147 | if section_names is None: |
|
147 | 148 | section_names = self.section_names() |
|
148 | 149 | |
|
149 | 150 | my_config = self._find_my_config(cfg) |
|
150 | 151 | for name, config_value in my_config.iteritems(): |
|
151 | 152 | if name in traits: |
|
152 | 153 | if isinstance(config_value, LazyConfigValue): |
|
153 | 154 | # ConfigValue is a wrapper for using append / update on containers |
|
154 | 155 | # without having to copy the |
|
155 | 156 | initial = getattr(self, name) |
|
156 | 157 | config_value = config_value.get_value(initial) |
|
157 | 158 | # We have to do a deepcopy here if we don't deepcopy the entire |
|
158 | 159 | # config object. If we don't, a mutable config_value will be |
|
159 | 160 | # shared by all instances, effectively making it a class attribute. |
|
160 | 161 | setattr(self, name, deepcopy(config_value)) |
|
161 | 162 | |
|
162 | 163 | def _config_changed(self, name, old, new): |
|
163 | 164 | """Update all the class traits having ``config=True`` as metadata. |
|
164 | 165 | |
|
165 | 166 | For any class trait with a ``config`` metadata attribute that is |
|
166 | 167 | ``True``, we update the trait with the value of the corresponding |
|
167 | 168 | config entry. |
|
168 | 169 | """ |
|
169 | 170 | # Get all traits with a config metadata entry that is True |
|
170 | 171 | traits = self.traits(config=True) |
|
171 | 172 | |
|
172 | 173 | # We auto-load config section for this class as well as any parent |
|
173 | 174 | # classes that are Configurable subclasses. This starts with Configurable |
|
174 | 175 | # and works down the mro loading the config for each section. |
|
175 | 176 | section_names = self.section_names() |
|
176 | 177 | self._load_config(new, traits=traits, section_names=section_names) |
|
177 | 178 | |
|
178 | 179 | def update_config(self, config): |
|
179 | 180 | """Fire the traits events when the config is updated.""" |
|
180 | 181 | # Save a copy of the current config. |
|
181 | 182 | newconfig = deepcopy(self.config) |
|
182 | 183 | # Merge the new config into the current one. |
|
183 | 184 | newconfig.merge(config) |
|
184 | 185 | # Save the combined config as self.config, which triggers the traits |
|
185 | 186 | # events. |
|
186 | 187 | self.config = newconfig |
|
187 | 188 | |
|
188 | 189 | @classmethod |
|
189 | 190 | def class_get_help(cls, inst=None): |
|
190 | 191 | """Get the help string for this class in ReST format. |
|
191 | 192 | |
|
192 | 193 | If `inst` is given, it's current trait values will be used in place of |
|
193 | 194 | class defaults. |
|
194 | 195 | """ |
|
195 | 196 | assert inst is None or isinstance(inst, cls) |
|
196 | 197 | final_help = [] |
|
197 | 198 | final_help.append(u'%s options' % cls.__name__) |
|
198 | 199 | final_help.append(len(final_help[0])*u'-') |
|
199 | 200 | for k, v in sorted(cls.class_traits(config=True).iteritems()): |
|
200 | 201 | help = cls.class_get_trait_help(v, inst) |
|
201 | 202 | final_help.append(help) |
|
202 | 203 | return '\n'.join(final_help) |
|
203 | 204 | |
|
204 | 205 | @classmethod |
|
205 | 206 | def class_get_trait_help(cls, trait, inst=None): |
|
206 | 207 | """Get the help string for a single trait. |
|
207 | 208 | |
|
208 | 209 | If `inst` is given, it's current trait values will be used in place of |
|
209 | 210 | the class default. |
|
210 | 211 | """ |
|
211 | 212 | assert inst is None or isinstance(inst, cls) |
|
212 | 213 | lines = [] |
|
213 | 214 | header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__) |
|
214 | 215 | lines.append(header) |
|
215 | 216 | if inst is not None: |
|
216 | 217 | lines.append(indent('Current: %r' % getattr(inst, trait.name), 4)) |
|
217 | 218 | else: |
|
218 | 219 | try: |
|
219 | 220 | dvr = repr(trait.get_default_value()) |
|
220 | 221 | except Exception: |
|
221 | 222 | dvr = None # ignore defaults we can't construct |
|
222 | 223 | if dvr is not None: |
|
223 | 224 | if len(dvr) > 64: |
|
224 | 225 | dvr = dvr[:61]+'...' |
|
225 | 226 | lines.append(indent('Default: %s' % dvr, 4)) |
|
226 | 227 | if 'Enum' in trait.__class__.__name__: |
|
227 | 228 | # include Enum choices |
|
228 | 229 | lines.append(indent('Choices: %r' % (trait.values,))) |
|
229 | 230 | |
|
230 | 231 | help = trait.get_metadata('help') |
|
231 | 232 | if help is not None: |
|
232 | 233 | help = '\n'.join(wrap_paragraphs(help, 76)) |
|
233 | 234 | lines.append(indent(help, 4)) |
|
234 | 235 | return '\n'.join(lines) |
|
235 | 236 | |
|
236 | 237 | @classmethod |
|
237 | 238 | def class_print_help(cls, inst=None): |
|
238 | 239 | """Get the help string for a single trait and print it.""" |
|
239 |
print |
|
|
240 | print(cls.class_get_help(inst)) | |
|
240 | 241 | |
|
241 | 242 | @classmethod |
|
242 | 243 | def class_config_section(cls): |
|
243 | 244 | """Get the config class config section""" |
|
244 | 245 | def c(s): |
|
245 | 246 | """return a commented, wrapped block.""" |
|
246 | 247 | s = '\n\n'.join(wrap_paragraphs(s, 78)) |
|
247 | 248 | |
|
248 | 249 | return '# ' + s.replace('\n', '\n# ') |
|
249 | 250 | |
|
250 | 251 | # section header |
|
251 | 252 | breaker = '#' + '-'*78 |
|
252 | 253 | s = "# %s configuration" % cls.__name__ |
|
253 | 254 | lines = [breaker, s, breaker, ''] |
|
254 | 255 | # get the description trait |
|
255 | 256 | desc = cls.class_traits().get('description') |
|
256 | 257 | if desc: |
|
257 | 258 | desc = desc.default_value |
|
258 | 259 | else: |
|
259 | 260 | # no description trait, use __doc__ |
|
260 | 261 | desc = getattr(cls, '__doc__', '') |
|
261 | 262 | if desc: |
|
262 | 263 | lines.append(c(desc)) |
|
263 | 264 | lines.append('') |
|
264 | 265 | |
|
265 | 266 | parents = [] |
|
266 | 267 | for parent in cls.mro(): |
|
267 | 268 | # only include parents that are not base classes |
|
268 | 269 | # and are not the class itself |
|
269 | 270 | # and have some configurable traits to inherit |
|
270 | 271 | if parent is not cls and issubclass(parent, Configurable) and \ |
|
271 | 272 | parent.class_traits(config=True): |
|
272 | 273 | parents.append(parent) |
|
273 | 274 | |
|
274 | 275 | if parents: |
|
275 | 276 | pstr = ', '.join([ p.__name__ for p in parents ]) |
|
276 | 277 | lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr))) |
|
277 | 278 | lines.append('') |
|
278 | 279 | |
|
279 | 280 | for name, trait in cls.class_traits(config=True).iteritems(): |
|
280 | 281 | help = trait.get_metadata('help') or '' |
|
281 | 282 | lines.append(c(help)) |
|
282 | 283 | lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value())) |
|
283 | 284 | lines.append('') |
|
284 | 285 | return '\n'.join(lines) |
|
285 | 286 | |
|
286 | 287 | |
|
287 | 288 | |
|
288 | 289 | class SingletonConfigurable(Configurable): |
|
289 | 290 | """A configurable that only allows one instance. |
|
290 | 291 | |
|
291 | 292 | This class is for classes that should only have one instance of itself |
|
292 | 293 | or *any* subclass. To create and retrieve such a class use the |
|
293 | 294 | :meth:`SingletonConfigurable.instance` method. |
|
294 | 295 | """ |
|
295 | 296 | |
|
296 | 297 | _instance = None |
|
297 | 298 | |
|
298 | 299 | @classmethod |
|
299 | 300 | def _walk_mro(cls): |
|
300 | 301 | """Walk the cls.mro() for parent classes that are also singletons |
|
301 | 302 | |
|
302 | 303 | For use in instance() |
|
303 | 304 | """ |
|
304 | 305 | |
|
305 | 306 | for subclass in cls.mro(): |
|
306 | 307 | if issubclass(cls, subclass) and \ |
|
307 | 308 | issubclass(subclass, SingletonConfigurable) and \ |
|
308 | 309 | subclass != SingletonConfigurable: |
|
309 | 310 | yield subclass |
|
310 | 311 | |
|
311 | 312 | @classmethod |
|
312 | 313 | def clear_instance(cls): |
|
313 | 314 | """unset _instance for this class and singleton parents. |
|
314 | 315 | """ |
|
315 | 316 | if not cls.initialized(): |
|
316 | 317 | return |
|
317 | 318 | for subclass in cls._walk_mro(): |
|
318 | 319 | if isinstance(subclass._instance, cls): |
|
319 | 320 | # only clear instances that are instances |
|
320 | 321 | # of the calling class |
|
321 | 322 | subclass._instance = None |
|
322 | 323 | |
|
323 | 324 | @classmethod |
|
324 | 325 | def instance(cls, *args, **kwargs): |
|
325 | 326 | """Returns a global instance of this class. |
|
326 | 327 | |
|
327 | 328 | This method create a new instance if none have previously been created |
|
328 | 329 | and returns a previously created instance is one already exists. |
|
329 | 330 | |
|
330 | 331 | The arguments and keyword arguments passed to this method are passed |
|
331 | 332 | on to the :meth:`__init__` method of the class upon instantiation. |
|
332 | 333 | |
|
333 | 334 | Examples |
|
334 | 335 | -------- |
|
335 | 336 | |
|
336 | 337 | Create a singleton class using instance, and retrieve it:: |
|
337 | 338 | |
|
338 | 339 | >>> from IPython.config.configurable import SingletonConfigurable |
|
339 | 340 | >>> class Foo(SingletonConfigurable): pass |
|
340 | 341 | >>> foo = Foo.instance() |
|
341 | 342 | >>> foo == Foo.instance() |
|
342 | 343 | True |
|
343 | 344 | |
|
344 | 345 | Create a subclass that is retrived using the base class instance:: |
|
345 | 346 | |
|
346 | 347 | >>> class Bar(SingletonConfigurable): pass |
|
347 | 348 | >>> class Bam(Bar): pass |
|
348 | 349 | >>> bam = Bam.instance() |
|
349 | 350 | >>> bam == Bar.instance() |
|
350 | 351 | True |
|
351 | 352 | """ |
|
352 | 353 | # Create and save the instance |
|
353 | 354 | if cls._instance is None: |
|
354 | 355 | inst = cls(*args, **kwargs) |
|
355 | 356 | # Now make sure that the instance will also be returned by |
|
356 | 357 | # parent classes' _instance attribute. |
|
357 | 358 | for subclass in cls._walk_mro(): |
|
358 | 359 | subclass._instance = inst |
|
359 | 360 | |
|
360 | 361 | if isinstance(cls._instance, cls): |
|
361 | 362 | return cls._instance |
|
362 | 363 | else: |
|
363 | 364 | raise MultipleInstanceError( |
|
364 | 365 | 'Multiple incompatible subclass instances of ' |
|
365 | 366 | '%s are being created.' % cls.__name__ |
|
366 | 367 | ) |
|
367 | 368 | |
|
368 | 369 | @classmethod |
|
369 | 370 | def initialized(cls): |
|
370 | 371 | """Has an instance been created?""" |
|
371 | 372 | return hasattr(cls, "_instance") and cls._instance is not None |
|
372 | 373 | |
|
373 | 374 | |
|
374 | 375 | class LoggingConfigurable(Configurable): |
|
375 | 376 | """A parent class for Configurables that log. |
|
376 | 377 | |
|
377 | 378 | Subclasses have a log trait, and the default behavior |
|
378 | 379 | is to get the logger from the currently running Application |
|
379 | 380 | via Application.instance().log. |
|
380 | 381 | """ |
|
381 | 382 | |
|
382 | 383 | log = Instance('logging.Logger') |
|
383 | 384 | def _log_default(self): |
|
384 | 385 | from IPython.config.application import Application |
|
385 | 386 | return Application.instance().log |
|
386 | 387 | |
|
387 | 388 |
@@ -1,220 +1,221 b'' | |||
|
1 | 1 | """Logger class for IPython's logging facilities. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | |
|
4 | 5 | #***************************************************************************** |
|
5 | 6 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
6 | 7 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
7 | 8 | # |
|
8 | 9 | # Distributed under the terms of the BSD License. The full license is in |
|
9 | 10 | # the file COPYING, distributed as part of this software. |
|
10 | 11 | #***************************************************************************** |
|
11 | 12 | |
|
12 | 13 | #**************************************************************************** |
|
13 | 14 | # Modules and globals |
|
14 | 15 | |
|
15 | 16 | # Python standard modules |
|
16 | 17 | import glob |
|
17 | 18 | import io |
|
18 | 19 | import os |
|
19 | 20 | import time |
|
20 | 21 | |
|
21 | 22 | from IPython.utils.py3compat import str_to_unicode |
|
22 | 23 | |
|
23 | 24 | #**************************************************************************** |
|
24 | 25 | # FIXME: This class isn't a mixin anymore, but it still needs attributes from |
|
25 | 26 | # ipython and does input cache management. Finish cleanup later... |
|
26 | 27 | |
|
27 | 28 | class Logger(object): |
|
28 | 29 | """A Logfile class with different policies for file creation""" |
|
29 | 30 | |
|
30 | 31 | def __init__(self, home_dir, logfname='Logger.log', loghead=u'', |
|
31 | 32 | logmode='over'): |
|
32 | 33 | |
|
33 | 34 | # this is the full ipython instance, we need some attributes from it |
|
34 | 35 | # which won't exist until later. What a mess, clean up later... |
|
35 | 36 | self.home_dir = home_dir |
|
36 | 37 | |
|
37 | 38 | self.logfname = logfname |
|
38 | 39 | self.loghead = loghead |
|
39 | 40 | self.logmode = logmode |
|
40 | 41 | self.logfile = None |
|
41 | 42 | |
|
42 | 43 | # Whether to log raw or processed input |
|
43 | 44 | self.log_raw_input = False |
|
44 | 45 | |
|
45 | 46 | # whether to also log output |
|
46 | 47 | self.log_output = False |
|
47 | 48 | |
|
48 | 49 | # whether to put timestamps before each log entry |
|
49 | 50 | self.timestamp = False |
|
50 | 51 | |
|
51 | 52 | # activity control flags |
|
52 | 53 | self.log_active = False |
|
53 | 54 | |
|
54 | 55 | # logmode is a validated property |
|
55 | 56 | def _set_mode(self,mode): |
|
56 | 57 | if mode not in ['append','backup','global','over','rotate']: |
|
57 | 58 | raise ValueError('invalid log mode %s given' % mode) |
|
58 | 59 | self._logmode = mode |
|
59 | 60 | |
|
60 | 61 | def _get_mode(self): |
|
61 | 62 | return self._logmode |
|
62 | 63 | |
|
63 | 64 | logmode = property(_get_mode,_set_mode) |
|
64 | 65 | |
|
65 | 66 | def logstart(self, logfname=None, loghead=None, logmode=None, |
|
66 | 67 | log_output=False, timestamp=False, log_raw_input=False): |
|
67 | 68 | """Generate a new log-file with a default header. |
|
68 | 69 | |
|
69 | 70 | Raises RuntimeError if the log has already been started""" |
|
70 | 71 | |
|
71 | 72 | if self.logfile is not None: |
|
72 | 73 | raise RuntimeError('Log file is already active: %s' % |
|
73 | 74 | self.logfname) |
|
74 | 75 | |
|
75 | 76 | # The parameters can override constructor defaults |
|
76 | 77 | if logfname is not None: self.logfname = logfname |
|
77 | 78 | if loghead is not None: self.loghead = loghead |
|
78 | 79 | if logmode is not None: self.logmode = logmode |
|
79 | 80 | |
|
80 | 81 | # Parameters not part of the constructor |
|
81 | 82 | self.timestamp = timestamp |
|
82 | 83 | self.log_output = log_output |
|
83 | 84 | self.log_raw_input = log_raw_input |
|
84 | 85 | |
|
85 | 86 | # init depending on the log mode requested |
|
86 | 87 | isfile = os.path.isfile |
|
87 | 88 | logmode = self.logmode |
|
88 | 89 | |
|
89 | 90 | if logmode == 'append': |
|
90 | 91 | self.logfile = io.open(self.logfname, 'a', encoding='utf-8') |
|
91 | 92 | |
|
92 | 93 | elif logmode == 'backup': |
|
93 | 94 | if isfile(self.logfname): |
|
94 | 95 | backup_logname = self.logfname+'~' |
|
95 | 96 | # Manually remove any old backup, since os.rename may fail |
|
96 | 97 | # under Windows. |
|
97 | 98 | if isfile(backup_logname): |
|
98 | 99 | os.remove(backup_logname) |
|
99 | 100 | os.rename(self.logfname,backup_logname) |
|
100 | 101 | self.logfile = io.open(self.logfname, 'w', encoding='utf-8') |
|
101 | 102 | |
|
102 | 103 | elif logmode == 'global': |
|
103 | 104 | self.logfname = os.path.join(self.home_dir,self.logfname) |
|
104 | 105 | self.logfile = io.open(self.logfname, 'a', encoding='utf-8') |
|
105 | 106 | |
|
106 | 107 | elif logmode == 'over': |
|
107 | 108 | if isfile(self.logfname): |
|
108 | 109 | os.remove(self.logfname) |
|
109 | 110 | self.logfile = io.open(self.logfname,'w', encoding='utf-8') |
|
110 | 111 | |
|
111 | 112 | elif logmode == 'rotate': |
|
112 | 113 | if isfile(self.logfname): |
|
113 | 114 | if isfile(self.logfname+'.001~'): |
|
114 | 115 | old = glob.glob(self.logfname+'.*~') |
|
115 | 116 | old.sort() |
|
116 | 117 | old.reverse() |
|
117 | 118 | for f in old: |
|
118 | 119 | root, ext = os.path.splitext(f) |
|
119 | 120 | num = int(ext[1:-1])+1 |
|
120 | 121 | os.rename(f, root+'.'+repr(num).zfill(3)+'~') |
|
121 | 122 | os.rename(self.logfname, self.logfname+'.001~') |
|
122 | 123 | self.logfile = io.open(self.logfname, 'w', encoding='utf-8') |
|
123 | 124 | |
|
124 | 125 | if logmode != 'append': |
|
125 | 126 | self.logfile.write(self.loghead) |
|
126 | 127 | |
|
127 | 128 | self.logfile.flush() |
|
128 | 129 | self.log_active = True |
|
129 | 130 | |
|
130 | 131 | def switch_log(self,val): |
|
131 | 132 | """Switch logging on/off. val should be ONLY a boolean.""" |
|
132 | 133 | |
|
133 | 134 | if val not in [False,True,0,1]: |
|
134 | 135 | raise ValueError('Call switch_log ONLY with a boolean argument, ' |
|
135 | 136 | 'not with: %s' % val) |
|
136 | 137 | |
|
137 | 138 | label = {0:'OFF',1:'ON',False:'OFF',True:'ON'} |
|
138 | 139 | |
|
139 | 140 | if self.logfile is None: |
|
140 |
print |
|
|
141 | print(""" | |
|
141 | 142 | Logging hasn't been started yet (use logstart for that). |
|
142 | 143 | |
|
143 | 144 | %logon/%logoff are for temporarily starting and stopping logging for a logfile |
|
144 | 145 | which already exists. But you must first start the logging process with |
|
145 | %logstart (optionally giving a logfile name).""" | |
|
146 | %logstart (optionally giving a logfile name).""") | |
|
146 | 147 | |
|
147 | 148 | else: |
|
148 | 149 | if self.log_active == val: |
|
149 |
print |
|
|
150 | print('Logging is already',label[val]) | |
|
150 | 151 | else: |
|
151 |
print |
|
|
152 | print('Switching logging',label[val]) | |
|
152 | 153 | self.log_active = not self.log_active |
|
153 | 154 | self.log_active_out = self.log_active |
|
154 | 155 | |
|
155 | 156 | def logstate(self): |
|
156 | 157 | """Print a status message about the logger.""" |
|
157 | 158 | if self.logfile is None: |
|
158 |
print |
|
|
159 | print('Logging has not been activated.') | |
|
159 | 160 | else: |
|
160 | 161 | state = self.log_active and 'active' or 'temporarily suspended' |
|
161 |
print |
|
|
162 |
print |
|
|
163 |
print |
|
|
164 |
print |
|
|
165 |
print |
|
|
166 |
print |
|
|
162 | print('Filename :',self.logfname) | |
|
163 | print('Mode :',self.logmode) | |
|
164 | print('Output logging :',self.log_output) | |
|
165 | print('Raw input log :',self.log_raw_input) | |
|
166 | print('Timestamping :',self.timestamp) | |
|
167 | print('State :',state) | |
|
167 | 168 | |
|
168 | 169 | def log(self, line_mod, line_ori): |
|
169 | 170 | """Write the sources to a log. |
|
170 | 171 | |
|
171 | 172 | Inputs: |
|
172 | 173 | |
|
173 | 174 | - line_mod: possibly modified input, such as the transformations made |
|
174 | 175 | by input prefilters or input handlers of various kinds. This should |
|
175 | 176 | always be valid Python. |
|
176 | 177 | |
|
177 | 178 | - line_ori: unmodified input line from the user. This is not |
|
178 | 179 | necessarily valid Python. |
|
179 | 180 | """ |
|
180 | 181 | |
|
181 | 182 | # Write the log line, but decide which one according to the |
|
182 | 183 | # log_raw_input flag, set when the log is started. |
|
183 | 184 | if self.log_raw_input: |
|
184 | 185 | self.log_write(line_ori) |
|
185 | 186 | else: |
|
186 | 187 | self.log_write(line_mod) |
|
187 | 188 | |
|
188 | 189 | def log_write(self, data, kind='input'): |
|
189 | 190 | """Write data to the log file, if active""" |
|
190 | 191 | |
|
191 | 192 | #print 'data: %r' % data # dbg |
|
192 | 193 | if self.log_active and data: |
|
193 | 194 | write = self.logfile.write |
|
194 | 195 | if kind=='input': |
|
195 | 196 | if self.timestamp: |
|
196 | 197 | write(str_to_unicode(time.strftime('# %a, %d %b %Y %H:%M:%S\n', |
|
197 | 198 | time.localtime()))) |
|
198 | 199 | write(data) |
|
199 | 200 | elif kind=='output' and self.log_output: |
|
200 | 201 | odata = u'\n'.join([u'#[Out]# %s' % s |
|
201 | 202 | for s in data.splitlines()]) |
|
202 | 203 | write(u'%s\n' % odata) |
|
203 | 204 | self.logfile.flush() |
|
204 | 205 | |
|
205 | 206 | def logstop(self): |
|
206 | 207 | """Fully stop logging and close log file. |
|
207 | 208 | |
|
208 | 209 | In order to start logging again, a new logstart() call needs to be |
|
209 | 210 | made, possibly (though not necessarily) with a new filename, mode and |
|
210 | 211 | other options.""" |
|
211 | 212 | |
|
212 | 213 | if self.logfile is not None: |
|
213 | 214 | self.logfile.close() |
|
214 | 215 | self.logfile = None |
|
215 | 216 | else: |
|
216 |
print |
|
|
217 | print("Logging hadn't been started.") | |
|
217 | 218 | self.log_active = False |
|
218 | 219 | |
|
219 | 220 | # For backwards compatibility, in case anyone was using this. |
|
220 | 221 | close_log = logstop |
@@ -1,688 +1,689 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """Magic functions for InteractiveShell. |
|
3 | 3 | """ |
|
4 | from __future__ import print_function | |
|
4 | 5 | |
|
5 | 6 | #----------------------------------------------------------------------------- |
|
6 | 7 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
7 | 8 | # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu> |
|
8 | 9 | # Copyright (C) 2008 The IPython Development Team |
|
9 | 10 | |
|
10 | 11 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | 12 | # the file COPYING, distributed as part of this software. |
|
12 | 13 | #----------------------------------------------------------------------------- |
|
13 | 14 | |
|
14 | 15 | #----------------------------------------------------------------------------- |
|
15 | 16 | # Imports |
|
16 | 17 | #----------------------------------------------------------------------------- |
|
17 | 18 | # Stdlib |
|
18 | 19 | import os |
|
19 | 20 | import re |
|
20 | 21 | import sys |
|
21 | 22 | import types |
|
22 | 23 | from getopt import getopt, GetoptError |
|
23 | 24 | |
|
24 | 25 | # Our own |
|
25 | 26 | from IPython.config.configurable import Configurable |
|
26 | 27 | from IPython.core import oinspect |
|
27 | 28 | from IPython.core.error import UsageError |
|
28 | 29 | from IPython.core.inputsplitter import ESC_MAGIC, ESC_MAGIC2 |
|
29 | 30 | from IPython.external.decorator import decorator |
|
30 | 31 | from IPython.utils.ipstruct import Struct |
|
31 | 32 | from IPython.utils.process import arg_split |
|
32 | 33 | from IPython.utils.text import dedent |
|
33 | 34 | from IPython.utils.traitlets import Bool, Dict, Instance, MetaHasTraits |
|
34 | 35 | from IPython.utils.warn import error |
|
35 | 36 | |
|
36 | 37 | #----------------------------------------------------------------------------- |
|
37 | 38 | # Globals |
|
38 | 39 | #----------------------------------------------------------------------------- |
|
39 | 40 | |
|
40 | 41 | # A dict we'll use for each class that has magics, used as temporary storage to |
|
41 | 42 | # pass information between the @line/cell_magic method decorators and the |
|
42 | 43 | # @magics_class class decorator, because the method decorators have no |
|
43 | 44 | # access to the class when they run. See for more details: |
|
44 | 45 | # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class |
|
45 | 46 | |
|
46 | 47 | magics = dict(line={}, cell={}) |
|
47 | 48 | |
|
48 | 49 | magic_kinds = ('line', 'cell') |
|
49 | 50 | magic_spec = ('line', 'cell', 'line_cell') |
|
50 | 51 | magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2) |
|
51 | 52 | |
|
52 | 53 | #----------------------------------------------------------------------------- |
|
53 | 54 | # Utility classes and functions |
|
54 | 55 | #----------------------------------------------------------------------------- |
|
55 | 56 | |
|
56 | 57 | class Bunch: pass |
|
57 | 58 | |
|
58 | 59 | |
|
59 | 60 | def on_off(tag): |
|
60 | 61 | """Return an ON/OFF string for a 1/0 input. Simple utility function.""" |
|
61 | 62 | return ['OFF','ON'][tag] |
|
62 | 63 | |
|
63 | 64 | |
|
64 | 65 | def compress_dhist(dh): |
|
65 | 66 | """Compress a directory history into a new one with at most 20 entries. |
|
66 | 67 | |
|
67 | 68 | Return a new list made from the first and last 10 elements of dhist after |
|
68 | 69 | removal of duplicates. |
|
69 | 70 | """ |
|
70 | 71 | head, tail = dh[:-10], dh[-10:] |
|
71 | 72 | |
|
72 | 73 | newhead = [] |
|
73 | 74 | done = set() |
|
74 | 75 | for h in head: |
|
75 | 76 | if h in done: |
|
76 | 77 | continue |
|
77 | 78 | newhead.append(h) |
|
78 | 79 | done.add(h) |
|
79 | 80 | |
|
80 | 81 | return newhead + tail |
|
81 | 82 | |
|
82 | 83 | |
|
83 | 84 | def needs_local_scope(func): |
|
84 | 85 | """Decorator to mark magic functions which need to local scope to run.""" |
|
85 | 86 | func.needs_local_scope = True |
|
86 | 87 | return func |
|
87 | 88 | |
|
88 | 89 | #----------------------------------------------------------------------------- |
|
89 | 90 | # Class and method decorators for registering magics |
|
90 | 91 | #----------------------------------------------------------------------------- |
|
91 | 92 | |
|
92 | 93 | def magics_class(cls): |
|
93 | 94 | """Class decorator for all subclasses of the main Magics class. |
|
94 | 95 | |
|
95 | 96 | Any class that subclasses Magics *must* also apply this decorator, to |
|
96 | 97 | ensure that all the methods that have been decorated as line/cell magics |
|
97 | 98 | get correctly registered in the class instance. This is necessary because |
|
98 | 99 | when method decorators run, the class does not exist yet, so they |
|
99 | 100 | temporarily store their information into a module global. Application of |
|
100 | 101 | this class decorator copies that global data to the class instance and |
|
101 | 102 | clears the global. |
|
102 | 103 | |
|
103 | 104 | Obviously, this mechanism is not thread-safe, which means that the |
|
104 | 105 | *creation* of subclasses of Magic should only be done in a single-thread |
|
105 | 106 | context. Instantiation of the classes has no restrictions. Given that |
|
106 | 107 | these classes are typically created at IPython startup time and before user |
|
107 | 108 | application code becomes active, in practice this should not pose any |
|
108 | 109 | problems. |
|
109 | 110 | """ |
|
110 | 111 | cls.registered = True |
|
111 | 112 | cls.magics = dict(line = magics['line'], |
|
112 | 113 | cell = magics['cell']) |
|
113 | 114 | magics['line'] = {} |
|
114 | 115 | magics['cell'] = {} |
|
115 | 116 | return cls |
|
116 | 117 | |
|
117 | 118 | |
|
118 | 119 | def record_magic(dct, magic_kind, magic_name, func): |
|
119 | 120 | """Utility function to store a function as a magic of a specific kind. |
|
120 | 121 | |
|
121 | 122 | Parameters |
|
122 | 123 | ---------- |
|
123 | 124 | dct : dict |
|
124 | 125 | A dictionary with 'line' and 'cell' subdicts. |
|
125 | 126 | |
|
126 | 127 | magic_kind : str |
|
127 | 128 | Kind of magic to be stored. |
|
128 | 129 | |
|
129 | 130 | magic_name : str |
|
130 | 131 | Key to store the magic as. |
|
131 | 132 | |
|
132 | 133 | func : function |
|
133 | 134 | Callable object to store. |
|
134 | 135 | """ |
|
135 | 136 | if magic_kind == 'line_cell': |
|
136 | 137 | dct['line'][magic_name] = dct['cell'][magic_name] = func |
|
137 | 138 | else: |
|
138 | 139 | dct[magic_kind][magic_name] = func |
|
139 | 140 | |
|
140 | 141 | |
|
141 | 142 | def validate_type(magic_kind): |
|
142 | 143 | """Ensure that the given magic_kind is valid. |
|
143 | 144 | |
|
144 | 145 | Check that the given magic_kind is one of the accepted spec types (stored |
|
145 | 146 | in the global `magic_spec`), raise ValueError otherwise. |
|
146 | 147 | """ |
|
147 | 148 | if magic_kind not in magic_spec: |
|
148 | 149 | raise ValueError('magic_kind must be one of %s, %s given' % |
|
149 | 150 | magic_kinds, magic_kind) |
|
150 | 151 | |
|
151 | 152 | |
|
152 | 153 | # The docstrings for the decorator below will be fairly similar for the two |
|
153 | 154 | # types (method and function), so we generate them here once and reuse the |
|
154 | 155 | # templates below. |
|
155 | 156 | _docstring_template = \ |
|
156 | 157 | """Decorate the given {0} as {1} magic. |
|
157 | 158 | |
|
158 | 159 | The decorator can be used with or without arguments, as follows. |
|
159 | 160 | |
|
160 | 161 | i) without arguments: it will create a {1} magic named as the {0} being |
|
161 | 162 | decorated:: |
|
162 | 163 | |
|
163 | 164 | @deco |
|
164 | 165 | def foo(...) |
|
165 | 166 | |
|
166 | 167 | will create a {1} magic named `foo`. |
|
167 | 168 | |
|
168 | 169 | ii) with one string argument: which will be used as the actual name of the |
|
169 | 170 | resulting magic:: |
|
170 | 171 | |
|
171 | 172 | @deco('bar') |
|
172 | 173 | def foo(...) |
|
173 | 174 | |
|
174 | 175 | will create a {1} magic named `bar`. |
|
175 | 176 | """ |
|
176 | 177 | |
|
177 | 178 | # These two are decorator factories. While they are conceptually very similar, |
|
178 | 179 | # there are enough differences in the details that it's simpler to have them |
|
179 | 180 | # written as completely standalone functions rather than trying to share code |
|
180 | 181 | # and make a single one with convoluted logic. |
|
181 | 182 | |
|
182 | 183 | def _method_magic_marker(magic_kind): |
|
183 | 184 | """Decorator factory for methods in Magics subclasses. |
|
184 | 185 | """ |
|
185 | 186 | |
|
186 | 187 | validate_type(magic_kind) |
|
187 | 188 | |
|
188 | 189 | # This is a closure to capture the magic_kind. We could also use a class, |
|
189 | 190 | # but it's overkill for just that one bit of state. |
|
190 | 191 | def magic_deco(arg): |
|
191 | 192 | call = lambda f, *a, **k: f(*a, **k) |
|
192 | 193 | |
|
193 | 194 | if callable(arg): |
|
194 | 195 | # "Naked" decorator call (just @foo, no args) |
|
195 | 196 | func = arg |
|
196 | 197 | name = func.func_name |
|
197 | 198 | retval = decorator(call, func) |
|
198 | 199 | record_magic(magics, magic_kind, name, name) |
|
199 | 200 | elif isinstance(arg, basestring): |
|
200 | 201 | # Decorator called with arguments (@foo('bar')) |
|
201 | 202 | name = arg |
|
202 | 203 | def mark(func, *a, **kw): |
|
203 | 204 | record_magic(magics, magic_kind, name, func.func_name) |
|
204 | 205 | return decorator(call, func) |
|
205 | 206 | retval = mark |
|
206 | 207 | else: |
|
207 | 208 | raise TypeError("Decorator can only be called with " |
|
208 | 209 | "string or function") |
|
209 | 210 | return retval |
|
210 | 211 | |
|
211 | 212 | # Ensure the resulting decorator has a usable docstring |
|
212 | 213 | magic_deco.__doc__ = _docstring_template.format('method', magic_kind) |
|
213 | 214 | return magic_deco |
|
214 | 215 | |
|
215 | 216 | |
|
216 | 217 | def _function_magic_marker(magic_kind): |
|
217 | 218 | """Decorator factory for standalone functions. |
|
218 | 219 | """ |
|
219 | 220 | validate_type(magic_kind) |
|
220 | 221 | |
|
221 | 222 | # This is a closure to capture the magic_kind. We could also use a class, |
|
222 | 223 | # but it's overkill for just that one bit of state. |
|
223 | 224 | def magic_deco(arg): |
|
224 | 225 | call = lambda f, *a, **k: f(*a, **k) |
|
225 | 226 | |
|
226 | 227 | # Find get_ipython() in the caller's namespace |
|
227 | 228 | caller = sys._getframe(1) |
|
228 | 229 | for ns in ['f_locals', 'f_globals', 'f_builtins']: |
|
229 | 230 | get_ipython = getattr(caller, ns).get('get_ipython') |
|
230 | 231 | if get_ipython is not None: |
|
231 | 232 | break |
|
232 | 233 | else: |
|
233 | 234 | raise NameError('Decorator can only run in context where ' |
|
234 | 235 | '`get_ipython` exists') |
|
235 | 236 | |
|
236 | 237 | ip = get_ipython() |
|
237 | 238 | |
|
238 | 239 | if callable(arg): |
|
239 | 240 | # "Naked" decorator call (just @foo, no args) |
|
240 | 241 | func = arg |
|
241 | 242 | name = func.func_name |
|
242 | 243 | ip.register_magic_function(func, magic_kind, name) |
|
243 | 244 | retval = decorator(call, func) |
|
244 | 245 | elif isinstance(arg, basestring): |
|
245 | 246 | # Decorator called with arguments (@foo('bar')) |
|
246 | 247 | name = arg |
|
247 | 248 | def mark(func, *a, **kw): |
|
248 | 249 | ip.register_magic_function(func, magic_kind, name) |
|
249 | 250 | return decorator(call, func) |
|
250 | 251 | retval = mark |
|
251 | 252 | else: |
|
252 | 253 | raise TypeError("Decorator can only be called with " |
|
253 | 254 | "string or function") |
|
254 | 255 | return retval |
|
255 | 256 | |
|
256 | 257 | # Ensure the resulting decorator has a usable docstring |
|
257 | 258 | ds = _docstring_template.format('function', magic_kind) |
|
258 | 259 | |
|
259 | 260 | ds += dedent(""" |
|
260 | 261 | Note: this decorator can only be used in a context where IPython is already |
|
261 | 262 | active, so that the `get_ipython()` call succeeds. You can therefore use |
|
262 | 263 | it in your startup files loaded after IPython initializes, but *not* in the |
|
263 | 264 | IPython configuration file itself, which is executed before IPython is |
|
264 | 265 | fully up and running. Any file located in the `startup` subdirectory of |
|
265 | 266 | your configuration profile will be OK in this sense. |
|
266 | 267 | """) |
|
267 | 268 | |
|
268 | 269 | magic_deco.__doc__ = ds |
|
269 | 270 | return magic_deco |
|
270 | 271 | |
|
271 | 272 | |
|
272 | 273 | # Create the actual decorators for public use |
|
273 | 274 | |
|
274 | 275 | # These three are used to decorate methods in class definitions |
|
275 | 276 | line_magic = _method_magic_marker('line') |
|
276 | 277 | cell_magic = _method_magic_marker('cell') |
|
277 | 278 | line_cell_magic = _method_magic_marker('line_cell') |
|
278 | 279 | |
|
279 | 280 | # These three decorate standalone functions and perform the decoration |
|
280 | 281 | # immediately. They can only run where get_ipython() works |
|
281 | 282 | register_line_magic = _function_magic_marker('line') |
|
282 | 283 | register_cell_magic = _function_magic_marker('cell') |
|
283 | 284 | register_line_cell_magic = _function_magic_marker('line_cell') |
|
284 | 285 | |
|
285 | 286 | #----------------------------------------------------------------------------- |
|
286 | 287 | # Core Magic classes |
|
287 | 288 | #----------------------------------------------------------------------------- |
|
288 | 289 | |
|
289 | 290 | class MagicsManager(Configurable): |
|
290 | 291 | """Object that handles all magic-related functionality for IPython. |
|
291 | 292 | """ |
|
292 | 293 | # Non-configurable class attributes |
|
293 | 294 | |
|
294 | 295 | # A two-level dict, first keyed by magic type, then by magic function, and |
|
295 | 296 | # holding the actual callable object as value. This is the dict used for |
|
296 | 297 | # magic function dispatch |
|
297 | 298 | magics = Dict |
|
298 | 299 | |
|
299 | 300 | # A registry of the original objects that we've been given holding magics. |
|
300 | 301 | registry = Dict |
|
301 | 302 | |
|
302 | 303 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
|
303 | 304 | |
|
304 | 305 | auto_magic = Bool(True, config=True, help= |
|
305 | 306 | "Automatically call line magics without requiring explicit % prefix") |
|
306 | 307 | |
|
307 | 308 | def _auto_magic_changed(self, name, value): |
|
308 | 309 | self.shell.automagic = value |
|
309 | 310 | |
|
310 | 311 | _auto_status = [ |
|
311 | 312 | 'Automagic is OFF, % prefix IS needed for line magics.', |
|
312 | 313 | 'Automagic is ON, % prefix IS NOT needed for line magics.'] |
|
313 | 314 | |
|
314 | 315 | user_magics = Instance('IPython.core.magics.UserMagics') |
|
315 | 316 | |
|
316 | 317 | def __init__(self, shell=None, config=None, user_magics=None, **traits): |
|
317 | 318 | |
|
318 | 319 | super(MagicsManager, self).__init__(shell=shell, config=config, |
|
319 | 320 | user_magics=user_magics, **traits) |
|
320 | 321 | self.magics = dict(line={}, cell={}) |
|
321 | 322 | # Let's add the user_magics to the registry for uniformity, so *all* |
|
322 | 323 | # registered magic containers can be found there. |
|
323 | 324 | self.registry[user_magics.__class__.__name__] = user_magics |
|
324 | 325 | |
|
325 | 326 | def auto_status(self): |
|
326 | 327 | """Return descriptive string with automagic status.""" |
|
327 | 328 | return self._auto_status[self.auto_magic] |
|
328 | 329 | |
|
329 | 330 | def lsmagic(self): |
|
330 | 331 | """Return a dict of currently available magic functions. |
|
331 | 332 | |
|
332 | 333 | The return dict has the keys 'line' and 'cell', corresponding to the |
|
333 | 334 | two types of magics we support. Each value is a list of names. |
|
334 | 335 | """ |
|
335 | 336 | return self.magics |
|
336 | 337 | |
|
337 | 338 | def lsmagic_docs(self, brief=False, missing=''): |
|
338 | 339 | """Return dict of documentation of magic functions. |
|
339 | 340 | |
|
340 | 341 | The return dict has the keys 'line' and 'cell', corresponding to the |
|
341 | 342 | two types of magics we support. Each value is a dict keyed by magic |
|
342 | 343 | name whose value is the function docstring. If a docstring is |
|
343 | 344 | unavailable, the value of `missing` is used instead. |
|
344 | 345 | |
|
345 | 346 | If brief is True, only the first line of each docstring will be returned. |
|
346 | 347 | """ |
|
347 | 348 | docs = {} |
|
348 | 349 | for m_type in self.magics: |
|
349 | 350 | m_docs = {} |
|
350 | 351 | for m_name, m_func in self.magics[m_type].iteritems(): |
|
351 | 352 | if m_func.__doc__: |
|
352 | 353 | if brief: |
|
353 | 354 | m_docs[m_name] = m_func.__doc__.split('\n', 1)[0] |
|
354 | 355 | else: |
|
355 | 356 | m_docs[m_name] = m_func.__doc__.rstrip() |
|
356 | 357 | else: |
|
357 | 358 | m_docs[m_name] = missing |
|
358 | 359 | docs[m_type] = m_docs |
|
359 | 360 | return docs |
|
360 | 361 | |
|
361 | 362 | def register(self, *magic_objects): |
|
362 | 363 | """Register one or more instances of Magics. |
|
363 | 364 | |
|
364 | 365 | Take one or more classes or instances of classes that subclass the main |
|
365 | 366 | `core.Magic` class, and register them with IPython to use the magic |
|
366 | 367 | functions they provide. The registration process will then ensure that |
|
367 | 368 | any methods that have decorated to provide line and/or cell magics will |
|
368 | 369 | be recognized with the `%x`/`%%x` syntax as a line/cell magic |
|
369 | 370 | respectively. |
|
370 | 371 | |
|
371 | 372 | If classes are given, they will be instantiated with the default |
|
372 | 373 | constructor. If your classes need a custom constructor, you should |
|
373 | 374 | instanitate them first and pass the instance. |
|
374 | 375 | |
|
375 | 376 | The provided arguments can be an arbitrary mix of classes and instances. |
|
376 | 377 | |
|
377 | 378 | Parameters |
|
378 | 379 | ---------- |
|
379 | 380 | magic_objects : one or more classes or instances |
|
380 | 381 | """ |
|
381 | 382 | # Start by validating them to ensure they have all had their magic |
|
382 | 383 | # methods registered at the instance level |
|
383 | 384 | for m in magic_objects: |
|
384 | 385 | if not m.registered: |
|
385 | 386 | raise ValueError("Class of magics %r was constructed without " |
|
386 | 387 | "the @register_magics class decorator") |
|
387 | 388 | if type(m) in (type, MetaHasTraits): |
|
388 | 389 | # If we're given an uninstantiated class |
|
389 | 390 | m = m(shell=self.shell) |
|
390 | 391 | |
|
391 | 392 | # Now that we have an instance, we can register it and update the |
|
392 | 393 | # table of callables |
|
393 | 394 | self.registry[m.__class__.__name__] = m |
|
394 | 395 | for mtype in magic_kinds: |
|
395 | 396 | self.magics[mtype].update(m.magics[mtype]) |
|
396 | 397 | |
|
397 | 398 | def register_function(self, func, magic_kind='line', magic_name=None): |
|
398 | 399 | """Expose a standalone function as magic function for IPython. |
|
399 | 400 | |
|
400 | 401 | This will create an IPython magic (line, cell or both) from a |
|
401 | 402 | standalone function. The functions should have the following |
|
402 | 403 | signatures: |
|
403 | 404 | |
|
404 | 405 | * For line magics: `def f(line)` |
|
405 | 406 | * For cell magics: `def f(line, cell)` |
|
406 | 407 | * For a function that does both: `def f(line, cell=None)` |
|
407 | 408 | |
|
408 | 409 | In the latter case, the function will be called with `cell==None` when |
|
409 | 410 | invoked as `%f`, and with cell as a string when invoked as `%%f`. |
|
410 | 411 | |
|
411 | 412 | Parameters |
|
412 | 413 | ---------- |
|
413 | 414 | func : callable |
|
414 | 415 | Function to be registered as a magic. |
|
415 | 416 | |
|
416 | 417 | magic_kind : str |
|
417 | 418 | Kind of magic, one of 'line', 'cell' or 'line_cell' |
|
418 | 419 | |
|
419 | 420 | magic_name : optional str |
|
420 | 421 | If given, the name the magic will have in the IPython namespace. By |
|
421 | 422 | default, the name of the function itself is used. |
|
422 | 423 | """ |
|
423 | 424 | |
|
424 | 425 | # Create the new method in the user_magics and register it in the |
|
425 | 426 | # global table |
|
426 | 427 | validate_type(magic_kind) |
|
427 | 428 | magic_name = func.func_name if magic_name is None else magic_name |
|
428 | 429 | setattr(self.user_magics, magic_name, func) |
|
429 | 430 | record_magic(self.magics, magic_kind, magic_name, func) |
|
430 | 431 | |
|
431 | 432 | def define_magic(self, name, func): |
|
432 | 433 | """[Deprecated] Expose own function as magic function for IPython. |
|
433 | 434 | |
|
434 | 435 | Example:: |
|
435 | 436 | |
|
436 | 437 | def foo_impl(self, parameter_s=''): |
|
437 | 438 | 'My very own magic!. (Use docstrings, IPython reads them).' |
|
438 | 439 | print 'Magic function. Passed parameter is between < >:' |
|
439 | 440 | print '<%s>' % parameter_s |
|
440 | 441 | print 'The self object is:', self |
|
441 | 442 | |
|
442 | 443 | ip.define_magic('foo',foo_impl) |
|
443 | 444 | """ |
|
444 | 445 | meth = types.MethodType(func, self.user_magics) |
|
445 | 446 | setattr(self.user_magics, name, meth) |
|
446 | 447 | record_magic(self.magics, 'line', name, meth) |
|
447 | 448 | |
|
448 | 449 | def register_alias(self, alias_name, magic_name, magic_kind='line'): |
|
449 | 450 | """Register an alias to a magic function. |
|
450 | 451 | |
|
451 | 452 | The alias is an instance of :class:`MagicAlias`, which holds the |
|
452 | 453 | name and kind of the magic it should call. Binding is done at |
|
453 | 454 | call time, so if the underlying magic function is changed the alias |
|
454 | 455 | will call the new function. |
|
455 | 456 | |
|
456 | 457 | Parameters |
|
457 | 458 | ---------- |
|
458 | 459 | alias_name : str |
|
459 | 460 | The name of the magic to be registered. |
|
460 | 461 | |
|
461 | 462 | magic_name : str |
|
462 | 463 | The name of an existing magic. |
|
463 | 464 | |
|
464 | 465 | magic_kind : str |
|
465 | 466 | Kind of magic, one of 'line' or 'cell' |
|
466 | 467 | """ |
|
467 | 468 | |
|
468 | 469 | # `validate_type` is too permissive, as it allows 'line_cell' |
|
469 | 470 | # which we do not handle. |
|
470 | 471 | if magic_kind not in magic_kinds: |
|
471 | 472 | raise ValueError('magic_kind must be one of %s, %s given' % |
|
472 | 473 | magic_kinds, magic_kind) |
|
473 | 474 | |
|
474 | 475 | alias = MagicAlias(self.shell, magic_name, magic_kind) |
|
475 | 476 | setattr(self.user_magics, alias_name, alias) |
|
476 | 477 | record_magic(self.magics, magic_kind, alias_name, alias) |
|
477 | 478 | |
|
478 | 479 | # Key base class that provides the central functionality for magics. |
|
479 | 480 | |
|
480 | 481 | |
|
481 | 482 | class Magics(Configurable): |
|
482 | 483 | """Base class for implementing magic functions. |
|
483 | 484 | |
|
484 | 485 | Shell functions which can be reached as %function_name. All magic |
|
485 | 486 | functions should accept a string, which they can parse for their own |
|
486 | 487 | needs. This can make some functions easier to type, eg `%cd ../` |
|
487 | 488 | vs. `%cd("../")` |
|
488 | 489 | |
|
489 | 490 | Classes providing magic functions need to subclass this class, and they |
|
490 | 491 | MUST: |
|
491 | 492 | |
|
492 | 493 | - Use the method decorators `@line_magic` and `@cell_magic` to decorate |
|
493 | 494 | individual methods as magic functions, AND |
|
494 | 495 | |
|
495 | 496 | - Use the class decorator `@magics_class` to ensure that the magic |
|
496 | 497 | methods are properly registered at the instance level upon instance |
|
497 | 498 | initialization. |
|
498 | 499 | |
|
499 | 500 | See :mod:`magic_functions` for examples of actual implementation classes. |
|
500 | 501 | """ |
|
501 | 502 | # Dict holding all command-line options for each magic. |
|
502 | 503 | options_table = None |
|
503 | 504 | # Dict for the mapping of magic names to methods, set by class decorator |
|
504 | 505 | magics = None |
|
505 | 506 | # Flag to check that the class decorator was properly applied |
|
506 | 507 | registered = False |
|
507 | 508 | # Instance of IPython shell |
|
508 | 509 | shell = None |
|
509 | 510 | |
|
510 | 511 | def __init__(self, shell=None, **kwargs): |
|
511 | 512 | if not(self.__class__.registered): |
|
512 | 513 | raise ValueError('Magics subclass without registration - ' |
|
513 | 514 | 'did you forget to apply @magics_class?') |
|
514 | 515 | if shell is not None: |
|
515 | 516 | if hasattr(shell, 'configurables'): |
|
516 | 517 | shell.configurables.append(self) |
|
517 | 518 | if hasattr(shell, 'config'): |
|
518 | 519 | kwargs.setdefault('parent', shell) |
|
519 | 520 | kwargs['shell'] = shell |
|
520 | 521 | |
|
521 | 522 | self.shell = shell |
|
522 | 523 | self.options_table = {} |
|
523 | 524 | # The method decorators are run when the instance doesn't exist yet, so |
|
524 | 525 | # they can only record the names of the methods they are supposed to |
|
525 | 526 | # grab. Only now, that the instance exists, can we create the proper |
|
526 | 527 | # mapping to bound methods. So we read the info off the original names |
|
527 | 528 | # table and replace each method name by the actual bound method. |
|
528 | 529 | # But we mustn't clobber the *class* mapping, in case of multiple instances. |
|
529 | 530 | class_magics = self.magics |
|
530 | 531 | self.magics = {} |
|
531 | 532 | for mtype in magic_kinds: |
|
532 | 533 | tab = self.magics[mtype] = {} |
|
533 | 534 | cls_tab = class_magics[mtype] |
|
534 | 535 | for magic_name, meth_name in cls_tab.iteritems(): |
|
535 | 536 | if isinstance(meth_name, basestring): |
|
536 | 537 | # it's a method name, grab it |
|
537 | 538 | tab[magic_name] = getattr(self, meth_name) |
|
538 | 539 | else: |
|
539 | 540 | # it's the real thing |
|
540 | 541 | tab[magic_name] = meth_name |
|
541 | 542 | # Configurable **needs** to be initiated at the end or the config |
|
542 | 543 | # magics get screwed up. |
|
543 | 544 | super(Magics, self).__init__(**kwargs) |
|
544 | 545 | |
|
545 | 546 | def arg_err(self,func): |
|
546 | 547 | """Print docstring if incorrect arguments were passed""" |
|
547 |
print |
|
|
548 |
print |
|
|
548 | print('Error in arguments:') | |
|
549 | print(oinspect.getdoc(func)) | |
|
549 | 550 | |
|
550 | 551 | def format_latex(self, strng): |
|
551 | 552 | """Format a string for latex inclusion.""" |
|
552 | 553 | |
|
553 | 554 | # Characters that need to be escaped for latex: |
|
554 | 555 | escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE) |
|
555 | 556 | # Magic command names as headers: |
|
556 | 557 | cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC, |
|
557 | 558 | re.MULTILINE) |
|
558 | 559 | # Magic commands |
|
559 | 560 | cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC, |
|
560 | 561 | re.MULTILINE) |
|
561 | 562 | # Paragraph continue |
|
562 | 563 | par_re = re.compile(r'\\$',re.MULTILINE) |
|
563 | 564 | |
|
564 | 565 | # The "\n" symbol |
|
565 | 566 | newline_re = re.compile(r'\\n') |
|
566 | 567 | |
|
567 | 568 | # Now build the string for output: |
|
568 | 569 | #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng) |
|
569 | 570 | strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:', |
|
570 | 571 | strng) |
|
571 | 572 | strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng) |
|
572 | 573 | strng = par_re.sub(r'\\\\',strng) |
|
573 | 574 | strng = escape_re.sub(r'\\\1',strng) |
|
574 | 575 | strng = newline_re.sub(r'\\textbackslash{}n',strng) |
|
575 | 576 | return strng |
|
576 | 577 | |
|
577 | 578 | def parse_options(self, arg_str, opt_str, *long_opts, **kw): |
|
578 | 579 | """Parse options passed to an argument string. |
|
579 | 580 | |
|
580 | 581 | The interface is similar to that of getopt(), but it returns back a |
|
581 | 582 | Struct with the options as keys and the stripped argument string still |
|
582 | 583 | as a string. |
|
583 | 584 | |
|
584 | 585 | arg_str is quoted as a true sys.argv vector by using shlex.split. |
|
585 | 586 | This allows us to easily expand variables, glob files, quote |
|
586 | 587 | arguments, etc. |
|
587 | 588 | |
|
588 | 589 | Options: |
|
589 | 590 | -mode: default 'string'. If given as 'list', the argument string is |
|
590 | 591 | returned as a list (split on whitespace) instead of a string. |
|
591 | 592 | |
|
592 | 593 | -list_all: put all option values in lists. Normally only options |
|
593 | 594 | appearing more than once are put in a list. |
|
594 | 595 | |
|
595 | 596 | -posix (True): whether to split the input line in POSIX mode or not, |
|
596 | 597 | as per the conventions outlined in the shlex module from the |
|
597 | 598 | standard library.""" |
|
598 | 599 | |
|
599 | 600 | # inject default options at the beginning of the input line |
|
600 | 601 | caller = sys._getframe(1).f_code.co_name |
|
601 | 602 | arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str) |
|
602 | 603 | |
|
603 | 604 | mode = kw.get('mode','string') |
|
604 | 605 | if mode not in ['string','list']: |
|
605 | 606 | raise ValueError('incorrect mode given: %s' % mode) |
|
606 | 607 | # Get options |
|
607 | 608 | list_all = kw.get('list_all',0) |
|
608 | 609 | posix = kw.get('posix', os.name == 'posix') |
|
609 | 610 | strict = kw.get('strict', True) |
|
610 | 611 | |
|
611 | 612 | # Check if we have more than one argument to warrant extra processing: |
|
612 | 613 | odict = {} # Dictionary with options |
|
613 | 614 | args = arg_str.split() |
|
614 | 615 | if len(args) >= 1: |
|
615 | 616 | # If the list of inputs only has 0 or 1 thing in it, there's no |
|
616 | 617 | # need to look for options |
|
617 | 618 | argv = arg_split(arg_str, posix, strict) |
|
618 | 619 | # Do regular option processing |
|
619 | 620 | try: |
|
620 | 621 | opts,args = getopt(argv, opt_str, long_opts) |
|
621 | 622 | except GetoptError as e: |
|
622 | 623 | raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str, |
|
623 | 624 | " ".join(long_opts))) |
|
624 | 625 | for o,a in opts: |
|
625 | 626 | if o.startswith('--'): |
|
626 | 627 | o = o[2:] |
|
627 | 628 | else: |
|
628 | 629 | o = o[1:] |
|
629 | 630 | try: |
|
630 | 631 | odict[o].append(a) |
|
631 | 632 | except AttributeError: |
|
632 | 633 | odict[o] = [odict[o],a] |
|
633 | 634 | except KeyError: |
|
634 | 635 | if list_all: |
|
635 | 636 | odict[o] = [a] |
|
636 | 637 | else: |
|
637 | 638 | odict[o] = a |
|
638 | 639 | |
|
639 | 640 | # Prepare opts,args for return |
|
640 | 641 | opts = Struct(odict) |
|
641 | 642 | if mode == 'string': |
|
642 | 643 | args = ' '.join(args) |
|
643 | 644 | |
|
644 | 645 | return opts,args |
|
645 | 646 | |
|
646 | 647 | def default_option(self, fn, optstr): |
|
647 | 648 | """Make an entry in the options_table for fn, with value optstr""" |
|
648 | 649 | |
|
649 | 650 | if fn not in self.lsmagic(): |
|
650 | 651 | error("%s is not a magic function" % fn) |
|
651 | 652 | self.options_table[fn] = optstr |
|
652 | 653 | |
|
653 | 654 | |
|
654 | 655 | class MagicAlias(object): |
|
655 | 656 | """An alias to another magic function. |
|
656 | 657 | |
|
657 | 658 | An alias is determined by its magic name and magic kind. Lookup |
|
658 | 659 | is done at call time, so if the underlying magic changes the alias |
|
659 | 660 | will call the new function. |
|
660 | 661 | |
|
661 | 662 | Use the :meth:`MagicsManager.register_alias` method or the |
|
662 | 663 | `%alias_magic` magic function to create and register a new alias. |
|
663 | 664 | """ |
|
664 | 665 | def __init__(self, shell, magic_name, magic_kind): |
|
665 | 666 | self.shell = shell |
|
666 | 667 | self.magic_name = magic_name |
|
667 | 668 | self.magic_kind = magic_kind |
|
668 | 669 | |
|
669 | 670 | self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name) |
|
670 | 671 | self.__doc__ = "Alias for `%s`." % self.pretty_target |
|
671 | 672 | |
|
672 | 673 | self._in_call = False |
|
673 | 674 | |
|
674 | 675 | def __call__(self, *args, **kwargs): |
|
675 | 676 | """Call the magic alias.""" |
|
676 | 677 | fn = self.shell.find_magic(self.magic_name, self.magic_kind) |
|
677 | 678 | if fn is None: |
|
678 | 679 | raise UsageError("Magic `%s` not found." % self.pretty_target) |
|
679 | 680 | |
|
680 | 681 | # Protect against infinite recursion. |
|
681 | 682 | if self._in_call: |
|
682 | 683 | raise UsageError("Infinite recursion detected; " |
|
683 | 684 | "magic aliases cannot call themselves.") |
|
684 | 685 | self._in_call = True |
|
685 | 686 | try: |
|
686 | 687 | return fn(*args, **kwargs) |
|
687 | 688 | finally: |
|
688 | 689 | self._in_call = False |
@@ -1,128 +1,129 b'' | |||
|
1 | 1 | """Implementation of magic functions that control various automatic behaviors. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Our own packages |
|
16 | 17 | from IPython.core.magic import Bunch, Magics, magics_class, line_magic |
|
17 | 18 | from IPython.testing.skipdoctest import skip_doctest |
|
18 | 19 | from IPython.utils.warn import error |
|
19 | 20 | |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | # Magic implementation classes |
|
22 | 23 | #----------------------------------------------------------------------------- |
|
23 | 24 | |
|
24 | 25 | @magics_class |
|
25 | 26 | class AutoMagics(Magics): |
|
26 | 27 | """Magics that control various autoX behaviors.""" |
|
27 | 28 | |
|
28 | 29 | def __init__(self, shell): |
|
29 | 30 | super(AutoMagics, self).__init__(shell) |
|
30 | 31 | # namespace for holding state we may need |
|
31 | 32 | self._magic_state = Bunch() |
|
32 | 33 | |
|
33 | 34 | @line_magic |
|
34 | 35 | def automagic(self, parameter_s=''): |
|
35 | 36 | """Make magic functions callable without having to type the initial %. |
|
36 | 37 | |
|
37 | 38 | Without argumentsl toggles on/off (when off, you must call it as |
|
38 | 39 | %automagic, of course). With arguments it sets the value, and you can |
|
39 | 40 | use any of (case insensitive): |
|
40 | 41 | |
|
41 | 42 | - on, 1, True: to activate |
|
42 | 43 | |
|
43 | 44 | - off, 0, False: to deactivate. |
|
44 | 45 | |
|
45 | 46 | Note that magic functions have lowest priority, so if there's a |
|
46 | 47 | variable whose name collides with that of a magic fn, automagic won't |
|
47 | 48 | work for that function (you get the variable instead). However, if you |
|
48 | 49 | delete the variable (del var), the previously shadowed magic function |
|
49 | 50 | becomes visible to automagic again.""" |
|
50 | 51 | |
|
51 | 52 | arg = parameter_s.lower() |
|
52 | 53 | mman = self.shell.magics_manager |
|
53 | 54 | if arg in ('on', '1', 'true'): |
|
54 | 55 | val = True |
|
55 | 56 | elif arg in ('off', '0', 'false'): |
|
56 | 57 | val = False |
|
57 | 58 | else: |
|
58 | 59 | val = not mman.auto_magic |
|
59 | 60 | mman.auto_magic = val |
|
60 |
print |
|
|
61 | print('\n' + self.shell.magics_manager.auto_status()) | |
|
61 | 62 | |
|
62 | 63 | @skip_doctest |
|
63 | 64 | @line_magic |
|
64 | 65 | def autocall(self, parameter_s=''): |
|
65 | 66 | """Make functions callable without having to type parentheses. |
|
66 | 67 | |
|
67 | 68 | Usage: |
|
68 | 69 | |
|
69 | 70 | %autocall [mode] |
|
70 | 71 | |
|
71 | 72 | The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the |
|
72 | 73 | value is toggled on and off (remembering the previous state). |
|
73 | 74 | |
|
74 | 75 | In more detail, these values mean: |
|
75 | 76 | |
|
76 | 77 | 0 -> fully disabled |
|
77 | 78 | |
|
78 | 79 | 1 -> active, but do not apply if there are no arguments on the line. |
|
79 | 80 | |
|
80 | 81 | In this mode, you get:: |
|
81 | 82 | |
|
82 | 83 | In [1]: callable |
|
83 | 84 | Out[1]: <built-in function callable> |
|
84 | 85 | |
|
85 | 86 | In [2]: callable 'hello' |
|
86 | 87 | ------> callable('hello') |
|
87 | 88 | Out[2]: False |
|
88 | 89 | |
|
89 | 90 | 2 -> Active always. Even if no arguments are present, the callable |
|
90 | 91 | object is called:: |
|
91 | 92 | |
|
92 | 93 | In [2]: float |
|
93 | 94 | ------> float() |
|
94 | 95 | Out[2]: 0.0 |
|
95 | 96 | |
|
96 | 97 | Note that even with autocall off, you can still use '/' at the start of |
|
97 | 98 | a line to treat the first argument on the command line as a function |
|
98 | 99 | and add parentheses to it:: |
|
99 | 100 | |
|
100 | 101 | In [8]: /str 43 |
|
101 | 102 | ------> str(43) |
|
102 | 103 | Out[8]: '43' |
|
103 | 104 | |
|
104 | 105 | # all-random (note for auto-testing) |
|
105 | 106 | """ |
|
106 | 107 | |
|
107 | 108 | if parameter_s: |
|
108 | 109 | arg = int(parameter_s) |
|
109 | 110 | else: |
|
110 | 111 | arg = 'toggle' |
|
111 | 112 | |
|
112 | 113 | if not arg in (0, 1, 2, 'toggle'): |
|
113 | 114 | error('Valid modes: (0->Off, 1->Smart, 2->Full') |
|
114 | 115 | return |
|
115 | 116 | |
|
116 | 117 | if arg in (0, 1, 2): |
|
117 | 118 | self.shell.autocall = arg |
|
118 | 119 | else: # toggle |
|
119 | 120 | if self.shell.autocall: |
|
120 | 121 | self._magic_state.autocall_save = self.shell.autocall |
|
121 | 122 | self.shell.autocall = 0 |
|
122 | 123 | else: |
|
123 | 124 | try: |
|
124 | 125 | self.shell.autocall = self._magic_state.autocall_save |
|
125 | 126 | except AttributeError: |
|
126 | 127 | self.shell.autocall = self._magic_state.autocall_save = 1 |
|
127 | 128 | |
|
128 |
print |
|
|
129 | print("Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall]) |
@@ -1,690 +1,691 b'' | |||
|
1 | 1 | """Implementation of code management magic functions. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Stdlib |
|
16 | 17 | import inspect |
|
17 | 18 | import io |
|
18 | 19 | import os |
|
19 | 20 | import re |
|
20 | 21 | import sys |
|
21 | 22 | import ast |
|
22 | 23 | from itertools import chain |
|
23 | 24 | |
|
24 | 25 | # Our own packages |
|
25 | 26 | from IPython.core.error import TryNext, StdinNotImplementedError, UsageError |
|
26 | 27 | from IPython.core.macro import Macro |
|
27 | 28 | from IPython.core.magic import Magics, magics_class, line_magic |
|
28 | 29 | from IPython.core.oinspect import find_file, find_source_lines |
|
29 | 30 | from IPython.testing.skipdoctest import skip_doctest |
|
30 | 31 | from IPython.utils import py3compat |
|
31 | 32 | from IPython.utils.contexts import preserve_keys |
|
32 | 33 | from IPython.utils.path import get_py_filename, unquote_filename |
|
33 | 34 | from IPython.utils.warn import warn, error |
|
34 | 35 | from IPython.utils.text import get_text_list |
|
35 | 36 | |
|
36 | 37 | #----------------------------------------------------------------------------- |
|
37 | 38 | # Magic implementation classes |
|
38 | 39 | #----------------------------------------------------------------------------- |
|
39 | 40 | |
|
40 | 41 | # Used for exception handling in magic_edit |
|
41 | 42 | class MacroToEdit(ValueError): pass |
|
42 | 43 | |
|
43 | 44 | ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$") |
|
44 | 45 | |
|
45 | 46 | # To match, e.g. 8-10 1:5 :10 3- |
|
46 | 47 | range_re = re.compile(r""" |
|
47 | 48 | (?P<start>\d+)? |
|
48 | 49 | ((?P<sep>[\-:]) |
|
49 | 50 | (?P<end>\d+)?)? |
|
50 | 51 | $""", re.VERBOSE) |
|
51 | 52 | |
|
52 | 53 | |
|
53 | 54 | def extract_code_ranges(ranges_str): |
|
54 | 55 | """Turn a string of range for %%load into 2-tuples of (start, stop) |
|
55 | 56 | ready to use as a slice of the content splitted by lines. |
|
56 | 57 | |
|
57 | 58 | Examples |
|
58 | 59 | -------- |
|
59 | 60 | list(extract_input_ranges("5-10 2")) |
|
60 | 61 | [(4, 10), (1, 2)] |
|
61 | 62 | """ |
|
62 | 63 | for range_str in ranges_str.split(): |
|
63 | 64 | rmatch = range_re.match(range_str) |
|
64 | 65 | if not rmatch: |
|
65 | 66 | continue |
|
66 | 67 | sep = rmatch.group("sep") |
|
67 | 68 | start = rmatch.group("start") |
|
68 | 69 | end = rmatch.group("end") |
|
69 | 70 | |
|
70 | 71 | if sep == '-': |
|
71 | 72 | start = int(start) - 1 if start else None |
|
72 | 73 | end = int(end) if end else None |
|
73 | 74 | elif sep == ':': |
|
74 | 75 | start = int(start) - 1 if start else None |
|
75 | 76 | end = int(end) - 1 if end else None |
|
76 | 77 | else: |
|
77 | 78 | end = int(start) |
|
78 | 79 | start = int(start) - 1 |
|
79 | 80 | yield (start, end) |
|
80 | 81 | |
|
81 | 82 | |
|
82 | 83 | @skip_doctest |
|
83 | 84 | def extract_symbols(code, symbols): |
|
84 | 85 | """ |
|
85 | 86 | Return a tuple (blocks, not_found) |
|
86 | 87 | where ``blocks`` is a list of code fragments |
|
87 | 88 | for each symbol parsed from code, and ``not_found`` are |
|
88 | 89 | symbols not found in the code. |
|
89 | 90 | |
|
90 | 91 | For example:: |
|
91 | 92 | |
|
92 | 93 | >>> code = '''a = 10 |
|
93 | 94 | |
|
94 | 95 | def b(): return 42 |
|
95 | 96 | |
|
96 | 97 | class A: pass''' |
|
97 | 98 | |
|
98 | 99 | >>> extract_symbols(code, 'A,b,z') |
|
99 | 100 | (["class A: pass", "def b(): return 42"], ['z']) |
|
100 | 101 | """ |
|
101 | 102 | symbols = symbols.split(',') |
|
102 | 103 | |
|
103 | 104 | # this will raise SyntaxError if code isn't valid Python |
|
104 | 105 | py_code = ast.parse(code) |
|
105 | 106 | |
|
106 | 107 | marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body] |
|
107 | 108 | code = code.split('\n') |
|
108 | 109 | |
|
109 | 110 | symbols_lines = {} |
|
110 | 111 | |
|
111 | 112 | # we already know the start_lineno of each symbol (marks). |
|
112 | 113 | # To find each end_lineno, we traverse in reverse order until each |
|
113 | 114 | # non-blank line |
|
114 | 115 | end = len(code) |
|
115 | 116 | for name, start in reversed(marks): |
|
116 | 117 | while not code[end - 1].strip(): |
|
117 | 118 | end -= 1 |
|
118 | 119 | if name: |
|
119 | 120 | symbols_lines[name] = (start - 1, end) |
|
120 | 121 | end = start - 1 |
|
121 | 122 | |
|
122 | 123 | # Now symbols_lines is a map |
|
123 | 124 | # {'symbol_name': (start_lineno, end_lineno), ...} |
|
124 | 125 | |
|
125 | 126 | # fill a list with chunks of codes for each requested symbol |
|
126 | 127 | blocks = [] |
|
127 | 128 | not_found = [] |
|
128 | 129 | for symbol in symbols: |
|
129 | 130 | if symbol in symbols_lines: |
|
130 | 131 | start, end = symbols_lines[symbol] |
|
131 | 132 | blocks.append('\n'.join(code[start:end]) + '\n') |
|
132 | 133 | else: |
|
133 | 134 | not_found.append(symbol) |
|
134 | 135 | |
|
135 | 136 | return blocks, not_found |
|
136 | 137 | |
|
137 | 138 | |
|
138 | 139 | class InteractivelyDefined(Exception): |
|
139 | 140 | """Exception for interactively defined variable in magic_edit""" |
|
140 | 141 | def __init__(self, index): |
|
141 | 142 | self.index = index |
|
142 | 143 | |
|
143 | 144 | |
|
144 | 145 | @magics_class |
|
145 | 146 | class CodeMagics(Magics): |
|
146 | 147 | """Magics related to code management (loading, saving, editing, ...).""" |
|
147 | 148 | |
|
148 | 149 | @line_magic |
|
149 | 150 | def save(self, parameter_s=''): |
|
150 | 151 | """Save a set of lines or a macro to a given filename. |
|
151 | 152 | |
|
152 | 153 | Usage:\\ |
|
153 | 154 | %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... |
|
154 | 155 | |
|
155 | 156 | Options: |
|
156 | 157 | |
|
157 | 158 | -r: use 'raw' input. By default, the 'processed' history is used, |
|
158 | 159 | so that magics are loaded in their transformed version to valid |
|
159 | 160 | Python. If this option is given, the raw input as typed as the |
|
160 | 161 | command line is used instead. |
|
161 | 162 | |
|
162 | 163 | -f: force overwrite. If file exists, %save will prompt for overwrite |
|
163 | 164 | unless -f is given. |
|
164 | 165 | |
|
165 | 166 | -a: append to the file instead of overwriting it. |
|
166 | 167 | |
|
167 | 168 | This function uses the same syntax as %history for input ranges, |
|
168 | 169 | then saves the lines to the filename you specify. |
|
169 | 170 | |
|
170 | 171 | It adds a '.py' extension to the file if you don't do so yourself, and |
|
171 | 172 | it asks for confirmation before overwriting existing files. |
|
172 | 173 | |
|
173 | 174 | If `-r` option is used, the default extension is `.ipy`. |
|
174 | 175 | """ |
|
175 | 176 | |
|
176 | 177 | opts,args = self.parse_options(parameter_s,'fra',mode='list') |
|
177 | 178 | if not args: |
|
178 | 179 | raise UsageError('Missing filename.') |
|
179 | 180 | raw = 'r' in opts |
|
180 | 181 | force = 'f' in opts |
|
181 | 182 | append = 'a' in opts |
|
182 | 183 | mode = 'a' if append else 'w' |
|
183 | 184 | ext = u'.ipy' if raw else u'.py' |
|
184 | 185 | fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) |
|
185 | 186 | if not fname.endswith((u'.py',u'.ipy')): |
|
186 | 187 | fname += ext |
|
187 | 188 | file_exists = os.path.isfile(fname) |
|
188 | 189 | if file_exists and not force and not append: |
|
189 | 190 | try: |
|
190 | 191 | overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n') |
|
191 | 192 | except StdinNotImplementedError: |
|
192 |
print |
|
|
193 | print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s)) | |
|
193 | 194 | return |
|
194 | 195 | if not overwrite : |
|
195 |
print |
|
|
196 | print('Operation cancelled.') | |
|
196 | 197 | return |
|
197 | 198 | try: |
|
198 | 199 | cmds = self.shell.find_user_code(codefrom,raw) |
|
199 | 200 | except (TypeError, ValueError) as e: |
|
200 |
print |
|
|
201 | print(e.args[0]) | |
|
201 | 202 | return |
|
202 | 203 | out = py3compat.cast_unicode(cmds) |
|
203 | 204 | with io.open(fname, mode, encoding="utf-8") as f: |
|
204 | 205 | if not file_exists or not append: |
|
205 | 206 | f.write(u"# coding: utf-8\n") |
|
206 | 207 | f.write(out) |
|
207 | 208 | # make sure we end on a newline |
|
208 | 209 | if not out.endswith(u'\n'): |
|
209 | 210 | f.write(u'\n') |
|
210 |
print |
|
|
211 |
print |
|
|
211 | print('The following commands were written to file `%s`:' % fname) | |
|
212 | print(cmds) | |
|
212 | 213 | |
|
213 | 214 | @line_magic |
|
214 | 215 | def pastebin(self, parameter_s=''): |
|
215 | 216 | """Upload code to Github's Gist paste bin, returning the URL. |
|
216 | 217 | |
|
217 | 218 | Usage:\\ |
|
218 | 219 | %pastebin [-d "Custom description"] 1-7 |
|
219 | 220 | |
|
220 | 221 | The argument can be an input history range, a filename, or the name of a |
|
221 | 222 | string or macro. |
|
222 | 223 | |
|
223 | 224 | Options: |
|
224 | 225 | |
|
225 | 226 | -d: Pass a custom description for the gist. The default will say |
|
226 | 227 | "Pasted from IPython". |
|
227 | 228 | """ |
|
228 | 229 | opts, args = self.parse_options(parameter_s, 'd:') |
|
229 | 230 | |
|
230 | 231 | try: |
|
231 | 232 | code = self.shell.find_user_code(args) |
|
232 | 233 | except (ValueError, TypeError) as e: |
|
233 |
print |
|
|
234 | print(e.args[0]) | |
|
234 | 235 | return |
|
235 | 236 | |
|
236 | 237 | from urllib2 import urlopen # Deferred import |
|
237 | 238 | import json |
|
238 | 239 | post_data = json.dumps({ |
|
239 | 240 | "description": opts.get('d', "Pasted from IPython"), |
|
240 | 241 | "public": True, |
|
241 | 242 | "files": { |
|
242 | 243 | "file1.py": { |
|
243 | 244 | "content": code |
|
244 | 245 | } |
|
245 | 246 | } |
|
246 | 247 | }).encode('utf-8') |
|
247 | 248 | |
|
248 | 249 | response = urlopen("https://api.github.com/gists", post_data) |
|
249 | 250 | response_data = json.loads(response.read().decode('utf-8')) |
|
250 | 251 | return response_data['html_url'] |
|
251 | 252 | |
|
252 | 253 | @line_magic |
|
253 | 254 | def loadpy(self, arg_s): |
|
254 | 255 | """Alias of `%load` |
|
255 | 256 | |
|
256 | 257 | `%loadpy` has gained some flexibility and dropped the requirement of a `.py` |
|
257 | 258 | extension. So it has been renamed simply into %load. You can look at |
|
258 | 259 | `%load`'s docstring for more info. |
|
259 | 260 | """ |
|
260 | 261 | self.load(arg_s) |
|
261 | 262 | |
|
262 | 263 | @line_magic |
|
263 | 264 | def load(self, arg_s): |
|
264 | 265 | """Load code into the current frontend. |
|
265 | 266 | |
|
266 | 267 | Usage:\\ |
|
267 | 268 | %load [options] source |
|
268 | 269 | |
|
269 | 270 | where source can be a filename, URL, input history range or macro |
|
270 | 271 | |
|
271 | 272 | Options: |
|
272 | 273 | -------- |
|
273 | 274 | -r <lines>: Specify lines or ranges of lines to load from the source. |
|
274 | 275 | Ranges could be specified as x-y (x..y) or in python-style x:y |
|
275 | 276 | (x..(y-1)). Both limits x and y can be left blank (meaning the |
|
276 | 277 | beginning and end of the file, respectively). |
|
277 | 278 | |
|
278 | 279 | -s <symbols>: Specify function or classes to load from python source. |
|
279 | 280 | |
|
280 | 281 | -y : Don't ask confirmation for loading source above 200 000 characters. |
|
281 | 282 | |
|
282 | 283 | This magic command can either take a local filename, a URL, an history |
|
283 | 284 | range (see %history) or a macro as argument, it will prompt for |
|
284 | 285 | confirmation before loading source with more than 200 000 characters, unless |
|
285 | 286 | -y flag is passed or if the frontend does not support raw_input:: |
|
286 | 287 | |
|
287 | 288 | %load myscript.py |
|
288 | 289 | %load 7-27 |
|
289 | 290 | %load myMacro |
|
290 | 291 | %load http://www.example.com/myscript.py |
|
291 | 292 | %load -r 5-10 myscript.py |
|
292 | 293 | %load -r 10-20,30,40: foo.py |
|
293 | 294 | %load -s MyClass,wonder_function myscript.py |
|
294 | 295 | """ |
|
295 | 296 | opts,args = self.parse_options(arg_s,'ys:r:') |
|
296 | 297 | |
|
297 | 298 | if not args: |
|
298 | 299 | raise UsageError('Missing filename, URL, input history range, ' |
|
299 | 300 | 'or macro.') |
|
300 | 301 | |
|
301 | 302 | contents = self.shell.find_user_code(args) |
|
302 | 303 | |
|
303 | 304 | if 's' in opts: |
|
304 | 305 | try: |
|
305 | 306 | blocks, not_found = extract_symbols(contents, opts['s']) |
|
306 | 307 | except SyntaxError: |
|
307 | 308 | # non python code |
|
308 | 309 | error("Unable to parse the input as valid Python code") |
|
309 | 310 | return |
|
310 | 311 | |
|
311 | 312 | if len(not_found) == 1: |
|
312 | 313 | warn('The symbol `%s` was not found' % not_found[0]) |
|
313 | 314 | elif len(not_found) > 1: |
|
314 | 315 | warn('The symbols %s were not found' % get_text_list(not_found, |
|
315 | 316 | wrap_item_with='`') |
|
316 | 317 | ) |
|
317 | 318 | |
|
318 | 319 | contents = '\n'.join(blocks) |
|
319 | 320 | |
|
320 | 321 | if 'r' in opts: |
|
321 | 322 | ranges = opts['r'].replace(',', ' ') |
|
322 | 323 | lines = contents.split('\n') |
|
323 | 324 | slices = extract_code_ranges(ranges) |
|
324 | 325 | contents = [lines[slice(*slc)] for slc in slices] |
|
325 | 326 | contents = '\n'.join(chain.from_iterable(contents)) |
|
326 | 327 | |
|
327 | 328 | l = len(contents) |
|
328 | 329 | |
|
329 | 330 | # 200 000 is ~ 2500 full 80 caracter lines |
|
330 | 331 | # so in average, more than 5000 lines |
|
331 | 332 | if l > 200000 and 'y' not in opts: |
|
332 | 333 | try: |
|
333 | 334 | ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\ |
|
334 | 335 | " (%d characters). Continue (y/[N]) ?" % l), default='n' ) |
|
335 | 336 | except StdinNotImplementedError: |
|
336 | 337 | #asume yes if raw input not implemented |
|
337 | 338 | ans = True |
|
338 | 339 | |
|
339 | 340 | if ans is False : |
|
340 |
print |
|
|
341 | print('Operation cancelled.') | |
|
341 | 342 | return |
|
342 | 343 | |
|
343 | 344 | self.shell.set_next_input(contents) |
|
344 | 345 | |
|
345 | 346 | @staticmethod |
|
346 | 347 | def _find_edit_target(shell, args, opts, last_call): |
|
347 | 348 | """Utility method used by magic_edit to find what to edit.""" |
|
348 | 349 | |
|
349 | 350 | def make_filename(arg): |
|
350 | 351 | "Make a filename from the given args" |
|
351 | 352 | arg = unquote_filename(arg) |
|
352 | 353 | try: |
|
353 | 354 | filename = get_py_filename(arg) |
|
354 | 355 | except IOError: |
|
355 | 356 | # If it ends with .py but doesn't already exist, assume we want |
|
356 | 357 | # a new file. |
|
357 | 358 | if arg.endswith('.py'): |
|
358 | 359 | filename = arg |
|
359 | 360 | else: |
|
360 | 361 | filename = None |
|
361 | 362 | return filename |
|
362 | 363 | |
|
363 | 364 | # Set a few locals from the options for convenience: |
|
364 | 365 | opts_prev = 'p' in opts |
|
365 | 366 | opts_raw = 'r' in opts |
|
366 | 367 | |
|
367 | 368 | # custom exceptions |
|
368 | 369 | class DataIsObject(Exception): pass |
|
369 | 370 | |
|
370 | 371 | # Default line number value |
|
371 | 372 | lineno = opts.get('n',None) |
|
372 | 373 | |
|
373 | 374 | if opts_prev: |
|
374 | 375 | args = '_%s' % last_call[0] |
|
375 | 376 | if args not in shell.user_ns: |
|
376 | 377 | args = last_call[1] |
|
377 | 378 | |
|
378 | 379 | # by default this is done with temp files, except when the given |
|
379 | 380 | # arg is a filename |
|
380 | 381 | use_temp = True |
|
381 | 382 | |
|
382 | 383 | data = '' |
|
383 | 384 | |
|
384 | 385 | # First, see if the arguments should be a filename. |
|
385 | 386 | filename = make_filename(args) |
|
386 | 387 | if filename: |
|
387 | 388 | use_temp = False |
|
388 | 389 | elif args: |
|
389 | 390 | # Mode where user specifies ranges of lines, like in %macro. |
|
390 | 391 | data = shell.extract_input_lines(args, opts_raw) |
|
391 | 392 | if not data: |
|
392 | 393 | try: |
|
393 | 394 | # Load the parameter given as a variable. If not a string, |
|
394 | 395 | # process it as an object instead (below) |
|
395 | 396 | |
|
396 | 397 | #print '*** args',args,'type',type(args) # dbg |
|
397 | 398 | data = eval(args, shell.user_ns) |
|
398 | 399 | if not isinstance(data, basestring): |
|
399 | 400 | raise DataIsObject |
|
400 | 401 | |
|
401 | 402 | except (NameError,SyntaxError): |
|
402 | 403 | # given argument is not a variable, try as a filename |
|
403 | 404 | filename = make_filename(args) |
|
404 | 405 | if filename is None: |
|
405 | 406 | warn("Argument given (%s) can't be found as a variable " |
|
406 | 407 | "or as a filename." % args) |
|
407 | 408 | return (None, None, None) |
|
408 | 409 | use_temp = False |
|
409 | 410 | |
|
410 | 411 | except DataIsObject: |
|
411 | 412 | # macros have a special edit function |
|
412 | 413 | if isinstance(data, Macro): |
|
413 | 414 | raise MacroToEdit(data) |
|
414 | 415 | |
|
415 | 416 | # For objects, try to edit the file where they are defined |
|
416 | 417 | filename = find_file(data) |
|
417 | 418 | if filename: |
|
418 | 419 | if 'fakemodule' in filename.lower() and \ |
|
419 | 420 | inspect.isclass(data): |
|
420 | 421 | # class created by %edit? Try to find source |
|
421 | 422 | # by looking for method definitions instead, the |
|
422 | 423 | # __module__ in those classes is FakeModule. |
|
423 | 424 | attrs = [getattr(data, aname) for aname in dir(data)] |
|
424 | 425 | for attr in attrs: |
|
425 | 426 | if not inspect.ismethod(attr): |
|
426 | 427 | continue |
|
427 | 428 | filename = find_file(attr) |
|
428 | 429 | if filename and \ |
|
429 | 430 | 'fakemodule' not in filename.lower(): |
|
430 | 431 | # change the attribute to be the edit |
|
431 | 432 | # target instead |
|
432 | 433 | data = attr |
|
433 | 434 | break |
|
434 | 435 | |
|
435 | 436 | m = ipython_input_pat.match(os.path.basename(filename)) |
|
436 | 437 | if m: |
|
437 | 438 | raise InteractivelyDefined(int(m.groups()[0])) |
|
438 | 439 | |
|
439 | 440 | datafile = 1 |
|
440 | 441 | if filename is None: |
|
441 | 442 | filename = make_filename(args) |
|
442 | 443 | datafile = 1 |
|
443 | 444 | if filename is not None: |
|
444 | 445 | # only warn about this if we get a real name |
|
445 | 446 | warn('Could not find file where `%s` is defined.\n' |
|
446 | 447 | 'Opening a file named `%s`' % (args, filename)) |
|
447 | 448 | # Now, make sure we can actually read the source (if it was |
|
448 | 449 | # in a temp file it's gone by now). |
|
449 | 450 | if datafile: |
|
450 | 451 | if lineno is None: |
|
451 | 452 | lineno = find_source_lines(data) |
|
452 | 453 | if lineno is None: |
|
453 | 454 | filename = make_filename(args) |
|
454 | 455 | if filename is None: |
|
455 | 456 | warn('The file where `%s` was defined ' |
|
456 | 457 | 'cannot be read or found.' % data) |
|
457 | 458 | return (None, None, None) |
|
458 | 459 | use_temp = False |
|
459 | 460 | |
|
460 | 461 | if use_temp: |
|
461 | 462 | filename = shell.mktempfile(data) |
|
462 |
print |
|
|
463 | print('IPython will make a temporary file named:',filename) | |
|
463 | 464 | |
|
464 | 465 | # use last_call to remember the state of the previous call, but don't |
|
465 | 466 | # let it be clobbered by successive '-p' calls. |
|
466 | 467 | try: |
|
467 | 468 | last_call[0] = shell.displayhook.prompt_count |
|
468 | 469 | if not opts_prev: |
|
469 | 470 | last_call[1] = args |
|
470 | 471 | except: |
|
471 | 472 | pass |
|
472 | 473 | |
|
473 | 474 | |
|
474 | 475 | return filename, lineno, use_temp |
|
475 | 476 | |
|
476 | 477 | def _edit_macro(self,mname,macro): |
|
477 | 478 | """open an editor with the macro data in a file""" |
|
478 | 479 | filename = self.shell.mktempfile(macro.value) |
|
479 | 480 | self.shell.hooks.editor(filename) |
|
480 | 481 | |
|
481 | 482 | # and make a new macro object, to replace the old one |
|
482 | 483 | with open(filename) as mfile: |
|
483 | 484 | mvalue = mfile.read() |
|
484 | 485 | self.shell.user_ns[mname] = Macro(mvalue) |
|
485 | 486 | |
|
486 | 487 | @skip_doctest |
|
487 | 488 | @line_magic |
|
488 | 489 | def edit(self, parameter_s='',last_call=['','']): |
|
489 | 490 | """Bring up an editor and execute the resulting code. |
|
490 | 491 | |
|
491 | 492 | Usage: |
|
492 | 493 | %edit [options] [args] |
|
493 | 494 | |
|
494 | 495 | %edit runs IPython's editor hook. The default version of this hook is |
|
495 | 496 | set to call the editor specified by your $EDITOR environment variable. |
|
496 | 497 | If this isn't found, it will default to vi under Linux/Unix and to |
|
497 | 498 | notepad under Windows. See the end of this docstring for how to change |
|
498 | 499 | the editor hook. |
|
499 | 500 | |
|
500 | 501 | You can also set the value of this editor via the |
|
501 | 502 | ``TerminalInteractiveShell.editor`` option in your configuration file. |
|
502 | 503 | This is useful if you wish to use a different editor from your typical |
|
503 | 504 | default with IPython (and for Windows users who typically don't set |
|
504 | 505 | environment variables). |
|
505 | 506 | |
|
506 | 507 | This command allows you to conveniently edit multi-line code right in |
|
507 | 508 | your IPython session. |
|
508 | 509 | |
|
509 | 510 | If called without arguments, %edit opens up an empty editor with a |
|
510 | 511 | temporary file and will execute the contents of this file when you |
|
511 | 512 | close it (don't forget to save it!). |
|
512 | 513 | |
|
513 | 514 | |
|
514 | 515 | Options: |
|
515 | 516 | |
|
516 | 517 | -n <number>: open the editor at a specified line number. By default, |
|
517 | 518 | the IPython editor hook uses the unix syntax 'editor +N filename', but |
|
518 | 519 | you can configure this by providing your own modified hook if your |
|
519 | 520 | favorite editor supports line-number specifications with a different |
|
520 | 521 | syntax. |
|
521 | 522 | |
|
522 | 523 | -p: this will call the editor with the same data as the previous time |
|
523 | 524 | it was used, regardless of how long ago (in your current session) it |
|
524 | 525 | was. |
|
525 | 526 | |
|
526 | 527 | -r: use 'raw' input. This option only applies to input taken from the |
|
527 | 528 | user's history. By default, the 'processed' history is used, so that |
|
528 | 529 | magics are loaded in their transformed version to valid Python. If |
|
529 | 530 | this option is given, the raw input as typed as the command line is |
|
530 | 531 | used instead. When you exit the editor, it will be executed by |
|
531 | 532 | IPython's own processor. |
|
532 | 533 | |
|
533 | 534 | -x: do not execute the edited code immediately upon exit. This is |
|
534 | 535 | mainly useful if you are editing programs which need to be called with |
|
535 | 536 | command line arguments, which you can then do using %run. |
|
536 | 537 | |
|
537 | 538 | |
|
538 | 539 | Arguments: |
|
539 | 540 | |
|
540 | 541 | If arguments are given, the following possibilities exist: |
|
541 | 542 | |
|
542 | 543 | - If the argument is a filename, IPython will load that into the |
|
543 | 544 | editor. It will execute its contents with execfile() when you exit, |
|
544 | 545 | loading any code in the file into your interactive namespace. |
|
545 | 546 | |
|
546 | 547 | - The arguments are ranges of input history, e.g. "7 ~1/4-6". |
|
547 | 548 | The syntax is the same as in the %history magic. |
|
548 | 549 | |
|
549 | 550 | - If the argument is a string variable, its contents are loaded |
|
550 | 551 | into the editor. You can thus edit any string which contains |
|
551 | 552 | python code (including the result of previous edits). |
|
552 | 553 | |
|
553 | 554 | - If the argument is the name of an object (other than a string), |
|
554 | 555 | IPython will try to locate the file where it was defined and open the |
|
555 | 556 | editor at the point where it is defined. You can use `%edit function` |
|
556 | 557 | to load an editor exactly at the point where 'function' is defined, |
|
557 | 558 | edit it and have the file be executed automatically. |
|
558 | 559 | |
|
559 | 560 | - If the object is a macro (see %macro for details), this opens up your |
|
560 | 561 | specified editor with a temporary file containing the macro's data. |
|
561 | 562 | Upon exit, the macro is reloaded with the contents of the file. |
|
562 | 563 | |
|
563 | 564 | Note: opening at an exact line is only supported under Unix, and some |
|
564 | 565 | editors (like kedit and gedit up to Gnome 2.8) do not understand the |
|
565 | 566 | '+NUMBER' parameter necessary for this feature. Good editors like |
|
566 | 567 | (X)Emacs, vi, jed, pico and joe all do. |
|
567 | 568 | |
|
568 | 569 | After executing your code, %edit will return as output the code you |
|
569 | 570 | typed in the editor (except when it was an existing file). This way |
|
570 | 571 | you can reload the code in further invocations of %edit as a variable, |
|
571 | 572 | via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of |
|
572 | 573 | the output. |
|
573 | 574 | |
|
574 | 575 | Note that %edit is also available through the alias %ed. |
|
575 | 576 | |
|
576 | 577 | This is an example of creating a simple function inside the editor and |
|
577 | 578 | then modifying it. First, start up the editor:: |
|
578 | 579 | |
|
579 | 580 | In [1]: edit |
|
580 | 581 | Editing... done. Executing edited code... |
|
581 | 582 | Out[1]: 'def foo():\\n print "foo() was defined in an editing |
|
582 | 583 | session"\\n' |
|
583 | 584 | |
|
584 | 585 | We can then call the function foo():: |
|
585 | 586 | |
|
586 | 587 | In [2]: foo() |
|
587 | 588 | foo() was defined in an editing session |
|
588 | 589 | |
|
589 | 590 | Now we edit foo. IPython automatically loads the editor with the |
|
590 | 591 | (temporary) file where foo() was previously defined:: |
|
591 | 592 | |
|
592 | 593 | In [3]: edit foo |
|
593 | 594 | Editing... done. Executing edited code... |
|
594 | 595 | |
|
595 | 596 | And if we call foo() again we get the modified version:: |
|
596 | 597 | |
|
597 | 598 | In [4]: foo() |
|
598 | 599 | foo() has now been changed! |
|
599 | 600 | |
|
600 | 601 | Here is an example of how to edit a code snippet successive |
|
601 | 602 | times. First we call the editor:: |
|
602 | 603 | |
|
603 | 604 | In [5]: edit |
|
604 | 605 | Editing... done. Executing edited code... |
|
605 | 606 | hello |
|
606 | 607 | Out[5]: "print 'hello'\\n" |
|
607 | 608 | |
|
608 | 609 | Now we call it again with the previous output (stored in _):: |
|
609 | 610 | |
|
610 | 611 | In [6]: edit _ |
|
611 | 612 | Editing... done. Executing edited code... |
|
612 | 613 | hello world |
|
613 | 614 | Out[6]: "print 'hello world'\\n" |
|
614 | 615 | |
|
615 | 616 | Now we call it with the output #8 (stored in _8, also as Out[8]):: |
|
616 | 617 | |
|
617 | 618 | In [7]: edit _8 |
|
618 | 619 | Editing... done. Executing edited code... |
|
619 | 620 | hello again |
|
620 | 621 | Out[7]: "print 'hello again'\\n" |
|
621 | 622 | |
|
622 | 623 | |
|
623 | 624 | Changing the default editor hook: |
|
624 | 625 | |
|
625 | 626 | If you wish to write your own editor hook, you can put it in a |
|
626 | 627 | configuration file which you load at startup time. The default hook |
|
627 | 628 | is defined in the IPython.core.hooks module, and you can use that as a |
|
628 | 629 | starting example for further modifications. That file also has |
|
629 | 630 | general instructions on how to set a new hook for use once you've |
|
630 | 631 | defined it.""" |
|
631 | 632 | opts,args = self.parse_options(parameter_s,'prxn:') |
|
632 | 633 | |
|
633 | 634 | try: |
|
634 | 635 | filename, lineno, is_temp = self._find_edit_target(self.shell, |
|
635 | 636 | args, opts, last_call) |
|
636 | 637 | except MacroToEdit as e: |
|
637 | 638 | self._edit_macro(args, e.args[0]) |
|
638 | 639 | return |
|
639 | 640 | except InteractivelyDefined as e: |
|
640 |
print |
|
|
641 | print("Editing In[%i]" % e.index) | |
|
641 | 642 | args = str(e.index) |
|
642 | 643 | filename, lineno, is_temp = self._find_edit_target(self.shell, |
|
643 | 644 | args, opts, last_call) |
|
644 | 645 | if filename is None: |
|
645 | 646 | # nothing was found, warnings have already been issued, |
|
646 | 647 | # just give up. |
|
647 | 648 | return |
|
648 | 649 | |
|
649 | 650 | # do actual editing here |
|
650 |
print |
|
|
651 | print('Editing...', end=' ') | |
|
651 | 652 | sys.stdout.flush() |
|
652 | 653 | try: |
|
653 | 654 | # Quote filenames that may have spaces in them |
|
654 | 655 | if ' ' in filename: |
|
655 | 656 | filename = "'%s'" % filename |
|
656 | 657 | self.shell.hooks.editor(filename,lineno) |
|
657 | 658 | except TryNext: |
|
658 | 659 | warn('Could not open editor') |
|
659 | 660 | return |
|
660 | 661 | |
|
661 | 662 | # XXX TODO: should this be generalized for all string vars? |
|
662 | 663 | # For now, this is special-cased to blocks created by cpaste |
|
663 | 664 | if args.strip() == 'pasted_block': |
|
664 | 665 | with open(filename, 'r') as f: |
|
665 | 666 | self.shell.user_ns['pasted_block'] = f.read() |
|
666 | 667 | |
|
667 | 668 | if 'x' in opts: # -x prevents actual execution |
|
668 | ||
|
669 | print() | |
|
669 | 670 | else: |
|
670 |
print |
|
|
671 | print('done. Executing edited code...') | |
|
671 | 672 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
672 | 673 | if not is_temp: |
|
673 | 674 | self.shell.user_ns['__file__'] = filename |
|
674 | 675 | if 'r' in opts: # Untranslated IPython code |
|
675 | 676 | with open(filename, 'r') as f: |
|
676 | 677 | source = f.read() |
|
677 | 678 | self.shell.run_cell(source, store_history=False) |
|
678 | 679 | else: |
|
679 | 680 | self.shell.safe_execfile(filename, self.shell.user_ns, |
|
680 | 681 | self.shell.user_ns) |
|
681 | 682 | |
|
682 | 683 | if is_temp: |
|
683 | 684 | try: |
|
684 | 685 | return open(filename).read() |
|
685 | 686 | except IOError as msg: |
|
686 | 687 | if msg.filename == filename: |
|
687 | 688 | warn('File not found. Did you forget to save?') |
|
688 | 689 | return |
|
689 | 690 | else: |
|
690 | 691 | self.shell.showtraceback() |
@@ -1,158 +1,159 b'' | |||
|
1 | 1 | """Implementation of configuration-related magic functions. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Stdlib |
|
16 | 17 | import re |
|
17 | 18 | |
|
18 | 19 | # Our own packages |
|
19 | 20 | from IPython.core.error import UsageError |
|
20 | 21 | from IPython.core.magic import Magics, magics_class, line_magic |
|
21 | 22 | from IPython.utils.warn import error |
|
22 | 23 | |
|
23 | 24 | #----------------------------------------------------------------------------- |
|
24 | 25 | # Magic implementation classes |
|
25 | 26 | #----------------------------------------------------------------------------- |
|
26 | 27 | |
|
27 | 28 | reg = re.compile('^\w+\.\w+$') |
|
28 | 29 | @magics_class |
|
29 | 30 | class ConfigMagics(Magics): |
|
30 | 31 | |
|
31 | 32 | def __init__(self, shell): |
|
32 | 33 | super(ConfigMagics, self).__init__(shell) |
|
33 | 34 | self.configurables = [] |
|
34 | 35 | |
|
35 | 36 | @line_magic |
|
36 | 37 | def config(self, s): |
|
37 | 38 | """configure IPython |
|
38 | 39 | |
|
39 | 40 | %config Class[.trait=value] |
|
40 | 41 | |
|
41 | 42 | This magic exposes most of the IPython config system. Any |
|
42 | 43 | Configurable class should be able to be configured with the simple |
|
43 | 44 | line:: |
|
44 | 45 | |
|
45 | 46 | %config Class.trait=value |
|
46 | 47 | |
|
47 | 48 | Where `value` will be resolved in the user's namespace, if it is an |
|
48 | 49 | expression or variable name. |
|
49 | 50 | |
|
50 | 51 | Examples |
|
51 | 52 | -------- |
|
52 | 53 | |
|
53 | 54 | To see what classes are available for config, pass no arguments:: |
|
54 | 55 | |
|
55 | 56 | In [1]: %config |
|
56 | 57 | Available objects for config: |
|
57 | 58 | TerminalInteractiveShell |
|
58 | 59 | HistoryManager |
|
59 | 60 | PrefilterManager |
|
60 | 61 | AliasManager |
|
61 | 62 | IPCompleter |
|
62 | 63 | PromptManager |
|
63 | 64 | DisplayFormatter |
|
64 | 65 | |
|
65 | 66 | To view what is configurable on a given class, just pass the class |
|
66 | 67 | name:: |
|
67 | 68 | |
|
68 | 69 | In [2]: %config IPCompleter |
|
69 | 70 | IPCompleter options |
|
70 | 71 | ----------------- |
|
71 | 72 | IPCompleter.omit__names=<Enum> |
|
72 | 73 | Current: 2 |
|
73 | 74 | Choices: (0, 1, 2) |
|
74 | 75 | Instruct the completer to omit private method names |
|
75 | 76 | Specifically, when completing on ``object.<tab>``. |
|
76 | 77 | When 2 [default]: all names that start with '_' will be excluded. |
|
77 | 78 | When 1: all 'magic' names (``__foo__``) will be excluded. |
|
78 | 79 | When 0: nothing will be excluded. |
|
79 | 80 | IPCompleter.merge_completions=<CBool> |
|
80 | 81 | Current: True |
|
81 | 82 | Whether to merge completion results into a single list |
|
82 | 83 | If False, only the completion results from the first non-empty |
|
83 | 84 | completer will be returned. |
|
84 | 85 | IPCompleter.limit_to__all__=<CBool> |
|
85 | 86 | Current: False |
|
86 | 87 | Instruct the completer to use __all__ for the completion |
|
87 | 88 | Specifically, when completing on ``object.<tab>``. |
|
88 | 89 | When True: only those names in obj.__all__ will be included. |
|
89 | 90 | When False [default]: the __all__ attribute is ignored |
|
90 | 91 | IPCompleter.greedy=<CBool> |
|
91 | 92 | Current: False |
|
92 | 93 | Activate greedy completion |
|
93 | 94 | This will enable completion on elements of lists, results of |
|
94 | 95 | function calls, etc., but can be unsafe because the code is |
|
95 | 96 | actually evaluated on TAB. |
|
96 | 97 | |
|
97 | 98 | but the real use is in setting values:: |
|
98 | 99 | |
|
99 | 100 | In [3]: %config IPCompleter.greedy = True |
|
100 | 101 | |
|
101 | 102 | and these values are read from the user_ns if they are variables:: |
|
102 | 103 | |
|
103 | 104 | In [4]: feeling_greedy=False |
|
104 | 105 | |
|
105 | 106 | In [5]: %config IPCompleter.greedy = feeling_greedy |
|
106 | 107 | |
|
107 | 108 | """ |
|
108 | 109 | from IPython.config.loader import Config |
|
109 | 110 | # some IPython objects are Configurable, but do not yet have |
|
110 | 111 | # any configurable traits. Exclude them from the effects of |
|
111 | 112 | # this magic, as their presence is just noise: |
|
112 | 113 | configurables = [ c for c in self.shell.configurables |
|
113 | 114 | if c.__class__.class_traits(config=True) ] |
|
114 | 115 | classnames = [ c.__class__.__name__ for c in configurables ] |
|
115 | 116 | |
|
116 | 117 | line = s.strip() |
|
117 | 118 | if not line: |
|
118 | 119 | # print available configurable names |
|
119 |
print |
|
|
120 | print("Available objects for config:") | |
|
120 | 121 | for name in classnames: |
|
121 |
print |
|
|
122 | print(" ", name) | |
|
122 | 123 | return |
|
123 | 124 | elif line in classnames: |
|
124 | 125 | # `%config TerminalInteractiveShell` will print trait info for |
|
125 | 126 | # TerminalInteractiveShell |
|
126 | 127 | c = configurables[classnames.index(line)] |
|
127 | 128 | cls = c.__class__ |
|
128 | 129 | help = cls.class_get_help(c) |
|
129 | 130 | # strip leading '--' from cl-args: |
|
130 | 131 | help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) |
|
131 |
print |
|
|
132 | print(help) | |
|
132 | 133 | return |
|
133 | 134 | elif reg.match(line): |
|
134 | 135 | cls, attr = line.split('.') |
|
135 | 136 | return getattr(configurables[classnames.index(cls)],attr) |
|
136 | 137 | elif '=' not in line: |
|
137 | 138 | msg = "Invalid config statement: %r, "\ |
|
138 | 139 | "should be `Class.trait = value`." |
|
139 | 140 | |
|
140 | 141 | ll = line.lower() |
|
141 | 142 | for classname in classnames: |
|
142 | 143 | if ll == classname.lower(): |
|
143 | 144 | msg = msg + '\nDid you mean %s (note the case)?' % classname |
|
144 | 145 | break |
|
145 | 146 | |
|
146 | 147 | raise UsageError( msg % line) |
|
147 | 148 | |
|
148 | 149 | # otherwise, assume we are setting configurables. |
|
149 | 150 | # leave quotes on args when splitting, because we want |
|
150 | 151 | # unquoted args to eval in user_ns |
|
151 | 152 | cfg = Config() |
|
152 | 153 | exec "cfg."+line in locals(), self.shell.user_ns |
|
153 | 154 | |
|
154 | 155 | for configurable in configurables: |
|
155 | 156 | try: |
|
156 | 157 | configurable.update_config(cfg) |
|
157 | 158 | except Exception as e: |
|
158 | 159 | error(e) |
@@ -1,45 +1,46 b'' | |||
|
1 | 1 | """Deprecated Magic functions. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Our own packages |
|
16 | 17 | from IPython.core.magic import Magics, magics_class, line_magic |
|
17 | 18 | |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | # Magic implementation classes |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | |
|
22 | 23 | @magics_class |
|
23 | 24 | class DeprecatedMagics(Magics): |
|
24 | 25 | """Magics slated for later removal.""" |
|
25 | 26 | |
|
26 | 27 | @line_magic |
|
27 | 28 | def install_profiles(self, parameter_s=''): |
|
28 | 29 | """%install_profiles has been deprecated.""" |
|
29 |
print |
|
|
30 | print('\n'.join([ | |
|
30 | 31 | "%install_profiles has been deprecated.", |
|
31 | 32 | "Use `ipython profile list` to view available profiles.", |
|
32 | 33 | "Requesting a profile with `ipython profile create <name>`", |
|
33 | 34 | "or `ipython --profile=<name>` will start with the bundled", |
|
34 | 35 | "profile of that name if it exists." |
|
35 | ]) | |
|
36 | ])) | |
|
36 | 37 | |
|
37 | 38 | @line_magic |
|
38 | 39 | def install_default_config(self, parameter_s=''): |
|
39 | 40 | """%install_default_config has been deprecated.""" |
|
40 |
print |
|
|
41 | print('\n'.join([ | |
|
41 | 42 | "%install_default_config has been deprecated.", |
|
42 | 43 | "Use `ipython profile create <name>` to initialize a profile", |
|
43 | 44 | "with the default config files.", |
|
44 | 45 | "Add `--reset` to overwrite already existing config files with defaults." |
|
45 | ]) | |
|
46 | ])) |
@@ -1,1287 +1,1288 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Implementation of execution-related magic functions. |
|
3 | 3 | """ |
|
4 | from __future__ import print_function | |
|
4 | 5 | #----------------------------------------------------------------------------- |
|
5 | 6 | # Copyright (c) 2012 The IPython Development Team. |
|
6 | 7 | # |
|
7 | 8 | # Distributed under the terms of the Modified BSD License. |
|
8 | 9 | # |
|
9 | 10 | # The full license is in the file COPYING.txt, distributed with this software. |
|
10 | 11 | #----------------------------------------------------------------------------- |
|
11 | 12 | |
|
12 | 13 | #----------------------------------------------------------------------------- |
|
13 | 14 | # Imports |
|
14 | 15 | #----------------------------------------------------------------------------- |
|
15 | 16 | |
|
16 | 17 | # Stdlib |
|
17 | 18 | import __builtin__ as builtin_mod |
|
18 | 19 | import ast |
|
19 | 20 | import bdb |
|
20 | 21 | import os |
|
21 | 22 | import sys |
|
22 | 23 | import time |
|
23 | 24 | from StringIO import StringIO |
|
24 | 25 | |
|
25 | 26 | # cProfile was added in Python2.5 |
|
26 | 27 | try: |
|
27 | 28 | import cProfile as profile |
|
28 | 29 | import pstats |
|
29 | 30 | except ImportError: |
|
30 | 31 | # profile isn't bundled by default in Debian for license reasons |
|
31 | 32 | try: |
|
32 | 33 | import profile, pstats |
|
33 | 34 | except ImportError: |
|
34 | 35 | profile = pstats = None |
|
35 | 36 | |
|
36 | 37 | # Our own packages |
|
37 | 38 | from IPython.core import debugger, oinspect |
|
38 | 39 | from IPython.core import magic_arguments |
|
39 | 40 | from IPython.core import page |
|
40 | 41 | from IPython.core.error import UsageError |
|
41 | 42 | from IPython.core.macro import Macro |
|
42 | 43 | from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic, |
|
43 | 44 | line_cell_magic, on_off, needs_local_scope) |
|
44 | 45 | from IPython.testing.skipdoctest import skip_doctest |
|
45 | 46 | from IPython.utils import py3compat |
|
46 | 47 | from IPython.utils.contexts import preserve_keys |
|
47 | 48 | from IPython.utils.io import capture_output |
|
48 | 49 | from IPython.utils.ipstruct import Struct |
|
49 | 50 | from IPython.utils.module_paths import find_mod |
|
50 | 51 | from IPython.utils.path import get_py_filename, unquote_filename, shellglob |
|
51 | 52 | from IPython.utils.timing import clock, clock2 |
|
52 | 53 | from IPython.utils.warn import warn, error |
|
53 | 54 | |
|
54 | 55 | |
|
55 | 56 | #----------------------------------------------------------------------------- |
|
56 | 57 | # Magic implementation classes |
|
57 | 58 | #----------------------------------------------------------------------------- |
|
58 | 59 | |
|
59 | 60 | |
|
60 | 61 | class TimeitResult(object): |
|
61 | 62 | """ |
|
62 | 63 | Object returned by the timeit magic with info about the run. |
|
63 | 64 | |
|
64 | 65 | Contain the following attributes : |
|
65 | 66 | |
|
66 | 67 | loops: (int) number of loop done per measurement |
|
67 | 68 | repeat: (int) number of time the mesurement has been repeated |
|
68 | 69 | best: (float) best execusion time / number |
|
69 | 70 | all_runs: (list of float) execusion time of each run (in s) |
|
70 | 71 | compile_time: (float) time of statement compilation (s) |
|
71 | 72 | |
|
72 | 73 | """ |
|
73 | 74 | |
|
74 | 75 | def __init__(self, loops, repeat, best, all_runs, compile_time, precision): |
|
75 | 76 | self.loops = loops |
|
76 | 77 | self.repeat = repeat |
|
77 | 78 | self.best = best |
|
78 | 79 | self.all_runs = all_runs |
|
79 | 80 | self.compile_time = compile_time |
|
80 | 81 | self._precision = precision |
|
81 | 82 | |
|
82 | 83 | def _repr_pretty_(self, p , cycle): |
|
83 | 84 | unic = u"%d loops, best of %d: %s per loop" % (self.loops, self.repeat, |
|
84 | 85 | _format_time(self.best, self._precision)) |
|
85 | 86 | p.text(u'<TimeitResult : '+unic+u'>') |
|
86 | 87 | |
|
87 | 88 | |
|
88 | 89 | |
|
89 | 90 | |
|
90 | 91 | @magics_class |
|
91 | 92 | class ExecutionMagics(Magics): |
|
92 | 93 | """Magics related to code execution, debugging, profiling, etc. |
|
93 | 94 | |
|
94 | 95 | """ |
|
95 | 96 | |
|
96 | 97 | def __init__(self, shell): |
|
97 | 98 | super(ExecutionMagics, self).__init__(shell) |
|
98 | 99 | if profile is None: |
|
99 | 100 | self.prun = self.profile_missing_notice |
|
100 | 101 | # Default execution function used to actually run user code. |
|
101 | 102 | self.default_runner = None |
|
102 | 103 | |
|
103 | 104 | def profile_missing_notice(self, *args, **kwargs): |
|
104 | 105 | error("""\ |
|
105 | 106 | The profile module could not be found. It has been removed from the standard |
|
106 | 107 | python packages because of its non-free license. To use profiling, install the |
|
107 | 108 | python-profiler package from non-free.""") |
|
108 | 109 | |
|
109 | 110 | @skip_doctest |
|
110 | 111 | @line_cell_magic |
|
111 | 112 | def prun(self, parameter_s='', cell=None): |
|
112 | 113 | |
|
113 | 114 | """Run a statement through the python code profiler. |
|
114 | 115 | |
|
115 | 116 | Usage, in line mode: |
|
116 | 117 | %prun [options] statement |
|
117 | 118 | |
|
118 | 119 | Usage, in cell mode: |
|
119 | 120 | %%prun [options] [statement] |
|
120 | 121 | code... |
|
121 | 122 | code... |
|
122 | 123 | |
|
123 | 124 | In cell mode, the additional code lines are appended to the (possibly |
|
124 | 125 | empty) statement in the first line. Cell mode allows you to easily |
|
125 | 126 | profile multiline blocks without having to put them in a separate |
|
126 | 127 | function. |
|
127 | 128 | |
|
128 | 129 | The given statement (which doesn't require quote marks) is run via the |
|
129 | 130 | python profiler in a manner similar to the profile.run() function. |
|
130 | 131 | Namespaces are internally managed to work correctly; profile.run |
|
131 | 132 | cannot be used in IPython because it makes certain assumptions about |
|
132 | 133 | namespaces which do not hold under IPython. |
|
133 | 134 | |
|
134 | 135 | Options: |
|
135 | 136 | |
|
136 | 137 | -l <limit> |
|
137 | 138 | you can place restrictions on what or how much of the |
|
138 | 139 | profile gets printed. The limit value can be: |
|
139 | 140 | |
|
140 | 141 | * A string: only information for function names containing this string |
|
141 | 142 | is printed. |
|
142 | 143 | |
|
143 | 144 | * An integer: only these many lines are printed. |
|
144 | 145 | |
|
145 | 146 | * A float (between 0 and 1): this fraction of the report is printed |
|
146 | 147 | (for example, use a limit of 0.4 to see the topmost 40% only). |
|
147 | 148 | |
|
148 | 149 | You can combine several limits with repeated use of the option. For |
|
149 | 150 | example, ``-l __init__ -l 5`` will print only the topmost 5 lines of |
|
150 | 151 | information about class constructors. |
|
151 | 152 | |
|
152 | 153 | -r |
|
153 | 154 | return the pstats.Stats object generated by the profiling. This |
|
154 | 155 | object has all the information about the profile in it, and you can |
|
155 | 156 | later use it for further analysis or in other functions. |
|
156 | 157 | |
|
157 | 158 | -s <key> |
|
158 | 159 | sort profile by given key. You can provide more than one key |
|
159 | 160 | by using the option several times: '-s key1 -s key2 -s key3...'. The |
|
160 | 161 | default sorting key is 'time'. |
|
161 | 162 | |
|
162 | 163 | The following is copied verbatim from the profile documentation |
|
163 | 164 | referenced below: |
|
164 | 165 | |
|
165 | 166 | When more than one key is provided, additional keys are used as |
|
166 | 167 | secondary criteria when the there is equality in all keys selected |
|
167 | 168 | before them. |
|
168 | 169 | |
|
169 | 170 | Abbreviations can be used for any key names, as long as the |
|
170 | 171 | abbreviation is unambiguous. The following are the keys currently |
|
171 | 172 | defined: |
|
172 | 173 | |
|
173 | 174 | ============ ===================== |
|
174 | 175 | Valid Arg Meaning |
|
175 | 176 | ============ ===================== |
|
176 | 177 | "calls" call count |
|
177 | 178 | "cumulative" cumulative time |
|
178 | 179 | "file" file name |
|
179 | 180 | "module" file name |
|
180 | 181 | "pcalls" primitive call count |
|
181 | 182 | "line" line number |
|
182 | 183 | "name" function name |
|
183 | 184 | "nfl" name/file/line |
|
184 | 185 | "stdname" standard name |
|
185 | 186 | "time" internal time |
|
186 | 187 | ============ ===================== |
|
187 | 188 | |
|
188 | 189 | Note that all sorts on statistics are in descending order (placing |
|
189 | 190 | most time consuming items first), where as name, file, and line number |
|
190 | 191 | searches are in ascending order (i.e., alphabetical). The subtle |
|
191 | 192 | distinction between "nfl" and "stdname" is that the standard name is a |
|
192 | 193 | sort of the name as printed, which means that the embedded line |
|
193 | 194 | numbers get compared in an odd way. For example, lines 3, 20, and 40 |
|
194 | 195 | would (if the file names were the same) appear in the string order |
|
195 | 196 | "20" "3" and "40". In contrast, "nfl" does a numeric compare of the |
|
196 | 197 | line numbers. In fact, sort_stats("nfl") is the same as |
|
197 | 198 | sort_stats("name", "file", "line"). |
|
198 | 199 | |
|
199 | 200 | -T <filename> |
|
200 | 201 | save profile results as shown on screen to a text |
|
201 | 202 | file. The profile is still shown on screen. |
|
202 | 203 | |
|
203 | 204 | -D <filename> |
|
204 | 205 | save (via dump_stats) profile statistics to given |
|
205 | 206 | filename. This data is in a format understood by the pstats module, and |
|
206 | 207 | is generated by a call to the dump_stats() method of profile |
|
207 | 208 | objects. The profile is still shown on screen. |
|
208 | 209 | |
|
209 | 210 | -q |
|
210 | 211 | suppress output to the pager. Best used with -T and/or -D above. |
|
211 | 212 | |
|
212 | 213 | If you want to run complete programs under the profiler's control, use |
|
213 | 214 | ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts |
|
214 | 215 | contains profiler specific options as described here. |
|
215 | 216 | |
|
216 | 217 | You can read the complete documentation for the profile module with:: |
|
217 | 218 | |
|
218 | 219 | In [1]: import profile; profile.help() |
|
219 | 220 | """ |
|
220 | 221 | opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q', |
|
221 | 222 | list_all=True, posix=False) |
|
222 | 223 | if cell is not None: |
|
223 | 224 | arg_str += '\n' + cell |
|
224 | 225 | arg_str = self.shell.input_splitter.transform_cell(arg_str) |
|
225 | 226 | return self._run_with_profiler(arg_str, opts, self.shell.user_ns) |
|
226 | 227 | |
|
227 | 228 | def _run_with_profiler(self, code, opts, namespace): |
|
228 | 229 | """ |
|
229 | 230 | Run `code` with profiler. Used by ``%prun`` and ``%run -p``. |
|
230 | 231 | |
|
231 | 232 | Parameters |
|
232 | 233 | ---------- |
|
233 | 234 | code : str |
|
234 | 235 | Code to be executed. |
|
235 | 236 | opts : Struct |
|
236 | 237 | Options parsed by `self.parse_options`. |
|
237 | 238 | namespace : dict |
|
238 | 239 | A dictionary for Python namespace (e.g., `self.shell.user_ns`). |
|
239 | 240 | |
|
240 | 241 | """ |
|
241 | 242 | |
|
242 | 243 | # Fill default values for unspecified options: |
|
243 | 244 | opts.merge(Struct(D=[''], l=[], s=['time'], T=[''])) |
|
244 | 245 | |
|
245 | 246 | prof = profile.Profile() |
|
246 | 247 | try: |
|
247 | 248 | prof = prof.runctx(code, namespace, namespace) |
|
248 | 249 | sys_exit = '' |
|
249 | 250 | except SystemExit: |
|
250 | 251 | sys_exit = """*** SystemExit exception caught in code being profiled.""" |
|
251 | 252 | |
|
252 | 253 | stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) |
|
253 | 254 | |
|
254 | 255 | lims = opts.l |
|
255 | 256 | if lims: |
|
256 | 257 | lims = [] # rebuild lims with ints/floats/strings |
|
257 | 258 | for lim in opts.l: |
|
258 | 259 | try: |
|
259 | 260 | lims.append(int(lim)) |
|
260 | 261 | except ValueError: |
|
261 | 262 | try: |
|
262 | 263 | lims.append(float(lim)) |
|
263 | 264 | except ValueError: |
|
264 | 265 | lims.append(lim) |
|
265 | 266 | |
|
266 | 267 | # Trap output. |
|
267 | 268 | stdout_trap = StringIO() |
|
268 | 269 | stats_stream = stats.stream |
|
269 | 270 | try: |
|
270 | 271 | stats.stream = stdout_trap |
|
271 | 272 | stats.print_stats(*lims) |
|
272 | 273 | finally: |
|
273 | 274 | stats.stream = stats_stream |
|
274 | 275 | |
|
275 | 276 | output = stdout_trap.getvalue() |
|
276 | 277 | output = output.rstrip() |
|
277 | 278 | |
|
278 | 279 | if 'q' not in opts: |
|
279 | 280 | page.page(output) |
|
280 |
print |
|
|
281 | print(sys_exit, end=' ') | |
|
281 | 282 | |
|
282 | 283 | dump_file = opts.D[0] |
|
283 | 284 | text_file = opts.T[0] |
|
284 | 285 | if dump_file: |
|
285 | 286 | dump_file = unquote_filename(dump_file) |
|
286 | 287 | prof.dump_stats(dump_file) |
|
287 |
print |
|
|
288 | repr(dump_file)+'.',sys_exit | |
|
288 | print('\n*** Profile stats marshalled to file',\ | |
|
289 | repr(dump_file)+'.',sys_exit) | |
|
289 | 290 | if text_file: |
|
290 | 291 | text_file = unquote_filename(text_file) |
|
291 | 292 | pfile = open(text_file,'w') |
|
292 | 293 | pfile.write(output) |
|
293 | 294 | pfile.close() |
|
294 |
print |
|
|
295 | repr(text_file)+'.',sys_exit | |
|
295 | print('\n*** Profile printout saved to text file',\ | |
|
296 | repr(text_file)+'.',sys_exit) | |
|
296 | 297 | |
|
297 | 298 | if 'r' in opts: |
|
298 | 299 | return stats |
|
299 | 300 | else: |
|
300 | 301 | return None |
|
301 | 302 | |
|
302 | 303 | @line_magic |
|
303 | 304 | def pdb(self, parameter_s=''): |
|
304 | 305 | """Control the automatic calling of the pdb interactive debugger. |
|
305 | 306 | |
|
306 | 307 | Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without |
|
307 | 308 | argument it works as a toggle. |
|
308 | 309 | |
|
309 | 310 | When an exception is triggered, IPython can optionally call the |
|
310 | 311 | interactive pdb debugger after the traceback printout. %pdb toggles |
|
311 | 312 | this feature on and off. |
|
312 | 313 | |
|
313 | 314 | The initial state of this feature is set in your configuration |
|
314 | 315 | file (the option is ``InteractiveShell.pdb``). |
|
315 | 316 | |
|
316 | 317 | If you want to just activate the debugger AFTER an exception has fired, |
|
317 | 318 | without having to type '%pdb on' and rerunning your code, you can use |
|
318 | 319 | the %debug magic.""" |
|
319 | 320 | |
|
320 | 321 | par = parameter_s.strip().lower() |
|
321 | 322 | |
|
322 | 323 | if par: |
|
323 | 324 | try: |
|
324 | 325 | new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] |
|
325 | 326 | except KeyError: |
|
326 | 327 | print ('Incorrect argument. Use on/1, off/0, ' |
|
327 | 328 | 'or nothing for a toggle.') |
|
328 | 329 | return |
|
329 | 330 | else: |
|
330 | 331 | # toggle |
|
331 | 332 | new_pdb = not self.shell.call_pdb |
|
332 | 333 | |
|
333 | 334 | # set on the shell |
|
334 | 335 | self.shell.call_pdb = new_pdb |
|
335 |
print |
|
|
336 | print('Automatic pdb calling has been turned',on_off(new_pdb)) | |
|
336 | 337 | |
|
337 | 338 | @skip_doctest |
|
338 | 339 | @magic_arguments.magic_arguments() |
|
339 | 340 | @magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE', |
|
340 | 341 | help=""" |
|
341 | 342 | Set break point at LINE in FILE. |
|
342 | 343 | """ |
|
343 | 344 | ) |
|
344 | 345 | @magic_arguments.argument('statement', nargs='*', |
|
345 | 346 | help=""" |
|
346 | 347 | Code to run in debugger. |
|
347 | 348 | You can omit this in cell magic mode. |
|
348 | 349 | """ |
|
349 | 350 | ) |
|
350 | 351 | @line_cell_magic |
|
351 | 352 | def debug(self, line='', cell=None): |
|
352 | 353 | """Activate the interactive debugger. |
|
353 | 354 | |
|
354 | 355 | This magic command support two ways of activating debugger. |
|
355 | 356 | One is to activate debugger before executing code. This way, you |
|
356 | 357 | can set a break point, to step through the code from the point. |
|
357 | 358 | You can use this mode by giving statements to execute and optionally |
|
358 | 359 | a breakpoint. |
|
359 | 360 | |
|
360 | 361 | The other one is to activate debugger in post-mortem mode. You can |
|
361 | 362 | activate this mode simply running %debug without any argument. |
|
362 | 363 | If an exception has just occurred, this lets you inspect its stack |
|
363 | 364 | frames interactively. Note that this will always work only on the last |
|
364 | 365 | traceback that occurred, so you must call this quickly after an |
|
365 | 366 | exception that you wish to inspect has fired, because if another one |
|
366 | 367 | occurs, it clobbers the previous one. |
|
367 | 368 | |
|
368 | 369 | If you want IPython to automatically do this on every exception, see |
|
369 | 370 | the %pdb magic for more details. |
|
370 | 371 | """ |
|
371 | 372 | args = magic_arguments.parse_argstring(self.debug, line) |
|
372 | 373 | |
|
373 | 374 | if not (args.breakpoint or args.statement or cell): |
|
374 | 375 | self._debug_post_mortem() |
|
375 | 376 | else: |
|
376 | 377 | code = "\n".join(args.statement) |
|
377 | 378 | if cell: |
|
378 | 379 | code += "\n" + cell |
|
379 | 380 | self._debug_exec(code, args.breakpoint) |
|
380 | 381 | |
|
381 | 382 | def _debug_post_mortem(self): |
|
382 | 383 | self.shell.debugger(force=True) |
|
383 | 384 | |
|
384 | 385 | def _debug_exec(self, code, breakpoint): |
|
385 | 386 | if breakpoint: |
|
386 | 387 | (filename, bp_line) = breakpoint.split(':', 1) |
|
387 | 388 | bp_line = int(bp_line) |
|
388 | 389 | else: |
|
389 | 390 | (filename, bp_line) = (None, None) |
|
390 | 391 | self._run_with_debugger(code, self.shell.user_ns, filename, bp_line) |
|
391 | 392 | |
|
392 | 393 | @line_magic |
|
393 | 394 | def tb(self, s): |
|
394 | 395 | """Print the last traceback with the currently active exception mode. |
|
395 | 396 | |
|
396 | 397 | See %xmode for changing exception reporting modes.""" |
|
397 | 398 | self.shell.showtraceback() |
|
398 | 399 | |
|
399 | 400 | @skip_doctest |
|
400 | 401 | @line_magic |
|
401 | 402 | def run(self, parameter_s='', runner=None, |
|
402 | 403 | file_finder=get_py_filename): |
|
403 | 404 | """Run the named file inside IPython as a program. |
|
404 | 405 | |
|
405 | 406 | Usage:: |
|
406 | 407 | |
|
407 | 408 | %run [-n -i -e -G] |
|
408 | 409 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] |
|
409 | 410 | ( -m mod | file ) [args] |
|
410 | 411 | |
|
411 | 412 | Parameters after the filename are passed as command-line arguments to |
|
412 | 413 | the program (put in sys.argv). Then, control returns to IPython's |
|
413 | 414 | prompt. |
|
414 | 415 | |
|
415 | 416 | This is similar to running at a system prompt ``python file args``, |
|
416 | 417 | but with the advantage of giving you IPython's tracebacks, and of |
|
417 | 418 | loading all variables into your interactive namespace for further use |
|
418 | 419 | (unless -p is used, see below). |
|
419 | 420 | |
|
420 | 421 | The file is executed in a namespace initially consisting only of |
|
421 | 422 | ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus |
|
422 | 423 | sees its environment as if it were being run as a stand-alone program |
|
423 | 424 | (except for sharing global objects such as previously imported |
|
424 | 425 | modules). But after execution, the IPython interactive namespace gets |
|
425 | 426 | updated with all variables defined in the program (except for __name__ |
|
426 | 427 | and sys.argv). This allows for very convenient loading of code for |
|
427 | 428 | interactive work, while giving each program a 'clean sheet' to run in. |
|
428 | 429 | |
|
429 | 430 | Arguments are expanded using shell-like glob match. Patterns |
|
430 | 431 | '*', '?', '[seq]' and '[!seq]' can be used. Additionally, |
|
431 | 432 | tilde '~' will be expanded into user's home directory. Unlike |
|
432 | 433 | real shells, quotation does not suppress expansions. Use |
|
433 | 434 | *two* back slashes (e.g. ``\\\\*``) to suppress expansions. |
|
434 | 435 | To completely disable these expansions, you can use -G flag. |
|
435 | 436 | |
|
436 | 437 | Options: |
|
437 | 438 | |
|
438 | 439 | -n |
|
439 | 440 | __name__ is NOT set to '__main__', but to the running file's name |
|
440 | 441 | without extension (as python does under import). This allows running |
|
441 | 442 | scripts and reloading the definitions in them without calling code |
|
442 | 443 | protected by an ``if __name__ == "__main__"`` clause. |
|
443 | 444 | |
|
444 | 445 | -i |
|
445 | 446 | run the file in IPython's namespace instead of an empty one. This |
|
446 | 447 | is useful if you are experimenting with code written in a text editor |
|
447 | 448 | which depends on variables defined interactively. |
|
448 | 449 | |
|
449 | 450 | -e |
|
450 | 451 | ignore sys.exit() calls or SystemExit exceptions in the script |
|
451 | 452 | being run. This is particularly useful if IPython is being used to |
|
452 | 453 | run unittests, which always exit with a sys.exit() call. In such |
|
453 | 454 | cases you are interested in the output of the test results, not in |
|
454 | 455 | seeing a traceback of the unittest module. |
|
455 | 456 | |
|
456 | 457 | -t |
|
457 | 458 | print timing information at the end of the run. IPython will give |
|
458 | 459 | you an estimated CPU time consumption for your script, which under |
|
459 | 460 | Unix uses the resource module to avoid the wraparound problems of |
|
460 | 461 | time.clock(). Under Unix, an estimate of time spent on system tasks |
|
461 | 462 | is also given (for Windows platforms this is reported as 0.0). |
|
462 | 463 | |
|
463 | 464 | If -t is given, an additional ``-N<N>`` option can be given, where <N> |
|
464 | 465 | must be an integer indicating how many times you want the script to |
|
465 | 466 | run. The final timing report will include total and per run results. |
|
466 | 467 | |
|
467 | 468 | For example (testing the script uniq_stable.py):: |
|
468 | 469 | |
|
469 | 470 | In [1]: run -t uniq_stable |
|
470 | 471 | |
|
471 | 472 | IPython CPU timings (estimated): |
|
472 | 473 | User : 0.19597 s. |
|
473 | 474 | System: 0.0 s. |
|
474 | 475 | |
|
475 | 476 | In [2]: run -t -N5 uniq_stable |
|
476 | 477 | |
|
477 | 478 | IPython CPU timings (estimated): |
|
478 | 479 | Total runs performed: 5 |
|
479 | 480 | Times : Total Per run |
|
480 | 481 | User : 0.910862 s, 0.1821724 s. |
|
481 | 482 | System: 0.0 s, 0.0 s. |
|
482 | 483 | |
|
483 | 484 | -d |
|
484 | 485 | run your program under the control of pdb, the Python debugger. |
|
485 | 486 | This allows you to execute your program step by step, watch variables, |
|
486 | 487 | etc. Internally, what IPython does is similar to calling:: |
|
487 | 488 | |
|
488 | 489 | pdb.run('execfile("YOURFILENAME")') |
|
489 | 490 | |
|
490 | 491 | with a breakpoint set on line 1 of your file. You can change the line |
|
491 | 492 | number for this automatic breakpoint to be <N> by using the -bN option |
|
492 | 493 | (where N must be an integer). For example:: |
|
493 | 494 | |
|
494 | 495 | %run -d -b40 myscript |
|
495 | 496 | |
|
496 | 497 | will set the first breakpoint at line 40 in myscript.py. Note that |
|
497 | 498 | the first breakpoint must be set on a line which actually does |
|
498 | 499 | something (not a comment or docstring) for it to stop execution. |
|
499 | 500 | |
|
500 | 501 | Or you can specify a breakpoint in a different file:: |
|
501 | 502 | |
|
502 | 503 | %run -d -b myotherfile.py:20 myscript |
|
503 | 504 | |
|
504 | 505 | When the pdb debugger starts, you will see a (Pdb) prompt. You must |
|
505 | 506 | first enter 'c' (without quotes) to start execution up to the first |
|
506 | 507 | breakpoint. |
|
507 | 508 | |
|
508 | 509 | Entering 'help' gives information about the use of the debugger. You |
|
509 | 510 | can easily see pdb's full documentation with "import pdb;pdb.help()" |
|
510 | 511 | at a prompt. |
|
511 | 512 | |
|
512 | 513 | -p |
|
513 | 514 | run program under the control of the Python profiler module (which |
|
514 | 515 | prints a detailed report of execution times, function calls, etc). |
|
515 | 516 | |
|
516 | 517 | You can pass other options after -p which affect the behavior of the |
|
517 | 518 | profiler itself. See the docs for %prun for details. |
|
518 | 519 | |
|
519 | 520 | In this mode, the program's variables do NOT propagate back to the |
|
520 | 521 | IPython interactive namespace (because they remain in the namespace |
|
521 | 522 | where the profiler executes them). |
|
522 | 523 | |
|
523 | 524 | Internally this triggers a call to %prun, see its documentation for |
|
524 | 525 | details on the options available specifically for profiling. |
|
525 | 526 | |
|
526 | 527 | There is one special usage for which the text above doesn't apply: |
|
527 | 528 | if the filename ends with .ipy, the file is run as ipython script, |
|
528 | 529 | just as if the commands were written on IPython prompt. |
|
529 | 530 | |
|
530 | 531 | -m |
|
531 | 532 | specify module name to load instead of script path. Similar to |
|
532 | 533 | the -m option for the python interpreter. Use this option last if you |
|
533 | 534 | want to combine with other %run options. Unlike the python interpreter |
|
534 | 535 | only source modules are allowed no .pyc or .pyo files. |
|
535 | 536 | For example:: |
|
536 | 537 | |
|
537 | 538 | %run -m example |
|
538 | 539 | |
|
539 | 540 | will run the example module. |
|
540 | 541 | |
|
541 | 542 | -G |
|
542 | 543 | disable shell-like glob expansion of arguments. |
|
543 | 544 | |
|
544 | 545 | """ |
|
545 | 546 | |
|
546 | 547 | # get arguments and set sys.argv for program to be run. |
|
547 | 548 | opts, arg_lst = self.parse_options(parameter_s, |
|
548 | 549 | 'nidtN:b:pD:l:rs:T:em:G', |
|
549 | 550 | mode='list', list_all=1) |
|
550 | 551 | if "m" in opts: |
|
551 | 552 | modulename = opts["m"][0] |
|
552 | 553 | modpath = find_mod(modulename) |
|
553 | 554 | if modpath is None: |
|
554 | 555 | warn('%r is not a valid modulename on sys.path'%modulename) |
|
555 | 556 | return |
|
556 | 557 | arg_lst = [modpath] + arg_lst |
|
557 | 558 | try: |
|
558 | 559 | filename = file_finder(arg_lst[0]) |
|
559 | 560 | except IndexError: |
|
560 | 561 | warn('you must provide at least a filename.') |
|
561 |
print |
|
|
562 | print('\n%run:\n', oinspect.getdoc(self.run)) | |
|
562 | 563 | return |
|
563 | 564 | except IOError as e: |
|
564 | 565 | try: |
|
565 | 566 | msg = str(e) |
|
566 | 567 | except UnicodeError: |
|
567 | 568 | msg = e.message |
|
568 | 569 | error(msg) |
|
569 | 570 | return |
|
570 | 571 | |
|
571 | 572 | if filename.lower().endswith('.ipy'): |
|
572 | 573 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
573 | 574 | self.shell.user_ns['__file__'] = filename |
|
574 | 575 | self.shell.safe_execfile_ipy(filename) |
|
575 | 576 | return |
|
576 | 577 | |
|
577 | 578 | # Control the response to exit() calls made by the script being run |
|
578 | 579 | exit_ignore = 'e' in opts |
|
579 | 580 | |
|
580 | 581 | # Make sure that the running script gets a proper sys.argv as if it |
|
581 | 582 | # were run from a system shell. |
|
582 | 583 | save_argv = sys.argv # save it for later restoring |
|
583 | 584 | |
|
584 | 585 | if 'G' in opts: |
|
585 | 586 | args = arg_lst[1:] |
|
586 | 587 | else: |
|
587 | 588 | # tilde and glob expansion |
|
588 | 589 | args = shellglob(map(os.path.expanduser, arg_lst[1:])) |
|
589 | 590 | |
|
590 | 591 | sys.argv = [filename] + args # put in the proper filename |
|
591 | 592 | # protect sys.argv from potential unicode strings on Python 2: |
|
592 | 593 | if not py3compat.PY3: |
|
593 | 594 | sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] |
|
594 | 595 | |
|
595 | 596 | if 'i' in opts: |
|
596 | 597 | # Run in user's interactive namespace |
|
597 | 598 | prog_ns = self.shell.user_ns |
|
598 | 599 | __name__save = self.shell.user_ns['__name__'] |
|
599 | 600 | prog_ns['__name__'] = '__main__' |
|
600 | 601 | main_mod = self.shell.user_module |
|
601 | 602 | |
|
602 | 603 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must |
|
603 | 604 | # set the __file__ global in the script's namespace |
|
604 | 605 | # TK: Is this necessary in interactive mode? |
|
605 | 606 | prog_ns['__file__'] = filename |
|
606 | 607 | else: |
|
607 | 608 | # Run in a fresh, empty namespace |
|
608 | 609 | if 'n' in opts: |
|
609 | 610 | name = os.path.splitext(os.path.basename(filename))[0] |
|
610 | 611 | else: |
|
611 | 612 | name = '__main__' |
|
612 | 613 | |
|
613 | 614 | # The shell MUST hold a reference to prog_ns so after %run |
|
614 | 615 | # exits, the python deletion mechanism doesn't zero it out |
|
615 | 616 | # (leaving dangling references). See interactiveshell for details |
|
616 | 617 | main_mod = self.shell.new_main_mod(filename, name) |
|
617 | 618 | prog_ns = main_mod.__dict__ |
|
618 | 619 | |
|
619 | 620 | # pickle fix. See interactiveshell for an explanation. But we need to |
|
620 | 621 | # make sure that, if we overwrite __main__, we replace it at the end |
|
621 | 622 | main_mod_name = prog_ns['__name__'] |
|
622 | 623 | |
|
623 | 624 | if main_mod_name == '__main__': |
|
624 | 625 | restore_main = sys.modules['__main__'] |
|
625 | 626 | else: |
|
626 | 627 | restore_main = False |
|
627 | 628 | |
|
628 | 629 | # This needs to be undone at the end to prevent holding references to |
|
629 | 630 | # every single object ever created. |
|
630 | 631 | sys.modules[main_mod_name] = main_mod |
|
631 | 632 | |
|
632 | 633 | if 'p' in opts or 'd' in opts: |
|
633 | 634 | if 'm' in opts: |
|
634 | 635 | code = 'run_module(modulename, prog_ns)' |
|
635 | 636 | code_ns = { |
|
636 | 637 | 'run_module': self.shell.safe_run_module, |
|
637 | 638 | 'prog_ns': prog_ns, |
|
638 | 639 | 'modulename': modulename, |
|
639 | 640 | } |
|
640 | 641 | else: |
|
641 | 642 | code = 'execfile(filename, prog_ns)' |
|
642 | 643 | code_ns = { |
|
643 | 644 | 'execfile': self.shell.safe_execfile, |
|
644 | 645 | 'prog_ns': prog_ns, |
|
645 | 646 | 'filename': get_py_filename(filename), |
|
646 | 647 | } |
|
647 | 648 | |
|
648 | 649 | try: |
|
649 | 650 | stats = None |
|
650 | 651 | with self.shell.readline_no_record: |
|
651 | 652 | if 'p' in opts: |
|
652 | 653 | stats = self._run_with_profiler(code, opts, code_ns) |
|
653 | 654 | else: |
|
654 | 655 | if 'd' in opts: |
|
655 | 656 | bp_file, bp_line = parse_breakpoint( |
|
656 | 657 | opts.get('b', ['1'])[0], filename) |
|
657 | 658 | self._run_with_debugger( |
|
658 | 659 | code, code_ns, filename, bp_line, bp_file) |
|
659 | 660 | else: |
|
660 | 661 | if 'm' in opts: |
|
661 | 662 | def run(): |
|
662 | 663 | self.shell.safe_run_module(modulename, prog_ns) |
|
663 | 664 | else: |
|
664 | 665 | if runner is None: |
|
665 | 666 | runner = self.default_runner |
|
666 | 667 | if runner is None: |
|
667 | 668 | runner = self.shell.safe_execfile |
|
668 | 669 | |
|
669 | 670 | def run(): |
|
670 | 671 | runner(filename, prog_ns, prog_ns, |
|
671 | 672 | exit_ignore=exit_ignore) |
|
672 | 673 | |
|
673 | 674 | if 't' in opts: |
|
674 | 675 | # timed execution |
|
675 | 676 | try: |
|
676 | 677 | nruns = int(opts['N'][0]) |
|
677 | 678 | if nruns < 1: |
|
678 | 679 | error('Number of runs must be >=1') |
|
679 | 680 | return |
|
680 | 681 | except (KeyError): |
|
681 | 682 | nruns = 1 |
|
682 | 683 | self._run_with_timing(run, nruns) |
|
683 | 684 | else: |
|
684 | 685 | # regular execution |
|
685 | 686 | run() |
|
686 | 687 | |
|
687 | 688 | if 'i' in opts: |
|
688 | 689 | self.shell.user_ns['__name__'] = __name__save |
|
689 | 690 | else: |
|
690 | 691 | # update IPython interactive namespace |
|
691 | 692 | |
|
692 | 693 | # Some forms of read errors on the file may mean the |
|
693 | 694 | # __name__ key was never set; using pop we don't have to |
|
694 | 695 | # worry about a possible KeyError. |
|
695 | 696 | prog_ns.pop('__name__', None) |
|
696 | 697 | |
|
697 | 698 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
698 | 699 | self.shell.user_ns.update(prog_ns) |
|
699 | 700 | finally: |
|
700 | 701 | # It's a bit of a mystery why, but __builtins__ can change from |
|
701 | 702 | # being a module to becoming a dict missing some key data after |
|
702 | 703 | # %run. As best I can see, this is NOT something IPython is doing |
|
703 | 704 | # at all, and similar problems have been reported before: |
|
704 | 705 | # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html |
|
705 | 706 | # Since this seems to be done by the interpreter itself, the best |
|
706 | 707 | # we can do is to at least restore __builtins__ for the user on |
|
707 | 708 | # exit. |
|
708 | 709 | self.shell.user_ns['__builtins__'] = builtin_mod |
|
709 | 710 | |
|
710 | 711 | # Ensure key global structures are restored |
|
711 | 712 | sys.argv = save_argv |
|
712 | 713 | if restore_main: |
|
713 | 714 | sys.modules['__main__'] = restore_main |
|
714 | 715 | else: |
|
715 | 716 | # Remove from sys.modules the reference to main_mod we'd |
|
716 | 717 | # added. Otherwise it will trap references to objects |
|
717 | 718 | # contained therein. |
|
718 | 719 | del sys.modules[main_mod_name] |
|
719 | 720 | |
|
720 | 721 | return stats |
|
721 | 722 | |
|
722 | 723 | def _run_with_debugger(self, code, code_ns, filename=None, |
|
723 | 724 | bp_line=None, bp_file=None): |
|
724 | 725 | """ |
|
725 | 726 | Run `code` in debugger with a break point. |
|
726 | 727 | |
|
727 | 728 | Parameters |
|
728 | 729 | ---------- |
|
729 | 730 | code : str |
|
730 | 731 | Code to execute. |
|
731 | 732 | code_ns : dict |
|
732 | 733 | A namespace in which `code` is executed. |
|
733 | 734 | filename : str |
|
734 | 735 | `code` is ran as if it is in `filename`. |
|
735 | 736 | bp_line : int, optional |
|
736 | 737 | Line number of the break point. |
|
737 | 738 | bp_file : str, optional |
|
738 | 739 | Path to the file in which break point is specified. |
|
739 | 740 | `filename` is used if not given. |
|
740 | 741 | |
|
741 | 742 | Raises |
|
742 | 743 | ------ |
|
743 | 744 | UsageError |
|
744 | 745 | If the break point given by `bp_line` is not valid. |
|
745 | 746 | |
|
746 | 747 | """ |
|
747 | 748 | deb = debugger.Pdb(self.shell.colors) |
|
748 | 749 | # reset Breakpoint state, which is moronically kept |
|
749 | 750 | # in a class |
|
750 | 751 | bdb.Breakpoint.next = 1 |
|
751 | 752 | bdb.Breakpoint.bplist = {} |
|
752 | 753 | bdb.Breakpoint.bpbynumber = [None] |
|
753 | 754 | if bp_line is not None: |
|
754 | 755 | # Set an initial breakpoint to stop execution |
|
755 | 756 | maxtries = 10 |
|
756 | 757 | bp_file = bp_file or filename |
|
757 | 758 | checkline = deb.checkline(bp_file, bp_line) |
|
758 | 759 | if not checkline: |
|
759 | 760 | for bp in range(bp_line + 1, bp_line + maxtries + 1): |
|
760 | 761 | if deb.checkline(bp_file, bp): |
|
761 | 762 | break |
|
762 | 763 | else: |
|
763 | 764 | msg = ("\nI failed to find a valid line to set " |
|
764 | 765 | "a breakpoint\n" |
|
765 | 766 | "after trying up to line: %s.\n" |
|
766 | 767 | "Please set a valid breakpoint manually " |
|
767 | 768 | "with the -b option." % bp) |
|
768 | 769 | raise UsageError(msg) |
|
769 | 770 | # if we find a good linenumber, set the breakpoint |
|
770 | 771 | deb.do_break('%s:%s' % (bp_file, bp_line)) |
|
771 | 772 | |
|
772 | 773 | if filename: |
|
773 | 774 | # Mimic Pdb._runscript(...) |
|
774 | 775 | deb._wait_for_mainpyfile = True |
|
775 | 776 | deb.mainpyfile = deb.canonic(filename) |
|
776 | 777 | |
|
777 | 778 | # Start file run |
|
778 |
print |
|
|
779 | print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt) | |
|
779 | 780 | try: |
|
780 | 781 | if filename: |
|
781 | 782 | # save filename so it can be used by methods on the deb object |
|
782 | 783 | deb._exec_filename = filename |
|
783 | 784 | deb.run(code, code_ns) |
|
784 | 785 | |
|
785 | 786 | except: |
|
786 | 787 | etype, value, tb = sys.exc_info() |
|
787 | 788 | # Skip three frames in the traceback: the %run one, |
|
788 | 789 | # one inside bdb.py, and the command-line typed by the |
|
789 | 790 | # user (run by exec in pdb itself). |
|
790 | 791 | self.shell.InteractiveTB(etype, value, tb, tb_offset=3) |
|
791 | 792 | |
|
792 | 793 | @staticmethod |
|
793 | 794 | def _run_with_timing(run, nruns): |
|
794 | 795 | """ |
|
795 | 796 | Run function `run` and print timing information. |
|
796 | 797 | |
|
797 | 798 | Parameters |
|
798 | 799 | ---------- |
|
799 | 800 | run : callable |
|
800 | 801 | Any callable object which takes no argument. |
|
801 | 802 | nruns : int |
|
802 | 803 | Number of times to execute `run`. |
|
803 | 804 | |
|
804 | 805 | """ |
|
805 | 806 | twall0 = time.time() |
|
806 | 807 | if nruns == 1: |
|
807 | 808 | t0 = clock2() |
|
808 | 809 | run() |
|
809 | 810 | t1 = clock2() |
|
810 | 811 | t_usr = t1[0] - t0[0] |
|
811 | 812 | t_sys = t1[1] - t0[1] |
|
812 |
print |
|
|
813 |
print |
|
|
814 |
print |
|
|
813 | print("\nIPython CPU timings (estimated):") | |
|
814 | print(" User : %10.2f s." % t_usr) | |
|
815 | print(" System : %10.2f s." % t_sys) | |
|
815 | 816 | else: |
|
816 | 817 | runs = range(nruns) |
|
817 | 818 | t0 = clock2() |
|
818 | 819 | for nr in runs: |
|
819 | 820 | run() |
|
820 | 821 | t1 = clock2() |
|
821 | 822 | t_usr = t1[0] - t0[0] |
|
822 | 823 | t_sys = t1[1] - t0[1] |
|
823 |
print |
|
|
824 |
print |
|
|
825 |
print |
|
|
826 |
print |
|
|
827 |
print |
|
|
824 | print("\nIPython CPU timings (estimated):") | |
|
825 | print("Total runs performed:", nruns) | |
|
826 | print(" Times : %10s %10s" % ('Total', 'Per run')) | |
|
827 | print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)) | |
|
828 | print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)) | |
|
828 | 829 | twall1 = time.time() |
|
829 |
print |
|
|
830 | print("Wall time: %10.2f s." % (twall1 - twall0)) | |
|
830 | 831 | |
|
831 | 832 | @skip_doctest |
|
832 | 833 | @line_cell_magic |
|
833 | 834 | def timeit(self, line='', cell=None): |
|
834 | 835 | """Time execution of a Python statement or expression |
|
835 | 836 | |
|
836 | 837 | Usage, in line mode: |
|
837 | 838 | %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement |
|
838 | 839 | or in cell mode: |
|
839 | 840 | %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code |
|
840 | 841 | code |
|
841 | 842 | code... |
|
842 | 843 | |
|
843 | 844 | Time execution of a Python statement or expression using the timeit |
|
844 | 845 | module. This function can be used both as a line and cell magic: |
|
845 | 846 | |
|
846 | 847 | - In line mode you can time a single-line statement (though multiple |
|
847 | 848 | ones can be chained with using semicolons). |
|
848 | 849 | |
|
849 | 850 | - In cell mode, the statement in the first line is used as setup code |
|
850 | 851 | (executed but not timed) and the body of the cell is timed. The cell |
|
851 | 852 | body has access to any variables created in the setup code. |
|
852 | 853 | |
|
853 | 854 | Options: |
|
854 | 855 | -n<N>: execute the given statement <N> times in a loop. If this value |
|
855 | 856 | is not given, a fitting value is chosen. |
|
856 | 857 | |
|
857 | 858 | -r<R>: repeat the loop iteration <R> times and take the best result. |
|
858 | 859 | Default: 3 |
|
859 | 860 | |
|
860 | 861 | -t: use time.time to measure the time, which is the default on Unix. |
|
861 | 862 | This function measures wall time. |
|
862 | 863 | |
|
863 | 864 | -c: use time.clock to measure the time, which is the default on |
|
864 | 865 | Windows and measures wall time. On Unix, resource.getrusage is used |
|
865 | 866 | instead and returns the CPU user time. |
|
866 | 867 | |
|
867 | 868 | -p<P>: use a precision of <P> digits to display the timing result. |
|
868 | 869 | Default: 3 |
|
869 | 870 | |
|
870 | 871 | -q: Quiet, do not print result. |
|
871 | 872 | |
|
872 | 873 | -o: return a TimeitResult that can be stored in a variable to inspect |
|
873 | 874 | the result in more details. |
|
874 | 875 | |
|
875 | 876 | |
|
876 | 877 | Examples |
|
877 | 878 | -------- |
|
878 | 879 | :: |
|
879 | 880 | |
|
880 | 881 | In [1]: %timeit pass |
|
881 | 882 | 10000000 loops, best of 3: 53.3 ns per loop |
|
882 | 883 | |
|
883 | 884 | In [2]: u = None |
|
884 | 885 | |
|
885 | 886 | In [3]: %timeit u is None |
|
886 | 887 | 10000000 loops, best of 3: 184 ns per loop |
|
887 | 888 | |
|
888 | 889 | In [4]: %timeit -r 4 u == None |
|
889 | 890 | 1000000 loops, best of 4: 242 ns per loop |
|
890 | 891 | |
|
891 | 892 | In [5]: import time |
|
892 | 893 | |
|
893 | 894 | In [6]: %timeit -n1 time.sleep(2) |
|
894 | 895 | 1 loops, best of 3: 2 s per loop |
|
895 | 896 | |
|
896 | 897 | |
|
897 | 898 | The times reported by %timeit will be slightly higher than those |
|
898 | 899 | reported by the timeit.py script when variables are accessed. This is |
|
899 | 900 | due to the fact that %timeit executes the statement in the namespace |
|
900 | 901 | of the shell, compared with timeit.py, which uses a single setup |
|
901 | 902 | statement to import function or create variables. Generally, the bias |
|
902 | 903 | does not matter as long as results from timeit.py are not mixed with |
|
903 | 904 | those from %timeit.""" |
|
904 | 905 | |
|
905 | 906 | import timeit |
|
906 | 907 | |
|
907 | 908 | opts, stmt = self.parse_options(line,'n:r:tcp:qo', |
|
908 | 909 | posix=False, strict=False) |
|
909 | 910 | if stmt == "" and cell is None: |
|
910 | 911 | return |
|
911 | 912 | |
|
912 | 913 | timefunc = timeit.default_timer |
|
913 | 914 | number = int(getattr(opts, "n", 0)) |
|
914 | 915 | repeat = int(getattr(opts, "r", timeit.default_repeat)) |
|
915 | 916 | precision = int(getattr(opts, "p", 3)) |
|
916 | 917 | quiet = 'q' in opts |
|
917 | 918 | return_result = 'o' in opts |
|
918 | 919 | if hasattr(opts, "t"): |
|
919 | 920 | timefunc = time.time |
|
920 | 921 | if hasattr(opts, "c"): |
|
921 | 922 | timefunc = clock |
|
922 | 923 | |
|
923 | 924 | timer = timeit.Timer(timer=timefunc) |
|
924 | 925 | # this code has tight coupling to the inner workings of timeit.Timer, |
|
925 | 926 | # but is there a better way to achieve that the code stmt has access |
|
926 | 927 | # to the shell namespace? |
|
927 | 928 | transform = self.shell.input_splitter.transform_cell |
|
928 | 929 | |
|
929 | 930 | if cell is None: |
|
930 | 931 | # called as line magic |
|
931 | 932 | ast_setup = ast.parse("pass") |
|
932 | 933 | ast_stmt = ast.parse(transform(stmt)) |
|
933 | 934 | else: |
|
934 | 935 | ast_setup = ast.parse(transform(stmt)) |
|
935 | 936 | ast_stmt = ast.parse(transform(cell)) |
|
936 | 937 | |
|
937 | 938 | ast_setup = self.shell.transform_ast(ast_setup) |
|
938 | 939 | ast_stmt = self.shell.transform_ast(ast_stmt) |
|
939 | 940 | |
|
940 | 941 | # This codestring is taken from timeit.template - we fill it in as an |
|
941 | 942 | # AST, so that we can apply our AST transformations to the user code |
|
942 | 943 | # without affecting the timing code. |
|
943 | 944 | timeit_ast_template = ast.parse('def inner(_it, _timer):\n' |
|
944 | 945 | ' setup\n' |
|
945 | 946 | ' _t0 = _timer()\n' |
|
946 | 947 | ' for _i in _it:\n' |
|
947 | 948 | ' stmt\n' |
|
948 | 949 | ' _t1 = _timer()\n' |
|
949 | 950 | ' return _t1 - _t0\n') |
|
950 | 951 | |
|
951 | 952 | class TimeitTemplateFiller(ast.NodeTransformer): |
|
952 | 953 | "This is quite tightly tied to the template definition above." |
|
953 | 954 | def visit_FunctionDef(self, node): |
|
954 | 955 | "Fill in the setup statement" |
|
955 | 956 | self.generic_visit(node) |
|
956 | 957 | if node.name == "inner": |
|
957 | 958 | node.body[:1] = ast_setup.body |
|
958 | 959 | |
|
959 | 960 | return node |
|
960 | 961 | |
|
961 | 962 | def visit_For(self, node): |
|
962 | 963 | "Fill in the statement to be timed" |
|
963 | 964 | if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt': |
|
964 | 965 | node.body = ast_stmt.body |
|
965 | 966 | return node |
|
966 | 967 | |
|
967 | 968 | timeit_ast = TimeitTemplateFiller().visit(timeit_ast_template) |
|
968 | 969 | timeit_ast = ast.fix_missing_locations(timeit_ast) |
|
969 | 970 | |
|
970 | 971 | # Track compilation time so it can be reported if too long |
|
971 | 972 | # Minimum time above which compilation time will be reported |
|
972 | 973 | tc_min = 0.1 |
|
973 | 974 | |
|
974 | 975 | t0 = clock() |
|
975 | 976 | code = compile(timeit_ast, "<magic-timeit>", "exec") |
|
976 | 977 | tc = clock()-t0 |
|
977 | 978 | |
|
978 | 979 | ns = {} |
|
979 | 980 | exec code in self.shell.user_ns, ns |
|
980 | 981 | timer.inner = ns["inner"] |
|
981 | 982 | |
|
982 | 983 | if number == 0: |
|
983 | 984 | # determine number so that 0.2 <= total time < 2.0 |
|
984 | 985 | number = 1 |
|
985 | 986 | for _ in range(1, 10): |
|
986 | 987 | if timer.timeit(number) >= 0.2: |
|
987 | 988 | break |
|
988 | 989 | number *= 10 |
|
989 | 990 | all_runs = timer.repeat(repeat, number) |
|
990 | 991 | best = min(all_runs) / number |
|
991 | 992 | if not quiet : |
|
992 |
print |
|
|
993 | _format_time(best, precision)) | |
|
993 | print(u"%d loops, best of %d: %s per loop" % (number, repeat, | |
|
994 | _format_time(best, precision))) | |
|
994 | 995 | if tc > tc_min: |
|
995 |
print |
|
|
996 | print("Compiler time: %.2f s" % tc) | |
|
996 | 997 | if return_result: |
|
997 | 998 | return TimeitResult(number, repeat, best, all_runs, tc, precision) |
|
998 | 999 | |
|
999 | 1000 | @skip_doctest |
|
1000 | 1001 | @needs_local_scope |
|
1001 | 1002 | @line_cell_magic |
|
1002 | 1003 | def time(self,line='', cell=None, local_ns=None): |
|
1003 | 1004 | """Time execution of a Python statement or expression. |
|
1004 | 1005 | |
|
1005 | 1006 | The CPU and wall clock times are printed, and the value of the |
|
1006 | 1007 | expression (if any) is returned. Note that under Win32, system time |
|
1007 | 1008 | is always reported as 0, since it can not be measured. |
|
1008 | 1009 | |
|
1009 | 1010 | This function can be used both as a line and cell magic: |
|
1010 | 1011 | |
|
1011 | 1012 | - In line mode you can time a single-line statement (though multiple |
|
1012 | 1013 | ones can be chained with using semicolons). |
|
1013 | 1014 | |
|
1014 | 1015 | - In cell mode, you can time the cell body (a directly |
|
1015 | 1016 | following statement raises an error). |
|
1016 | 1017 | |
|
1017 | 1018 | This function provides very basic timing functionality. Use the timeit |
|
1018 | 1019 | magic for more controll over the measurement. |
|
1019 | 1020 | |
|
1020 | 1021 | Examples |
|
1021 | 1022 | -------- |
|
1022 | 1023 | :: |
|
1023 | 1024 | |
|
1024 | 1025 | In [1]: %time 2**128 |
|
1025 | 1026 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1026 | 1027 | Wall time: 0.00 |
|
1027 | 1028 | Out[1]: 340282366920938463463374607431768211456L |
|
1028 | 1029 | |
|
1029 | 1030 | In [2]: n = 1000000 |
|
1030 | 1031 | |
|
1031 | 1032 | In [3]: %time sum(range(n)) |
|
1032 | 1033 | CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s |
|
1033 | 1034 | Wall time: 1.37 |
|
1034 | 1035 | Out[3]: 499999500000L |
|
1035 | 1036 | |
|
1036 | 1037 | In [4]: %time print 'hello world' |
|
1037 | 1038 | hello world |
|
1038 | 1039 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1039 | 1040 | Wall time: 0.00 |
|
1040 | 1041 | |
|
1041 | 1042 | Note that the time needed by Python to compile the given expression |
|
1042 | 1043 | will be reported if it is more than 0.1s. In this example, the |
|
1043 | 1044 | actual exponentiation is done by Python at compilation time, so while |
|
1044 | 1045 | the expression can take a noticeable amount of time to compute, that |
|
1045 | 1046 | time is purely due to the compilation: |
|
1046 | 1047 | |
|
1047 | 1048 | In [5]: %time 3**9999; |
|
1048 | 1049 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1049 | 1050 | Wall time: 0.00 s |
|
1050 | 1051 | |
|
1051 | 1052 | In [6]: %time 3**999999; |
|
1052 | 1053 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1053 | 1054 | Wall time: 0.00 s |
|
1054 | 1055 | Compiler : 0.78 s |
|
1055 | 1056 | """ |
|
1056 | 1057 | |
|
1057 | 1058 | # fail immediately if the given expression can't be compiled |
|
1058 | 1059 | |
|
1059 | 1060 | if line and cell: |
|
1060 | 1061 | raise UsageError("Can't use statement directly after '%%time'!") |
|
1061 | 1062 | |
|
1062 | 1063 | if cell: |
|
1063 | 1064 | expr = self.shell.input_transformer_manager.transform_cell(cell) |
|
1064 | 1065 | else: |
|
1065 | 1066 | expr = self.shell.input_transformer_manager.transform_cell(line) |
|
1066 | 1067 | |
|
1067 | 1068 | # Minimum time above which parse time will be reported |
|
1068 | 1069 | tp_min = 0.1 |
|
1069 | 1070 | |
|
1070 | 1071 | t0 = clock() |
|
1071 | 1072 | expr_ast = ast.parse(expr) |
|
1072 | 1073 | tp = clock()-t0 |
|
1073 | 1074 | |
|
1074 | 1075 | # Apply AST transformations |
|
1075 | 1076 | expr_ast = self.shell.transform_ast(expr_ast) |
|
1076 | 1077 | |
|
1077 | 1078 | # Minimum time above which compilation time will be reported |
|
1078 | 1079 | tc_min = 0.1 |
|
1079 | 1080 | |
|
1080 | 1081 | if len(expr_ast.body)==1 and isinstance(expr_ast.body[0], ast.Expr): |
|
1081 | 1082 | mode = 'eval' |
|
1082 | 1083 | source = '<timed eval>' |
|
1083 | 1084 | expr_ast = ast.Expression(expr_ast.body[0].value) |
|
1084 | 1085 | else: |
|
1085 | 1086 | mode = 'exec' |
|
1086 | 1087 | source = '<timed exec>' |
|
1087 | 1088 | t0 = clock() |
|
1088 | 1089 | code = compile(expr_ast, source, mode) |
|
1089 | 1090 | tc = clock()-t0 |
|
1090 | 1091 | |
|
1091 | 1092 | # skew measurement as little as possible |
|
1092 | 1093 | glob = self.shell.user_ns |
|
1093 | 1094 | wtime = time.time |
|
1094 | 1095 | # time execution |
|
1095 | 1096 | wall_st = wtime() |
|
1096 | 1097 | if mode=='eval': |
|
1097 | 1098 | st = clock2() |
|
1098 | 1099 | out = eval(code, glob, local_ns) |
|
1099 | 1100 | end = clock2() |
|
1100 | 1101 | else: |
|
1101 | 1102 | st = clock2() |
|
1102 | 1103 | exec code in glob, local_ns |
|
1103 | 1104 | end = clock2() |
|
1104 | 1105 | out = None |
|
1105 | 1106 | wall_end = wtime() |
|
1106 | 1107 | # Compute actual times and report |
|
1107 | 1108 | wall_time = wall_end-wall_st |
|
1108 | 1109 | cpu_user = end[0]-st[0] |
|
1109 | 1110 | cpu_sys = end[1]-st[1] |
|
1110 | 1111 | cpu_tot = cpu_user+cpu_sys |
|
1111 | 1112 | # On windows cpu_sys is always zero, so no new information to the next print |
|
1112 | 1113 | if sys.platform != 'win32': |
|
1113 |
print |
|
|
1114 | (_format_time(cpu_user),_format_time(cpu_sys),_format_time(cpu_tot)) | |
|
1115 |
print |
|
|
1114 | print("CPU times: user %s, sys: %s, total: %s" % \ | |
|
1115 | (_format_time(cpu_user),_format_time(cpu_sys),_format_time(cpu_tot))) | |
|
1116 | print("Wall time: %s" % _format_time(wall_time)) | |
|
1116 | 1117 | if tc > tc_min: |
|
1117 |
print |
|
|
1118 | print("Compiler : %s" % _format_time(tc)) | |
|
1118 | 1119 | if tp > tp_min: |
|
1119 |
print |
|
|
1120 | print("Parser : %s" % _format_time(tp)) | |
|
1120 | 1121 | return out |
|
1121 | 1122 | |
|
1122 | 1123 | @skip_doctest |
|
1123 | 1124 | @line_magic |
|
1124 | 1125 | def macro(self, parameter_s=''): |
|
1125 | 1126 | """Define a macro for future re-execution. It accepts ranges of history, |
|
1126 | 1127 | filenames or string objects. |
|
1127 | 1128 | |
|
1128 | 1129 | Usage:\\ |
|
1129 | 1130 | %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... |
|
1130 | 1131 | |
|
1131 | 1132 | Options: |
|
1132 | 1133 | |
|
1133 | 1134 | -r: use 'raw' input. By default, the 'processed' history is used, |
|
1134 | 1135 | so that magics are loaded in their transformed version to valid |
|
1135 | 1136 | Python. If this option is given, the raw input as typed at the |
|
1136 | 1137 | command line is used instead. |
|
1137 | 1138 | |
|
1138 | 1139 | -q: quiet macro definition. By default, a tag line is printed |
|
1139 | 1140 | to indicate the macro has been created, and then the contents of |
|
1140 | 1141 | the macro are printed. If this option is given, then no printout |
|
1141 | 1142 | is produced once the macro is created. |
|
1142 | 1143 | |
|
1143 | 1144 | This will define a global variable called `name` which is a string |
|
1144 | 1145 | made of joining the slices and lines you specify (n1,n2,... numbers |
|
1145 | 1146 | above) from your input history into a single string. This variable |
|
1146 | 1147 | acts like an automatic function which re-executes those lines as if |
|
1147 | 1148 | you had typed them. You just type 'name' at the prompt and the code |
|
1148 | 1149 | executes. |
|
1149 | 1150 | |
|
1150 | 1151 | The syntax for indicating input ranges is described in %history. |
|
1151 | 1152 | |
|
1152 | 1153 | Note: as a 'hidden' feature, you can also use traditional python slice |
|
1153 | 1154 | notation, where N:M means numbers N through M-1. |
|
1154 | 1155 | |
|
1155 | 1156 | For example, if your history contains (print using %hist -n ):: |
|
1156 | 1157 | |
|
1157 | 1158 | 44: x=1 |
|
1158 | 1159 | 45: y=3 |
|
1159 | 1160 | 46: z=x+y |
|
1160 | 1161 | 47: print x |
|
1161 | 1162 | 48: a=5 |
|
1162 | 1163 | 49: print 'x',x,'y',y |
|
1163 | 1164 | |
|
1164 | 1165 | you can create a macro with lines 44 through 47 (included) and line 49 |
|
1165 | 1166 | called my_macro with:: |
|
1166 | 1167 | |
|
1167 | 1168 | In [55]: %macro my_macro 44-47 49 |
|
1168 | 1169 | |
|
1169 | 1170 | Now, typing `my_macro` (without quotes) will re-execute all this code |
|
1170 | 1171 | in one pass. |
|
1171 | 1172 | |
|
1172 | 1173 | You don't need to give the line-numbers in order, and any given line |
|
1173 | 1174 | number can appear multiple times. You can assemble macros with any |
|
1174 | 1175 | lines from your input history in any order. |
|
1175 | 1176 | |
|
1176 | 1177 | The macro is a simple object which holds its value in an attribute, |
|
1177 | 1178 | but IPython's display system checks for macros and executes them as |
|
1178 | 1179 | code instead of printing them when you type their name. |
|
1179 | 1180 | |
|
1180 | 1181 | You can view a macro's contents by explicitly printing it with:: |
|
1181 | 1182 | |
|
1182 | 1183 | print macro_name |
|
1183 | 1184 | |
|
1184 | 1185 | """ |
|
1185 | 1186 | opts,args = self.parse_options(parameter_s,'rq',mode='list') |
|
1186 | 1187 | if not args: # List existing macros |
|
1187 | 1188 | return sorted(k for k,v in self.shell.user_ns.iteritems() if\ |
|
1188 | 1189 | isinstance(v, Macro)) |
|
1189 | 1190 | if len(args) == 1: |
|
1190 | 1191 | raise UsageError( |
|
1191 | 1192 | "%macro insufficient args; usage '%macro name n1-n2 n3-4...") |
|
1192 | 1193 | name, codefrom = args[0], " ".join(args[1:]) |
|
1193 | 1194 | |
|
1194 | 1195 | #print 'rng',ranges # dbg |
|
1195 | 1196 | try: |
|
1196 | 1197 | lines = self.shell.find_user_code(codefrom, 'r' in opts) |
|
1197 | 1198 | except (ValueError, TypeError) as e: |
|
1198 |
print |
|
|
1199 | print(e.args[0]) | |
|
1199 | 1200 | return |
|
1200 | 1201 | macro = Macro(lines) |
|
1201 | 1202 | self.shell.define_macro(name, macro) |
|
1202 | 1203 | if not ( 'q' in opts) : |
|
1203 |
print |
|
|
1204 |
print |
|
|
1205 |
print |
|
|
1204 | print('Macro `%s` created. To execute, type its name (without quotes).' % name) | |
|
1205 | print('=== Macro contents: ===') | |
|
1206 | print(macro, end=' ') | |
|
1206 | 1207 | |
|
1207 | 1208 | @magic_arguments.magic_arguments() |
|
1208 | 1209 | @magic_arguments.argument('output', type=str, default='', nargs='?', |
|
1209 | 1210 | help="""The name of the variable in which to store output. |
|
1210 | 1211 | This is a utils.io.CapturedIO object with stdout/err attributes |
|
1211 | 1212 | for the text of the captured output. |
|
1212 | 1213 | |
|
1213 | 1214 | CapturedOutput also has a show() method for displaying the output, |
|
1214 | 1215 | and __call__ as well, so you can use that to quickly display the |
|
1215 | 1216 | output. |
|
1216 | 1217 | |
|
1217 | 1218 | If unspecified, captured output is discarded. |
|
1218 | 1219 | """ |
|
1219 | 1220 | ) |
|
1220 | 1221 | @magic_arguments.argument('--no-stderr', action="store_true", |
|
1221 | 1222 | help="""Don't capture stderr.""" |
|
1222 | 1223 | ) |
|
1223 | 1224 | @magic_arguments.argument('--no-stdout', action="store_true", |
|
1224 | 1225 | help="""Don't capture stdout.""" |
|
1225 | 1226 | ) |
|
1226 | 1227 | @magic_arguments.argument('--no-display', action="store_true", |
|
1227 | 1228 | help="""Don't capture IPython's rich display.""" |
|
1228 | 1229 | ) |
|
1229 | 1230 | @cell_magic |
|
1230 | 1231 | def capture(self, line, cell): |
|
1231 | 1232 | """run the cell, capturing stdout, stderr, and IPython's rich display() calls.""" |
|
1232 | 1233 | args = magic_arguments.parse_argstring(self.capture, line) |
|
1233 | 1234 | out = not args.no_stdout |
|
1234 | 1235 | err = not args.no_stderr |
|
1235 | 1236 | disp = not args.no_display |
|
1236 | 1237 | with capture_output(out, err, disp) as io: |
|
1237 | 1238 | self.shell.run_cell(cell) |
|
1238 | 1239 | if args.output: |
|
1239 | 1240 | self.shell.user_ns[args.output] = io |
|
1240 | 1241 | |
|
1241 | 1242 | def parse_breakpoint(text, current_file): |
|
1242 | 1243 | '''Returns (file, line) for file:line and (current_file, line) for line''' |
|
1243 | 1244 | colon = text.find(':') |
|
1244 | 1245 | if colon == -1: |
|
1245 | 1246 | return current_file, int(text) |
|
1246 | 1247 | else: |
|
1247 | 1248 | return text[:colon], int(text[colon+1:]) |
|
1248 | 1249 | |
|
1249 | 1250 | def _format_time(timespan, precision=3): |
|
1250 | 1251 | """Formats the timespan in a human readable form""" |
|
1251 | 1252 | import math |
|
1252 | 1253 | |
|
1253 | 1254 | if timespan >= 60.0: |
|
1254 | 1255 | # we have more than a minute, format that in a human readable form |
|
1255 | 1256 | # Idea from http://snipplr.com/view/5713/ |
|
1256 | 1257 | parts = [("d", 60*60*24),("h", 60*60),("min", 60), ("s", 1)] |
|
1257 | 1258 | time = [] |
|
1258 | 1259 | leftover = timespan |
|
1259 | 1260 | for suffix, length in parts: |
|
1260 | 1261 | value = int(leftover / length) |
|
1261 | 1262 | if value > 0: |
|
1262 | 1263 | leftover = leftover % length |
|
1263 | 1264 | time.append(u'%s%s' % (str(value), suffix)) |
|
1264 | 1265 | if leftover < 1: |
|
1265 | 1266 | break |
|
1266 | 1267 | return " ".join(time) |
|
1267 | 1268 | |
|
1268 | 1269 | |
|
1269 | 1270 | # Unfortunately the unicode 'micro' symbol can cause problems in |
|
1270 | 1271 | # certain terminals. |
|
1271 | 1272 | # See bug: https://bugs.launchpad.net/ipython/+bug/348466 |
|
1272 | 1273 | # Try to prevent crashes by being more secure than it needs to |
|
1273 | 1274 | # E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set. |
|
1274 | 1275 | units = [u"s", u"ms",u'us',"ns"] # the save value |
|
1275 | 1276 | if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding: |
|
1276 | 1277 | try: |
|
1277 | 1278 | u'\xb5'.encode(sys.stdout.encoding) |
|
1278 | 1279 | units = [u"s", u"ms",u'\xb5s',"ns"] |
|
1279 | 1280 | except: |
|
1280 | 1281 | pass |
|
1281 | 1282 | scaling = [1, 1e3, 1e6, 1e9] |
|
1282 | 1283 | |
|
1283 | 1284 | if timespan > 0.0: |
|
1284 | 1285 | order = min(-int(math.floor(math.log10(timespan)) // 3), 3) |
|
1285 | 1286 | else: |
|
1286 | 1287 | order = 3 |
|
1287 | 1288 | return u"%.*g %s" % (precision, timespan * scaling[order], units[order]) |
@@ -1,92 +1,93 b'' | |||
|
1 | 1 | """Implementation of magic functions for the extension machinery. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Stdlib |
|
16 | 17 | import os |
|
17 | 18 | |
|
18 | 19 | # Our own packages |
|
19 | 20 | from IPython.core.error import UsageError |
|
20 | 21 | from IPython.core.magic import Magics, magics_class, line_magic |
|
21 | 22 | |
|
22 | 23 | #----------------------------------------------------------------------------- |
|
23 | 24 | # Magic implementation classes |
|
24 | 25 | #----------------------------------------------------------------------------- |
|
25 | 26 | |
|
26 | 27 | @magics_class |
|
27 | 28 | class ExtensionMagics(Magics): |
|
28 | 29 | """Magics to manage the IPython extensions system.""" |
|
29 | 30 | |
|
30 | 31 | @line_magic |
|
31 | 32 | def install_ext(self, parameter_s=''): |
|
32 | 33 | """Download and install an extension from a URL, e.g.:: |
|
33 | 34 | |
|
34 | 35 | %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py |
|
35 | 36 | |
|
36 | 37 | The URL should point to an importable Python module - either a .py file |
|
37 | 38 | or a .zip file. |
|
38 | 39 | |
|
39 | 40 | Parameters: |
|
40 | 41 | |
|
41 | 42 | -n filename : Specify a name for the file, rather than taking it from |
|
42 | 43 | the URL. |
|
43 | 44 | """ |
|
44 | 45 | opts, args = self.parse_options(parameter_s, 'n:') |
|
45 | 46 | try: |
|
46 | 47 | filename = self.shell.extension_manager.install_extension(args, |
|
47 | 48 | opts.get('n')) |
|
48 | 49 | except ValueError as e: |
|
49 |
print |
|
|
50 | print(e) | |
|
50 | 51 | return |
|
51 | 52 | |
|
52 | 53 | filename = os.path.basename(filename) |
|
53 |
print |
|
|
54 |
print |
|
|
54 | print("Installed %s. To use it, type:" % filename) | |
|
55 | print(" %%load_ext %s" % os.path.splitext(filename)[0]) | |
|
55 | 56 | |
|
56 | 57 | |
|
57 | 58 | @line_magic |
|
58 | 59 | def load_ext(self, module_str): |
|
59 | 60 | """Load an IPython extension by its module name.""" |
|
60 | 61 | if not module_str: |
|
61 | 62 | raise UsageError('Missing module name.') |
|
62 | 63 | res = self.shell.extension_manager.load_extension(module_str) |
|
63 | 64 | |
|
64 | 65 | if res == 'already loaded': |
|
65 |
print |
|
|
66 |
print |
|
|
66 | print("The %s extension is already loaded. To reload it, use:" % module_str) | |
|
67 | print(" %reload_ext", module_str) | |
|
67 | 68 | elif res == 'no load function': |
|
68 |
print |
|
|
69 | print("The %s module is not an IPython extension." % module_str) | |
|
69 | 70 | |
|
70 | 71 | @line_magic |
|
71 | 72 | def unload_ext(self, module_str): |
|
72 | 73 | """Unload an IPython extension by its module name. |
|
73 | 74 | |
|
74 | 75 | Not all extensions can be unloaded, only those which define an |
|
75 | 76 | ``unload_ipython_extension`` function. |
|
76 | 77 | """ |
|
77 | 78 | if not module_str: |
|
78 | 79 | raise UsageError('Missing module name.') |
|
79 | 80 | |
|
80 | 81 | res = self.shell.extension_manager.unload_extension(module_str) |
|
81 | 82 | |
|
82 | 83 | if res == 'no unload function': |
|
83 |
print |
|
|
84 | print("The %s extension doesn't define how to unload it." % module_str) | |
|
84 | 85 | elif res == "not loaded": |
|
85 |
print |
|
|
86 | print("The %s extension is not loaded." % module_str) | |
|
86 | 87 | |
|
87 | 88 | @line_magic |
|
88 | 89 | def reload_ext(self, module_str): |
|
89 | 90 | """Reload an IPython extension by its module name.""" |
|
90 | 91 | if not module_str: |
|
91 | 92 | raise UsageError('Missing module name.') |
|
92 | 93 | self.shell.extension_manager.reload_extension(module_str) |
@@ -1,704 +1,705 b'' | |||
|
1 | 1 | """Implementation of namespace-related magic functions. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Stdlib |
|
16 | 17 | import gc |
|
17 | 18 | import re |
|
18 | 19 | import sys |
|
19 | 20 | |
|
20 | 21 | # Our own packages |
|
21 | 22 | from IPython.core import page |
|
22 | 23 | from IPython.core.error import StdinNotImplementedError, UsageError |
|
23 | 24 | from IPython.core.magic import Magics, magics_class, line_magic |
|
24 | 25 | from IPython.testing.skipdoctest import skip_doctest |
|
25 | 26 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
26 | 27 | from IPython.utils.openpy import read_py_file |
|
27 | 28 | from IPython.utils.path import get_py_filename |
|
28 | 29 | |
|
29 | 30 | #----------------------------------------------------------------------------- |
|
30 | 31 | # Magic implementation classes |
|
31 | 32 | #----------------------------------------------------------------------------- |
|
32 | 33 | |
|
33 | 34 | @magics_class |
|
34 | 35 | class NamespaceMagics(Magics): |
|
35 | 36 | """Magics to manage various aspects of the user's namespace. |
|
36 | 37 | |
|
37 | 38 | These include listing variables, introspecting into them, etc. |
|
38 | 39 | """ |
|
39 | 40 | |
|
40 | 41 | @line_magic |
|
41 | 42 | def pinfo(self, parameter_s='', namespaces=None): |
|
42 | 43 | """Provide detailed information about an object. |
|
43 | 44 | |
|
44 | 45 | '%pinfo object' is just a synonym for object? or ?object.""" |
|
45 | 46 | |
|
46 | 47 | #print 'pinfo par: <%s>' % parameter_s # dbg |
|
47 | 48 | # detail_level: 0 -> obj? , 1 -> obj?? |
|
48 | 49 | detail_level = 0 |
|
49 | 50 | # We need to detect if we got called as 'pinfo pinfo foo', which can |
|
50 | 51 | # happen if the user types 'pinfo foo?' at the cmd line. |
|
51 | 52 | pinfo,qmark1,oname,qmark2 = \ |
|
52 | 53 | re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() |
|
53 | 54 | if pinfo or qmark1 or qmark2: |
|
54 | 55 | detail_level = 1 |
|
55 | 56 | if "*" in oname: |
|
56 | 57 | self.psearch(oname) |
|
57 | 58 | else: |
|
58 | 59 | self.shell._inspect('pinfo', oname, detail_level=detail_level, |
|
59 | 60 | namespaces=namespaces) |
|
60 | 61 | |
|
61 | 62 | @line_magic |
|
62 | 63 | def pinfo2(self, parameter_s='', namespaces=None): |
|
63 | 64 | """Provide extra detailed information about an object. |
|
64 | 65 | |
|
65 | 66 | '%pinfo2 object' is just a synonym for object?? or ??object.""" |
|
66 | 67 | self.shell._inspect('pinfo', parameter_s, detail_level=1, |
|
67 | 68 | namespaces=namespaces) |
|
68 | 69 | |
|
69 | 70 | @skip_doctest |
|
70 | 71 | @line_magic |
|
71 | 72 | def pdef(self, parameter_s='', namespaces=None): |
|
72 | 73 | """Print the call signature for any callable object. |
|
73 | 74 | |
|
74 | 75 | If the object is a class, print the constructor information. |
|
75 | 76 | |
|
76 | 77 | Examples |
|
77 | 78 | -------- |
|
78 | 79 | :: |
|
79 | 80 | |
|
80 | 81 | In [3]: %pdef urllib.urlopen |
|
81 | 82 | urllib.urlopen(url, data=None, proxies=None) |
|
82 | 83 | """ |
|
83 | 84 | self.shell._inspect('pdef',parameter_s, namespaces) |
|
84 | 85 | |
|
85 | 86 | @line_magic |
|
86 | 87 | def pdoc(self, parameter_s='', namespaces=None): |
|
87 | 88 | """Print the docstring for an object. |
|
88 | 89 | |
|
89 | 90 | If the given object is a class, it will print both the class and the |
|
90 | 91 | constructor docstrings.""" |
|
91 | 92 | self.shell._inspect('pdoc',parameter_s, namespaces) |
|
92 | 93 | |
|
93 | 94 | @line_magic |
|
94 | 95 | def psource(self, parameter_s='', namespaces=None): |
|
95 | 96 | """Print (or run through pager) the source code for an object.""" |
|
96 | 97 | if not parameter_s: |
|
97 | 98 | raise UsageError('Missing object name.') |
|
98 | 99 | self.shell._inspect('psource',parameter_s, namespaces) |
|
99 | 100 | |
|
100 | 101 | @line_magic |
|
101 | 102 | def pfile(self, parameter_s='', namespaces=None): |
|
102 | 103 | """Print (or run through pager) the file where an object is defined. |
|
103 | 104 | |
|
104 | 105 | The file opens at the line where the object definition begins. IPython |
|
105 | 106 | will honor the environment variable PAGER if set, and otherwise will |
|
106 | 107 | do its best to print the file in a convenient form. |
|
107 | 108 | |
|
108 | 109 | If the given argument is not an object currently defined, IPython will |
|
109 | 110 | try to interpret it as a filename (automatically adding a .py extension |
|
110 | 111 | if needed). You can thus use %pfile as a syntax highlighting code |
|
111 | 112 | viewer.""" |
|
112 | 113 | |
|
113 | 114 | # first interpret argument as an object name |
|
114 | 115 | out = self.shell._inspect('pfile',parameter_s, namespaces) |
|
115 | 116 | # if not, try the input as a filename |
|
116 | 117 | if out == 'not found': |
|
117 | 118 | try: |
|
118 | 119 | filename = get_py_filename(parameter_s) |
|
119 | 120 | except IOError as msg: |
|
120 |
print |
|
|
121 | print(msg) | |
|
121 | 122 | return |
|
122 | 123 | page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False))) |
|
123 | 124 | |
|
124 | 125 | @line_magic |
|
125 | 126 | def psearch(self, parameter_s=''): |
|
126 | 127 | """Search for object in namespaces by wildcard. |
|
127 | 128 | |
|
128 | 129 | %psearch [options] PATTERN [OBJECT TYPE] |
|
129 | 130 | |
|
130 | 131 | Note: ? can be used as a synonym for %psearch, at the beginning or at |
|
131 | 132 | the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the |
|
132 | 133 | rest of the command line must be unchanged (options come first), so |
|
133 | 134 | for example the following forms are equivalent |
|
134 | 135 | |
|
135 | 136 | %psearch -i a* function |
|
136 | 137 | -i a* function? |
|
137 | 138 | ?-i a* function |
|
138 | 139 | |
|
139 | 140 | Arguments: |
|
140 | 141 | |
|
141 | 142 | PATTERN |
|
142 | 143 | |
|
143 | 144 | where PATTERN is a string containing * as a wildcard similar to its |
|
144 | 145 | use in a shell. The pattern is matched in all namespaces on the |
|
145 | 146 | search path. By default objects starting with a single _ are not |
|
146 | 147 | matched, many IPython generated objects have a single |
|
147 | 148 | underscore. The default is case insensitive matching. Matching is |
|
148 | 149 | also done on the attributes of objects and not only on the objects |
|
149 | 150 | in a module. |
|
150 | 151 | |
|
151 | 152 | [OBJECT TYPE] |
|
152 | 153 | |
|
153 | 154 | Is the name of a python type from the types module. The name is |
|
154 | 155 | given in lowercase without the ending type, ex. StringType is |
|
155 | 156 | written string. By adding a type here only objects matching the |
|
156 | 157 | given type are matched. Using all here makes the pattern match all |
|
157 | 158 | types (this is the default). |
|
158 | 159 | |
|
159 | 160 | Options: |
|
160 | 161 | |
|
161 | 162 | -a: makes the pattern match even objects whose names start with a |
|
162 | 163 | single underscore. These names are normally omitted from the |
|
163 | 164 | search. |
|
164 | 165 | |
|
165 | 166 | -i/-c: make the pattern case insensitive/sensitive. If neither of |
|
166 | 167 | these options are given, the default is read from your configuration |
|
167 | 168 | file, with the option ``InteractiveShell.wildcards_case_sensitive``. |
|
168 | 169 | If this option is not specified in your configuration file, IPython's |
|
169 | 170 | internal default is to do a case sensitive search. |
|
170 | 171 | |
|
171 | 172 | -e/-s NAMESPACE: exclude/search a given namespace. The pattern you |
|
172 | 173 | specify can be searched in any of the following namespaces: |
|
173 | 174 | 'builtin', 'user', 'user_global','internal', 'alias', where |
|
174 | 175 | 'builtin' and 'user' are the search defaults. Note that you should |
|
175 | 176 | not use quotes when specifying namespaces. |
|
176 | 177 | |
|
177 | 178 | 'Builtin' contains the python module builtin, 'user' contains all |
|
178 | 179 | user data, 'alias' only contain the shell aliases and no python |
|
179 | 180 | objects, 'internal' contains objects used by IPython. The |
|
180 | 181 | 'user_global' namespace is only used by embedded IPython instances, |
|
181 | 182 | and it contains module-level globals. You can add namespaces to the |
|
182 | 183 | search with -s or exclude them with -e (these options can be given |
|
183 | 184 | more than once). |
|
184 | 185 | |
|
185 | 186 | Examples |
|
186 | 187 | -------- |
|
187 | 188 | :: |
|
188 | 189 | |
|
189 | 190 | %psearch a* -> objects beginning with an a |
|
190 | 191 | %psearch -e builtin a* -> objects NOT in the builtin space starting in a |
|
191 | 192 | %psearch a* function -> all functions beginning with an a |
|
192 | 193 | %psearch re.e* -> objects beginning with an e in module re |
|
193 | 194 | %psearch r*.e* -> objects that start with e in modules starting in r |
|
194 | 195 | %psearch r*.* string -> all strings in modules beginning with r |
|
195 | 196 | |
|
196 | 197 | Case sensitive search:: |
|
197 | 198 | |
|
198 | 199 | %psearch -c a* list all object beginning with lower case a |
|
199 | 200 | |
|
200 | 201 | Show objects beginning with a single _:: |
|
201 | 202 | |
|
202 | 203 | %psearch -a _* list objects beginning with a single underscore |
|
203 | 204 | """ |
|
204 | 205 | try: |
|
205 | 206 | parameter_s.encode('ascii') |
|
206 | 207 | except UnicodeEncodeError: |
|
207 |
print |
|
|
208 | print('Python identifiers can only contain ascii characters.') | |
|
208 | 209 | return |
|
209 | 210 | |
|
210 | 211 | # default namespaces to be searched |
|
211 | 212 | def_search = ['user_local', 'user_global', 'builtin'] |
|
212 | 213 | |
|
213 | 214 | # Process options/args |
|
214 | 215 | opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) |
|
215 | 216 | opt = opts.get |
|
216 | 217 | shell = self.shell |
|
217 | 218 | psearch = shell.inspector.psearch |
|
218 | 219 | |
|
219 | 220 | # select case options |
|
220 | 221 | if 'i' in opts: |
|
221 | 222 | ignore_case = True |
|
222 | 223 | elif 'c' in opts: |
|
223 | 224 | ignore_case = False |
|
224 | 225 | else: |
|
225 | 226 | ignore_case = not shell.wildcards_case_sensitive |
|
226 | 227 | |
|
227 | 228 | # Build list of namespaces to search from user options |
|
228 | 229 | def_search.extend(opt('s',[])) |
|
229 | 230 | ns_exclude = ns_exclude=opt('e',[]) |
|
230 | 231 | ns_search = [nm for nm in def_search if nm not in ns_exclude] |
|
231 | 232 | |
|
232 | 233 | # Call the actual search |
|
233 | 234 | try: |
|
234 | 235 | psearch(args,shell.ns_table,ns_search, |
|
235 | 236 | show_all=opt('a'),ignore_case=ignore_case) |
|
236 | 237 | except: |
|
237 | 238 | shell.showtraceback() |
|
238 | 239 | |
|
239 | 240 | @skip_doctest |
|
240 | 241 | @line_magic |
|
241 | 242 | def who_ls(self, parameter_s=''): |
|
242 | 243 | """Return a sorted list of all interactive variables. |
|
243 | 244 | |
|
244 | 245 | If arguments are given, only variables of types matching these |
|
245 | 246 | arguments are returned. |
|
246 | 247 | |
|
247 | 248 | Examples |
|
248 | 249 | -------- |
|
249 | 250 | |
|
250 | 251 | Define two variables and list them with who_ls:: |
|
251 | 252 | |
|
252 | 253 | In [1]: alpha = 123 |
|
253 | 254 | |
|
254 | 255 | In [2]: beta = 'test' |
|
255 | 256 | |
|
256 | 257 | In [3]: %who_ls |
|
257 | 258 | Out[3]: ['alpha', 'beta'] |
|
258 | 259 | |
|
259 | 260 | In [4]: %who_ls int |
|
260 | 261 | Out[4]: ['alpha'] |
|
261 | 262 | |
|
262 | 263 | In [5]: %who_ls str |
|
263 | 264 | Out[5]: ['beta'] |
|
264 | 265 | """ |
|
265 | 266 | |
|
266 | 267 | user_ns = self.shell.user_ns |
|
267 | 268 | user_ns_hidden = self.shell.user_ns_hidden |
|
268 | 269 | nonmatching = object() # This can never be in user_ns |
|
269 | 270 | out = [ i for i in user_ns |
|
270 | 271 | if not i.startswith('_') \ |
|
271 | 272 | and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ] |
|
272 | 273 | |
|
273 | 274 | typelist = parameter_s.split() |
|
274 | 275 | if typelist: |
|
275 | 276 | typeset = set(typelist) |
|
276 | 277 | out = [i for i in out if type(user_ns[i]).__name__ in typeset] |
|
277 | 278 | |
|
278 | 279 | out.sort() |
|
279 | 280 | return out |
|
280 | 281 | |
|
281 | 282 | @skip_doctest |
|
282 | 283 | @line_magic |
|
283 | 284 | def who(self, parameter_s=''): |
|
284 | 285 | """Print all interactive variables, with some minimal formatting. |
|
285 | 286 | |
|
286 | 287 | If any arguments are given, only variables whose type matches one of |
|
287 | 288 | these are printed. For example:: |
|
288 | 289 | |
|
289 | 290 | %who function str |
|
290 | 291 | |
|
291 | 292 | will only list functions and strings, excluding all other types of |
|
292 | 293 | variables. To find the proper type names, simply use type(var) at a |
|
293 | 294 | command line to see how python prints type names. For example: |
|
294 | 295 | |
|
295 | 296 | :: |
|
296 | 297 | |
|
297 | 298 | In [1]: type('hello')\\ |
|
298 | 299 | Out[1]: <type 'str'> |
|
299 | 300 | |
|
300 | 301 | indicates that the type name for strings is 'str'. |
|
301 | 302 | |
|
302 | 303 | ``%who`` always excludes executed names loaded through your configuration |
|
303 | 304 | file and things which are internal to IPython. |
|
304 | 305 | |
|
305 | 306 | This is deliberate, as typically you may load many modules and the |
|
306 | 307 | purpose of %who is to show you only what you've manually defined. |
|
307 | 308 | |
|
308 | 309 | Examples |
|
309 | 310 | -------- |
|
310 | 311 | |
|
311 | 312 | Define two variables and list them with who:: |
|
312 | 313 | |
|
313 | 314 | In [1]: alpha = 123 |
|
314 | 315 | |
|
315 | 316 | In [2]: beta = 'test' |
|
316 | 317 | |
|
317 | 318 | In [3]: %who |
|
318 | 319 | alpha beta |
|
319 | 320 | |
|
320 | 321 | In [4]: %who int |
|
321 | 322 | alpha |
|
322 | 323 | |
|
323 | 324 | In [5]: %who str |
|
324 | 325 | beta |
|
325 | 326 | """ |
|
326 | 327 | |
|
327 | 328 | varlist = self.who_ls(parameter_s) |
|
328 | 329 | if not varlist: |
|
329 | 330 | if parameter_s: |
|
330 |
print |
|
|
331 | print('No variables match your requested type.') | |
|
331 | 332 | else: |
|
332 |
print |
|
|
333 | print('Interactive namespace is empty.') | |
|
333 | 334 | return |
|
334 | 335 | |
|
335 | 336 | # if we have variables, move on... |
|
336 | 337 | count = 0 |
|
337 | 338 | for i in varlist: |
|
338 |
print |
|
|
339 | print(i+'\t', end=' ') | |
|
339 | 340 | count += 1 |
|
340 | 341 | if count > 8: |
|
341 | 342 | count = 0 |
|
342 | ||
|
343 | ||
|
343 | print() | |
|
344 | print() | |
|
344 | 345 | |
|
345 | 346 | @skip_doctest |
|
346 | 347 | @line_magic |
|
347 | 348 | def whos(self, parameter_s=''): |
|
348 | 349 | """Like %who, but gives some extra information about each variable. |
|
349 | 350 | |
|
350 | 351 | The same type filtering of %who can be applied here. |
|
351 | 352 | |
|
352 | 353 | For all variables, the type is printed. Additionally it prints: |
|
353 | 354 | |
|
354 | 355 | - For {},[],(): their length. |
|
355 | 356 | |
|
356 | 357 | - For numpy arrays, a summary with shape, number of |
|
357 | 358 | elements, typecode and size in memory. |
|
358 | 359 | |
|
359 | 360 | - Everything else: a string representation, snipping their middle if |
|
360 | 361 | too long. |
|
361 | 362 | |
|
362 | 363 | Examples |
|
363 | 364 | -------- |
|
364 | 365 | |
|
365 | 366 | Define two variables and list them with whos:: |
|
366 | 367 | |
|
367 | 368 | In [1]: alpha = 123 |
|
368 | 369 | |
|
369 | 370 | In [2]: beta = 'test' |
|
370 | 371 | |
|
371 | 372 | In [3]: %whos |
|
372 | 373 | Variable Type Data/Info |
|
373 | 374 | -------------------------------- |
|
374 | 375 | alpha int 123 |
|
375 | 376 | beta str test |
|
376 | 377 | """ |
|
377 | 378 | |
|
378 | 379 | varnames = self.who_ls(parameter_s) |
|
379 | 380 | if not varnames: |
|
380 | 381 | if parameter_s: |
|
381 |
print |
|
|
382 | print('No variables match your requested type.') | |
|
382 | 383 | else: |
|
383 |
print |
|
|
384 | print('Interactive namespace is empty.') | |
|
384 | 385 | return |
|
385 | 386 | |
|
386 | 387 | # if we have variables, move on... |
|
387 | 388 | |
|
388 | 389 | # for these types, show len() instead of data: |
|
389 | 390 | seq_types = ['dict', 'list', 'tuple'] |
|
390 | 391 | |
|
391 | 392 | # for numpy arrays, display summary info |
|
392 | 393 | ndarray_type = None |
|
393 | 394 | if 'numpy' in sys.modules: |
|
394 | 395 | try: |
|
395 | 396 | from numpy import ndarray |
|
396 | 397 | except ImportError: |
|
397 | 398 | pass |
|
398 | 399 | else: |
|
399 | 400 | ndarray_type = ndarray.__name__ |
|
400 | 401 | |
|
401 | 402 | # Find all variable names and types so we can figure out column sizes |
|
402 | 403 | def get_vars(i): |
|
403 | 404 | return self.shell.user_ns[i] |
|
404 | 405 | |
|
405 | 406 | # some types are well known and can be shorter |
|
406 | 407 | abbrevs = {'IPython.core.macro.Macro' : 'Macro'} |
|
407 | 408 | def type_name(v): |
|
408 | 409 | tn = type(v).__name__ |
|
409 | 410 | return abbrevs.get(tn,tn) |
|
410 | 411 | |
|
411 | 412 | varlist = map(get_vars,varnames) |
|
412 | 413 | |
|
413 | 414 | typelist = [] |
|
414 | 415 | for vv in varlist: |
|
415 | 416 | tt = type_name(vv) |
|
416 | 417 | |
|
417 | 418 | if tt=='instance': |
|
418 | 419 | typelist.append( abbrevs.get(str(vv.__class__), |
|
419 | 420 | str(vv.__class__))) |
|
420 | 421 | else: |
|
421 | 422 | typelist.append(tt) |
|
422 | 423 | |
|
423 | 424 | # column labels and # of spaces as separator |
|
424 | 425 | varlabel = 'Variable' |
|
425 | 426 | typelabel = 'Type' |
|
426 | 427 | datalabel = 'Data/Info' |
|
427 | 428 | colsep = 3 |
|
428 | 429 | # variable format strings |
|
429 | 430 | vformat = "{0:<{varwidth}}{1:<{typewidth}}" |
|
430 | 431 | aformat = "%s: %s elems, type `%s`, %s bytes" |
|
431 | 432 | # find the size of the columns to format the output nicely |
|
432 | 433 | varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep |
|
433 | 434 | typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep |
|
434 | 435 | # table header |
|
435 |
print |
|
|
436 | ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) | |
|
436 | print(varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ | |
|
437 | ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1)) | |
|
437 | 438 | # and the table itself |
|
438 | 439 | kb = 1024 |
|
439 | 440 | Mb = 1048576 # kb**2 |
|
440 | 441 | for vname,var,vtype in zip(varnames,varlist,typelist): |
|
441 |
print |
|
|
442 | print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ') | |
|
442 | 443 | if vtype in seq_types: |
|
443 |
print |
|
|
444 | print("n="+str(len(var))) | |
|
444 | 445 | elif vtype == ndarray_type: |
|
445 | 446 | vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] |
|
446 | 447 | if vtype==ndarray_type: |
|
447 | 448 | # numpy |
|
448 | 449 | vsize = var.size |
|
449 | 450 | vbytes = vsize*var.itemsize |
|
450 | 451 | vdtype = var.dtype |
|
451 | 452 | |
|
452 | 453 | if vbytes < 100000: |
|
453 |
print |
|
|
454 | print(aformat % (vshape, vsize, vdtype, vbytes)) | |
|
454 | 455 | else: |
|
455 |
print |
|
|
456 | print(aformat % (vshape, vsize, vdtype, vbytes), end=' ') | |
|
456 | 457 | if vbytes < Mb: |
|
457 |
print |
|
|
458 | print('(%s kb)' % (vbytes/kb,)) | |
|
458 | 459 | else: |
|
459 |
print |
|
|
460 | print('(%s Mb)' % (vbytes/Mb,)) | |
|
460 | 461 | else: |
|
461 | 462 | try: |
|
462 | 463 | vstr = str(var) |
|
463 | 464 | except UnicodeEncodeError: |
|
464 | 465 | vstr = unicode(var).encode(DEFAULT_ENCODING, |
|
465 | 466 | 'backslashreplace') |
|
466 | 467 | except: |
|
467 | 468 | vstr = "<object with id %d (str() failed)>" % id(var) |
|
468 | 469 | vstr = vstr.replace('\n', '\\n') |
|
469 | 470 | if len(vstr) < 50: |
|
470 |
print |
|
|
471 | print(vstr) | |
|
471 | 472 | else: |
|
472 |
print |
|
|
473 | print(vstr[:25] + "<...>" + vstr[-25:]) | |
|
473 | 474 | |
|
474 | 475 | @line_magic |
|
475 | 476 | def reset(self, parameter_s=''): |
|
476 | 477 | """Resets the namespace by removing all names defined by the user, if |
|
477 | 478 | called without arguments, or by removing some types of objects, such |
|
478 | 479 | as everything currently in IPython's In[] and Out[] containers (see |
|
479 | 480 | the parameters for details). |
|
480 | 481 | |
|
481 | 482 | Parameters |
|
482 | 483 | ---------- |
|
483 | 484 | -f : force reset without asking for confirmation. |
|
484 | 485 | |
|
485 | 486 | -s : 'Soft' reset: Only clears your namespace, leaving history intact. |
|
486 | 487 | References to objects may be kept. By default (without this option), |
|
487 | 488 | we do a 'hard' reset, giving you a new session and removing all |
|
488 | 489 | references to objects from the current session. |
|
489 | 490 | |
|
490 | 491 | in : reset input history |
|
491 | 492 | |
|
492 | 493 | out : reset output history |
|
493 | 494 | |
|
494 | 495 | dhist : reset directory history |
|
495 | 496 | |
|
496 | 497 | array : reset only variables that are NumPy arrays |
|
497 | 498 | |
|
498 | 499 | See Also |
|
499 | 500 | -------- |
|
500 | 501 | magic_reset_selective : invoked as ``%reset_selective`` |
|
501 | 502 | |
|
502 | 503 | Examples |
|
503 | 504 | -------- |
|
504 | 505 | :: |
|
505 | 506 | |
|
506 | 507 | In [6]: a = 1 |
|
507 | 508 | |
|
508 | 509 | In [7]: a |
|
509 | 510 | Out[7]: 1 |
|
510 | 511 | |
|
511 | 512 | In [8]: 'a' in _ip.user_ns |
|
512 | 513 | Out[8]: True |
|
513 | 514 | |
|
514 | 515 | In [9]: %reset -f |
|
515 | 516 | |
|
516 | 517 | In [1]: 'a' in _ip.user_ns |
|
517 | 518 | Out[1]: False |
|
518 | 519 | |
|
519 | 520 | In [2]: %reset -f in |
|
520 | 521 | Flushing input history |
|
521 | 522 | |
|
522 | 523 | In [3]: %reset -f dhist in |
|
523 | 524 | Flushing directory history |
|
524 | 525 | Flushing input history |
|
525 | 526 | |
|
526 | 527 | Notes |
|
527 | 528 | ----- |
|
528 | 529 | Calling this magic from clients that do not implement standard input, |
|
529 | 530 | such as the ipython notebook interface, will reset the namespace |
|
530 | 531 | without confirmation. |
|
531 | 532 | """ |
|
532 | 533 | opts, args = self.parse_options(parameter_s,'sf', mode='list') |
|
533 | 534 | if 'f' in opts: |
|
534 | 535 | ans = True |
|
535 | 536 | else: |
|
536 | 537 | try: |
|
537 | 538 | ans = self.shell.ask_yes_no( |
|
538 | 539 | "Once deleted, variables cannot be recovered. Proceed (y/[n])?", |
|
539 | 540 | default='n') |
|
540 | 541 | except StdinNotImplementedError: |
|
541 | 542 | ans = True |
|
542 | 543 | if not ans: |
|
543 |
print |
|
|
544 | print('Nothing done.') | |
|
544 | 545 | return |
|
545 | 546 | |
|
546 | 547 | if 's' in opts: # Soft reset |
|
547 | 548 | user_ns = self.shell.user_ns |
|
548 | 549 | for i in self.who_ls(): |
|
549 | 550 | del(user_ns[i]) |
|
550 | 551 | elif len(args) == 0: # Hard reset |
|
551 | 552 | self.shell.reset(new_session = False) |
|
552 | 553 | |
|
553 | 554 | # reset in/out/dhist/array: previously extensinions/clearcmd.py |
|
554 | 555 | ip = self.shell |
|
555 | 556 | user_ns = self.shell.user_ns # local lookup, heavily used |
|
556 | 557 | |
|
557 | 558 | for target in args: |
|
558 | 559 | target = target.lower() # make matches case insensitive |
|
559 | 560 | if target == 'out': |
|
560 |
print |
|
|
561 | print("Flushing output cache (%d entries)" % len(user_ns['_oh'])) | |
|
561 | 562 | self.shell.displayhook.flush() |
|
562 | 563 | |
|
563 | 564 | elif target == 'in': |
|
564 |
print |
|
|
565 | print("Flushing input history") | |
|
565 | 566 | pc = self.shell.displayhook.prompt_count + 1 |
|
566 | 567 | for n in range(1, pc): |
|
567 | 568 | key = '_i'+repr(n) |
|
568 | 569 | user_ns.pop(key,None) |
|
569 | 570 | user_ns.update(dict(_i=u'',_ii=u'',_iii=u'')) |
|
570 | 571 | hm = ip.history_manager |
|
571 | 572 | # don't delete these, as %save and %macro depending on the |
|
572 | 573 | # length of these lists to be preserved |
|
573 | 574 | hm.input_hist_parsed[:] = [''] * pc |
|
574 | 575 | hm.input_hist_raw[:] = [''] * pc |
|
575 | 576 | # hm has internal machinery for _i,_ii,_iii, clear it out |
|
576 | 577 | hm._i = hm._ii = hm._iii = hm._i00 = u'' |
|
577 | 578 | |
|
578 | 579 | elif target == 'array': |
|
579 | 580 | # Support cleaning up numpy arrays |
|
580 | 581 | try: |
|
581 | 582 | from numpy import ndarray |
|
582 | 583 | # This must be done with items and not iteritems because |
|
583 | 584 | # we're going to modify the dict in-place. |
|
584 | 585 | for x,val in user_ns.items(): |
|
585 | 586 | if isinstance(val,ndarray): |
|
586 | 587 | del user_ns[x] |
|
587 | 588 | except ImportError: |
|
588 |
print |
|
|
589 | print("reset array only works if Numpy is available.") | |
|
589 | 590 | |
|
590 | 591 | elif target == 'dhist': |
|
591 |
print |
|
|
592 | print("Flushing directory history") | |
|
592 | 593 | del user_ns['_dh'][:] |
|
593 | 594 | |
|
594 | 595 | else: |
|
595 |
print |
|
|
596 |
print |
|
|
596 | print("Don't know how to reset ", end=' ') | |
|
597 | print(target + ", please run `%reset?` for details") | |
|
597 | 598 | |
|
598 | 599 | gc.collect() |
|
599 | 600 | |
|
600 | 601 | @line_magic |
|
601 | 602 | def reset_selective(self, parameter_s=''): |
|
602 | 603 | """Resets the namespace by removing names defined by the user. |
|
603 | 604 | |
|
604 | 605 | Input/Output history are left around in case you need them. |
|
605 | 606 | |
|
606 | 607 | %reset_selective [-f] regex |
|
607 | 608 | |
|
608 | 609 | No action is taken if regex is not included |
|
609 | 610 | |
|
610 | 611 | Options |
|
611 | 612 | -f : force reset without asking for confirmation. |
|
612 | 613 | |
|
613 | 614 | See Also |
|
614 | 615 | -------- |
|
615 | 616 | magic_reset : invoked as ``%reset`` |
|
616 | 617 | |
|
617 | 618 | Examples |
|
618 | 619 | -------- |
|
619 | 620 | |
|
620 | 621 | We first fully reset the namespace so your output looks identical to |
|
621 | 622 | this example for pedagogical reasons; in practice you do not need a |
|
622 | 623 | full reset:: |
|
623 | 624 | |
|
624 | 625 | In [1]: %reset -f |
|
625 | 626 | |
|
626 | 627 | Now, with a clean namespace we can make a few variables and use |
|
627 | 628 | ``%reset_selective`` to only delete names that match our regexp:: |
|
628 | 629 | |
|
629 | 630 | In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 |
|
630 | 631 | |
|
631 | 632 | In [3]: who_ls |
|
632 | 633 | Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] |
|
633 | 634 | |
|
634 | 635 | In [4]: %reset_selective -f b[2-3]m |
|
635 | 636 | |
|
636 | 637 | In [5]: who_ls |
|
637 | 638 | Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] |
|
638 | 639 | |
|
639 | 640 | In [6]: %reset_selective -f d |
|
640 | 641 | |
|
641 | 642 | In [7]: who_ls |
|
642 | 643 | Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] |
|
643 | 644 | |
|
644 | 645 | In [8]: %reset_selective -f c |
|
645 | 646 | |
|
646 | 647 | In [9]: who_ls |
|
647 | 648 | Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] |
|
648 | 649 | |
|
649 | 650 | In [10]: %reset_selective -f b |
|
650 | 651 | |
|
651 | 652 | In [11]: who_ls |
|
652 | 653 | Out[11]: ['a'] |
|
653 | 654 | |
|
654 | 655 | Notes |
|
655 | 656 | ----- |
|
656 | 657 | Calling this magic from clients that do not implement standard input, |
|
657 | 658 | such as the ipython notebook interface, will reset the namespace |
|
658 | 659 | without confirmation. |
|
659 | 660 | """ |
|
660 | 661 | |
|
661 | 662 | opts, regex = self.parse_options(parameter_s,'f') |
|
662 | 663 | |
|
663 | 664 | if 'f' in opts: |
|
664 | 665 | ans = True |
|
665 | 666 | else: |
|
666 | 667 | try: |
|
667 | 668 | ans = self.shell.ask_yes_no( |
|
668 | 669 | "Once deleted, variables cannot be recovered. Proceed (y/[n])? ", |
|
669 | 670 | default='n') |
|
670 | 671 | except StdinNotImplementedError: |
|
671 | 672 | ans = True |
|
672 | 673 | if not ans: |
|
673 |
print |
|
|
674 | print('Nothing done.') | |
|
674 | 675 | return |
|
675 | 676 | user_ns = self.shell.user_ns |
|
676 | 677 | if not regex: |
|
677 |
print |
|
|
678 | print('No regex pattern specified. Nothing done.') | |
|
678 | 679 | return |
|
679 | 680 | else: |
|
680 | 681 | try: |
|
681 | 682 | m = re.compile(regex) |
|
682 | 683 | except TypeError: |
|
683 | 684 | raise TypeError('regex must be a string or compiled pattern') |
|
684 | 685 | for i in self.who_ls(): |
|
685 | 686 | if m.search(i): |
|
686 | 687 | del(user_ns[i]) |
|
687 | 688 | |
|
688 | 689 | @line_magic |
|
689 | 690 | def xdel(self, parameter_s=''): |
|
690 | 691 | """Delete a variable, trying to clear it from anywhere that |
|
691 | 692 | IPython's machinery has references to it. By default, this uses |
|
692 | 693 | the identity of the named object in the user namespace to remove |
|
693 | 694 | references held under other names. The object is also removed |
|
694 | 695 | from the output history. |
|
695 | 696 | |
|
696 | 697 | Options |
|
697 | 698 | -n : Delete the specified name from all namespaces, without |
|
698 | 699 | checking their identity. |
|
699 | 700 | """ |
|
700 | 701 | opts, varname = self.parse_options(parameter_s,'n') |
|
701 | 702 | try: |
|
702 | 703 | self.shell.del_var(varname, ('n' in opts)) |
|
703 | 704 | except (NameError, ValueError) as e: |
|
704 |
print |
|
|
705 | print(type(e).__name__ +": "+ str(e)) |
@@ -1,738 +1,739 b'' | |||
|
1 | 1 | """Implementation of magic functions for interaction with the OS. |
|
2 | 2 | |
|
3 | 3 | Note: this module is named 'osm' instead of 'os' to avoid a collision with the |
|
4 | 4 | builtin. |
|
5 | 5 | """ |
|
6 | from __future__ import print_function | |
|
6 | 7 | #----------------------------------------------------------------------------- |
|
7 | 8 | # Copyright (c) 2012 The IPython Development Team. |
|
8 | 9 | # |
|
9 | 10 | # Distributed under the terms of the Modified BSD License. |
|
10 | 11 | # |
|
11 | 12 | # The full license is in the file COPYING.txt, distributed with this software. |
|
12 | 13 | #----------------------------------------------------------------------------- |
|
13 | 14 | |
|
14 | 15 | #----------------------------------------------------------------------------- |
|
15 | 16 | # Imports |
|
16 | 17 | #----------------------------------------------------------------------------- |
|
17 | 18 | |
|
18 | 19 | # Stdlib |
|
19 | 20 | import io |
|
20 | 21 | import os |
|
21 | 22 | import re |
|
22 | 23 | import sys |
|
23 | 24 | from pprint import pformat |
|
24 | 25 | |
|
25 | 26 | # Our own packages |
|
26 | 27 | from IPython.core import magic_arguments |
|
27 | 28 | from IPython.core import oinspect |
|
28 | 29 | from IPython.core import page |
|
29 | 30 | from IPython.core.alias import AliasError, Alias |
|
30 | 31 | from IPython.core.error import UsageError |
|
31 | 32 | from IPython.core.magic import ( |
|
32 | 33 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic |
|
33 | 34 | ) |
|
34 | 35 | from IPython.testing.skipdoctest import skip_doctest |
|
35 | 36 | from IPython.utils.openpy import source_to_unicode |
|
36 | 37 | from IPython.utils.path import unquote_filename |
|
37 | 38 | from IPython.utils.process import abbrev_cwd |
|
38 | 39 | from IPython.utils.terminal import set_term_title |
|
39 | 40 | |
|
40 | 41 | #----------------------------------------------------------------------------- |
|
41 | 42 | # Magic implementation classes |
|
42 | 43 | #----------------------------------------------------------------------------- |
|
43 | 44 | @magics_class |
|
44 | 45 | class OSMagics(Magics): |
|
45 | 46 | """Magics to interact with the underlying OS (shell-type functionality). |
|
46 | 47 | """ |
|
47 | 48 | |
|
48 | 49 | @skip_doctest |
|
49 | 50 | @line_magic |
|
50 | 51 | def alias(self, parameter_s=''): |
|
51 | 52 | """Define an alias for a system command. |
|
52 | 53 | |
|
53 | 54 | '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' |
|
54 | 55 | |
|
55 | 56 | Then, typing 'alias_name params' will execute the system command 'cmd |
|
56 | 57 | params' (from your underlying operating system). |
|
57 | 58 | |
|
58 | 59 | Aliases have lower precedence than magic functions and Python normal |
|
59 | 60 | variables, so if 'foo' is both a Python variable and an alias, the |
|
60 | 61 | alias can not be executed until 'del foo' removes the Python variable. |
|
61 | 62 | |
|
62 | 63 | You can use the %l specifier in an alias definition to represent the |
|
63 | 64 | whole line when the alias is called. For example:: |
|
64 | 65 | |
|
65 | 66 | In [2]: alias bracket echo "Input in brackets: <%l>" |
|
66 | 67 | In [3]: bracket hello world |
|
67 | 68 | Input in brackets: <hello world> |
|
68 | 69 | |
|
69 | 70 | You can also define aliases with parameters using %s specifiers (one |
|
70 | 71 | per parameter):: |
|
71 | 72 | |
|
72 | 73 | In [1]: alias parts echo first %s second %s |
|
73 | 74 | In [2]: %parts A B |
|
74 | 75 | first A second B |
|
75 | 76 | In [3]: %parts A |
|
76 | 77 | Incorrect number of arguments: 2 expected. |
|
77 | 78 | parts is an alias to: 'echo first %s second %s' |
|
78 | 79 | |
|
79 | 80 | Note that %l and %s are mutually exclusive. You can only use one or |
|
80 | 81 | the other in your aliases. |
|
81 | 82 | |
|
82 | 83 | Aliases expand Python variables just like system calls using ! or !! |
|
83 | 84 | do: all expressions prefixed with '$' get expanded. For details of |
|
84 | 85 | the semantic rules, see PEP-215: |
|
85 | 86 | http://www.python.org/peps/pep-0215.html. This is the library used by |
|
86 | 87 | IPython for variable expansion. If you want to access a true shell |
|
87 | 88 | variable, an extra $ is necessary to prevent its expansion by |
|
88 | 89 | IPython:: |
|
89 | 90 | |
|
90 | 91 | In [6]: alias show echo |
|
91 | 92 | In [7]: PATH='A Python string' |
|
92 | 93 | In [8]: show $PATH |
|
93 | 94 | A Python string |
|
94 | 95 | In [9]: show $$PATH |
|
95 | 96 | /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... |
|
96 | 97 | |
|
97 | 98 | You can use the alias facility to acess all of $PATH. See the %rehash |
|
98 | 99 | and %rehashx functions, which automatically create aliases for the |
|
99 | 100 | contents of your $PATH. |
|
100 | 101 | |
|
101 | 102 | If called with no parameters, %alias prints the current alias table.""" |
|
102 | 103 | |
|
103 | 104 | par = parameter_s.strip() |
|
104 | 105 | if not par: |
|
105 | 106 | aliases = sorted(self.shell.alias_manager.aliases) |
|
106 | 107 | # stored = self.shell.db.get('stored_aliases', {} ) |
|
107 | 108 | # for k, v in stored: |
|
108 | 109 | # atab.append(k, v[0]) |
|
109 | 110 | |
|
110 |
print |
|
|
111 | print("Total number of aliases:", len(aliases)) | |
|
111 | 112 | sys.stdout.flush() |
|
112 | 113 | return aliases |
|
113 | 114 | |
|
114 | 115 | # Now try to define a new one |
|
115 | 116 | try: |
|
116 | 117 | alias,cmd = par.split(None, 1) |
|
117 | 118 | except TypeError: |
|
118 | print(oinspect.getdoc(self.alias)) | |
|
119 | print((oinspect.getdoc(self.alias))) | |
|
119 | 120 | return |
|
120 | 121 | |
|
121 | 122 | try: |
|
122 | 123 | self.shell.alias_manager.define_alias(alias, cmd) |
|
123 | 124 | except AliasError as e: |
|
124 | 125 | print(e) |
|
125 | 126 | # end magic_alias |
|
126 | 127 | |
|
127 | 128 | @line_magic |
|
128 | 129 | def unalias(self, parameter_s=''): |
|
129 | 130 | """Remove an alias""" |
|
130 | 131 | |
|
131 | 132 | aname = parameter_s.strip() |
|
132 | 133 | try: |
|
133 | 134 | self.shell.alias_manager.undefine_alias(aname) |
|
134 | 135 | except ValueError as e: |
|
135 | 136 | print(e) |
|
136 | 137 | return |
|
137 | 138 | |
|
138 | 139 | stored = self.shell.db.get('stored_aliases', {} ) |
|
139 | 140 | if aname in stored: |
|
140 |
print |
|
|
141 | print("Removing %stored alias",aname) | |
|
141 | 142 | del stored[aname] |
|
142 | 143 | self.shell.db['stored_aliases'] = stored |
|
143 | 144 | |
|
144 | 145 | @line_magic |
|
145 | 146 | def rehashx(self, parameter_s=''): |
|
146 | 147 | """Update the alias table with all executable files in $PATH. |
|
147 | 148 | |
|
148 | 149 | This version explicitly checks that every entry in $PATH is a file |
|
149 | 150 | with execute access (os.X_OK), so it is much slower than %rehash. |
|
150 | 151 | |
|
151 | 152 | Under Windows, it checks executability as a match against a |
|
152 | 153 | '|'-separated string of extensions, stored in the IPython config |
|
153 | 154 | variable win_exec_ext. This defaults to 'exe|com|bat'. |
|
154 | 155 | |
|
155 | 156 | This function also resets the root module cache of module completer, |
|
156 | 157 | used on slow filesystems. |
|
157 | 158 | """ |
|
158 | 159 | from IPython.core.alias import InvalidAliasError |
|
159 | 160 | |
|
160 | 161 | # for the benefit of module completer in ipy_completers.py |
|
161 | 162 | del self.shell.db['rootmodules_cache'] |
|
162 | 163 | |
|
163 | 164 | path = [os.path.abspath(os.path.expanduser(p)) for p in |
|
164 | 165 | os.environ.get('PATH','').split(os.pathsep)] |
|
165 | 166 | path = filter(os.path.isdir,path) |
|
166 | 167 | |
|
167 | 168 | syscmdlist = [] |
|
168 | 169 | # Now define isexec in a cross platform manner. |
|
169 | 170 | if os.name == 'posix': |
|
170 | 171 | isexec = lambda fname:os.path.isfile(fname) and \ |
|
171 | 172 | os.access(fname,os.X_OK) |
|
172 | 173 | else: |
|
173 | 174 | try: |
|
174 | 175 | winext = os.environ['pathext'].replace(';','|').replace('.','') |
|
175 | 176 | except KeyError: |
|
176 | 177 | winext = 'exe|com|bat|py' |
|
177 | 178 | if 'py' not in winext: |
|
178 | 179 | winext += '|py' |
|
179 | 180 | execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) |
|
180 | 181 | isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) |
|
181 | 182 | savedir = os.getcwdu() |
|
182 | 183 | |
|
183 | 184 | # Now walk the paths looking for executables to alias. |
|
184 | 185 | try: |
|
185 | 186 | # write the whole loop for posix/Windows so we don't have an if in |
|
186 | 187 | # the innermost part |
|
187 | 188 | if os.name == 'posix': |
|
188 | 189 | for pdir in path: |
|
189 | 190 | os.chdir(pdir) |
|
190 | 191 | for ff in os.listdir(pdir): |
|
191 | 192 | if isexec(ff): |
|
192 | 193 | try: |
|
193 | 194 | # Removes dots from the name since ipython |
|
194 | 195 | # will assume names with dots to be python. |
|
195 | 196 | if not self.shell.alias_manager.is_alias(ff): |
|
196 | 197 | self.shell.alias_manager.define_alias( |
|
197 | 198 | ff.replace('.',''), ff) |
|
198 | 199 | except InvalidAliasError: |
|
199 | 200 | pass |
|
200 | 201 | else: |
|
201 | 202 | syscmdlist.append(ff) |
|
202 | 203 | else: |
|
203 | 204 | no_alias = Alias.blacklist |
|
204 | 205 | for pdir in path: |
|
205 | 206 | os.chdir(pdir) |
|
206 | 207 | for ff in os.listdir(pdir): |
|
207 | 208 | base, ext = os.path.splitext(ff) |
|
208 | 209 | if isexec(ff) and base.lower() not in no_alias: |
|
209 | 210 | if ext.lower() == '.exe': |
|
210 | 211 | ff = base |
|
211 | 212 | try: |
|
212 | 213 | # Removes dots from the name since ipython |
|
213 | 214 | # will assume names with dots to be python. |
|
214 | 215 | self.shell.alias_manager.define_alias( |
|
215 | 216 | base.lower().replace('.',''), ff) |
|
216 | 217 | except InvalidAliasError: |
|
217 | 218 | pass |
|
218 | 219 | syscmdlist.append(ff) |
|
219 | 220 | self.shell.db['syscmdlist'] = syscmdlist |
|
220 | 221 | finally: |
|
221 | 222 | os.chdir(savedir) |
|
222 | 223 | |
|
223 | 224 | @skip_doctest |
|
224 | 225 | @line_magic |
|
225 | 226 | def pwd(self, parameter_s=''): |
|
226 | 227 | """Return the current working directory path. |
|
227 | 228 | |
|
228 | 229 | Examples |
|
229 | 230 | -------- |
|
230 | 231 | :: |
|
231 | 232 | |
|
232 | 233 | In [9]: pwd |
|
233 | 234 | Out[9]: '/home/tsuser/sprint/ipython' |
|
234 | 235 | """ |
|
235 | 236 | return os.getcwdu() |
|
236 | 237 | |
|
237 | 238 | @skip_doctest |
|
238 | 239 | @line_magic |
|
239 | 240 | def cd(self, parameter_s=''): |
|
240 | 241 | """Change the current working directory. |
|
241 | 242 | |
|
242 | 243 | This command automatically maintains an internal list of directories |
|
243 | 244 | you visit during your IPython session, in the variable _dh. The |
|
244 | 245 | command %dhist shows this history nicely formatted. You can also |
|
245 | 246 | do 'cd -<tab>' to see directory history conveniently. |
|
246 | 247 | |
|
247 | 248 | Usage: |
|
248 | 249 | |
|
249 | 250 | cd 'dir': changes to directory 'dir'. |
|
250 | 251 | |
|
251 | 252 | cd -: changes to the last visited directory. |
|
252 | 253 | |
|
253 | 254 | cd -<n>: changes to the n-th directory in the directory history. |
|
254 | 255 | |
|
255 | 256 | cd --foo: change to directory that matches 'foo' in history |
|
256 | 257 | |
|
257 | 258 | cd -b <bookmark_name>: jump to a bookmark set by %bookmark |
|
258 | 259 | (note: cd <bookmark_name> is enough if there is no |
|
259 | 260 | directory <bookmark_name>, but a bookmark with the name exists.) |
|
260 | 261 | 'cd -b <tab>' allows you to tab-complete bookmark names. |
|
261 | 262 | |
|
262 | 263 | Options: |
|
263 | 264 | |
|
264 | 265 | -q: quiet. Do not print the working directory after the cd command is |
|
265 | 266 | executed. By default IPython's cd command does print this directory, |
|
266 | 267 | since the default prompts do not display path information. |
|
267 | 268 | |
|
268 | 269 | Note that !cd doesn't work for this purpose because the shell where |
|
269 | 270 | !command runs is immediately discarded after executing 'command'. |
|
270 | 271 | |
|
271 | 272 | Examples |
|
272 | 273 | -------- |
|
273 | 274 | :: |
|
274 | 275 | |
|
275 | 276 | In [10]: cd parent/child |
|
276 | 277 | /home/tsuser/parent/child |
|
277 | 278 | """ |
|
278 | 279 | |
|
279 | 280 | oldcwd = os.getcwdu() |
|
280 | 281 | numcd = re.match(r'(-)(\d+)$',parameter_s) |
|
281 | 282 | # jump in directory history by number |
|
282 | 283 | if numcd: |
|
283 | 284 | nn = int(numcd.group(2)) |
|
284 | 285 | try: |
|
285 | 286 | ps = self.shell.user_ns['_dh'][nn] |
|
286 | 287 | except IndexError: |
|
287 |
print |
|
|
288 | print('The requested directory does not exist in history.') | |
|
288 | 289 | return |
|
289 | 290 | else: |
|
290 | 291 | opts = {} |
|
291 | 292 | elif parameter_s.startswith('--'): |
|
292 | 293 | ps = None |
|
293 | 294 | fallback = None |
|
294 | 295 | pat = parameter_s[2:] |
|
295 | 296 | dh = self.shell.user_ns['_dh'] |
|
296 | 297 | # first search only by basename (last component) |
|
297 | 298 | for ent in reversed(dh): |
|
298 | 299 | if pat in os.path.basename(ent) and os.path.isdir(ent): |
|
299 | 300 | ps = ent |
|
300 | 301 | break |
|
301 | 302 | |
|
302 | 303 | if fallback is None and pat in ent and os.path.isdir(ent): |
|
303 | 304 | fallback = ent |
|
304 | 305 | |
|
305 | 306 | # if we have no last part match, pick the first full path match |
|
306 | 307 | if ps is None: |
|
307 | 308 | ps = fallback |
|
308 | 309 | |
|
309 | 310 | if ps is None: |
|
310 |
print |
|
|
311 | print("No matching entry in directory history") | |
|
311 | 312 | return |
|
312 | 313 | else: |
|
313 | 314 | opts = {} |
|
314 | 315 | |
|
315 | 316 | |
|
316 | 317 | else: |
|
317 | 318 | #turn all non-space-escaping backslashes to slashes, |
|
318 | 319 | # for c:\windows\directory\names\ |
|
319 | 320 | parameter_s = re.sub(r'\\(?! )','/', parameter_s) |
|
320 | 321 | opts,ps = self.parse_options(parameter_s,'qb',mode='string') |
|
321 | 322 | # jump to previous |
|
322 | 323 | if ps == '-': |
|
323 | 324 | try: |
|
324 | 325 | ps = self.shell.user_ns['_dh'][-2] |
|
325 | 326 | except IndexError: |
|
326 | 327 | raise UsageError('%cd -: No previous directory to change to.') |
|
327 | 328 | # jump to bookmark if needed |
|
328 | 329 | else: |
|
329 | 330 | if not os.path.isdir(ps) or 'b' in opts: |
|
330 | 331 | bkms = self.shell.db.get('bookmarks', {}) |
|
331 | 332 | |
|
332 | 333 | if ps in bkms: |
|
333 | 334 | target = bkms[ps] |
|
334 |
print |
|
|
335 | print('(bookmark:%s) -> %s' % (ps, target)) | |
|
335 | 336 | ps = target |
|
336 | 337 | else: |
|
337 | 338 | if 'b' in opts: |
|
338 | 339 | raise UsageError("Bookmark '%s' not found. " |
|
339 | 340 | "Use '%%bookmark -l' to see your bookmarks." % ps) |
|
340 | 341 | |
|
341 | 342 | # strip extra quotes on Windows, because os.chdir doesn't like them |
|
342 | 343 | ps = unquote_filename(ps) |
|
343 | 344 | # at this point ps should point to the target dir |
|
344 | 345 | if ps: |
|
345 | 346 | try: |
|
346 | 347 | os.chdir(os.path.expanduser(ps)) |
|
347 | 348 | if hasattr(self.shell, 'term_title') and self.shell.term_title: |
|
348 | 349 | set_term_title('IPython: ' + abbrev_cwd()) |
|
349 | 350 | except OSError: |
|
350 |
print |
|
|
351 | print(sys.exc_info()[1]) | |
|
351 | 352 | else: |
|
352 | 353 | cwd = os.getcwdu() |
|
353 | 354 | dhist = self.shell.user_ns['_dh'] |
|
354 | 355 | if oldcwd != cwd: |
|
355 | 356 | dhist.append(cwd) |
|
356 | 357 | self.shell.db['dhist'] = compress_dhist(dhist)[-100:] |
|
357 | 358 | |
|
358 | 359 | else: |
|
359 | 360 | os.chdir(self.shell.home_dir) |
|
360 | 361 | if hasattr(self.shell, 'term_title') and self.shell.term_title: |
|
361 | 362 | set_term_title('IPython: ' + '~') |
|
362 | 363 | cwd = os.getcwdu() |
|
363 | 364 | dhist = self.shell.user_ns['_dh'] |
|
364 | 365 | |
|
365 | 366 | if oldcwd != cwd: |
|
366 | 367 | dhist.append(cwd) |
|
367 | 368 | self.shell.db['dhist'] = compress_dhist(dhist)[-100:] |
|
368 | 369 | if not 'q' in opts and self.shell.user_ns['_dh']: |
|
369 |
print |
|
|
370 | print(self.shell.user_ns['_dh'][-1]) | |
|
370 | 371 | |
|
371 | 372 | |
|
372 | 373 | @line_magic |
|
373 | 374 | def env(self, parameter_s=''): |
|
374 | 375 | """List environment variables.""" |
|
375 | 376 | |
|
376 | 377 | return dict(os.environ) |
|
377 | 378 | |
|
378 | 379 | @line_magic |
|
379 | 380 | def pushd(self, parameter_s=''): |
|
380 | 381 | """Place the current dir on stack and change directory. |
|
381 | 382 | |
|
382 | 383 | Usage:\\ |
|
383 | 384 | %pushd ['dirname'] |
|
384 | 385 | """ |
|
385 | 386 | |
|
386 | 387 | dir_s = self.shell.dir_stack |
|
387 | 388 | tgt = os.path.expanduser(unquote_filename(parameter_s)) |
|
388 | 389 | cwd = os.getcwdu().replace(self.shell.home_dir,'~') |
|
389 | 390 | if tgt: |
|
390 | 391 | self.cd(parameter_s) |
|
391 | 392 | dir_s.insert(0,cwd) |
|
392 | 393 | return self.shell.magic('dirs') |
|
393 | 394 | |
|
394 | 395 | @line_magic |
|
395 | 396 | def popd(self, parameter_s=''): |
|
396 | 397 | """Change to directory popped off the top of the stack. |
|
397 | 398 | """ |
|
398 | 399 | if not self.shell.dir_stack: |
|
399 | 400 | raise UsageError("%popd on empty stack") |
|
400 | 401 | top = self.shell.dir_stack.pop(0) |
|
401 | 402 | self.cd(top) |
|
402 |
print |
|
|
403 | print("popd ->",top) | |
|
403 | 404 | |
|
404 | 405 | @line_magic |
|
405 | 406 | def dirs(self, parameter_s=''): |
|
406 | 407 | """Return the current directory stack.""" |
|
407 | 408 | |
|
408 | 409 | return self.shell.dir_stack |
|
409 | 410 | |
|
410 | 411 | @line_magic |
|
411 | 412 | def dhist(self, parameter_s=''): |
|
412 | 413 | """Print your history of visited directories. |
|
413 | 414 | |
|
414 | 415 | %dhist -> print full history\\ |
|
415 | 416 | %dhist n -> print last n entries only\\ |
|
416 | 417 | %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\ |
|
417 | 418 | |
|
418 | 419 | This history is automatically maintained by the %cd command, and |
|
419 | 420 | always available as the global list variable _dh. You can use %cd -<n> |
|
420 | 421 | to go to directory number <n>. |
|
421 | 422 | |
|
422 | 423 | Note that most of time, you should view directory history by entering |
|
423 | 424 | cd -<TAB>. |
|
424 | 425 | |
|
425 | 426 | """ |
|
426 | 427 | |
|
427 | 428 | dh = self.shell.user_ns['_dh'] |
|
428 | 429 | if parameter_s: |
|
429 | 430 | try: |
|
430 | 431 | args = map(int,parameter_s.split()) |
|
431 | 432 | except: |
|
432 | 433 | self.arg_err(self.dhist) |
|
433 | 434 | return |
|
434 | 435 | if len(args) == 1: |
|
435 | 436 | ini,fin = max(len(dh)-(args[0]),0),len(dh) |
|
436 | 437 | elif len(args) == 2: |
|
437 | 438 | ini,fin = args |
|
438 | 439 | fin = min(fin, len(dh)) |
|
439 | 440 | else: |
|
440 | 441 | self.arg_err(self.dhist) |
|
441 | 442 | return |
|
442 | 443 | else: |
|
443 | 444 | ini,fin = 0,len(dh) |
|
444 |
print |
|
|
445 | print('Directory history (kept in _dh)') | |
|
445 | 446 | for i in range(ini, fin): |
|
446 |
print |
|
|
447 | print("%d: %s" % (i, dh[i])) | |
|
447 | 448 | |
|
448 | 449 | @skip_doctest |
|
449 | 450 | @line_magic |
|
450 | 451 | def sc(self, parameter_s=''): |
|
451 | 452 | """Shell capture - run shell command and capture output (DEPRECATED use !). |
|
452 | 453 | |
|
453 | 454 | DEPRECATED. Suboptimal, retained for backwards compatibility. |
|
454 | 455 | |
|
455 | 456 | You should use the form 'var = !command' instead. Example: |
|
456 | 457 | |
|
457 | 458 | "%sc -l myfiles = ls ~" should now be written as |
|
458 | 459 | |
|
459 | 460 | "myfiles = !ls ~" |
|
460 | 461 | |
|
461 | 462 | myfiles.s, myfiles.l and myfiles.n still apply as documented |
|
462 | 463 | below. |
|
463 | 464 | |
|
464 | 465 | -- |
|
465 | 466 | %sc [options] varname=command |
|
466 | 467 | |
|
467 | 468 | IPython will run the given command using commands.getoutput(), and |
|
468 | 469 | will then update the user's interactive namespace with a variable |
|
469 | 470 | called varname, containing the value of the call. Your command can |
|
470 | 471 | contain shell wildcards, pipes, etc. |
|
471 | 472 | |
|
472 | 473 | The '=' sign in the syntax is mandatory, and the variable name you |
|
473 | 474 | supply must follow Python's standard conventions for valid names. |
|
474 | 475 | |
|
475 | 476 | (A special format without variable name exists for internal use) |
|
476 | 477 | |
|
477 | 478 | Options: |
|
478 | 479 | |
|
479 | 480 | -l: list output. Split the output on newlines into a list before |
|
480 | 481 | assigning it to the given variable. By default the output is stored |
|
481 | 482 | as a single string. |
|
482 | 483 | |
|
483 | 484 | -v: verbose. Print the contents of the variable. |
|
484 | 485 | |
|
485 | 486 | In most cases you should not need to split as a list, because the |
|
486 | 487 | returned value is a special type of string which can automatically |
|
487 | 488 | provide its contents either as a list (split on newlines) or as a |
|
488 | 489 | space-separated string. These are convenient, respectively, either |
|
489 | 490 | for sequential processing or to be passed to a shell command. |
|
490 | 491 | |
|
491 | 492 | For example:: |
|
492 | 493 | |
|
493 | 494 | # Capture into variable a |
|
494 | 495 | In [1]: sc a=ls *py |
|
495 | 496 | |
|
496 | 497 | # a is a string with embedded newlines |
|
497 | 498 | In [2]: a |
|
498 | 499 | Out[2]: 'setup.py\\nwin32_manual_post_install.py' |
|
499 | 500 | |
|
500 | 501 | # which can be seen as a list: |
|
501 | 502 | In [3]: a.l |
|
502 | 503 | Out[3]: ['setup.py', 'win32_manual_post_install.py'] |
|
503 | 504 | |
|
504 | 505 | # or as a whitespace-separated string: |
|
505 | 506 | In [4]: a.s |
|
506 | 507 | Out[4]: 'setup.py win32_manual_post_install.py' |
|
507 | 508 | |
|
508 | 509 | # a.s is useful to pass as a single command line: |
|
509 | 510 | In [5]: !wc -l $a.s |
|
510 | 511 | 146 setup.py |
|
511 | 512 | 130 win32_manual_post_install.py |
|
512 | 513 | 276 total |
|
513 | 514 | |
|
514 | 515 | # while the list form is useful to loop over: |
|
515 | 516 | In [6]: for f in a.l: |
|
516 | 517 | ...: !wc -l $f |
|
517 | 518 | ...: |
|
518 | 519 | 146 setup.py |
|
519 | 520 | 130 win32_manual_post_install.py |
|
520 | 521 | |
|
521 | 522 | Similarly, the lists returned by the -l option are also special, in |
|
522 | 523 | the sense that you can equally invoke the .s attribute on them to |
|
523 | 524 | automatically get a whitespace-separated string from their contents:: |
|
524 | 525 | |
|
525 | 526 | In [7]: sc -l b=ls *py |
|
526 | 527 | |
|
527 | 528 | In [8]: b |
|
528 | 529 | Out[8]: ['setup.py', 'win32_manual_post_install.py'] |
|
529 | 530 | |
|
530 | 531 | In [9]: b.s |
|
531 | 532 | Out[9]: 'setup.py win32_manual_post_install.py' |
|
532 | 533 | |
|
533 | 534 | In summary, both the lists and strings used for output capture have |
|
534 | 535 | the following special attributes:: |
|
535 | 536 | |
|
536 | 537 | .l (or .list) : value as list. |
|
537 | 538 | .n (or .nlstr): value as newline-separated string. |
|
538 | 539 | .s (or .spstr): value as space-separated string. |
|
539 | 540 | """ |
|
540 | 541 | |
|
541 | 542 | opts,args = self.parse_options(parameter_s, 'lv') |
|
542 | 543 | # Try to get a variable name and command to run |
|
543 | 544 | try: |
|
544 | 545 | # the variable name must be obtained from the parse_options |
|
545 | 546 | # output, which uses shlex.split to strip options out. |
|
546 | 547 | var,_ = args.split('=', 1) |
|
547 | 548 | var = var.strip() |
|
548 | 549 | # But the command has to be extracted from the original input |
|
549 | 550 | # parameter_s, not on what parse_options returns, to avoid the |
|
550 | 551 | # quote stripping which shlex.split performs on it. |
|
551 | 552 | _,cmd = parameter_s.split('=', 1) |
|
552 | 553 | except ValueError: |
|
553 | 554 | var,cmd = '','' |
|
554 | 555 | # If all looks ok, proceed |
|
555 | 556 | split = 'l' in opts |
|
556 | 557 | out = self.shell.getoutput(cmd, split=split) |
|
557 | 558 | if 'v' in opts: |
|
558 |
print |
|
|
559 | print('%s ==\n%s' % (var, pformat(out))) | |
|
559 | 560 | if var: |
|
560 | 561 | self.shell.user_ns.update({var:out}) |
|
561 | 562 | else: |
|
562 | 563 | return out |
|
563 | 564 | |
|
564 | 565 | @line_cell_magic |
|
565 | 566 | def sx(self, line='', cell=None): |
|
566 | 567 | """Shell execute - run shell command and capture output (!! is short-hand). |
|
567 | 568 | |
|
568 | 569 | %sx command |
|
569 | 570 | |
|
570 | 571 | IPython will run the given command using commands.getoutput(), and |
|
571 | 572 | return the result formatted as a list (split on '\\n'). Since the |
|
572 | 573 | output is _returned_, it will be stored in ipython's regular output |
|
573 | 574 | cache Out[N] and in the '_N' automatic variables. |
|
574 | 575 | |
|
575 | 576 | Notes: |
|
576 | 577 | |
|
577 | 578 | 1) If an input line begins with '!!', then %sx is automatically |
|
578 | 579 | invoked. That is, while:: |
|
579 | 580 | |
|
580 | 581 | !ls |
|
581 | 582 | |
|
582 | 583 | causes ipython to simply issue system('ls'), typing:: |
|
583 | 584 | |
|
584 | 585 | !!ls |
|
585 | 586 | |
|
586 | 587 | is a shorthand equivalent to:: |
|
587 | 588 | |
|
588 | 589 | %sx ls |
|
589 | 590 | |
|
590 | 591 | 2) %sx differs from %sc in that %sx automatically splits into a list, |
|
591 | 592 | like '%sc -l'. The reason for this is to make it as easy as possible |
|
592 | 593 | to process line-oriented shell output via further python commands. |
|
593 | 594 | %sc is meant to provide much finer control, but requires more |
|
594 | 595 | typing. |
|
595 | 596 | |
|
596 | 597 | 3) Just like %sc -l, this is a list with special attributes: |
|
597 | 598 | :: |
|
598 | 599 | |
|
599 | 600 | .l (or .list) : value as list. |
|
600 | 601 | .n (or .nlstr): value as newline-separated string. |
|
601 | 602 | .s (or .spstr): value as whitespace-separated string. |
|
602 | 603 | |
|
603 | 604 | This is very useful when trying to use such lists as arguments to |
|
604 | 605 | system commands.""" |
|
605 | 606 | |
|
606 | 607 | if cell is None: |
|
607 | 608 | # line magic |
|
608 | 609 | return self.shell.getoutput(line) |
|
609 | 610 | else: |
|
610 | 611 | opts,args = self.parse_options(line, '', 'out=') |
|
611 | 612 | output = self.shell.getoutput(cell) |
|
612 | 613 | out_name = opts.get('out', opts.get('o')) |
|
613 | 614 | if out_name: |
|
614 | 615 | self.shell.user_ns[out_name] = output |
|
615 | 616 | else: |
|
616 | 617 | return output |
|
617 | 618 | |
|
618 | 619 | system = line_cell_magic('system')(sx) |
|
619 | 620 | bang = cell_magic('!')(sx) |
|
620 | 621 | |
|
621 | 622 | @line_magic |
|
622 | 623 | def bookmark(self, parameter_s=''): |
|
623 | 624 | """Manage IPython's bookmark system. |
|
624 | 625 | |
|
625 | 626 | %bookmark <name> - set bookmark to current dir |
|
626 | 627 | %bookmark <name> <dir> - set bookmark to <dir> |
|
627 | 628 | %bookmark -l - list all bookmarks |
|
628 | 629 | %bookmark -d <name> - remove bookmark |
|
629 | 630 | %bookmark -r - remove all bookmarks |
|
630 | 631 | |
|
631 | 632 | You can later on access a bookmarked folder with:: |
|
632 | 633 | |
|
633 | 634 | %cd -b <name> |
|
634 | 635 | |
|
635 | 636 | or simply '%cd <name>' if there is no directory called <name> AND |
|
636 | 637 | there is such a bookmark defined. |
|
637 | 638 | |
|
638 | 639 | Your bookmarks persist through IPython sessions, but they are |
|
639 | 640 | associated with each profile.""" |
|
640 | 641 | |
|
641 | 642 | opts,args = self.parse_options(parameter_s,'drl',mode='list') |
|
642 | 643 | if len(args) > 2: |
|
643 | 644 | raise UsageError("%bookmark: too many arguments") |
|
644 | 645 | |
|
645 | 646 | bkms = self.shell.db.get('bookmarks',{}) |
|
646 | 647 | |
|
647 | 648 | if 'd' in opts: |
|
648 | 649 | try: |
|
649 | 650 | todel = args[0] |
|
650 | 651 | except IndexError: |
|
651 | 652 | raise UsageError( |
|
652 | 653 | "%bookmark -d: must provide a bookmark to delete") |
|
653 | 654 | else: |
|
654 | 655 | try: |
|
655 | 656 | del bkms[todel] |
|
656 | 657 | except KeyError: |
|
657 | 658 | raise UsageError( |
|
658 | 659 | "%%bookmark -d: Can't delete bookmark '%s'" % todel) |
|
659 | 660 | |
|
660 | 661 | elif 'r' in opts: |
|
661 | 662 | bkms = {} |
|
662 | 663 | elif 'l' in opts: |
|
663 | 664 | bks = bkms.keys() |
|
664 | 665 | bks.sort() |
|
665 | 666 | if bks: |
|
666 | 667 | size = max(map(len, bks)) |
|
667 | 668 | else: |
|
668 | 669 | size = 0 |
|
669 | 670 | fmt = '%-'+str(size)+'s -> %s' |
|
670 |
print |
|
|
671 | print('Current bookmarks:') | |
|
671 | 672 | for bk in bks: |
|
672 |
print |
|
|
673 | print(fmt % (bk, bkms[bk])) | |
|
673 | 674 | else: |
|
674 | 675 | if not args: |
|
675 | 676 | raise UsageError("%bookmark: You must specify the bookmark name") |
|
676 | 677 | elif len(args)==1: |
|
677 | 678 | bkms[args[0]] = os.getcwdu() |
|
678 | 679 | elif len(args)==2: |
|
679 | 680 | bkms[args[0]] = args[1] |
|
680 | 681 | self.shell.db['bookmarks'] = bkms |
|
681 | 682 | |
|
682 | 683 | @line_magic |
|
683 | 684 | def pycat(self, parameter_s=''): |
|
684 | 685 | """Show a syntax-highlighted file through a pager. |
|
685 | 686 | |
|
686 | 687 | This magic is similar to the cat utility, but it will assume the file |
|
687 | 688 | to be Python source and will show it with syntax highlighting. |
|
688 | 689 | |
|
689 | 690 | This magic command can either take a local filename, an url, |
|
690 | 691 | an history range (see %history) or a macro as argument :: |
|
691 | 692 | |
|
692 | 693 | %pycat myscript.py |
|
693 | 694 | %pycat 7-27 |
|
694 | 695 | %pycat myMacro |
|
695 | 696 | %pycat http://www.example.com/myscript.py |
|
696 | 697 | """ |
|
697 | 698 | if not parameter_s: |
|
698 | 699 | raise UsageError('Missing filename, URL, input history range, ' |
|
699 | 700 | 'or macro.') |
|
700 | 701 | |
|
701 | 702 | try : |
|
702 | 703 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) |
|
703 | 704 | except (ValueError, IOError): |
|
704 |
print |
|
|
705 | print("Error: no such file, variable, URL, history range or macro") | |
|
705 | 706 | return |
|
706 | 707 | |
|
707 | 708 | page.page(self.shell.pycolorize(source_to_unicode(cont))) |
|
708 | 709 | |
|
709 | 710 | @magic_arguments.magic_arguments() |
|
710 | 711 | @magic_arguments.argument( |
|
711 | 712 | '-a', '--append', action='store_true', default=False, |
|
712 | 713 | help='Append contents of the cell to an existing file. ' |
|
713 | 714 | 'The file will be created if it does not exist.' |
|
714 | 715 | ) |
|
715 | 716 | @magic_arguments.argument( |
|
716 | 717 | 'filename', type=unicode, |
|
717 | 718 | help='file to write' |
|
718 | 719 | ) |
|
719 | 720 | @cell_magic |
|
720 | 721 | def writefile(self, line, cell): |
|
721 | 722 | """Write the contents of the cell to a file. |
|
722 | 723 | |
|
723 | 724 | The file will be overwritten unless the -a (--append) flag is specified. |
|
724 | 725 | """ |
|
725 | 726 | args = magic_arguments.parse_argstring(self.writefile, line) |
|
726 | 727 | filename = os.path.expanduser(unquote_filename(args.filename)) |
|
727 | 728 | |
|
728 | 729 | if os.path.exists(filename): |
|
729 | 730 | if args.append: |
|
730 |
print |
|
|
731 | print("Appending to %s" % filename) | |
|
731 | 732 | else: |
|
732 |
print |
|
|
733 | print("Overwriting %s" % filename) | |
|
733 | 734 | else: |
|
734 |
print |
|
|
735 | print("Writing %s" % filename) | |
|
735 | 736 | |
|
736 | 737 | mode = 'a' if args.append else 'w' |
|
737 | 738 | with io.open(filename, mode, encoding='utf-8') as f: |
|
738 | 739 | f.write(cell) |
@@ -1,143 +1,144 b'' | |||
|
1 | 1 | """Implementation of magic functions for matplotlib/pylab support. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | # Our own packages |
|
16 | 17 | from IPython.config.application import Application |
|
17 | 18 | from IPython.core import magic_arguments |
|
18 | 19 | from IPython.core.magic import Magics, magics_class, line_magic |
|
19 | 20 | from IPython.testing.skipdoctest import skip_doctest |
|
20 | 21 | from IPython.utils.warn import warn |
|
21 | 22 | from IPython.core.pylabtools import backends |
|
22 | 23 | |
|
23 | 24 | #----------------------------------------------------------------------------- |
|
24 | 25 | # Magic implementation classes |
|
25 | 26 | #----------------------------------------------------------------------------- |
|
26 | 27 | |
|
27 | 28 | magic_gui_arg = magic_arguments.argument( |
|
28 | 29 | 'gui', nargs='?', |
|
29 | 30 | help="""Name of the matplotlib backend to use %s. |
|
30 | 31 | If given, the corresponding matplotlib backend is used, |
|
31 | 32 | otherwise it will be matplotlib's default |
|
32 | 33 | (which you can set in your matplotlib config file). |
|
33 | 34 | """ % str(tuple(sorted(backends.keys()))) |
|
34 | 35 | ) |
|
35 | 36 | |
|
36 | 37 | |
|
37 | 38 | @magics_class |
|
38 | 39 | class PylabMagics(Magics): |
|
39 | 40 | """Magics related to matplotlib's pylab support""" |
|
40 | 41 | |
|
41 | 42 | @skip_doctest |
|
42 | 43 | @line_magic |
|
43 | 44 | @magic_arguments.magic_arguments() |
|
44 | 45 | @magic_gui_arg |
|
45 | 46 | def matplotlib(self, line=''): |
|
46 | 47 | """Set up matplotlib to work interactively. |
|
47 | 48 | |
|
48 | 49 | This function lets you activate matplotlib interactive support |
|
49 | 50 | at any point during an IPython session. |
|
50 | 51 | It does not import anything into the interactive namespace. |
|
51 | 52 | |
|
52 | 53 | If you are using the inline matplotlib backend for embedded figures, |
|
53 | 54 | you can adjust its behavior via the %config magic:: |
|
54 | 55 | |
|
55 | 56 | # enable SVG figures, necessary for SVG+XHTML export in the qtconsole |
|
56 | 57 | In [1]: %config InlineBackend.figure_format = 'svg' |
|
57 | 58 | |
|
58 | 59 | # change the behavior of closing all figures at the end of each |
|
59 | 60 | # execution (cell), or allowing reuse of active figures across |
|
60 | 61 | # cells: |
|
61 | 62 | In [2]: %config InlineBackend.close_figures = False |
|
62 | 63 | |
|
63 | 64 | Examples |
|
64 | 65 | -------- |
|
65 | 66 | In this case, where the MPL default is TkAgg:: |
|
66 | 67 | |
|
67 | 68 | In [2]: %matplotlib |
|
68 | 69 | Using matplotlib backend: TkAgg |
|
69 | 70 | |
|
70 | 71 | But you can explicitly request a different backend:: |
|
71 | 72 | |
|
72 | 73 | In [3]: %matplotlib qt |
|
73 | 74 | """ |
|
74 | 75 | args = magic_arguments.parse_argstring(self.matplotlib, line) |
|
75 | 76 | gui, backend = self.shell.enable_matplotlib(args.gui) |
|
76 | 77 | self._show_matplotlib_backend(args.gui, backend) |
|
77 | 78 | |
|
78 | 79 | @skip_doctest |
|
79 | 80 | @line_magic |
|
80 | 81 | @magic_arguments.magic_arguments() |
|
81 | 82 | @magic_arguments.argument( |
|
82 | 83 | '--no-import-all', action='store_true', default=None, |
|
83 | 84 | help="""Prevent IPython from performing ``import *`` into the interactive namespace. |
|
84 | 85 | |
|
85 | 86 | You can govern the default behavior of this flag with the |
|
86 | 87 | InteractiveShellApp.pylab_import_all configurable. |
|
87 | 88 | """ |
|
88 | 89 | ) |
|
89 | 90 | @magic_gui_arg |
|
90 | 91 | def pylab(self, line=''): |
|
91 | 92 | """Load numpy and matplotlib to work interactively. |
|
92 | 93 | |
|
93 | 94 | This function lets you activate pylab (matplotlib, numpy and |
|
94 | 95 | interactive support) at any point during an IPython session. |
|
95 | 96 | |
|
96 | 97 | %pylab makes the following imports:: |
|
97 | 98 | |
|
98 | 99 | import numpy |
|
99 | 100 | import matplotlib |
|
100 | 101 | from matplotlib import pylab, mlab, pyplot |
|
101 | 102 | np = numpy |
|
102 | 103 | plt = pyplot |
|
103 | 104 | |
|
104 | 105 | from IPython.display import display |
|
105 | 106 | from IPython.core.pylabtools import figsize, getfigs |
|
106 | 107 | |
|
107 | 108 | from pylab import * |
|
108 | 109 | from numpy import * |
|
109 | 110 | |
|
110 | 111 | If you pass `--no-import-all`, the last two `*` imports will be excluded. |
|
111 | 112 | |
|
112 | 113 | See the %matplotlib magic for more details about activating matplotlib |
|
113 | 114 | without affecting the interactive namespace. |
|
114 | 115 | """ |
|
115 | 116 | args = magic_arguments.parse_argstring(self.pylab, line) |
|
116 | 117 | if args.no_import_all is None: |
|
117 | 118 | # get default from Application |
|
118 | 119 | if Application.initialized(): |
|
119 | 120 | app = Application.instance() |
|
120 | 121 | try: |
|
121 | 122 | import_all = app.pylab_import_all |
|
122 | 123 | except AttributeError: |
|
123 | 124 | import_all = True |
|
124 | 125 | else: |
|
125 | 126 | # nothing specified, no app - default True |
|
126 | 127 | import_all = True |
|
127 | 128 | else: |
|
128 | 129 | # invert no-import flag |
|
129 | 130 | import_all = not args.no_import_all |
|
130 | 131 | |
|
131 | 132 | gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all) |
|
132 | 133 | self._show_matplotlib_backend(args.gui, backend) |
|
133 | 134 | print ("Populating the interactive namespace from numpy and matplotlib") |
|
134 | 135 | if clobbered: |
|
135 | 136 | warn("pylab import has clobbered these variables: %s" % clobbered + |
|
136 | 137 | "\n`%pylab --no-import-all` prevents importing * from pylab and numpy" |
|
137 | 138 | ) |
|
138 | 139 | |
|
139 | 140 | def _show_matplotlib_backend(self, gui, backend): |
|
140 | 141 | """show matplotlib message backend message""" |
|
141 | 142 | if not gui or gui == 'auto': |
|
142 |
print |
|
|
143 | print(("Using matplotlib backend: %s" % backend)) | |
|
143 | 144 |
@@ -1,279 +1,280 b'' | |||
|
1 | 1 | """Magic functions for running cells in various scripts.""" |
|
2 | from __future__ import print_function | |
|
2 | 3 | #----------------------------------------------------------------------------- |
|
3 | 4 | # Copyright (c) 2012 The IPython Development Team. |
|
4 | 5 | # |
|
5 | 6 | # Distributed under the terms of the Modified BSD License. |
|
6 | 7 | # |
|
7 | 8 | # The full license is in the file COPYING.txt, distributed with this software. |
|
8 | 9 | #----------------------------------------------------------------------------- |
|
9 | 10 | |
|
10 | 11 | #----------------------------------------------------------------------------- |
|
11 | 12 | # Imports |
|
12 | 13 | #----------------------------------------------------------------------------- |
|
13 | 14 | |
|
14 | 15 | # Stdlib |
|
15 | 16 | import errno |
|
16 | 17 | import os |
|
17 | 18 | import sys |
|
18 | 19 | import signal |
|
19 | 20 | import time |
|
20 | 21 | from subprocess import Popen, PIPE |
|
21 | 22 | import atexit |
|
22 | 23 | |
|
23 | 24 | # Our own packages |
|
24 | 25 | from IPython.config.configurable import Configurable |
|
25 | 26 | from IPython.core import magic_arguments |
|
26 | 27 | from IPython.core.magic import ( |
|
27 | 28 | Magics, magics_class, line_magic, cell_magic |
|
28 | 29 | ) |
|
29 | 30 | from IPython.lib.backgroundjobs import BackgroundJobManager |
|
30 | 31 | from IPython.utils import py3compat |
|
31 | 32 | from IPython.utils.process import arg_split |
|
32 | 33 | from IPython.utils.traitlets import List, Dict |
|
33 | 34 | |
|
34 | 35 | #----------------------------------------------------------------------------- |
|
35 | 36 | # Magic implementation classes |
|
36 | 37 | #----------------------------------------------------------------------------- |
|
37 | 38 | |
|
38 | 39 | def script_args(f): |
|
39 | 40 | """single decorator for adding script args""" |
|
40 | 41 | args = [ |
|
41 | 42 | magic_arguments.argument( |
|
42 | 43 | '--out', type=str, |
|
43 | 44 | help="""The variable in which to store stdout from the script. |
|
44 | 45 | If the script is backgrounded, this will be the stdout *pipe*, |
|
45 | 46 | instead of the stderr text itself. |
|
46 | 47 | """ |
|
47 | 48 | ), |
|
48 | 49 | magic_arguments.argument( |
|
49 | 50 | '--err', type=str, |
|
50 | 51 | help="""The variable in which to store stderr from the script. |
|
51 | 52 | If the script is backgrounded, this will be the stderr *pipe*, |
|
52 | 53 | instead of the stderr text itself. |
|
53 | 54 | """ |
|
54 | 55 | ), |
|
55 | 56 | magic_arguments.argument( |
|
56 | 57 | '--bg', action="store_true", |
|
57 | 58 | help="""Whether to run the script in the background. |
|
58 | 59 | If given, the only way to see the output of the command is |
|
59 | 60 | with --out/err. |
|
60 | 61 | """ |
|
61 | 62 | ), |
|
62 | 63 | magic_arguments.argument( |
|
63 | 64 | '--proc', type=str, |
|
64 | 65 | help="""The variable in which to store Popen instance. |
|
65 | 66 | This is used only when --bg option is given. |
|
66 | 67 | """ |
|
67 | 68 | ), |
|
68 | 69 | ] |
|
69 | 70 | for arg in args: |
|
70 | 71 | f = arg(f) |
|
71 | 72 | return f |
|
72 | 73 | |
|
73 | 74 | @magics_class |
|
74 | 75 | class ScriptMagics(Magics): |
|
75 | 76 | """Magics for talking to scripts |
|
76 | 77 | |
|
77 | 78 | This defines a base `%%script` cell magic for running a cell |
|
78 | 79 | with a program in a subprocess, and registers a few top-level |
|
79 | 80 | magics that call %%script with common interpreters. |
|
80 | 81 | """ |
|
81 | 82 | script_magics = List(config=True, |
|
82 | 83 | help="""Extra script cell magics to define |
|
83 | 84 | |
|
84 | 85 | This generates simple wrappers of `%%script foo` as `%%foo`. |
|
85 | 86 | |
|
86 | 87 | If you want to add script magics that aren't on your path, |
|
87 | 88 | specify them in script_paths |
|
88 | 89 | """, |
|
89 | 90 | ) |
|
90 | 91 | def _script_magics_default(self): |
|
91 | 92 | """default to a common list of programs""" |
|
92 | 93 | |
|
93 | 94 | defaults = [ |
|
94 | 95 | 'sh', |
|
95 | 96 | 'bash', |
|
96 | 97 | 'perl', |
|
97 | 98 | 'ruby', |
|
98 | 99 | 'python', |
|
99 | 100 | 'python3', |
|
100 | 101 | 'pypy', |
|
101 | 102 | ] |
|
102 | 103 | if os.name == 'nt': |
|
103 | 104 | defaults.extend([ |
|
104 | 105 | 'cmd', |
|
105 | 106 | 'powershell', |
|
106 | 107 | ]) |
|
107 | 108 | |
|
108 | 109 | return defaults |
|
109 | 110 | |
|
110 | 111 | script_paths = Dict(config=True, |
|
111 | 112 | help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' |
|
112 | 113 | |
|
113 | 114 | Only necessary for items in script_magics where the default path will not |
|
114 | 115 | find the right interpreter. |
|
115 | 116 | """ |
|
116 | 117 | ) |
|
117 | 118 | |
|
118 | 119 | def __init__(self, shell=None): |
|
119 | 120 | super(ScriptMagics, self).__init__(shell=shell) |
|
120 | 121 | self._generate_script_magics() |
|
121 | 122 | self.job_manager = BackgroundJobManager() |
|
122 | 123 | self.bg_processes = [] |
|
123 | 124 | atexit.register(self.kill_bg_processes) |
|
124 | 125 | |
|
125 | 126 | def __del__(self): |
|
126 | 127 | self.kill_bg_processes() |
|
127 | 128 | |
|
128 | 129 | def _generate_script_magics(self): |
|
129 | 130 | cell_magics = self.magics['cell'] |
|
130 | 131 | for name in self.script_magics: |
|
131 | 132 | cell_magics[name] = self._make_script_magic(name) |
|
132 | 133 | |
|
133 | 134 | def _make_script_magic(self, name): |
|
134 | 135 | """make a named magic, that calls %%script with a particular program""" |
|
135 | 136 | # expand to explicit path if necessary: |
|
136 | 137 | script = self.script_paths.get(name, name) |
|
137 | 138 | |
|
138 | 139 | @magic_arguments.magic_arguments() |
|
139 | 140 | @script_args |
|
140 | 141 | def named_script_magic(line, cell): |
|
141 | 142 | # if line, add it as cl-flags |
|
142 | 143 | if line: |
|
143 | 144 | line = "%s %s" % (script, line) |
|
144 | 145 | else: |
|
145 | 146 | line = script |
|
146 | 147 | return self.shebang(line, cell) |
|
147 | 148 | |
|
148 | 149 | # write a basic docstring: |
|
149 | 150 | named_script_magic.__doc__ = \ |
|
150 | 151 | """%%{name} script magic |
|
151 | 152 | |
|
152 | 153 | Run cells with {script} in a subprocess. |
|
153 | 154 | |
|
154 | 155 | This is a shortcut for `%%script {script}` |
|
155 | 156 | """.format(**locals()) |
|
156 | 157 | |
|
157 | 158 | return named_script_magic |
|
158 | 159 | |
|
159 | 160 | @magic_arguments.magic_arguments() |
|
160 | 161 | @script_args |
|
161 | 162 | @cell_magic("script") |
|
162 | 163 | def shebang(self, line, cell): |
|
163 | 164 | """Run a cell via a shell command |
|
164 | 165 | |
|
165 | 166 | The `%%script` line is like the #! line of script, |
|
166 | 167 | specifying a program (bash, perl, ruby, etc.) with which to run. |
|
167 | 168 | |
|
168 | 169 | The rest of the cell is run by that program. |
|
169 | 170 | |
|
170 | 171 | Examples |
|
171 | 172 | -------- |
|
172 | 173 | :: |
|
173 | 174 | |
|
174 | 175 | In [1]: %%script bash |
|
175 | 176 | ...: for i in 1 2 3; do |
|
176 | 177 | ...: echo $i |
|
177 | 178 | ...: done |
|
178 | 179 | 1 |
|
179 | 180 | 2 |
|
180 | 181 | 3 |
|
181 | 182 | """ |
|
182 | 183 | argv = arg_split(line, posix = not sys.platform.startswith('win')) |
|
183 | 184 | args, cmd = self.shebang.parser.parse_known_args(argv) |
|
184 | 185 | |
|
185 | 186 | try: |
|
186 | 187 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) |
|
187 | 188 | except OSError as e: |
|
188 | 189 | if e.errno == errno.ENOENT: |
|
189 |
print |
|
|
190 | print("Couldn't find program: %r" % cmd[0]) | |
|
190 | 191 | return |
|
191 | 192 | else: |
|
192 | 193 | raise |
|
193 | 194 | |
|
194 | 195 | cell = cell.encode('utf8', 'replace') |
|
195 | 196 | if args.bg: |
|
196 | 197 | self.bg_processes.append(p) |
|
197 | 198 | self._gc_bg_processes() |
|
198 | 199 | if args.out: |
|
199 | 200 | self.shell.user_ns[args.out] = p.stdout |
|
200 | 201 | if args.err: |
|
201 | 202 | self.shell.user_ns[args.err] = p.stderr |
|
202 | 203 | self.job_manager.new(self._run_script, p, cell, daemon=True) |
|
203 | 204 | if args.proc: |
|
204 | 205 | self.shell.user_ns[args.proc] = p |
|
205 | 206 | return |
|
206 | 207 | |
|
207 | 208 | try: |
|
208 | 209 | out, err = p.communicate(cell) |
|
209 | 210 | except KeyboardInterrupt: |
|
210 | 211 | try: |
|
211 | 212 | p.send_signal(signal.SIGINT) |
|
212 | 213 | time.sleep(0.1) |
|
213 | 214 | if p.poll() is not None: |
|
214 |
print |
|
|
215 | print("Process is interrupted.") | |
|
215 | 216 | return |
|
216 | 217 | p.terminate() |
|
217 | 218 | time.sleep(0.1) |
|
218 | 219 | if p.poll() is not None: |
|
219 |
print |
|
|
220 | print("Process is terminated.") | |
|
220 | 221 | return |
|
221 | 222 | p.kill() |
|
222 |
print |
|
|
223 | print("Process is killed.") | |
|
223 | 224 | except OSError: |
|
224 | 225 | pass |
|
225 | 226 | except Exception as e: |
|
226 |
print |
|
|
227 | % (p.pid, e) | |
|
227 | print("Error while terminating subprocess (pid=%i): %s" \ | |
|
228 | % (p.pid, e)) | |
|
228 | 229 | return |
|
229 | 230 | out = py3compat.bytes_to_str(out) |
|
230 | 231 | err = py3compat.bytes_to_str(err) |
|
231 | 232 | if args.out: |
|
232 | 233 | self.shell.user_ns[args.out] = out |
|
233 | 234 | else: |
|
234 | 235 | sys.stdout.write(out) |
|
235 | 236 | sys.stdout.flush() |
|
236 | 237 | if args.err: |
|
237 | 238 | self.shell.user_ns[args.err] = err |
|
238 | 239 | else: |
|
239 | 240 | sys.stderr.write(err) |
|
240 | 241 | sys.stderr.flush() |
|
241 | 242 | |
|
242 | 243 | def _run_script(self, p, cell): |
|
243 | 244 | """callback for running the script in the background""" |
|
244 | 245 | p.stdin.write(cell) |
|
245 | 246 | p.stdin.close() |
|
246 | 247 | p.wait() |
|
247 | 248 | |
|
248 | 249 | @line_magic("killbgscripts") |
|
249 | 250 | def killbgscripts(self, _nouse_=''): |
|
250 | 251 | """Kill all BG processes started by %%script and its family.""" |
|
251 | 252 | self.kill_bg_processes() |
|
252 |
print |
|
|
253 | print("All background processes were killed.") | |
|
253 | 254 | |
|
254 | 255 | def kill_bg_processes(self): |
|
255 | 256 | """Kill all BG processes which are still running.""" |
|
256 | 257 | for p in self.bg_processes: |
|
257 | 258 | if p.poll() is None: |
|
258 | 259 | try: |
|
259 | 260 | p.send_signal(signal.SIGINT) |
|
260 | 261 | except: |
|
261 | 262 | pass |
|
262 | 263 | time.sleep(0.1) |
|
263 | 264 | for p in self.bg_processes: |
|
264 | 265 | if p.poll() is None: |
|
265 | 266 | try: |
|
266 | 267 | p.terminate() |
|
267 | 268 | except: |
|
268 | 269 | pass |
|
269 | 270 | time.sleep(0.1) |
|
270 | 271 | for p in self.bg_processes: |
|
271 | 272 | if p.poll() is None: |
|
272 | 273 | try: |
|
273 | 274 | p.kill() |
|
274 | 275 | except: |
|
275 | 276 | pass |
|
276 | 277 | self._gc_bg_processes() |
|
277 | 278 | |
|
278 | 279 | def _gc_bg_processes(self): |
|
279 | 280 | self.bg_processes = [p for p in self.bg_processes if p.poll() is None] |
@@ -1,313 +1,314 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | An application for managing IPython profiles. |
|
4 | 4 | |
|
5 | 5 | To be invoked as the `ipython profile` subcommand. |
|
6 | 6 | |
|
7 | 7 | Authors: |
|
8 | 8 | |
|
9 | 9 | * Min RK |
|
10 | 10 | |
|
11 | 11 | """ |
|
12 | from __future__ import print_function | |
|
12 | 13 | |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | # Copyright (C) 2008 The IPython Development Team |
|
15 | 16 | # |
|
16 | 17 | # Distributed under the terms of the BSD License. The full license is in |
|
17 | 18 | # the file COPYING, distributed as part of this software. |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | # Imports |
|
22 | 23 | #----------------------------------------------------------------------------- |
|
23 | 24 | |
|
24 | 25 | import os |
|
25 | 26 | |
|
26 | 27 | from IPython.config.application import Application |
|
27 | 28 | from IPython.core.application import ( |
|
28 | 29 | BaseIPythonApplication, base_flags |
|
29 | 30 | ) |
|
30 | 31 | from IPython.core.profiledir import ProfileDir |
|
31 | 32 | from IPython.utils.importstring import import_item |
|
32 | 33 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir |
|
33 | 34 | from IPython.utils.traitlets import Unicode, Bool, Dict |
|
34 | 35 | |
|
35 | 36 | #----------------------------------------------------------------------------- |
|
36 | 37 | # Constants |
|
37 | 38 | #----------------------------------------------------------------------------- |
|
38 | 39 | |
|
39 | 40 | create_help = """Create an IPython profile by name |
|
40 | 41 | |
|
41 | 42 | Create an ipython profile directory by its name or |
|
42 | 43 | profile directory path. Profile directories contain |
|
43 | 44 | configuration, log and security related files and are named |
|
44 | 45 | using the convention 'profile_<name>'. By default they are |
|
45 | 46 | located in your ipython directory. Once created, you will |
|
46 | 47 | can edit the configuration files in the profile |
|
47 | 48 | directory to configure IPython. Most users will create a |
|
48 | 49 | profile directory by name, |
|
49 | 50 | `ipython profile create myprofile`, which will put the directory |
|
50 | 51 | in `<ipython_dir>/profile_myprofile`. |
|
51 | 52 | """ |
|
52 | 53 | list_help = """List available IPython profiles |
|
53 | 54 | |
|
54 | 55 | List all available profiles, by profile location, that can |
|
55 | 56 | be found in the current working directly or in the ipython |
|
56 | 57 | directory. Profile directories are named using the convention |
|
57 | 58 | 'profile_<profile>'. |
|
58 | 59 | """ |
|
59 | 60 | profile_help = """Manage IPython profiles |
|
60 | 61 | |
|
61 | 62 | Profile directories contain |
|
62 | 63 | configuration, log and security related files and are named |
|
63 | 64 | using the convention 'profile_<name>'. By default they are |
|
64 | 65 | located in your ipython directory. You can create profiles |
|
65 | 66 | with `ipython profile create <name>`, or see the profiles you |
|
66 | 67 | already have with `ipython profile list` |
|
67 | 68 | |
|
68 | 69 | To get started configuring IPython, simply do: |
|
69 | 70 | |
|
70 | 71 | $> ipython profile create |
|
71 | 72 | |
|
72 | 73 | and IPython will create the default profile in <ipython_dir>/profile_default, |
|
73 | 74 | where you can edit ipython_config.py to start configuring IPython. |
|
74 | 75 | |
|
75 | 76 | """ |
|
76 | 77 | |
|
77 | 78 | _list_examples = "ipython profile list # list all profiles" |
|
78 | 79 | |
|
79 | 80 | _create_examples = """ |
|
80 | 81 | ipython profile create foo # create profile foo w/ default config files |
|
81 | 82 | ipython profile create foo --reset # restage default config files over current |
|
82 | 83 | ipython profile create foo --parallel # also stage parallel config files |
|
83 | 84 | """ |
|
84 | 85 | |
|
85 | 86 | _main_examples = """ |
|
86 | 87 | ipython profile create -h # show the help string for the create subcommand |
|
87 | 88 | ipython profile list -h # show the help string for the list subcommand |
|
88 | 89 | |
|
89 | 90 | ipython locate profile foo # print the path to the directory for profile 'foo' |
|
90 | 91 | """ |
|
91 | 92 | |
|
92 | 93 | #----------------------------------------------------------------------------- |
|
93 | 94 | # Profile Application Class (for `ipython profile` subcommand) |
|
94 | 95 | #----------------------------------------------------------------------------- |
|
95 | 96 | |
|
96 | 97 | |
|
97 | 98 | def list_profiles_in(path): |
|
98 | 99 | """list profiles in a given root directory""" |
|
99 | 100 | files = os.listdir(path) |
|
100 | 101 | profiles = [] |
|
101 | 102 | for f in files: |
|
102 | 103 | try: |
|
103 | 104 | full_path = os.path.join(path, f) |
|
104 | 105 | except UnicodeError: |
|
105 | 106 | continue |
|
106 | 107 | if os.path.isdir(full_path) and f.startswith('profile_'): |
|
107 | 108 | profiles.append(f.split('_',1)[-1]) |
|
108 | 109 | return profiles |
|
109 | 110 | |
|
110 | 111 | |
|
111 | 112 | def list_bundled_profiles(): |
|
112 | 113 | """list profiles that are bundled with IPython.""" |
|
113 | 114 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile') |
|
114 | 115 | files = os.listdir(path) |
|
115 | 116 | profiles = [] |
|
116 | 117 | for profile in files: |
|
117 | 118 | full_path = os.path.join(path, profile) |
|
118 | 119 | if os.path.isdir(full_path) and profile != "__pycache__": |
|
119 | 120 | profiles.append(profile) |
|
120 | 121 | return profiles |
|
121 | 122 | |
|
122 | 123 | |
|
123 | 124 | class ProfileLocate(BaseIPythonApplication): |
|
124 | 125 | description = """print the path to an IPython profile dir""" |
|
125 | 126 | |
|
126 | 127 | def parse_command_line(self, argv=None): |
|
127 | 128 | super(ProfileLocate, self).parse_command_line(argv) |
|
128 | 129 | if self.extra_args: |
|
129 | 130 | self.profile = self.extra_args[0] |
|
130 | 131 | |
|
131 | 132 | def start(self): |
|
132 |
print |
|
|
133 | print(self.profile_dir.location) | |
|
133 | 134 | |
|
134 | 135 | |
|
135 | 136 | class ProfileList(Application): |
|
136 | 137 | name = u'ipython-profile' |
|
137 | 138 | description = list_help |
|
138 | 139 | examples = _list_examples |
|
139 | 140 | |
|
140 | 141 | aliases = Dict({ |
|
141 | 142 | 'ipython-dir' : 'ProfileList.ipython_dir', |
|
142 | 143 | 'log-level' : 'Application.log_level', |
|
143 | 144 | }) |
|
144 | 145 | flags = Dict(dict( |
|
145 | 146 | debug = ({'Application' : {'log_level' : 0}}, |
|
146 | 147 | "Set Application.log_level to 0, maximizing log output." |
|
147 | 148 | ) |
|
148 | 149 | )) |
|
149 | 150 | |
|
150 | 151 | ipython_dir = Unicode(get_ipython_dir(), config=True, |
|
151 | 152 | help=""" |
|
152 | 153 | The name of the IPython directory. This directory is used for logging |
|
153 | 154 | configuration (through profiles), history storage, etc. The default |
|
154 | 155 | is usually $HOME/.ipython. This options can also be specified through |
|
155 | 156 | the environment variable IPYTHONDIR. |
|
156 | 157 | """ |
|
157 | 158 | ) |
|
158 | 159 | |
|
159 | 160 | |
|
160 | 161 | def _print_profiles(self, profiles): |
|
161 | 162 | """print list of profiles, indented.""" |
|
162 | 163 | for profile in profiles: |
|
163 |
print |
|
|
164 | print(' %s' % profile) | |
|
164 | 165 | |
|
165 | 166 | def list_profile_dirs(self): |
|
166 | 167 | profiles = list_bundled_profiles() |
|
167 | 168 | if profiles: |
|
168 | ||
|
169 |
print |
|
|
169 | print() | |
|
170 | print("Available profiles in IPython:") | |
|
170 | 171 | self._print_profiles(profiles) |
|
171 | ||
|
172 |
print |
|
|
173 |
print |
|
|
174 |
print |
|
|
172 | print() | |
|
173 | print(" The first request for a bundled profile will copy it") | |
|
174 | print(" into your IPython directory (%s)," % self.ipython_dir) | |
|
175 | print(" where you can customize it.") | |
|
175 | 176 | |
|
176 | 177 | profiles = list_profiles_in(self.ipython_dir) |
|
177 | 178 | if profiles: |
|
178 | ||
|
179 |
print |
|
|
179 | print() | |
|
180 | print("Available profiles in %s:" % self.ipython_dir) | |
|
180 | 181 | self._print_profiles(profiles) |
|
181 | 182 | |
|
182 | 183 | profiles = list_profiles_in(os.getcwdu()) |
|
183 | 184 | if profiles: |
|
184 | ||
|
185 |
print |
|
|
185 | print() | |
|
186 | print("Available profiles in current directory (%s):" % os.getcwdu()) | |
|
186 | 187 | self._print_profiles(profiles) |
|
187 | 188 | |
|
188 | ||
|
189 |
print |
|
|
190 |
print |
|
|
191 | ||
|
189 | print() | |
|
190 | print("To use any of the above profiles, start IPython with:") | |
|
191 | print(" ipython --profile=<name>") | |
|
192 | print() | |
|
192 | 193 | |
|
193 | 194 | def start(self): |
|
194 | 195 | self.list_profile_dirs() |
|
195 | 196 | |
|
196 | 197 | |
|
197 | 198 | create_flags = {} |
|
198 | 199 | create_flags.update(base_flags) |
|
199 | 200 | # don't include '--init' flag, which implies running profile create in other apps |
|
200 | 201 | create_flags.pop('init') |
|
201 | 202 | create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}}, |
|
202 | 203 | "reset config files in this profile to the defaults.") |
|
203 | 204 | create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}}, |
|
204 | 205 | "Include the config files for parallel " |
|
205 | 206 | "computing apps (ipengine, ipcontroller, etc.)") |
|
206 | 207 | |
|
207 | 208 | |
|
208 | 209 | class ProfileCreate(BaseIPythonApplication): |
|
209 | 210 | name = u'ipython-profile' |
|
210 | 211 | description = create_help |
|
211 | 212 | examples = _create_examples |
|
212 | 213 | auto_create = Bool(True, config=False) |
|
213 | 214 | def _log_format_default(self): |
|
214 | 215 | return "[%(name)s] %(message)s" |
|
215 | 216 | |
|
216 | 217 | def _copy_config_files_default(self): |
|
217 | 218 | return True |
|
218 | 219 | |
|
219 | 220 | parallel = Bool(False, config=True, |
|
220 | 221 | help="whether to include parallel computing config files") |
|
221 | 222 | def _parallel_changed(self, name, old, new): |
|
222 | 223 | parallel_files = [ 'ipcontroller_config.py', |
|
223 | 224 | 'ipengine_config.py', |
|
224 | 225 | 'ipcluster_config.py' |
|
225 | 226 | ] |
|
226 | 227 | if new: |
|
227 | 228 | for cf in parallel_files: |
|
228 | 229 | self.config_files.append(cf) |
|
229 | 230 | else: |
|
230 | 231 | for cf in parallel_files: |
|
231 | 232 | if cf in self.config_files: |
|
232 | 233 | self.config_files.remove(cf) |
|
233 | 234 | |
|
234 | 235 | def parse_command_line(self, argv): |
|
235 | 236 | super(ProfileCreate, self).parse_command_line(argv) |
|
236 | 237 | # accept positional arg as profile name |
|
237 | 238 | if self.extra_args: |
|
238 | 239 | self.profile = self.extra_args[0] |
|
239 | 240 | |
|
240 | 241 | flags = Dict(create_flags) |
|
241 | 242 | |
|
242 | 243 | classes = [ProfileDir] |
|
243 | 244 | |
|
244 | 245 | def _import_app(self, app_path): |
|
245 | 246 | """import an app class""" |
|
246 | 247 | app = None |
|
247 | 248 | name = app_path.rsplit('.', 1)[-1] |
|
248 | 249 | try: |
|
249 | 250 | app = import_item(app_path) |
|
250 | 251 | except ImportError as e: |
|
251 | 252 | self.log.info("Couldn't import %s, config file will be excluded", name) |
|
252 | 253 | except Exception: |
|
253 | 254 | self.log.warn('Unexpected error importing %s', name, exc_info=True) |
|
254 | 255 | return app |
|
255 | 256 | |
|
256 | 257 | def init_config_files(self): |
|
257 | 258 | super(ProfileCreate, self).init_config_files() |
|
258 | 259 | # use local imports, since these classes may import from here |
|
259 | 260 | from IPython.terminal.ipapp import TerminalIPythonApp |
|
260 | 261 | apps = [TerminalIPythonApp] |
|
261 | 262 | for app_path in ( |
|
262 | 263 | 'IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp', |
|
263 | 264 | 'IPython.html.notebookapp.NotebookApp', |
|
264 | 265 | 'IPython.nbconvert.nbconvertapp.NbConvertApp', |
|
265 | 266 | ): |
|
266 | 267 | app = self._import_app(app_path) |
|
267 | 268 | if app is not None: |
|
268 | 269 | apps.append(app) |
|
269 | 270 | if self.parallel: |
|
270 | 271 | from IPython.parallel.apps.ipcontrollerapp import IPControllerApp |
|
271 | 272 | from IPython.parallel.apps.ipengineapp import IPEngineApp |
|
272 | 273 | from IPython.parallel.apps.ipclusterapp import IPClusterStart |
|
273 | 274 | from IPython.parallel.apps.iploggerapp import IPLoggerApp |
|
274 | 275 | apps.extend([ |
|
275 | 276 | IPControllerApp, |
|
276 | 277 | IPEngineApp, |
|
277 | 278 | IPClusterStart, |
|
278 | 279 | IPLoggerApp, |
|
279 | 280 | ]) |
|
280 | 281 | for App in apps: |
|
281 | 282 | app = App() |
|
282 | 283 | app.config.update(self.config) |
|
283 | 284 | app.log = self.log |
|
284 | 285 | app.overwrite = self.overwrite |
|
285 | 286 | app.copy_config_files=True |
|
286 | 287 | app.profile = self.profile |
|
287 | 288 | app.init_profile_dir() |
|
288 | 289 | app.init_config_files() |
|
289 | 290 | |
|
290 | 291 | def stage_default_config_file(self): |
|
291 | 292 | pass |
|
292 | 293 | |
|
293 | 294 | |
|
294 | 295 | class ProfileApp(Application): |
|
295 | 296 | name = u'ipython-profile' |
|
296 | 297 | description = profile_help |
|
297 | 298 | examples = _main_examples |
|
298 | 299 | |
|
299 | 300 | subcommands = Dict(dict( |
|
300 | 301 | create = (ProfileCreate, ProfileCreate.description.splitlines()[0]), |
|
301 | 302 | list = (ProfileList, ProfileList.description.splitlines()[0]), |
|
302 | 303 | locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]), |
|
303 | 304 | )) |
|
304 | 305 | |
|
305 | 306 | def start(self): |
|
306 | 307 | if self.subapp is None: |
|
307 |
print |
|
|
308 | ||
|
308 | print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())) | |
|
309 | print() | |
|
309 | 310 | self.print_description() |
|
310 | 311 | self.print_subcommands() |
|
311 | 312 | self.exit(1) |
|
312 | 313 | else: |
|
313 | 314 | return self.subapp.start() |
@@ -1,339 +1,340 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Pylab (matplotlib) support utilities. |
|
3 | 3 | |
|
4 | 4 | Authors |
|
5 | 5 | ------- |
|
6 | 6 | |
|
7 | 7 | * Fernando Perez. |
|
8 | 8 | * Brian Granger |
|
9 | 9 | """ |
|
10 | from __future__ import print_function | |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Copyright (C) 2009 The IPython Development Team |
|
13 | 14 | # |
|
14 | 15 | # Distributed under the terms of the BSD License. The full license is in |
|
15 | 16 | # the file COPYING, distributed as part of this software. |
|
16 | 17 | #----------------------------------------------------------------------------- |
|
17 | 18 | |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | # Imports |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | |
|
22 | 23 | import sys |
|
23 | 24 | from io import BytesIO |
|
24 | 25 | |
|
25 | 26 | from IPython.core.display import _pngxy |
|
26 | 27 | from IPython.utils.decorators import flag_calls |
|
27 | 28 | |
|
28 | 29 | # If user specifies a GUI, that dictates the backend, otherwise we read the |
|
29 | 30 | # user's mpl default from the mpl rc structure |
|
30 | 31 | backends = {'tk': 'TkAgg', |
|
31 | 32 | 'gtk': 'GTKAgg', |
|
32 | 33 | 'wx': 'WXAgg', |
|
33 | 34 | 'qt': 'Qt4Agg', # qt3 not supported |
|
34 | 35 | 'qt4': 'Qt4Agg', |
|
35 | 36 | 'osx': 'MacOSX', |
|
36 | 37 | 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'} |
|
37 | 38 | |
|
38 | 39 | # We also need a reverse backends2guis mapping that will properly choose which |
|
39 | 40 | # GUI support to activate based on the desired matplotlib backend. For the |
|
40 | 41 | # most part it's just a reverse of the above dict, but we also need to add a |
|
41 | 42 | # few others that map to the same GUI manually: |
|
42 | 43 | backend2gui = dict(zip(backends.values(), backends.keys())) |
|
43 | 44 | # Our tests expect backend2gui to just return 'qt' |
|
44 | 45 | backend2gui['Qt4Agg'] = 'qt' |
|
45 | 46 | # In the reverse mapping, there are a few extra valid matplotlib backends that |
|
46 | 47 | # map to the same GUI support |
|
47 | 48 | backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk' |
|
48 | 49 | backend2gui['WX'] = 'wx' |
|
49 | 50 | backend2gui['CocoaAgg'] = 'osx' |
|
50 | 51 | |
|
51 | 52 | #----------------------------------------------------------------------------- |
|
52 | 53 | # Matplotlib utilities |
|
53 | 54 | #----------------------------------------------------------------------------- |
|
54 | 55 | |
|
55 | 56 | |
|
56 | 57 | def getfigs(*fig_nums): |
|
57 | 58 | """Get a list of matplotlib figures by figure numbers. |
|
58 | 59 | |
|
59 | 60 | If no arguments are given, all available figures are returned. If the |
|
60 | 61 | argument list contains references to invalid figures, a warning is printed |
|
61 | 62 | but the function continues pasting further figures. |
|
62 | 63 | |
|
63 | 64 | Parameters |
|
64 | 65 | ---------- |
|
65 | 66 | figs : tuple |
|
66 | 67 | A tuple of ints giving the figure numbers of the figures to return. |
|
67 | 68 | """ |
|
68 | 69 | from matplotlib._pylab_helpers import Gcf |
|
69 | 70 | if not fig_nums: |
|
70 | 71 | fig_managers = Gcf.get_all_fig_managers() |
|
71 | 72 | return [fm.canvas.figure for fm in fig_managers] |
|
72 | 73 | else: |
|
73 | 74 | figs = [] |
|
74 | 75 | for num in fig_nums: |
|
75 | 76 | f = Gcf.figs.get(num) |
|
76 | 77 | if f is None: |
|
77 | print('Warning: figure %s not available.' % num) | |
|
78 | print(('Warning: figure %s not available.' % num)) | |
|
78 | 79 | else: |
|
79 | 80 | figs.append(f.canvas.figure) |
|
80 | 81 | return figs |
|
81 | 82 | |
|
82 | 83 | |
|
83 | 84 | def figsize(sizex, sizey): |
|
84 | 85 | """Set the default figure size to be [sizex, sizey]. |
|
85 | 86 | |
|
86 | 87 | This is just an easy to remember, convenience wrapper that sets:: |
|
87 | 88 | |
|
88 | 89 | matplotlib.rcParams['figure.figsize'] = [sizex, sizey] |
|
89 | 90 | """ |
|
90 | 91 | import matplotlib |
|
91 | 92 | matplotlib.rcParams['figure.figsize'] = [sizex, sizey] |
|
92 | 93 | |
|
93 | 94 | |
|
94 | 95 | def print_figure(fig, fmt='png'): |
|
95 | 96 | """Convert a figure to svg or png for inline display.""" |
|
96 | 97 | from matplotlib import rcParams |
|
97 | 98 | # When there's an empty figure, we shouldn't return anything, otherwise we |
|
98 | 99 | # get big blank areas in the qt console. |
|
99 | 100 | if not fig.axes and not fig.lines: |
|
100 | 101 | return |
|
101 | 102 | |
|
102 | 103 | fc = fig.get_facecolor() |
|
103 | 104 | ec = fig.get_edgecolor() |
|
104 | 105 | bytes_io = BytesIO() |
|
105 | 106 | dpi = rcParams['savefig.dpi'] |
|
106 | 107 | if fmt == 'retina': |
|
107 | 108 | dpi = dpi * 2 |
|
108 | 109 | fmt = 'png' |
|
109 | 110 | fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight', |
|
110 | 111 | facecolor=fc, edgecolor=ec, dpi=dpi) |
|
111 | 112 | data = bytes_io.getvalue() |
|
112 | 113 | return data |
|
113 | 114 | |
|
114 | 115 | def retina_figure(fig): |
|
115 | 116 | """format a figure as a pixel-doubled (retina) PNG""" |
|
116 | 117 | pngdata = print_figure(fig, fmt='retina') |
|
117 | 118 | w, h = _pngxy(pngdata) |
|
118 | 119 | metadata = dict(width=w//2, height=h//2) |
|
119 | 120 | return pngdata, metadata |
|
120 | 121 | |
|
121 | 122 | # We need a little factory function here to create the closure where |
|
122 | 123 | # safe_execfile can live. |
|
123 | 124 | def mpl_runner(safe_execfile): |
|
124 | 125 | """Factory to return a matplotlib-enabled runner for %run. |
|
125 | 126 | |
|
126 | 127 | Parameters |
|
127 | 128 | ---------- |
|
128 | 129 | safe_execfile : function |
|
129 | 130 | This must be a function with the same interface as the |
|
130 | 131 | :meth:`safe_execfile` method of IPython. |
|
131 | 132 | |
|
132 | 133 | Returns |
|
133 | 134 | ------- |
|
134 | 135 | A function suitable for use as the ``runner`` argument of the %run magic |
|
135 | 136 | function. |
|
136 | 137 | """ |
|
137 | 138 | |
|
138 | 139 | def mpl_execfile(fname,*where,**kw): |
|
139 | 140 | """matplotlib-aware wrapper around safe_execfile. |
|
140 | 141 | |
|
141 | 142 | Its interface is identical to that of the :func:`execfile` builtin. |
|
142 | 143 | |
|
143 | 144 | This is ultimately a call to execfile(), but wrapped in safeties to |
|
144 | 145 | properly handle interactive rendering.""" |
|
145 | 146 | |
|
146 | 147 | import matplotlib |
|
147 | 148 | import matplotlib.pylab as pylab |
|
148 | 149 | |
|
149 | 150 | #print '*** Matplotlib runner ***' # dbg |
|
150 | 151 | # turn off rendering until end of script |
|
151 | 152 | is_interactive = matplotlib.rcParams['interactive'] |
|
152 | 153 | matplotlib.interactive(False) |
|
153 | 154 | safe_execfile(fname,*where,**kw) |
|
154 | 155 | matplotlib.interactive(is_interactive) |
|
155 | 156 | # make rendering call now, if the user tried to do it |
|
156 | 157 | if pylab.draw_if_interactive.called: |
|
157 | 158 | pylab.draw() |
|
158 | 159 | pylab.draw_if_interactive.called = False |
|
159 | 160 | |
|
160 | 161 | return mpl_execfile |
|
161 | 162 | |
|
162 | 163 | |
|
163 | 164 | def select_figure_format(shell, fmt): |
|
164 | 165 | """Select figure format for inline backend, can be 'png', 'retina', or 'svg'. |
|
165 | 166 | |
|
166 | 167 | Using this method ensures only one figure format is active at a time. |
|
167 | 168 | """ |
|
168 | 169 | from matplotlib.figure import Figure |
|
169 | 170 | from IPython.kernel.zmq.pylab import backend_inline |
|
170 | 171 | |
|
171 | 172 | svg_formatter = shell.display_formatter.formatters['image/svg+xml'] |
|
172 | 173 | png_formatter = shell.display_formatter.formatters['image/png'] |
|
173 | 174 | |
|
174 | 175 | if fmt == 'png': |
|
175 | 176 | svg_formatter.type_printers.pop(Figure, None) |
|
176 | 177 | png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png')) |
|
177 | 178 | elif fmt in ('png2x', 'retina'): |
|
178 | 179 | svg_formatter.type_printers.pop(Figure, None) |
|
179 | 180 | png_formatter.for_type(Figure, retina_figure) |
|
180 | 181 | elif fmt == 'svg': |
|
181 | 182 | png_formatter.type_printers.pop(Figure, None) |
|
182 | 183 | svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg')) |
|
183 | 184 | else: |
|
184 | 185 | raise ValueError("supported formats are: 'png', 'retina', 'svg', not %r" % fmt) |
|
185 | 186 | |
|
186 | 187 | # set the format to be used in the backend() |
|
187 | 188 | backend_inline._figure_format = fmt |
|
188 | 189 | |
|
189 | 190 | #----------------------------------------------------------------------------- |
|
190 | 191 | # Code for initializing matplotlib and importing pylab |
|
191 | 192 | #----------------------------------------------------------------------------- |
|
192 | 193 | |
|
193 | 194 | |
|
194 | 195 | def find_gui_and_backend(gui=None, gui_select=None): |
|
195 | 196 | """Given a gui string return the gui and mpl backend. |
|
196 | 197 | |
|
197 | 198 | Parameters |
|
198 | 199 | ---------- |
|
199 | 200 | gui : str |
|
200 | 201 | Can be one of ('tk','gtk','wx','qt','qt4','inline'). |
|
201 | 202 | gui_select : str |
|
202 | 203 | Can be one of ('tk','gtk','wx','qt','qt4','inline'). |
|
203 | 204 | This is any gui already selected by the shell. |
|
204 | 205 | |
|
205 | 206 | Returns |
|
206 | 207 | ------- |
|
207 | 208 | A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg', |
|
208 | 209 | 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline'). |
|
209 | 210 | """ |
|
210 | 211 | |
|
211 | 212 | import matplotlib |
|
212 | 213 | |
|
213 | 214 | if gui and gui != 'auto': |
|
214 | 215 | # select backend based on requested gui |
|
215 | 216 | backend = backends[gui] |
|
216 | 217 | else: |
|
217 | 218 | # We need to read the backend from the original data structure, *not* |
|
218 | 219 | # from mpl.rcParams, since a prior invocation of %matplotlib may have |
|
219 | 220 | # overwritten that. |
|
220 | 221 | # WARNING: this assumes matplotlib 1.1 or newer!! |
|
221 | 222 | backend = matplotlib.rcParamsOrig['backend'] |
|
222 | 223 | # In this case, we need to find what the appropriate gui selection call |
|
223 | 224 | # should be for IPython, so we can activate inputhook accordingly |
|
224 | 225 | gui = backend2gui.get(backend, None) |
|
225 | 226 | |
|
226 | 227 | # If we have already had a gui active, we need it and inline are the |
|
227 | 228 | # ones allowed. |
|
228 | 229 | if gui_select and gui != gui_select: |
|
229 | 230 | gui = gui_select |
|
230 | 231 | backend = backends[gui] |
|
231 | 232 | |
|
232 | 233 | return gui, backend |
|
233 | 234 | |
|
234 | 235 | |
|
235 | 236 | def activate_matplotlib(backend): |
|
236 | 237 | """Activate the given backend and set interactive to True.""" |
|
237 | 238 | |
|
238 | 239 | import matplotlib |
|
239 | 240 | matplotlib.interactive(True) |
|
240 | 241 | |
|
241 | 242 | # Matplotlib had a bug where even switch_backend could not force |
|
242 | 243 | # the rcParam to update. This needs to be set *before* the module |
|
243 | 244 | # magic of switch_backend(). |
|
244 | 245 | matplotlib.rcParams['backend'] = backend |
|
245 | 246 | |
|
246 | 247 | import matplotlib.pyplot |
|
247 | 248 | matplotlib.pyplot.switch_backend(backend) |
|
248 | 249 | |
|
249 | 250 | # This must be imported last in the matplotlib series, after |
|
250 | 251 | # backend/interactivity choices have been made |
|
251 | 252 | import matplotlib.pylab as pylab |
|
252 | 253 | |
|
253 | 254 | pylab.show._needmain = False |
|
254 | 255 | # We need to detect at runtime whether show() is called by the user. |
|
255 | 256 | # For this, we wrap it into a decorator which adds a 'called' flag. |
|
256 | 257 | pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive) |
|
257 | 258 | |
|
258 | 259 | |
|
259 | 260 | def import_pylab(user_ns, import_all=True): |
|
260 | 261 | """Populate the namespace with pylab-related values. |
|
261 | 262 | |
|
262 | 263 | Imports matplotlib, pylab, numpy, and everything from pylab and numpy. |
|
263 | 264 | |
|
264 | 265 | Also imports a few names from IPython (figsize, display, getfigs) |
|
265 | 266 | |
|
266 | 267 | """ |
|
267 | 268 | |
|
268 | 269 | # Import numpy as np/pyplot as plt are conventions we're trying to |
|
269 | 270 | # somewhat standardize on. Making them available to users by default |
|
270 | 271 | # will greatly help this. |
|
271 | 272 | s = ("import numpy\n" |
|
272 | 273 | "import matplotlib\n" |
|
273 | 274 | "from matplotlib import pylab, mlab, pyplot\n" |
|
274 | 275 | "np = numpy\n" |
|
275 | 276 | "plt = pyplot\n" |
|
276 | 277 | ) |
|
277 | 278 | exec s in user_ns |
|
278 | 279 | |
|
279 | 280 | if import_all: |
|
280 | 281 | s = ("from matplotlib.pylab import *\n" |
|
281 | 282 | "from numpy import *\n") |
|
282 | 283 | exec s in user_ns |
|
283 | 284 | |
|
284 | 285 | # IPython symbols to add |
|
285 | 286 | user_ns['figsize'] = figsize |
|
286 | 287 | from IPython.core.display import display |
|
287 | 288 | # Add display and getfigs to the user's namespace |
|
288 | 289 | user_ns['display'] = display |
|
289 | 290 | user_ns['getfigs'] = getfigs |
|
290 | 291 | |
|
291 | 292 | |
|
292 | 293 | def configure_inline_support(shell, backend): |
|
293 | 294 | """Configure an IPython shell object for matplotlib use. |
|
294 | 295 | |
|
295 | 296 | Parameters |
|
296 | 297 | ---------- |
|
297 | 298 | shell : InteractiveShell instance |
|
298 | 299 | |
|
299 | 300 | backend : matplotlib backend |
|
300 | 301 | """ |
|
301 | 302 | # If using our svg payload backend, register the post-execution |
|
302 | 303 | # function that will pick up the results for display. This can only be |
|
303 | 304 | # done with access to the real shell object. |
|
304 | 305 | |
|
305 | 306 | # Note: if we can't load the inline backend, then there's no point |
|
306 | 307 | # continuing (such as in terminal-only shells in environments without |
|
307 | 308 | # zeromq available). |
|
308 | 309 | try: |
|
309 | 310 | from IPython.kernel.zmq.pylab.backend_inline import InlineBackend |
|
310 | 311 | except ImportError: |
|
311 | 312 | return |
|
312 | 313 | from matplotlib import pyplot |
|
313 | 314 | |
|
314 | 315 | cfg = InlineBackend.instance(parent=shell) |
|
315 | 316 | cfg.shell = shell |
|
316 | 317 | if cfg not in shell.configurables: |
|
317 | 318 | shell.configurables.append(cfg) |
|
318 | 319 | |
|
319 | 320 | if backend == backends['inline']: |
|
320 | 321 | from IPython.kernel.zmq.pylab.backend_inline import flush_figures |
|
321 | 322 | shell.register_post_execute(flush_figures) |
|
322 | 323 | |
|
323 | 324 | # Save rcParams that will be overwrittern |
|
324 | 325 | shell._saved_rcParams = dict() |
|
325 | 326 | for k in cfg.rc: |
|
326 | 327 | shell._saved_rcParams[k] = pyplot.rcParams[k] |
|
327 | 328 | # load inline_rc |
|
328 | 329 | pyplot.rcParams.update(cfg.rc) |
|
329 | 330 | else: |
|
330 | 331 | from IPython.kernel.zmq.pylab.backend_inline import flush_figures |
|
331 | 332 | if flush_figures in shell._post_execute: |
|
332 | 333 | shell._post_execute.pop(flush_figures) |
|
333 | 334 | if hasattr(shell, '_saved_rcParams'): |
|
334 | 335 | pyplot.rcParams.update(shell._saved_rcParams) |
|
335 | 336 | del shell._saved_rcParams |
|
336 | 337 | |
|
337 | 338 | # Setup the default figure format |
|
338 | 339 | select_figure_format(shell, cfg.figure_format) |
|
339 | 340 |
@@ -1,409 +1,410 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | A mixin for :class:`~IPython.core.application.Application` classes that |
|
4 | 4 | launch InteractiveShell instances, load extensions, etc. |
|
5 | 5 | |
|
6 | 6 | Authors |
|
7 | 7 | ------- |
|
8 | 8 | |
|
9 | 9 | * Min Ragan-Kelley |
|
10 | 10 | """ |
|
11 | 11 | |
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | # Copyright (C) 2008-2011 The IPython Development Team |
|
14 | 14 | # |
|
15 | 15 | # Distributed under the terms of the BSD License. The full license is in |
|
16 | 16 | # the file COPYING, distributed as part of this software. |
|
17 | 17 | #----------------------------------------------------------------------------- |
|
18 | 18 | |
|
19 | 19 | #----------------------------------------------------------------------------- |
|
20 | 20 | # Imports |
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | |
|
23 | 23 | from __future__ import absolute_import |
|
24 | from __future__ import print_function | |
|
24 | 25 | |
|
25 | 26 | import glob |
|
26 | 27 | import os |
|
27 | 28 | import sys |
|
28 | 29 | |
|
29 | 30 | from IPython.config.application import boolean_flag |
|
30 | 31 | from IPython.config.configurable import Configurable |
|
31 | 32 | from IPython.config.loader import Config |
|
32 | 33 | from IPython.core import pylabtools |
|
33 | 34 | from IPython.utils import py3compat |
|
34 | 35 | from IPython.utils.contexts import preserve_keys |
|
35 | 36 | from IPython.utils.path import filefind |
|
36 | 37 | from IPython.utils.traitlets import ( |
|
37 | 38 | Unicode, Instance, List, Bool, CaselessStrEnum, Dict |
|
38 | 39 | ) |
|
39 | 40 | from IPython.lib.inputhook import guis |
|
40 | 41 | |
|
41 | 42 | #----------------------------------------------------------------------------- |
|
42 | 43 | # Aliases and Flags |
|
43 | 44 | #----------------------------------------------------------------------------- |
|
44 | 45 | |
|
45 | 46 | gui_keys = tuple(sorted([ key for key in guis if key is not None ])) |
|
46 | 47 | |
|
47 | 48 | backend_keys = sorted(pylabtools.backends.keys()) |
|
48 | 49 | backend_keys.insert(0, 'auto') |
|
49 | 50 | |
|
50 | 51 | shell_flags = {} |
|
51 | 52 | |
|
52 | 53 | addflag = lambda *args: shell_flags.update(boolean_flag(*args)) |
|
53 | 54 | addflag('autoindent', 'InteractiveShell.autoindent', |
|
54 | 55 | 'Turn on autoindenting.', 'Turn off autoindenting.' |
|
55 | 56 | ) |
|
56 | 57 | addflag('automagic', 'InteractiveShell.automagic', |
|
57 | 58 | """Turn on the auto calling of magic commands. Type %%magic at the |
|
58 | 59 | IPython prompt for more information.""", |
|
59 | 60 | 'Turn off the auto calling of magic commands.' |
|
60 | 61 | ) |
|
61 | 62 | addflag('pdb', 'InteractiveShell.pdb', |
|
62 | 63 | "Enable auto calling the pdb debugger after every exception.", |
|
63 | 64 | "Disable auto calling the pdb debugger after every exception." |
|
64 | 65 | ) |
|
65 | 66 | # pydb flag doesn't do any config, as core.debugger switches on import, |
|
66 | 67 | # which is before parsing. This just allows the flag to be passed. |
|
67 | 68 | shell_flags.update(dict( |
|
68 | 69 | pydb = ({}, |
|
69 | 70 | """Use the third party 'pydb' package as debugger, instead of pdb. |
|
70 | 71 | Requires that pydb is installed.""" |
|
71 | 72 | ) |
|
72 | 73 | )) |
|
73 | 74 | addflag('pprint', 'PlainTextFormatter.pprint', |
|
74 | 75 | "Enable auto pretty printing of results.", |
|
75 | 76 | "Disable auto pretty printing of results." |
|
76 | 77 | ) |
|
77 | 78 | addflag('color-info', 'InteractiveShell.color_info', |
|
78 | 79 | """IPython can display information about objects via a set of func- |
|
79 | 80 | tions, and optionally can use colors for this, syntax highlighting |
|
80 | 81 | source code and various other elements. However, because this |
|
81 | 82 | information is passed through a pager (like 'less') and many pagers get |
|
82 | 83 | confused with color codes, this option is off by default. You can test |
|
83 | 84 | it and turn it on permanently in your ipython_config.py file if it |
|
84 | 85 | works for you. Test it and turn it on permanently if it works with |
|
85 | 86 | your system. The magic function %%color_info allows you to toggle this |
|
86 | 87 | interactively for testing.""", |
|
87 | 88 | "Disable using colors for info related things." |
|
88 | 89 | ) |
|
89 | 90 | addflag('deep-reload', 'InteractiveShell.deep_reload', |
|
90 | 91 | """Enable deep (recursive) reloading by default. IPython can use the |
|
91 | 92 | deep_reload module which reloads changes in modules recursively (it |
|
92 | 93 | replaces the reload() function, so you don't need to change anything to |
|
93 | 94 | use it). deep_reload() forces a full reload of modules whose code may |
|
94 | 95 | have changed, which the default reload() function does not. When |
|
95 | 96 | deep_reload is off, IPython will use the normal reload(), but |
|
96 | 97 | deep_reload will still be available as dreload(). This feature is off |
|
97 | 98 | by default [which means that you have both normal reload() and |
|
98 | 99 | dreload()].""", |
|
99 | 100 | "Disable deep (recursive) reloading by default." |
|
100 | 101 | ) |
|
101 | 102 | nosep_config = Config() |
|
102 | 103 | nosep_config.InteractiveShell.separate_in = '' |
|
103 | 104 | nosep_config.InteractiveShell.separate_out = '' |
|
104 | 105 | nosep_config.InteractiveShell.separate_out2 = '' |
|
105 | 106 | |
|
106 | 107 | shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.") |
|
107 | 108 | shell_flags['pylab'] = ( |
|
108 | 109 | {'InteractiveShellApp' : {'pylab' : 'auto'}}, |
|
109 | 110 | """Pre-load matplotlib and numpy for interactive use with |
|
110 | 111 | the default matplotlib backend.""" |
|
111 | 112 | ) |
|
112 | 113 | shell_flags['matplotlib'] = ( |
|
113 | 114 | {'InteractiveShellApp' : {'matplotlib' : 'auto'}}, |
|
114 | 115 | """Configure matplotlib for interactive use with |
|
115 | 116 | the default matplotlib backend.""" |
|
116 | 117 | ) |
|
117 | 118 | |
|
118 | 119 | # it's possible we don't want short aliases for *all* of these: |
|
119 | 120 | shell_aliases = dict( |
|
120 | 121 | autocall='InteractiveShell.autocall', |
|
121 | 122 | colors='InteractiveShell.colors', |
|
122 | 123 | logfile='InteractiveShell.logfile', |
|
123 | 124 | logappend='InteractiveShell.logappend', |
|
124 | 125 | c='InteractiveShellApp.code_to_run', |
|
125 | 126 | m='InteractiveShellApp.module_to_run', |
|
126 | 127 | ext='InteractiveShellApp.extra_extension', |
|
127 | 128 | gui='InteractiveShellApp.gui', |
|
128 | 129 | pylab='InteractiveShellApp.pylab', |
|
129 | 130 | matplotlib='InteractiveShellApp.matplotlib', |
|
130 | 131 | ) |
|
131 | 132 | shell_aliases['cache-size'] = 'InteractiveShell.cache_size' |
|
132 | 133 | |
|
133 | 134 | #----------------------------------------------------------------------------- |
|
134 | 135 | # Main classes and functions |
|
135 | 136 | #----------------------------------------------------------------------------- |
|
136 | 137 | |
|
137 | 138 | class InteractiveShellApp(Configurable): |
|
138 | 139 | """A Mixin for applications that start InteractiveShell instances. |
|
139 | 140 | |
|
140 | 141 | Provides configurables for loading extensions and executing files |
|
141 | 142 | as part of configuring a Shell environment. |
|
142 | 143 | |
|
143 | 144 | The following methods should be called by the :meth:`initialize` method |
|
144 | 145 | of the subclass: |
|
145 | 146 | |
|
146 | 147 | - :meth:`init_path` |
|
147 | 148 | - :meth:`init_shell` (to be implemented by the subclass) |
|
148 | 149 | - :meth:`init_gui_pylab` |
|
149 | 150 | - :meth:`init_extensions` |
|
150 | 151 | - :meth:`init_code` |
|
151 | 152 | """ |
|
152 | 153 | extensions = List(Unicode, config=True, |
|
153 | 154 | help="A list of dotted module names of IPython extensions to load." |
|
154 | 155 | ) |
|
155 | 156 | extra_extension = Unicode('', config=True, |
|
156 | 157 | help="dotted module name of an IPython extension to load." |
|
157 | 158 | ) |
|
158 | 159 | def _extra_extension_changed(self, name, old, new): |
|
159 | 160 | if new: |
|
160 | 161 | # add to self.extensions |
|
161 | 162 | self.extensions.append(new) |
|
162 | 163 | |
|
163 | 164 | # Extensions that are always loaded (not configurable) |
|
164 | 165 | default_extensions = List(Unicode, [u'storemagic'], config=False) |
|
165 | 166 | |
|
166 | 167 | exec_files = List(Unicode, config=True, |
|
167 | 168 | help="""List of files to run at IPython startup.""" |
|
168 | 169 | ) |
|
169 | 170 | file_to_run = Unicode('', config=True, |
|
170 | 171 | help="""A file to be run""") |
|
171 | 172 | |
|
172 | 173 | exec_lines = List(Unicode, config=True, |
|
173 | 174 | help="""lines of code to run at IPython startup.""" |
|
174 | 175 | ) |
|
175 | 176 | code_to_run = Unicode('', config=True, |
|
176 | 177 | help="Execute the given command string." |
|
177 | 178 | ) |
|
178 | 179 | module_to_run = Unicode('', config=True, |
|
179 | 180 | help="Run the module as a script." |
|
180 | 181 | ) |
|
181 | 182 | gui = CaselessStrEnum(gui_keys, config=True, |
|
182 | 183 | help="Enable GUI event loop integration with any of {0}.".format(gui_keys) |
|
183 | 184 | ) |
|
184 | 185 | matplotlib = CaselessStrEnum(backend_keys, |
|
185 | 186 | config=True, |
|
186 | 187 | help="""Configure matplotlib for interactive use with |
|
187 | 188 | the default matplotlib backend.""" |
|
188 | 189 | ) |
|
189 | 190 | pylab = CaselessStrEnum(backend_keys, |
|
190 | 191 | config=True, |
|
191 | 192 | help="""Pre-load matplotlib and numpy for interactive use, |
|
192 | 193 | selecting a particular matplotlib backend and loop integration. |
|
193 | 194 | """ |
|
194 | 195 | ) |
|
195 | 196 | pylab_import_all = Bool(True, config=True, |
|
196 | 197 | help="""If true, IPython will populate the user namespace with numpy, pylab, etc. |
|
197 | 198 | and an 'import *' is done from numpy and pylab, when using pylab mode. |
|
198 | 199 | |
|
199 | 200 | When False, pylab mode should not import any names into the user namespace. |
|
200 | 201 | """ |
|
201 | 202 | ) |
|
202 | 203 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
|
203 | 204 | |
|
204 | 205 | user_ns = Instance(dict, args=None, allow_none=True) |
|
205 | 206 | def _user_ns_changed(self, name, old, new): |
|
206 | 207 | if self.shell is not None: |
|
207 | 208 | self.shell.user_ns = new |
|
208 | 209 | self.shell.init_user_ns() |
|
209 | 210 | |
|
210 | 211 | def init_path(self): |
|
211 | 212 | """Add current working directory, '', to sys.path""" |
|
212 | 213 | if sys.path[0] != '': |
|
213 | 214 | sys.path.insert(0, '') |
|
214 | 215 | |
|
215 | 216 | def init_shell(self): |
|
216 | 217 | raise NotImplementedError("Override in subclasses") |
|
217 | 218 | |
|
218 | 219 | def init_gui_pylab(self): |
|
219 | 220 | """Enable GUI event loop integration, taking pylab into account.""" |
|
220 | 221 | enable = False |
|
221 | 222 | shell = self.shell |
|
222 | 223 | if self.pylab: |
|
223 | 224 | enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all) |
|
224 | 225 | key = self.pylab |
|
225 | 226 | elif self.matplotlib: |
|
226 | 227 | enable = shell.enable_matplotlib |
|
227 | 228 | key = self.matplotlib |
|
228 | 229 | elif self.gui: |
|
229 | 230 | enable = shell.enable_gui |
|
230 | 231 | key = self.gui |
|
231 | 232 | |
|
232 | 233 | if not enable: |
|
233 | 234 | return |
|
234 | 235 | |
|
235 | 236 | try: |
|
236 | 237 | r = enable(key) |
|
237 | 238 | except ImportError: |
|
238 | 239 | self.log.warn("Eventloop or matplotlib integration failed. Is matplotlib installed?") |
|
239 | 240 | self.shell.showtraceback() |
|
240 | 241 | return |
|
241 | 242 | except Exception: |
|
242 | 243 | self.log.warn("GUI event loop or pylab initialization failed") |
|
243 | 244 | self.shell.showtraceback() |
|
244 | 245 | return |
|
245 | 246 | |
|
246 | 247 | if isinstance(r, tuple): |
|
247 | 248 | gui, backend = r[:2] |
|
248 | 249 | self.log.info("Enabling GUI event loop integration, " |
|
249 | 250 | "eventloop=%s, matplotlib=%s", gui, backend) |
|
250 | 251 | if key == "auto": |
|
251 |
print |
|
|
252 | print(("Using matplotlib backend: %s" % backend)) | |
|
252 | 253 | else: |
|
253 | 254 | gui = r |
|
254 | 255 | self.log.info("Enabling GUI event loop integration, " |
|
255 | 256 | "eventloop=%s", gui) |
|
256 | 257 | |
|
257 | 258 | def init_extensions(self): |
|
258 | 259 | """Load all IPython extensions in IPythonApp.extensions. |
|
259 | 260 | |
|
260 | 261 | This uses the :meth:`ExtensionManager.load_extensions` to load all |
|
261 | 262 | the extensions listed in ``self.extensions``. |
|
262 | 263 | """ |
|
263 | 264 | try: |
|
264 | 265 | self.log.debug("Loading IPython extensions...") |
|
265 | 266 | extensions = self.default_extensions + self.extensions |
|
266 | 267 | for ext in extensions: |
|
267 | 268 | try: |
|
268 | 269 | self.log.info("Loading IPython extension: %s" % ext) |
|
269 | 270 | self.shell.extension_manager.load_extension(ext) |
|
270 | 271 | except: |
|
271 | 272 | self.log.warn("Error in loading extension: %s" % ext + |
|
272 | 273 | "\nCheck your config files in %s" % self.profile_dir.location |
|
273 | 274 | ) |
|
274 | 275 | self.shell.showtraceback() |
|
275 | 276 | except: |
|
276 | 277 | self.log.warn("Unknown error in loading extensions:") |
|
277 | 278 | self.shell.showtraceback() |
|
278 | 279 | |
|
279 | 280 | def init_code(self): |
|
280 | 281 | """run the pre-flight code, specified via exec_lines""" |
|
281 | 282 | self._run_startup_files() |
|
282 | 283 | self._run_exec_lines() |
|
283 | 284 | self._run_exec_files() |
|
284 | 285 | self._run_cmd_line_code() |
|
285 | 286 | self._run_module() |
|
286 | 287 | |
|
287 | 288 | # flush output, so itwon't be attached to the first cell |
|
288 | 289 | sys.stdout.flush() |
|
289 | 290 | sys.stderr.flush() |
|
290 | 291 | |
|
291 | 292 | # Hide variables defined here from %who etc. |
|
292 | 293 | self.shell.user_ns_hidden.update(self.shell.user_ns) |
|
293 | 294 | |
|
294 | 295 | def _run_exec_lines(self): |
|
295 | 296 | """Run lines of code in IPythonApp.exec_lines in the user's namespace.""" |
|
296 | 297 | if not self.exec_lines: |
|
297 | 298 | return |
|
298 | 299 | try: |
|
299 | 300 | self.log.debug("Running code from IPythonApp.exec_lines...") |
|
300 | 301 | for line in self.exec_lines: |
|
301 | 302 | try: |
|
302 | 303 | self.log.info("Running code in user namespace: %s" % |
|
303 | 304 | line) |
|
304 | 305 | self.shell.run_cell(line, store_history=False) |
|
305 | 306 | except: |
|
306 | 307 | self.log.warn("Error in executing line in user " |
|
307 | 308 | "namespace: %s" % line) |
|
308 | 309 | self.shell.showtraceback() |
|
309 | 310 | except: |
|
310 | 311 | self.log.warn("Unknown error in handling IPythonApp.exec_lines:") |
|
311 | 312 | self.shell.showtraceback() |
|
312 | 313 | |
|
313 | 314 | def _exec_file(self, fname): |
|
314 | 315 | try: |
|
315 | 316 | full_filename = filefind(fname, [u'.', self.ipython_dir]) |
|
316 | 317 | except IOError as e: |
|
317 | 318 | self.log.warn("File not found: %r"%fname) |
|
318 | 319 | return |
|
319 | 320 | # Make sure that the running script gets a proper sys.argv as if it |
|
320 | 321 | # were run from a system shell. |
|
321 | 322 | save_argv = sys.argv |
|
322 | 323 | sys.argv = [full_filename] + self.extra_args[1:] |
|
323 | 324 | # protect sys.argv from potential unicode strings on Python 2: |
|
324 | 325 | if not py3compat.PY3: |
|
325 | 326 | sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] |
|
326 | 327 | try: |
|
327 | 328 | if os.path.isfile(full_filename): |
|
328 | 329 | self.log.info("Running file in user namespace: %s" % |
|
329 | 330 | full_filename) |
|
330 | 331 | # Ensure that __file__ is always defined to match Python |
|
331 | 332 | # behavior. |
|
332 | 333 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
333 | 334 | self.shell.user_ns['__file__'] = fname |
|
334 | 335 | if full_filename.endswith('.ipy'): |
|
335 | 336 | self.shell.safe_execfile_ipy(full_filename) |
|
336 | 337 | else: |
|
337 | 338 | # default to python, even without extension |
|
338 | 339 | self.shell.safe_execfile(full_filename, |
|
339 | 340 | self.shell.user_ns) |
|
340 | 341 | finally: |
|
341 | 342 | sys.argv = save_argv |
|
342 | 343 | |
|
343 | 344 | def _run_startup_files(self): |
|
344 | 345 | """Run files from profile startup directory""" |
|
345 | 346 | startup_dir = self.profile_dir.startup_dir |
|
346 | 347 | startup_files = [] |
|
347 | 348 | if os.environ.get('PYTHONSTARTUP', False): |
|
348 | 349 | startup_files.append(os.environ['PYTHONSTARTUP']) |
|
349 | 350 | startup_files += glob.glob(os.path.join(startup_dir, '*.py')) |
|
350 | 351 | startup_files += glob.glob(os.path.join(startup_dir, '*.ipy')) |
|
351 | 352 | if not startup_files: |
|
352 | 353 | return |
|
353 | 354 | |
|
354 | 355 | self.log.debug("Running startup files from %s...", startup_dir) |
|
355 | 356 | try: |
|
356 | 357 | for fname in sorted(startup_files): |
|
357 | 358 | self._exec_file(fname) |
|
358 | 359 | except: |
|
359 | 360 | self.log.warn("Unknown error in handling startup files:") |
|
360 | 361 | self.shell.showtraceback() |
|
361 | 362 | |
|
362 | 363 | def _run_exec_files(self): |
|
363 | 364 | """Run files from IPythonApp.exec_files""" |
|
364 | 365 | if not self.exec_files: |
|
365 | 366 | return |
|
366 | 367 | |
|
367 | 368 | self.log.debug("Running files in IPythonApp.exec_files...") |
|
368 | 369 | try: |
|
369 | 370 | for fname in self.exec_files: |
|
370 | 371 | self._exec_file(fname) |
|
371 | 372 | except: |
|
372 | 373 | self.log.warn("Unknown error in handling IPythonApp.exec_files:") |
|
373 | 374 | self.shell.showtraceback() |
|
374 | 375 | |
|
375 | 376 | def _run_cmd_line_code(self): |
|
376 | 377 | """Run code or file specified at the command-line""" |
|
377 | 378 | if self.code_to_run: |
|
378 | 379 | line = self.code_to_run |
|
379 | 380 | try: |
|
380 | 381 | self.log.info("Running code given at command line (c=): %s" % |
|
381 | 382 | line) |
|
382 | 383 | self.shell.run_cell(line, store_history=False) |
|
383 | 384 | except: |
|
384 | 385 | self.log.warn("Error in executing line in user namespace: %s" % |
|
385 | 386 | line) |
|
386 | 387 | self.shell.showtraceback() |
|
387 | 388 | |
|
388 | 389 | # Like Python itself, ignore the second if the first of these is present |
|
389 | 390 | elif self.file_to_run: |
|
390 | 391 | fname = self.file_to_run |
|
391 | 392 | try: |
|
392 | 393 | self._exec_file(fname) |
|
393 | 394 | except: |
|
394 | 395 | self.log.warn("Error in executing file in user namespace: %s" % |
|
395 | 396 | fname) |
|
396 | 397 | self.shell.showtraceback() |
|
397 | 398 | |
|
398 | 399 | def _run_module(self): |
|
399 | 400 | """Run module specified at the command-line.""" |
|
400 | 401 | if self.module_to_run: |
|
401 | 402 | # Make sure that the module gets a proper sys.argv as if it were |
|
402 | 403 | # run using `python -m`. |
|
403 | 404 | save_argv = sys.argv |
|
404 | 405 | sys.argv = [sys.executable] + self.extra_args |
|
405 | 406 | try: |
|
406 | 407 | self.shell.safe_run_module(self.module_to_run, |
|
407 | 408 | self.shell.user_ns) |
|
408 | 409 | finally: |
|
409 | 410 | sys.argv = save_argv |
@@ -1,2 +1,3 b'' | |||
|
1 | from __future__ import print_function | |
|
1 | 2 | import sys |
|
2 |
print |
|
|
3 | print(sys.argv[1:]) |
@@ -1,47 +1,48 b'' | |||
|
1 | 1 | """Minimal script to reproduce our nasty reference counting bug. |
|
2 | 2 | |
|
3 | 3 | The problem is related to https://github.com/ipython/ipython/issues/141 |
|
4 | 4 | |
|
5 | 5 | The original fix for that appeared to work, but John D. Hunter found a |
|
6 | 6 | matplotlib example which, when run twice in a row, would break. The problem |
|
7 | 7 | were references held by open figures to internals of Tkinter. |
|
8 | 8 | |
|
9 | 9 | This code reproduces the problem that John saw, without matplotlib. |
|
10 | 10 | |
|
11 | 11 | This script is meant to be called by other parts of the test suite that call it |
|
12 | 12 | via %run as if it were executed interactively by the user. As of 2011-05-29, |
|
13 | 13 | test_run.py calls it. |
|
14 | 14 | """ |
|
15 | from __future__ import print_function | |
|
15 | 16 | |
|
16 | 17 | #----------------------------------------------------------------------------- |
|
17 | 18 | # Module imports |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | import sys |
|
20 | 21 | |
|
21 | 22 | from IPython import get_ipython |
|
22 | 23 | |
|
23 | 24 | #----------------------------------------------------------------------------- |
|
24 | 25 | # Globals |
|
25 | 26 | #----------------------------------------------------------------------------- |
|
26 | 27 | |
|
27 | 28 | # This needs to be here because nose and other test runners will import |
|
28 | 29 | # this module. Importing this module has potential side effects that we |
|
29 | 30 | # want to prevent. |
|
30 | 31 | if __name__ == '__main__': |
|
31 | 32 | |
|
32 | 33 | ip = get_ipython() |
|
33 | 34 | |
|
34 | 35 | if not '_refbug_cache' in ip.user_ns: |
|
35 | 36 | ip.user_ns['_refbug_cache'] = [] |
|
36 | 37 | |
|
37 | 38 | |
|
38 | 39 | aglobal = 'Hello' |
|
39 | 40 | def f(): |
|
40 | 41 | return aglobal |
|
41 | 42 | |
|
42 | 43 | cache = ip.user_ns['_refbug_cache'] |
|
43 | 44 | cache.append(f) |
|
44 | 45 | |
|
45 | 46 | def call_f(): |
|
46 | 47 | for func in cache: |
|
47 |
print |
|
|
48 | print('lowercased:',func().lower()) |
@@ -1,153 +1,154 b'' | |||
|
1 | 1 | """Tests for debugging machinery. |
|
2 | 2 | """ |
|
3 | from __future__ import print_function | |
|
3 | 4 | #----------------------------------------------------------------------------- |
|
4 | 5 | # Copyright (c) 2012, The IPython Development Team. |
|
5 | 6 | # |
|
6 | 7 | # Distributed under the terms of the Modified BSD License. |
|
7 | 8 | # |
|
8 | 9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | # Imports |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | import sys |
|
16 | 17 | |
|
17 | 18 | # third-party |
|
18 | 19 | import nose.tools as nt |
|
19 | 20 | |
|
20 | 21 | # Our own |
|
21 | 22 | from IPython.core import debugger |
|
22 | 23 | |
|
23 | 24 | #----------------------------------------------------------------------------- |
|
24 | 25 | # Helper classes, from CPython's Pdb test suite |
|
25 | 26 | #----------------------------------------------------------------------------- |
|
26 | 27 | |
|
27 | 28 | class _FakeInput(object): |
|
28 | 29 | """ |
|
29 | 30 | A fake input stream for pdb's interactive debugger. Whenever a |
|
30 | 31 | line is read, print it (to simulate the user typing it), and then |
|
31 | 32 | return it. The set of lines to return is specified in the |
|
32 | 33 | constructor; they should not have trailing newlines. |
|
33 | 34 | """ |
|
34 | 35 | def __init__(self, lines): |
|
35 | 36 | self.lines = iter(lines) |
|
36 | 37 | |
|
37 | 38 | def readline(self): |
|
38 | 39 | line = next(self.lines) |
|
39 |
print |
|
|
40 | print(line) | |
|
40 | 41 | return line+'\n' |
|
41 | 42 | |
|
42 | 43 | class PdbTestInput(object): |
|
43 | 44 | """Context manager that makes testing Pdb in doctests easier.""" |
|
44 | 45 | |
|
45 | 46 | def __init__(self, input): |
|
46 | 47 | self.input = input |
|
47 | 48 | |
|
48 | 49 | def __enter__(self): |
|
49 | 50 | self.real_stdin = sys.stdin |
|
50 | 51 | sys.stdin = _FakeInput(self.input) |
|
51 | 52 | |
|
52 | 53 | def __exit__(self, *exc): |
|
53 | 54 | sys.stdin = self.real_stdin |
|
54 | 55 | |
|
55 | 56 | #----------------------------------------------------------------------------- |
|
56 | 57 | # Tests |
|
57 | 58 | #----------------------------------------------------------------------------- |
|
58 | 59 | |
|
59 | 60 | def test_longer_repr(): |
|
60 | 61 | from repr import repr as trepr |
|
61 | 62 | |
|
62 | 63 | a = '1234567890'* 7 |
|
63 | 64 | ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'" |
|
64 | 65 | a_trunc = "'123456789012...8901234567890'" |
|
65 | 66 | nt.assert_equal(trepr(a), a_trunc) |
|
66 | 67 | # The creation of our tracer modifies the repr module's repr function |
|
67 | 68 | # in-place, since that global is used directly by the stdlib's pdb module. |
|
68 | 69 | t = debugger.Tracer() |
|
69 | 70 | nt.assert_equal(trepr(a), ar) |
|
70 | 71 | |
|
71 | 72 | def test_ipdb_magics(): |
|
72 | 73 | '''Test calling some IPython magics from ipdb. |
|
73 | 74 | |
|
74 | 75 | First, set up some test functions and classes which we can inspect. |
|
75 | 76 | |
|
76 | 77 | >>> class ExampleClass(object): |
|
77 | 78 | ... """Docstring for ExampleClass.""" |
|
78 | 79 | ... def __init__(self): |
|
79 | 80 | ... """Docstring for ExampleClass.__init__""" |
|
80 | 81 | ... pass |
|
81 | 82 | ... def __str__(self): |
|
82 | 83 | ... return "ExampleClass()" |
|
83 | 84 | |
|
84 | 85 | >>> def example_function(x, y, z="hello"): |
|
85 | 86 | ... """Docstring for example_function.""" |
|
86 | 87 | ... pass |
|
87 | 88 | |
|
88 | 89 | >>> old_trace = sys.gettrace() |
|
89 | 90 | |
|
90 | 91 | Create a function which triggers ipdb. |
|
91 | 92 | |
|
92 | 93 | >>> def trigger_ipdb(): |
|
93 | 94 | ... a = ExampleClass() |
|
94 | 95 | ... debugger.Pdb().set_trace() |
|
95 | 96 | |
|
96 | 97 | >>> with PdbTestInput([ |
|
97 | 98 | ... 'pdef example_function', |
|
98 | 99 | ... 'pdoc ExampleClass', |
|
99 | 100 | ... 'pinfo a', |
|
100 | 101 | ... 'continue', |
|
101 | 102 | ... ]): |
|
102 | 103 | ... trigger_ipdb() |
|
103 | 104 | --Return-- |
|
104 | 105 | None |
|
105 | 106 | > <doctest ...>(3)trigger_ipdb() |
|
106 | 107 | 1 def trigger_ipdb(): |
|
107 | 108 | 2 a = ExampleClass() |
|
108 | 109 | ----> 3 debugger.Pdb().set_trace() |
|
109 | 110 | <BLANKLINE> |
|
110 | 111 | ipdb> pdef example_function |
|
111 | 112 | example_function(x, y, z='hello') |
|
112 | 113 | ipdb> pdoc ExampleClass |
|
113 | 114 | Class Docstring: |
|
114 | 115 | Docstring for ExampleClass. |
|
115 | 116 | Constructor Docstring: |
|
116 | 117 | Docstring for ExampleClass.__init__ |
|
117 | 118 | ipdb> pinfo a |
|
118 | 119 | Type: ExampleClass |
|
119 | 120 | String Form:ExampleClass() |
|
120 | 121 | Namespace: Local... |
|
121 | 122 | Docstring: Docstring for ExampleClass. |
|
122 | 123 | Constructor Docstring:Docstring for ExampleClass.__init__ |
|
123 | 124 | ipdb> continue |
|
124 | 125 | |
|
125 | 126 | Restore previous trace function, e.g. for coverage.py |
|
126 | 127 | |
|
127 | 128 | >>> sys.settrace(old_trace) |
|
128 | 129 | ''' |
|
129 | 130 | |
|
130 | 131 | def test_ipdb_magics2(): |
|
131 | 132 | '''Test ipdb with a very short function. |
|
132 | 133 | |
|
133 | 134 | >>> old_trace = sys.gettrace() |
|
134 | 135 | |
|
135 | 136 | >>> def bar(): |
|
136 | 137 | ... pass |
|
137 | 138 | |
|
138 | 139 | Run ipdb. |
|
139 | 140 | |
|
140 | 141 | >>> with PdbTestInput([ |
|
141 | 142 | ... 'continue', |
|
142 | 143 | ... ]): |
|
143 | 144 | ... debugger.Pdb().runcall(bar) |
|
144 | 145 | > <doctest ...>(2)bar() |
|
145 | 146 | 1 def bar(): |
|
146 | 147 | ----> 2 pass |
|
147 | 148 | <BLANKLINE> |
|
148 | 149 | ipdb> continue |
|
149 | 150 | |
|
150 | 151 | Restore previous trace function, e.g. for coverage.py |
|
151 | 152 | |
|
152 | 153 | >>> sys.settrace(old_trace) |
|
153 | 154 | ''' |
@@ -1,582 +1,583 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Tests for the inputsplitter module. |
|
3 | 3 | |
|
4 | 4 | Authors |
|
5 | 5 | ------- |
|
6 | 6 | * Fernando Perez |
|
7 | 7 | * Robert Kern |
|
8 | 8 | """ |
|
9 | from __future__ import print_function | |
|
9 | 10 | #----------------------------------------------------------------------------- |
|
10 | 11 | # Copyright (C) 2010-2011 The IPython Development Team |
|
11 | 12 | # |
|
12 | 13 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | 14 | # the file COPYING, distributed as part of this software. |
|
14 | 15 | #----------------------------------------------------------------------------- |
|
15 | 16 | |
|
16 | 17 | #----------------------------------------------------------------------------- |
|
17 | 18 | # Imports |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | # stdlib |
|
20 | 21 | import unittest |
|
21 | 22 | import sys |
|
22 | 23 | |
|
23 | 24 | # Third party |
|
24 | 25 | import nose.tools as nt |
|
25 | 26 | |
|
26 | 27 | # Our own |
|
27 | 28 | from IPython.core import inputsplitter as isp |
|
28 | 29 | from IPython.core.tests.test_inputtransformer import syntax, syntax_ml |
|
29 | 30 | from IPython.testing import tools as tt |
|
30 | 31 | from IPython.utils import py3compat |
|
31 | 32 | |
|
32 | 33 | #----------------------------------------------------------------------------- |
|
33 | 34 | # Semi-complete examples (also used as tests) |
|
34 | 35 | #----------------------------------------------------------------------------- |
|
35 | 36 | |
|
36 | 37 | # Note: at the bottom, there's a slightly more complete version of this that |
|
37 | 38 | # can be useful during development of code here. |
|
38 | 39 | |
|
39 | 40 | def mini_interactive_loop(input_func): |
|
40 | 41 | """Minimal example of the logic of an interactive interpreter loop. |
|
41 | 42 | |
|
42 | 43 | This serves as an example, and it is used by the test system with a fake |
|
43 | 44 | raw_input that simulates interactive input.""" |
|
44 | 45 | |
|
45 | 46 | from IPython.core.inputsplitter import InputSplitter |
|
46 | 47 | |
|
47 | 48 | isp = InputSplitter() |
|
48 | 49 | # In practice, this input loop would be wrapped in an outside loop to read |
|
49 | 50 | # input indefinitely, until some exit/quit command was issued. Here we |
|
50 | 51 | # only illustrate the basic inner loop. |
|
51 | 52 | while isp.push_accepts_more(): |
|
52 | 53 | indent = ' '*isp.indent_spaces |
|
53 | 54 | prompt = '>>> ' + indent |
|
54 | 55 | line = indent + input_func(prompt) |
|
55 | 56 | isp.push(line) |
|
56 | 57 | |
|
57 | 58 | # Here we just return input so we can use it in a test suite, but a real |
|
58 | 59 | # interpreter would instead send it for execution somewhere. |
|
59 | 60 | src = isp.source_reset() |
|
60 | 61 | #print 'Input source was:\n', src # dbg |
|
61 | 62 | return src |
|
62 | 63 | |
|
63 | 64 | #----------------------------------------------------------------------------- |
|
64 | 65 | # Test utilities, just for local use |
|
65 | 66 | #----------------------------------------------------------------------------- |
|
66 | 67 | |
|
67 | 68 | def assemble(block): |
|
68 | 69 | """Assemble a block into multi-line sub-blocks.""" |
|
69 | 70 | return ['\n'.join(sub_block)+'\n' for sub_block in block] |
|
70 | 71 | |
|
71 | 72 | |
|
72 | 73 | def pseudo_input(lines): |
|
73 | 74 | """Return a function that acts like raw_input but feeds the input list.""" |
|
74 | 75 | ilines = iter(lines) |
|
75 | 76 | def raw_in(prompt): |
|
76 | 77 | try: |
|
77 | 78 | return next(ilines) |
|
78 | 79 | except StopIteration: |
|
79 | 80 | return '' |
|
80 | 81 | return raw_in |
|
81 | 82 | |
|
82 | 83 | #----------------------------------------------------------------------------- |
|
83 | 84 | # Tests |
|
84 | 85 | #----------------------------------------------------------------------------- |
|
85 | 86 | def test_spaces(): |
|
86 | 87 | tests = [('', 0), |
|
87 | 88 | (' ', 1), |
|
88 | 89 | ('\n', 0), |
|
89 | 90 | (' \n', 1), |
|
90 | 91 | ('x', 0), |
|
91 | 92 | (' x', 1), |
|
92 | 93 | (' x',2), |
|
93 | 94 | (' x',4), |
|
94 | 95 | # Note: tabs are counted as a single whitespace! |
|
95 | 96 | ('\tx', 1), |
|
96 | 97 | ('\t x', 2), |
|
97 | 98 | ] |
|
98 | 99 | tt.check_pairs(isp.num_ini_spaces, tests) |
|
99 | 100 | |
|
100 | 101 | |
|
101 | 102 | def test_remove_comments(): |
|
102 | 103 | tests = [('text', 'text'), |
|
103 | 104 | ('text # comment', 'text '), |
|
104 | 105 | ('text # comment\n', 'text \n'), |
|
105 | 106 | ('text # comment \n', 'text \n'), |
|
106 | 107 | ('line # c \nline\n','line \nline\n'), |
|
107 | 108 | ('line # c \nline#c2 \nline\nline #c\n\n', |
|
108 | 109 | 'line \nline\nline\nline \n\n'), |
|
109 | 110 | ] |
|
110 | 111 | tt.check_pairs(isp.remove_comments, tests) |
|
111 | 112 | |
|
112 | 113 | |
|
113 | 114 | def test_get_input_encoding(): |
|
114 | 115 | encoding = isp.get_input_encoding() |
|
115 | 116 | nt.assert_true(isinstance(encoding, basestring)) |
|
116 | 117 | # simple-minded check that at least encoding a simple string works with the |
|
117 | 118 | # encoding we got. |
|
118 | 119 | nt.assert_equal(u'test'.encode(encoding), b'test') |
|
119 | 120 | |
|
120 | 121 | |
|
121 | 122 | class NoInputEncodingTestCase(unittest.TestCase): |
|
122 | 123 | def setUp(self): |
|
123 | 124 | self.old_stdin = sys.stdin |
|
124 | 125 | class X: pass |
|
125 | 126 | fake_stdin = X() |
|
126 | 127 | sys.stdin = fake_stdin |
|
127 | 128 | |
|
128 | 129 | def test(self): |
|
129 | 130 | # Verify that if sys.stdin has no 'encoding' attribute we do the right |
|
130 | 131 | # thing |
|
131 | 132 | enc = isp.get_input_encoding() |
|
132 | 133 | self.assertEqual(enc, 'ascii') |
|
133 | 134 | |
|
134 | 135 | def tearDown(self): |
|
135 | 136 | sys.stdin = self.old_stdin |
|
136 | 137 | |
|
137 | 138 | |
|
138 | 139 | class InputSplitterTestCase(unittest.TestCase): |
|
139 | 140 | def setUp(self): |
|
140 | 141 | self.isp = isp.InputSplitter() |
|
141 | 142 | |
|
142 | 143 | def test_reset(self): |
|
143 | 144 | isp = self.isp |
|
144 | 145 | isp.push('x=1') |
|
145 | 146 | isp.reset() |
|
146 | 147 | self.assertEqual(isp._buffer, []) |
|
147 | 148 | self.assertEqual(isp.indent_spaces, 0) |
|
148 | 149 | self.assertEqual(isp.source, '') |
|
149 | 150 | self.assertEqual(isp.code, None) |
|
150 | 151 | self.assertEqual(isp._is_complete, False) |
|
151 | 152 | |
|
152 | 153 | def test_source(self): |
|
153 | 154 | self.isp._store('1') |
|
154 | 155 | self.isp._store('2') |
|
155 | 156 | self.assertEqual(self.isp.source, '1\n2\n') |
|
156 | 157 | self.assertTrue(len(self.isp._buffer)>0) |
|
157 | 158 | self.assertEqual(self.isp.source_reset(), '1\n2\n') |
|
158 | 159 | self.assertEqual(self.isp._buffer, []) |
|
159 | 160 | self.assertEqual(self.isp.source, '') |
|
160 | 161 | |
|
161 | 162 | def test_indent(self): |
|
162 | 163 | isp = self.isp # shorthand |
|
163 | 164 | isp.push('x=1') |
|
164 | 165 | self.assertEqual(isp.indent_spaces, 0) |
|
165 | 166 | isp.push('if 1:\n x=1') |
|
166 | 167 | self.assertEqual(isp.indent_spaces, 4) |
|
167 | 168 | isp.push('y=2\n') |
|
168 | 169 | self.assertEqual(isp.indent_spaces, 0) |
|
169 | 170 | |
|
170 | 171 | def test_indent2(self): |
|
171 | 172 | isp = self.isp |
|
172 | 173 | isp.push('if 1:') |
|
173 | 174 | self.assertEqual(isp.indent_spaces, 4) |
|
174 | 175 | isp.push(' x=1') |
|
175 | 176 | self.assertEqual(isp.indent_spaces, 4) |
|
176 | 177 | # Blank lines shouldn't change the indent level |
|
177 | 178 | isp.push(' '*2) |
|
178 | 179 | self.assertEqual(isp.indent_spaces, 4) |
|
179 | 180 | |
|
180 | 181 | def test_indent3(self): |
|
181 | 182 | isp = self.isp |
|
182 | 183 | # When a multiline statement contains parens or multiline strings, we |
|
183 | 184 | # shouldn't get confused. |
|
184 | 185 | isp.push("if 1:") |
|
185 | 186 | isp.push(" x = (1+\n 2)") |
|
186 | 187 | self.assertEqual(isp.indent_spaces, 4) |
|
187 | 188 | |
|
188 | 189 | def test_indent4(self): |
|
189 | 190 | isp = self.isp |
|
190 | 191 | # whitespace after ':' should not screw up indent level |
|
191 | 192 | isp.push('if 1: \n x=1') |
|
192 | 193 | self.assertEqual(isp.indent_spaces, 4) |
|
193 | 194 | isp.push('y=2\n') |
|
194 | 195 | self.assertEqual(isp.indent_spaces, 0) |
|
195 | 196 | isp.push('if 1:\t\n x=1') |
|
196 | 197 | self.assertEqual(isp.indent_spaces, 4) |
|
197 | 198 | isp.push('y=2\n') |
|
198 | 199 | self.assertEqual(isp.indent_spaces, 0) |
|
199 | 200 | |
|
200 | 201 | def test_dedent_pass(self): |
|
201 | 202 | isp = self.isp # shorthand |
|
202 | 203 | # should NOT cause dedent |
|
203 | 204 | isp.push('if 1:\n passes = 5') |
|
204 | 205 | self.assertEqual(isp.indent_spaces, 4) |
|
205 | 206 | isp.push('if 1:\n pass') |
|
206 | 207 | self.assertEqual(isp.indent_spaces, 0) |
|
207 | 208 | isp.push('if 1:\n pass ') |
|
208 | 209 | self.assertEqual(isp.indent_spaces, 0) |
|
209 | 210 | |
|
210 | 211 | def test_dedent_break(self): |
|
211 | 212 | isp = self.isp # shorthand |
|
212 | 213 | # should NOT cause dedent |
|
213 | 214 | isp.push('while 1:\n breaks = 5') |
|
214 | 215 | self.assertEqual(isp.indent_spaces, 4) |
|
215 | 216 | isp.push('while 1:\n break') |
|
216 | 217 | self.assertEqual(isp.indent_spaces, 0) |
|
217 | 218 | isp.push('while 1:\n break ') |
|
218 | 219 | self.assertEqual(isp.indent_spaces, 0) |
|
219 | 220 | |
|
220 | 221 | def test_dedent_continue(self): |
|
221 | 222 | isp = self.isp # shorthand |
|
222 | 223 | # should NOT cause dedent |
|
223 | 224 | isp.push('while 1:\n continues = 5') |
|
224 | 225 | self.assertEqual(isp.indent_spaces, 4) |
|
225 | 226 | isp.push('while 1:\n continue') |
|
226 | 227 | self.assertEqual(isp.indent_spaces, 0) |
|
227 | 228 | isp.push('while 1:\n continue ') |
|
228 | 229 | self.assertEqual(isp.indent_spaces, 0) |
|
229 | 230 | |
|
230 | 231 | def test_dedent_raise(self): |
|
231 | 232 | isp = self.isp # shorthand |
|
232 | 233 | # should NOT cause dedent |
|
233 | 234 | isp.push('if 1:\n raised = 4') |
|
234 | 235 | self.assertEqual(isp.indent_spaces, 4) |
|
235 | 236 | isp.push('if 1:\n raise TypeError()') |
|
236 | 237 | self.assertEqual(isp.indent_spaces, 0) |
|
237 | 238 | isp.push('if 1:\n raise') |
|
238 | 239 | self.assertEqual(isp.indent_spaces, 0) |
|
239 | 240 | isp.push('if 1:\n raise ') |
|
240 | 241 | self.assertEqual(isp.indent_spaces, 0) |
|
241 | 242 | |
|
242 | 243 | def test_dedent_return(self): |
|
243 | 244 | isp = self.isp # shorthand |
|
244 | 245 | # should NOT cause dedent |
|
245 | 246 | isp.push('if 1:\n returning = 4') |
|
246 | 247 | self.assertEqual(isp.indent_spaces, 4) |
|
247 | 248 | isp.push('if 1:\n return 5 + 493') |
|
248 | 249 | self.assertEqual(isp.indent_spaces, 0) |
|
249 | 250 | isp.push('if 1:\n return') |
|
250 | 251 | self.assertEqual(isp.indent_spaces, 0) |
|
251 | 252 | isp.push('if 1:\n return ') |
|
252 | 253 | self.assertEqual(isp.indent_spaces, 0) |
|
253 | 254 | isp.push('if 1:\n return(0)') |
|
254 | 255 | self.assertEqual(isp.indent_spaces, 0) |
|
255 | 256 | |
|
256 | 257 | def test_push(self): |
|
257 | 258 | isp = self.isp |
|
258 | 259 | self.assertTrue(isp.push('x=1')) |
|
259 | 260 | |
|
260 | 261 | def test_push2(self): |
|
261 | 262 | isp = self.isp |
|
262 | 263 | self.assertFalse(isp.push('if 1:')) |
|
263 | 264 | for line in [' x=1', '# a comment', ' y=2']: |
|
264 | 265 | print(line) |
|
265 | 266 | self.assertTrue(isp.push(line)) |
|
266 | 267 | |
|
267 | 268 | def test_push3(self): |
|
268 | 269 | isp = self.isp |
|
269 | 270 | isp.push('if True:') |
|
270 | 271 | isp.push(' a = 1') |
|
271 | 272 | self.assertFalse(isp.push('b = [1,')) |
|
272 | 273 | |
|
273 | 274 | def test_push_accepts_more(self): |
|
274 | 275 | isp = self.isp |
|
275 | 276 | isp.push('x=1') |
|
276 | 277 | self.assertFalse(isp.push_accepts_more()) |
|
277 | 278 | |
|
278 | 279 | def test_push_accepts_more2(self): |
|
279 | 280 | isp = self.isp |
|
280 | 281 | isp.push('if 1:') |
|
281 | 282 | self.assertTrue(isp.push_accepts_more()) |
|
282 | 283 | isp.push(' x=1') |
|
283 | 284 | self.assertTrue(isp.push_accepts_more()) |
|
284 | 285 | isp.push('') |
|
285 | 286 | self.assertFalse(isp.push_accepts_more()) |
|
286 | 287 | |
|
287 | 288 | def test_push_accepts_more3(self): |
|
288 | 289 | isp = self.isp |
|
289 | 290 | isp.push("x = (2+\n3)") |
|
290 | 291 | self.assertFalse(isp.push_accepts_more()) |
|
291 | 292 | |
|
292 | 293 | def test_push_accepts_more4(self): |
|
293 | 294 | isp = self.isp |
|
294 | 295 | # When a multiline statement contains parens or multiline strings, we |
|
295 | 296 | # shouldn't get confused. |
|
296 | 297 | # FIXME: we should be able to better handle de-dents in statements like |
|
297 | 298 | # multiline strings and multiline expressions (continued with \ or |
|
298 | 299 | # parens). Right now we aren't handling the indentation tracking quite |
|
299 | 300 | # correctly with this, though in practice it may not be too much of a |
|
300 | 301 | # problem. We'll need to see. |
|
301 | 302 | isp.push("if 1:") |
|
302 | 303 | isp.push(" x = (2+") |
|
303 | 304 | isp.push(" 3)") |
|
304 | 305 | self.assertTrue(isp.push_accepts_more()) |
|
305 | 306 | isp.push(" y = 3") |
|
306 | 307 | self.assertTrue(isp.push_accepts_more()) |
|
307 | 308 | isp.push('') |
|
308 | 309 | self.assertFalse(isp.push_accepts_more()) |
|
309 | 310 | |
|
310 | 311 | def test_push_accepts_more5(self): |
|
311 | 312 | isp = self.isp |
|
312 | 313 | isp.push('try:') |
|
313 | 314 | isp.push(' a = 5') |
|
314 | 315 | isp.push('except:') |
|
315 | 316 | isp.push(' raise') |
|
316 | 317 | # We want to be able to add an else: block at this point, so it should |
|
317 | 318 | # wait for a blank line. |
|
318 | 319 | self.assertTrue(isp.push_accepts_more()) |
|
319 | 320 | |
|
320 | 321 | def test_continuation(self): |
|
321 | 322 | isp = self.isp |
|
322 | 323 | isp.push("import os, \\") |
|
323 | 324 | self.assertTrue(isp.push_accepts_more()) |
|
324 | 325 | isp.push("sys") |
|
325 | 326 | self.assertFalse(isp.push_accepts_more()) |
|
326 | 327 | |
|
327 | 328 | def test_syntax_error(self): |
|
328 | 329 | isp = self.isp |
|
329 | 330 | # Syntax errors immediately produce a 'ready' block, so the invalid |
|
330 | 331 | # Python can be sent to the kernel for evaluation with possible ipython |
|
331 | 332 | # special-syntax conversion. |
|
332 | 333 | isp.push('run foo') |
|
333 | 334 | self.assertFalse(isp.push_accepts_more()) |
|
334 | 335 | |
|
335 | 336 | def test_unicode(self): |
|
336 | 337 | self.isp.push(u"Pérez") |
|
337 | 338 | self.isp.push(u'\xc3\xa9') |
|
338 | 339 | self.isp.push(u"u'\xc3\xa9'") |
|
339 | 340 | |
|
340 | 341 | def test_line_continuation(self): |
|
341 | 342 | """ Test issue #2108.""" |
|
342 | 343 | isp = self.isp |
|
343 | 344 | # A blank line after a line continuation should not accept more |
|
344 | 345 | isp.push("1 \\\n\n") |
|
345 | 346 | self.assertFalse(isp.push_accepts_more()) |
|
346 | 347 | # Whitespace after a \ is a SyntaxError. The only way to test that |
|
347 | 348 | # here is to test that push doesn't accept more (as with |
|
348 | 349 | # test_syntax_error() above). |
|
349 | 350 | isp.push(r"1 \ ") |
|
350 | 351 | self.assertFalse(isp.push_accepts_more()) |
|
351 | 352 | # Even if the line is continuable (c.f. the regular Python |
|
352 | 353 | # interpreter) |
|
353 | 354 | isp.push(r"(1 \ ") |
|
354 | 355 | self.assertFalse(isp.push_accepts_more()) |
|
355 | 356 | |
|
356 | 357 | class InteractiveLoopTestCase(unittest.TestCase): |
|
357 | 358 | """Tests for an interactive loop like a python shell. |
|
358 | 359 | """ |
|
359 | 360 | def check_ns(self, lines, ns): |
|
360 | 361 | """Validate that the given input lines produce the resulting namespace. |
|
361 | 362 | |
|
362 | 363 | Note: the input lines are given exactly as they would be typed in an |
|
363 | 364 | auto-indenting environment, as mini_interactive_loop above already does |
|
364 | 365 | auto-indenting and prepends spaces to the input. |
|
365 | 366 | """ |
|
366 | 367 | src = mini_interactive_loop(pseudo_input(lines)) |
|
367 | 368 | test_ns = {} |
|
368 | 369 | exec src in test_ns |
|
369 | 370 | # We can't check that the provided ns is identical to the test_ns, |
|
370 | 371 | # because Python fills test_ns with extra keys (copyright, etc). But |
|
371 | 372 | # we can check that the given dict is *contained* in test_ns |
|
372 | 373 | for k,v in ns.iteritems(): |
|
373 | 374 | self.assertEqual(test_ns[k], v) |
|
374 | 375 | |
|
375 | 376 | def test_simple(self): |
|
376 | 377 | self.check_ns(['x=1'], dict(x=1)) |
|
377 | 378 | |
|
378 | 379 | def test_simple2(self): |
|
379 | 380 | self.check_ns(['if 1:', 'x=2'], dict(x=2)) |
|
380 | 381 | |
|
381 | 382 | def test_xy(self): |
|
382 | 383 | self.check_ns(['x=1; y=2'], dict(x=1, y=2)) |
|
383 | 384 | |
|
384 | 385 | def test_abc(self): |
|
385 | 386 | self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3)) |
|
386 | 387 | |
|
387 | 388 | def test_multi(self): |
|
388 | 389 | self.check_ns(['x =(1+','1+','2)'], dict(x=4)) |
|
389 | 390 | |
|
390 | 391 | |
|
391 | 392 | class IPythonInputTestCase(InputSplitterTestCase): |
|
392 | 393 | """By just creating a new class whose .isp is a different instance, we |
|
393 | 394 | re-run the same test battery on the new input splitter. |
|
394 | 395 | |
|
395 | 396 | In addition, this runs the tests over the syntax and syntax_ml dicts that |
|
396 | 397 | were tested by individual functions, as part of the OO interface. |
|
397 | 398 | |
|
398 | 399 | It also makes some checks on the raw buffer storage. |
|
399 | 400 | """ |
|
400 | 401 | |
|
401 | 402 | def setUp(self): |
|
402 | 403 | self.isp = isp.IPythonInputSplitter() |
|
403 | 404 | |
|
404 | 405 | def test_syntax(self): |
|
405 | 406 | """Call all single-line syntax tests from the main object""" |
|
406 | 407 | isp = self.isp |
|
407 | 408 | for example in syntax.itervalues(): |
|
408 | 409 | for raw, out_t in example: |
|
409 | 410 | if raw.startswith(' '): |
|
410 | 411 | continue |
|
411 | 412 | |
|
412 | 413 | isp.push(raw+'\n') |
|
413 | 414 | out, out_raw = isp.source_raw_reset() |
|
414 | 415 | self.assertEqual(out.rstrip(), out_t, |
|
415 | 416 | tt.pair_fail_msg.format("inputsplitter",raw, out_t, out)) |
|
416 | 417 | self.assertEqual(out_raw.rstrip(), raw.rstrip()) |
|
417 | 418 | |
|
418 | 419 | def test_syntax_multiline(self): |
|
419 | 420 | isp = self.isp |
|
420 | 421 | for example in syntax_ml.itervalues(): |
|
421 | 422 | for line_pairs in example: |
|
422 | 423 | out_t_parts = [] |
|
423 | 424 | raw_parts = [] |
|
424 | 425 | for lraw, out_t_part in line_pairs: |
|
425 | 426 | if out_t_part is not None: |
|
426 | 427 | out_t_parts.append(out_t_part) |
|
427 | 428 | |
|
428 | 429 | if lraw is not None: |
|
429 | 430 | isp.push(lraw) |
|
430 | 431 | raw_parts.append(lraw) |
|
431 | 432 | |
|
432 | 433 | out, out_raw = isp.source_raw_reset() |
|
433 | 434 | out_t = '\n'.join(out_t_parts).rstrip() |
|
434 | 435 | raw = '\n'.join(raw_parts).rstrip() |
|
435 | 436 | self.assertEqual(out.rstrip(), out_t) |
|
436 | 437 | self.assertEqual(out_raw.rstrip(), raw) |
|
437 | 438 | |
|
438 | 439 | def test_syntax_multiline_cell(self): |
|
439 | 440 | isp = self.isp |
|
440 | 441 | for example in syntax_ml.itervalues(): |
|
441 | 442 | |
|
442 | 443 | out_t_parts = [] |
|
443 | 444 | for line_pairs in example: |
|
444 | 445 | raw = '\n'.join(r for r, _ in line_pairs if r is not None) |
|
445 | 446 | out_t = '\n'.join(t for _,t in line_pairs if t is not None) |
|
446 | 447 | out = isp.transform_cell(raw) |
|
447 | 448 | # Match ignoring trailing whitespace |
|
448 | 449 | self.assertEqual(out.rstrip(), out_t.rstrip()) |
|
449 | 450 | |
|
450 | 451 | def test_cellmagic_preempt(self): |
|
451 | 452 | isp = self.isp |
|
452 | 453 | for raw, name, line, cell in [ |
|
453 | 454 | ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'), |
|
454 | 455 | ("%%cellm \nline\n>>>hi", u'cellm', u'', u'line\n>>>hi'), |
|
455 | 456 | (">>>%%cellm \nline\n>>>hi", u'cellm', u'', u'line\nhi'), |
|
456 | 457 | ("%%cellm \n>>>hi", u'cellm', u'', u'hi'), |
|
457 | 458 | ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'), |
|
458 | 459 | ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'), |
|
459 | 460 | ]: |
|
460 | 461 | expected = "get_ipython().run_cell_magic(%r, %r, %r)" % ( |
|
461 | 462 | name, line, cell |
|
462 | 463 | ) |
|
463 | 464 | out = isp.transform_cell(raw) |
|
464 | 465 | self.assertEqual(out.rstrip(), expected.rstrip()) |
|
465 | 466 | |
|
466 | 467 | |
|
467 | 468 | |
|
468 | 469 | #----------------------------------------------------------------------------- |
|
469 | 470 | # Main - use as a script, mostly for developer experiments |
|
470 | 471 | #----------------------------------------------------------------------------- |
|
471 | 472 | |
|
472 | 473 | if __name__ == '__main__': |
|
473 | 474 | # A simple demo for interactive experimentation. This code will not get |
|
474 | 475 | # picked up by any test suite. |
|
475 | 476 | from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter |
|
476 | 477 | |
|
477 | 478 | # configure here the syntax to use, prompt and whether to autoindent |
|
478 | 479 | #isp, start_prompt = InputSplitter(), '>>> ' |
|
479 | 480 | isp, start_prompt = IPythonInputSplitter(), 'In> ' |
|
480 | 481 | |
|
481 | 482 | autoindent = True |
|
482 | 483 | #autoindent = False |
|
483 | 484 | |
|
484 | 485 | try: |
|
485 | 486 | while True: |
|
486 | 487 | prompt = start_prompt |
|
487 | 488 | while isp.push_accepts_more(): |
|
488 | 489 | indent = ' '*isp.indent_spaces |
|
489 | 490 | if autoindent: |
|
490 | 491 | line = indent + raw_input(prompt+indent) |
|
491 | 492 | else: |
|
492 | 493 | line = raw_input(prompt) |
|
493 | 494 | isp.push(line) |
|
494 | 495 | prompt = '... ' |
|
495 | 496 | |
|
496 | 497 | # Here we just return input so we can use it in a test suite, but a |
|
497 | 498 | # real interpreter would instead send it for execution somewhere. |
|
498 | 499 | #src = isp.source; raise EOFError # dbg |
|
499 | 500 | src, raw = isp.source_raw_reset() |
|
500 |
print |
|
|
501 |
print |
|
|
501 | print('Input source was:\n', src) | |
|
502 | print('Raw source was:\n', raw) | |
|
502 | 503 | except EOFError: |
|
503 |
print |
|
|
504 | print('Bye') | |
|
504 | 505 | |
|
505 | 506 | # Tests for cell magics support |
|
506 | 507 | |
|
507 | 508 | def test_last_blank(): |
|
508 | 509 | nt.assert_false(isp.last_blank('')) |
|
509 | 510 | nt.assert_false(isp.last_blank('abc')) |
|
510 | 511 | nt.assert_false(isp.last_blank('abc\n')) |
|
511 | 512 | nt.assert_false(isp.last_blank('abc\na')) |
|
512 | 513 | |
|
513 | 514 | nt.assert_true(isp.last_blank('\n')) |
|
514 | 515 | nt.assert_true(isp.last_blank('\n ')) |
|
515 | 516 | nt.assert_true(isp.last_blank('abc\n ')) |
|
516 | 517 | nt.assert_true(isp.last_blank('abc\n\n')) |
|
517 | 518 | nt.assert_true(isp.last_blank('abc\nd\n\n')) |
|
518 | 519 | nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) |
|
519 | 520 | nt.assert_true(isp.last_blank('abc \n \n \n\n')) |
|
520 | 521 | |
|
521 | 522 | |
|
522 | 523 | def test_last_two_blanks(): |
|
523 | 524 | nt.assert_false(isp.last_two_blanks('')) |
|
524 | 525 | nt.assert_false(isp.last_two_blanks('abc')) |
|
525 | 526 | nt.assert_false(isp.last_two_blanks('abc\n')) |
|
526 | 527 | nt.assert_false(isp.last_two_blanks('abc\n\na')) |
|
527 | 528 | nt.assert_false(isp.last_two_blanks('abc\n \n')) |
|
528 | 529 | nt.assert_false(isp.last_two_blanks('abc\n\n')) |
|
529 | 530 | |
|
530 | 531 | nt.assert_true(isp.last_two_blanks('\n\n')) |
|
531 | 532 | nt.assert_true(isp.last_two_blanks('\n\n ')) |
|
532 | 533 | nt.assert_true(isp.last_two_blanks('\n \n')) |
|
533 | 534 | nt.assert_true(isp.last_two_blanks('abc\n\n ')) |
|
534 | 535 | nt.assert_true(isp.last_two_blanks('abc\n\n\n')) |
|
535 | 536 | nt.assert_true(isp.last_two_blanks('abc\n\n \n')) |
|
536 | 537 | nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) |
|
537 | 538 | nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) |
|
538 | 539 | nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) |
|
539 | 540 | nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) |
|
540 | 541 | |
|
541 | 542 | |
|
542 | 543 | class CellMagicsCommon(object): |
|
543 | 544 | |
|
544 | 545 | def test_whole_cell(self): |
|
545 | 546 | src = "%%cellm line\nbody\n" |
|
546 | 547 | sp = self.sp |
|
547 | 548 | sp.push(src) |
|
548 | 549 | out = sp.source_reset() |
|
549 | 550 | ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n" |
|
550 | 551 | nt.assert_equal(out, py3compat.u_format(ref)) |
|
551 | 552 | |
|
552 | 553 | def test_cellmagic_help(self): |
|
553 | 554 | self.sp.push('%%cellm?') |
|
554 | 555 | nt.assert_false(self.sp.push_accepts_more()) |
|
555 | 556 | |
|
556 | 557 | def tearDown(self): |
|
557 | 558 | self.sp.reset() |
|
558 | 559 | |
|
559 | 560 | |
|
560 | 561 | class CellModeCellMagics(CellMagicsCommon, unittest.TestCase): |
|
561 | 562 | sp = isp.IPythonInputSplitter(line_input_checker=False) |
|
562 | 563 | |
|
563 | 564 | def test_incremental(self): |
|
564 | 565 | sp = self.sp |
|
565 | 566 | sp.push('%%cellm firstline\n') |
|
566 | 567 | nt.assert_true(sp.push_accepts_more()) #1 |
|
567 | 568 | sp.push('line2\n') |
|
568 | 569 | nt.assert_true(sp.push_accepts_more()) #2 |
|
569 | 570 | sp.push('\n') |
|
570 | 571 | # This should accept a blank line and carry on until the cell is reset |
|
571 | 572 | nt.assert_true(sp.push_accepts_more()) #3 |
|
572 | 573 | |
|
573 | 574 | class LineModeCellMagics(CellMagicsCommon, unittest.TestCase): |
|
574 | 575 | sp = isp.IPythonInputSplitter(line_input_checker=True) |
|
575 | 576 | |
|
576 | 577 | def test_incremental(self): |
|
577 | 578 | sp = self.sp |
|
578 | 579 | sp.push('%%cellm line2\n') |
|
579 | 580 | nt.assert_true(sp.push_accepts_more()) #1 |
|
580 | 581 | sp.push('\n') |
|
581 | 582 | # In this case, a blank line should end the cell magic |
|
582 | 583 | nt.assert_false(sp.push_accepts_more()) #2 |
@@ -1,1266 +1,1267 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """ |
|
3 | 3 | ultratb.py -- Spice up your tracebacks! |
|
4 | 4 | |
|
5 | 5 | * ColorTB |
|
6 | 6 | I've always found it a bit hard to visually parse tracebacks in Python. The |
|
7 | 7 | ColorTB class is a solution to that problem. It colors the different parts of a |
|
8 | 8 | traceback in a manner similar to what you would expect from a syntax-highlighting |
|
9 | 9 | text editor. |
|
10 | 10 | |
|
11 | 11 | Installation instructions for ColorTB:: |
|
12 | 12 | |
|
13 | 13 | import sys,ultratb |
|
14 | 14 | sys.excepthook = ultratb.ColorTB() |
|
15 | 15 | |
|
16 | 16 | * VerboseTB |
|
17 | 17 | I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds |
|
18 | 18 | of useful info when a traceback occurs. Ping originally had it spit out HTML |
|
19 | 19 | and intended it for CGI programmers, but why should they have all the fun? I |
|
20 | 20 | altered it to spit out colored text to the terminal. It's a bit overwhelming, |
|
21 | 21 | but kind of neat, and maybe useful for long-running programs that you believe |
|
22 | 22 | are bug-free. If a crash *does* occur in that type of program you want details. |
|
23 | 23 | Give it a shot--you'll love it or you'll hate it. |
|
24 | 24 | |
|
25 | 25 | .. note:: |
|
26 | 26 | |
|
27 | 27 | The Verbose mode prints the variables currently visible where the exception |
|
28 | 28 | happened (shortening their strings if too long). This can potentially be |
|
29 | 29 | very slow, if you happen to have a huge data structure whose string |
|
30 | 30 | representation is complex to compute. Your computer may appear to freeze for |
|
31 | 31 | a while with cpu usage at 100%. If this occurs, you can cancel the traceback |
|
32 | 32 | with Ctrl-C (maybe hitting it more than once). |
|
33 | 33 | |
|
34 | 34 | If you encounter this kind of situation often, you may want to use the |
|
35 | 35 | Verbose_novars mode instead of the regular Verbose, which avoids formatting |
|
36 | 36 | variables (but otherwise includes the information and context given by |
|
37 | 37 | Verbose). |
|
38 | 38 | |
|
39 | 39 | |
|
40 | 40 | Installation instructions for ColorTB:: |
|
41 | 41 | |
|
42 | 42 | import sys,ultratb |
|
43 | 43 | sys.excepthook = ultratb.VerboseTB() |
|
44 | 44 | |
|
45 | 45 | Note: Much of the code in this module was lifted verbatim from the standard |
|
46 | 46 | library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. |
|
47 | 47 | |
|
48 | 48 | Color schemes |
|
49 | 49 | ------------- |
|
50 | 50 | |
|
51 | 51 | The colors are defined in the class TBTools through the use of the |
|
52 | 52 | ColorSchemeTable class. Currently the following exist: |
|
53 | 53 | |
|
54 | 54 | - NoColor: allows all of this module to be used in any terminal (the color |
|
55 | 55 | escapes are just dummy blank strings). |
|
56 | 56 | |
|
57 | 57 | - Linux: is meant to look good in a terminal like the Linux console (black |
|
58 | 58 | or very dark background). |
|
59 | 59 | |
|
60 | 60 | - LightBG: similar to Linux but swaps dark/light colors to be more readable |
|
61 | 61 | in light background terminals. |
|
62 | 62 | |
|
63 | 63 | You can implement other color schemes easily, the syntax is fairly |
|
64 | 64 | self-explanatory. Please send back new schemes you develop to the author for |
|
65 | 65 | possible inclusion in future releases. |
|
66 | 66 | |
|
67 | 67 | Inheritance diagram: |
|
68 | 68 | |
|
69 | 69 | .. inheritance-diagram:: IPython.core.ultratb |
|
70 | 70 | :parts: 3 |
|
71 | 71 | """ |
|
72 | 72 | |
|
73 | 73 | #***************************************************************************** |
|
74 | 74 | # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu> |
|
75 | 75 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> |
|
76 | 76 | # |
|
77 | 77 | # Distributed under the terms of the BSD License. The full license is in |
|
78 | 78 | # the file COPYING, distributed as part of this software. |
|
79 | 79 | #***************************************************************************** |
|
80 | 80 | |
|
81 | 81 | from __future__ import unicode_literals |
|
82 | from __future__ import print_function | |
|
82 | 83 | |
|
83 | 84 | import inspect |
|
84 | 85 | import keyword |
|
85 | 86 | import linecache |
|
86 | 87 | import os |
|
87 | 88 | import pydoc |
|
88 | 89 | import re |
|
89 | 90 | import sys |
|
90 | 91 | import time |
|
91 | 92 | import tokenize |
|
92 | 93 | import traceback |
|
93 | 94 | import types |
|
94 | 95 | |
|
95 | 96 | try: # Python 2 |
|
96 | 97 | generate_tokens = tokenize.generate_tokens |
|
97 | 98 | except AttributeError: # Python 3 |
|
98 | 99 | generate_tokens = tokenize.tokenize |
|
99 | 100 | |
|
100 | 101 | # For purposes of monkeypatching inspect to fix a bug in it. |
|
101 | 102 | from inspect import getsourcefile, getfile, getmodule,\ |
|
102 | 103 | ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode |
|
103 | 104 | |
|
104 | 105 | # IPython's own modules |
|
105 | 106 | # Modified pdb which doesn't damage IPython's readline handling |
|
106 | 107 | from IPython import get_ipython |
|
107 | 108 | from IPython.core import debugger |
|
108 | 109 | from IPython.core.display_trap import DisplayTrap |
|
109 | 110 | from IPython.core.excolors import exception_colors |
|
110 | 111 | from IPython.utils import PyColorize |
|
111 | 112 | from IPython.utils import io |
|
112 | 113 | from IPython.utils import openpy |
|
113 | 114 | from IPython.utils import path as util_path |
|
114 | 115 | from IPython.utils import py3compat |
|
115 | 116 | from IPython.utils import ulinecache |
|
116 | 117 | from IPython.utils.data import uniq_stable |
|
117 | 118 | from IPython.utils.warn import info, error |
|
118 | 119 | |
|
119 | 120 | # Globals |
|
120 | 121 | # amount of space to put line numbers before verbose tracebacks |
|
121 | 122 | INDENT_SIZE = 8 |
|
122 | 123 | |
|
123 | 124 | # Default color scheme. This is used, for example, by the traceback |
|
124 | 125 | # formatter. When running in an actual IPython instance, the user's rc.colors |
|
125 | 126 | # value is used, but havinga module global makes this functionality available |
|
126 | 127 | # to users of ultratb who are NOT running inside ipython. |
|
127 | 128 | DEFAULT_SCHEME = 'NoColor' |
|
128 | 129 | |
|
129 | 130 | #--------------------------------------------------------------------------- |
|
130 | 131 | # Code begins |
|
131 | 132 | |
|
132 | 133 | # Utility functions |
|
133 | 134 | def inspect_error(): |
|
134 | 135 | """Print a message about internal inspect errors. |
|
135 | 136 | |
|
136 | 137 | These are unfortunately quite common.""" |
|
137 | 138 | |
|
138 | 139 | error('Internal Python error in the inspect module.\n' |
|
139 | 140 | 'Below is the traceback from this internal error.\n') |
|
140 | 141 | |
|
141 | 142 | # This function is a monkeypatch we apply to the Python inspect module. We have |
|
142 | 143 | # now found when it's needed (see discussion on issue gh-1456), and we have a |
|
143 | 144 | # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if |
|
144 | 145 | # the monkeypatch is not applied. TK, Aug 2012. |
|
145 | 146 | def findsource(object): |
|
146 | 147 | """Return the entire source file and starting line number for an object. |
|
147 | 148 | |
|
148 | 149 | The argument may be a module, class, method, function, traceback, frame, |
|
149 | 150 | or code object. The source code is returned as a list of all the lines |
|
150 | 151 | in the file and the line number indexes a line in that list. An IOError |
|
151 | 152 | is raised if the source code cannot be retrieved. |
|
152 | 153 | |
|
153 | 154 | FIXED version with which we monkeypatch the stdlib to work around a bug.""" |
|
154 | 155 | |
|
155 | 156 | file = getsourcefile(object) or getfile(object) |
|
156 | 157 | # If the object is a frame, then trying to get the globals dict from its |
|
157 | 158 | # module won't work. Instead, the frame object itself has the globals |
|
158 | 159 | # dictionary. |
|
159 | 160 | globals_dict = None |
|
160 | 161 | if inspect.isframe(object): |
|
161 | 162 | # XXX: can this ever be false? |
|
162 | 163 | globals_dict = object.f_globals |
|
163 | 164 | else: |
|
164 | 165 | module = getmodule(object, file) |
|
165 | 166 | if module: |
|
166 | 167 | globals_dict = module.__dict__ |
|
167 | 168 | lines = linecache.getlines(file, globals_dict) |
|
168 | 169 | if not lines: |
|
169 | 170 | raise IOError('could not get source code') |
|
170 | 171 | |
|
171 | 172 | if ismodule(object): |
|
172 | 173 | return lines, 0 |
|
173 | 174 | |
|
174 | 175 | if isclass(object): |
|
175 | 176 | name = object.__name__ |
|
176 | 177 | pat = re.compile(r'^(\s*)class\s*' + name + r'\b') |
|
177 | 178 | # make some effort to find the best matching class definition: |
|
178 | 179 | # use the one with the least indentation, which is the one |
|
179 | 180 | # that's most probably not inside a function definition. |
|
180 | 181 | candidates = [] |
|
181 | 182 | for i in range(len(lines)): |
|
182 | 183 | match = pat.match(lines[i]) |
|
183 | 184 | if match: |
|
184 | 185 | # if it's at toplevel, it's already the best one |
|
185 | 186 | if lines[i][0] == 'c': |
|
186 | 187 | return lines, i |
|
187 | 188 | # else add whitespace to candidate list |
|
188 | 189 | candidates.append((match.group(1), i)) |
|
189 | 190 | if candidates: |
|
190 | 191 | # this will sort by whitespace, and by line number, |
|
191 | 192 | # less whitespace first |
|
192 | 193 | candidates.sort() |
|
193 | 194 | return lines, candidates[0][1] |
|
194 | 195 | else: |
|
195 | 196 | raise IOError('could not find class definition') |
|
196 | 197 | |
|
197 | 198 | if ismethod(object): |
|
198 | 199 | object = object.im_func |
|
199 | 200 | if isfunction(object): |
|
200 | 201 | object = object.func_code |
|
201 | 202 | if istraceback(object): |
|
202 | 203 | object = object.tb_frame |
|
203 | 204 | if isframe(object): |
|
204 | 205 | object = object.f_code |
|
205 | 206 | if iscode(object): |
|
206 | 207 | if not hasattr(object, 'co_firstlineno'): |
|
207 | 208 | raise IOError('could not find function definition') |
|
208 | 209 | pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)') |
|
209 | 210 | pmatch = pat.match |
|
210 | 211 | # fperez - fix: sometimes, co_firstlineno can give a number larger than |
|
211 | 212 | # the length of lines, which causes an error. Safeguard against that. |
|
212 | 213 | lnum = min(object.co_firstlineno,len(lines))-1 |
|
213 | 214 | while lnum > 0: |
|
214 | 215 | if pmatch(lines[lnum]): break |
|
215 | 216 | lnum -= 1 |
|
216 | 217 | |
|
217 | 218 | return lines, lnum |
|
218 | 219 | raise IOError('could not find code object') |
|
219 | 220 | |
|
220 | 221 | # Monkeypatch inspect to apply our bugfix. This code only works with Python >= 2.5 |
|
221 | 222 | inspect.findsource = findsource |
|
222 | 223 | |
|
223 | 224 | def fix_frame_records_filenames(records): |
|
224 | 225 | """Try to fix the filenames in each record from inspect.getinnerframes(). |
|
225 | 226 | |
|
226 | 227 | Particularly, modules loaded from within zip files have useless filenames |
|
227 | 228 | attached to their code object, and inspect.getinnerframes() just uses it. |
|
228 | 229 | """ |
|
229 | 230 | fixed_records = [] |
|
230 | 231 | for frame, filename, line_no, func_name, lines, index in records: |
|
231 | 232 | # Look inside the frame's globals dictionary for __file__, which should |
|
232 | 233 | # be better. |
|
233 | 234 | better_fn = frame.f_globals.get('__file__', None) |
|
234 | 235 | if isinstance(better_fn, str): |
|
235 | 236 | # Check the type just in case someone did something weird with |
|
236 | 237 | # __file__. It might also be None if the error occurred during |
|
237 | 238 | # import. |
|
238 | 239 | filename = better_fn |
|
239 | 240 | fixed_records.append((frame, filename, line_no, func_name, lines, index)) |
|
240 | 241 | return fixed_records |
|
241 | 242 | |
|
242 | 243 | |
|
243 | 244 | def _fixed_getinnerframes(etb, context=1,tb_offset=0): |
|
244 | 245 | LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 |
|
245 | 246 | |
|
246 | 247 | records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) |
|
247 | 248 | |
|
248 | 249 | # If the error is at the console, don't build any context, since it would |
|
249 | 250 | # otherwise produce 5 blank lines printed out (there is no file at the |
|
250 | 251 | # console) |
|
251 | 252 | rec_check = records[tb_offset:] |
|
252 | 253 | try: |
|
253 | 254 | rname = rec_check[0][1] |
|
254 | 255 | if rname == '<ipython console>' or rname.endswith('<string>'): |
|
255 | 256 | return rec_check |
|
256 | 257 | except IndexError: |
|
257 | 258 | pass |
|
258 | 259 | |
|
259 | 260 | aux = traceback.extract_tb(etb) |
|
260 | 261 | assert len(records) == len(aux) |
|
261 | 262 | for i, (file, lnum, _, _) in zip(range(len(records)), aux): |
|
262 | 263 | maybeStart = lnum-1 - context//2 |
|
263 | 264 | start = max(maybeStart, 0) |
|
264 | 265 | end = start + context |
|
265 | 266 | lines = ulinecache.getlines(file)[start:end] |
|
266 | 267 | buf = list(records[i]) |
|
267 | 268 | buf[LNUM_POS] = lnum |
|
268 | 269 | buf[INDEX_POS] = lnum - 1 - start |
|
269 | 270 | buf[LINES_POS] = lines |
|
270 | 271 | records[i] = tuple(buf) |
|
271 | 272 | return records[tb_offset:] |
|
272 | 273 | |
|
273 | 274 | # Helper function -- largely belongs to VerboseTB, but we need the same |
|
274 | 275 | # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they |
|
275 | 276 | # can be recognized properly by ipython.el's py-traceback-line-re |
|
276 | 277 | # (SyntaxErrors have to be treated specially because they have no traceback) |
|
277 | 278 | |
|
278 | 279 | _parser = PyColorize.Parser() |
|
279 | 280 | |
|
280 | 281 | def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None): |
|
281 | 282 | numbers_width = INDENT_SIZE - 1 |
|
282 | 283 | res = [] |
|
283 | 284 | i = lnum - index |
|
284 | 285 | |
|
285 | 286 | # This lets us get fully syntax-highlighted tracebacks. |
|
286 | 287 | if scheme is None: |
|
287 | 288 | ipinst = get_ipython() |
|
288 | 289 | if ipinst is not None: |
|
289 | 290 | scheme = ipinst.colors |
|
290 | 291 | else: |
|
291 | 292 | scheme = DEFAULT_SCHEME |
|
292 | 293 | |
|
293 | 294 | _line_format = _parser.format2 |
|
294 | 295 | |
|
295 | 296 | for line in lines: |
|
296 | 297 | line = py3compat.cast_unicode(line) |
|
297 | 298 | |
|
298 | 299 | new_line, err = _line_format(line, 'str', scheme) |
|
299 | 300 | if not err: line = new_line |
|
300 | 301 | |
|
301 | 302 | if i == lnum: |
|
302 | 303 | # This is the line with the error |
|
303 | 304 | pad = numbers_width - len(str(i)) |
|
304 | 305 | if pad >= 3: |
|
305 | 306 | marker = '-'*(pad-3) + '-> ' |
|
306 | 307 | elif pad == 2: |
|
307 | 308 | marker = '> ' |
|
308 | 309 | elif pad == 1: |
|
309 | 310 | marker = '>' |
|
310 | 311 | else: |
|
311 | 312 | marker = '' |
|
312 | 313 | num = marker + str(i) |
|
313 | 314 | line = '%s%s%s %s%s' %(Colors.linenoEm, num, |
|
314 | 315 | Colors.line, line, Colors.Normal) |
|
315 | 316 | else: |
|
316 | 317 | num = '%*s' % (numbers_width,i) |
|
317 | 318 | line = '%s%s%s %s' %(Colors.lineno, num, |
|
318 | 319 | Colors.Normal, line) |
|
319 | 320 | |
|
320 | 321 | res.append(line) |
|
321 | 322 | if lvals and i == lnum: |
|
322 | 323 | res.append(lvals + '\n') |
|
323 | 324 | i = i + 1 |
|
324 | 325 | return res |
|
325 | 326 | |
|
326 | 327 | |
|
327 | 328 | #--------------------------------------------------------------------------- |
|
328 | 329 | # Module classes |
|
329 | 330 | class TBTools(object): |
|
330 | 331 | """Basic tools used by all traceback printer classes.""" |
|
331 | 332 | |
|
332 | 333 | # Number of frames to skip when reporting tracebacks |
|
333 | 334 | tb_offset = 0 |
|
334 | 335 | |
|
335 | 336 | def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None): |
|
336 | 337 | # Whether to call the interactive pdb debugger after printing |
|
337 | 338 | # tracebacks or not |
|
338 | 339 | self.call_pdb = call_pdb |
|
339 | 340 | |
|
340 | 341 | # Output stream to write to. Note that we store the original value in |
|
341 | 342 | # a private attribute and then make the public ostream a property, so |
|
342 | 343 | # that we can delay accessing io.stdout until runtime. The way |
|
343 | 344 | # things are written now, the io.stdout object is dynamically managed |
|
344 | 345 | # so a reference to it should NEVER be stored statically. This |
|
345 | 346 | # property approach confines this detail to a single location, and all |
|
346 | 347 | # subclasses can simply access self.ostream for writing. |
|
347 | 348 | self._ostream = ostream |
|
348 | 349 | |
|
349 | 350 | # Create color table |
|
350 | 351 | self.color_scheme_table = exception_colors() |
|
351 | 352 | |
|
352 | 353 | self.set_colors(color_scheme) |
|
353 | 354 | self.old_scheme = color_scheme # save initial value for toggles |
|
354 | 355 | |
|
355 | 356 | if call_pdb: |
|
356 | 357 | self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name) |
|
357 | 358 | else: |
|
358 | 359 | self.pdb = None |
|
359 | 360 | |
|
360 | 361 | def _get_ostream(self): |
|
361 | 362 | """Output stream that exceptions are written to. |
|
362 | 363 | |
|
363 | 364 | Valid values are: |
|
364 | 365 | |
|
365 | 366 | - None: the default, which means that IPython will dynamically resolve |
|
366 | 367 | to io.stdout. This ensures compatibility with most tools, including |
|
367 | 368 | Windows (where plain stdout doesn't recognize ANSI escapes). |
|
368 | 369 | |
|
369 | 370 | - Any object with 'write' and 'flush' attributes. |
|
370 | 371 | """ |
|
371 | 372 | return io.stdout if self._ostream is None else self._ostream |
|
372 | 373 | |
|
373 | 374 | def _set_ostream(self, val): |
|
374 | 375 | assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush')) |
|
375 | 376 | self._ostream = val |
|
376 | 377 | |
|
377 | 378 | ostream = property(_get_ostream, _set_ostream) |
|
378 | 379 | |
|
379 | 380 | def set_colors(self,*args,**kw): |
|
380 | 381 | """Shorthand access to the color table scheme selector method.""" |
|
381 | 382 | |
|
382 | 383 | # Set own color table |
|
383 | 384 | self.color_scheme_table.set_active_scheme(*args,**kw) |
|
384 | 385 | # for convenience, set Colors to the active scheme |
|
385 | 386 | self.Colors = self.color_scheme_table.active_colors |
|
386 | 387 | # Also set colors of debugger |
|
387 | 388 | if hasattr(self,'pdb') and self.pdb is not None: |
|
388 | 389 | self.pdb.set_colors(*args,**kw) |
|
389 | 390 | |
|
390 | 391 | def color_toggle(self): |
|
391 | 392 | """Toggle between the currently active color scheme and NoColor.""" |
|
392 | 393 | |
|
393 | 394 | if self.color_scheme_table.active_scheme_name == 'NoColor': |
|
394 | 395 | self.color_scheme_table.set_active_scheme(self.old_scheme) |
|
395 | 396 | self.Colors = self.color_scheme_table.active_colors |
|
396 | 397 | else: |
|
397 | 398 | self.old_scheme = self.color_scheme_table.active_scheme_name |
|
398 | 399 | self.color_scheme_table.set_active_scheme('NoColor') |
|
399 | 400 | self.Colors = self.color_scheme_table.active_colors |
|
400 | 401 | |
|
401 | 402 | def stb2text(self, stb): |
|
402 | 403 | """Convert a structured traceback (a list) to a string.""" |
|
403 | 404 | return '\n'.join(stb) |
|
404 | 405 | |
|
405 | 406 | def text(self, etype, value, tb, tb_offset=None, context=5): |
|
406 | 407 | """Return formatted traceback. |
|
407 | 408 | |
|
408 | 409 | Subclasses may override this if they add extra arguments. |
|
409 | 410 | """ |
|
410 | 411 | tb_list = self.structured_traceback(etype, value, tb, |
|
411 | 412 | tb_offset, context) |
|
412 | 413 | return self.stb2text(tb_list) |
|
413 | 414 | |
|
414 | 415 | def structured_traceback(self, etype, evalue, tb, tb_offset=None, |
|
415 | 416 | context=5, mode=None): |
|
416 | 417 | """Return a list of traceback frames. |
|
417 | 418 | |
|
418 | 419 | Must be implemented by each class. |
|
419 | 420 | """ |
|
420 | 421 | raise NotImplementedError() |
|
421 | 422 | |
|
422 | 423 | |
|
423 | 424 | #--------------------------------------------------------------------------- |
|
424 | 425 | class ListTB(TBTools): |
|
425 | 426 | """Print traceback information from a traceback list, with optional color. |
|
426 | 427 | |
|
427 | 428 | Calling requires 3 arguments: (etype, evalue, elist) |
|
428 | 429 | as would be obtained by:: |
|
429 | 430 | |
|
430 | 431 | etype, evalue, tb = sys.exc_info() |
|
431 | 432 | if tb: |
|
432 | 433 | elist = traceback.extract_tb(tb) |
|
433 | 434 | else: |
|
434 | 435 | elist = None |
|
435 | 436 | |
|
436 | 437 | It can thus be used by programs which need to process the traceback before |
|
437 | 438 | printing (such as console replacements based on the code module from the |
|
438 | 439 | standard library). |
|
439 | 440 | |
|
440 | 441 | Because they are meant to be called without a full traceback (only a |
|
441 | 442 | list), instances of this class can't call the interactive pdb debugger.""" |
|
442 | 443 | |
|
443 | 444 | def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None): |
|
444 | 445 | TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, |
|
445 | 446 | ostream=ostream) |
|
446 | 447 | |
|
447 | 448 | def __call__(self, etype, value, elist): |
|
448 | 449 | self.ostream.flush() |
|
449 | 450 | self.ostream.write(self.text(etype, value, elist)) |
|
450 | 451 | self.ostream.write('\n') |
|
451 | 452 | |
|
452 | 453 | def structured_traceback(self, etype, value, elist, tb_offset=None, |
|
453 | 454 | context=5): |
|
454 | 455 | """Return a color formatted string with the traceback info. |
|
455 | 456 | |
|
456 | 457 | Parameters |
|
457 | 458 | ---------- |
|
458 | 459 | etype : exception type |
|
459 | 460 | Type of the exception raised. |
|
460 | 461 | |
|
461 | 462 | value : object |
|
462 | 463 | Data stored in the exception |
|
463 | 464 | |
|
464 | 465 | elist : list |
|
465 | 466 | List of frames, see class docstring for details. |
|
466 | 467 | |
|
467 | 468 | tb_offset : int, optional |
|
468 | 469 | Number of frames in the traceback to skip. If not given, the |
|
469 | 470 | instance value is used (set in constructor). |
|
470 | 471 | |
|
471 | 472 | context : int, optional |
|
472 | 473 | Number of lines of context information to print. |
|
473 | 474 | |
|
474 | 475 | Returns |
|
475 | 476 | ------- |
|
476 | 477 | String with formatted exception. |
|
477 | 478 | """ |
|
478 | 479 | tb_offset = self.tb_offset if tb_offset is None else tb_offset |
|
479 | 480 | Colors = self.Colors |
|
480 | 481 | out_list = [] |
|
481 | 482 | if elist: |
|
482 | 483 | |
|
483 | 484 | if tb_offset and len(elist) > tb_offset: |
|
484 | 485 | elist = elist[tb_offset:] |
|
485 | 486 | |
|
486 | 487 | out_list.append('Traceback %s(most recent call last)%s:' % |
|
487 | 488 | (Colors.normalEm, Colors.Normal) + '\n') |
|
488 | 489 | out_list.extend(self._format_list(elist)) |
|
489 | 490 | # The exception info should be a single entry in the list. |
|
490 | 491 | lines = ''.join(self._format_exception_only(etype, value)) |
|
491 | 492 | out_list.append(lines) |
|
492 | 493 | |
|
493 | 494 | # Note: this code originally read: |
|
494 | 495 | |
|
495 | 496 | ## for line in lines[:-1]: |
|
496 | 497 | ## out_list.append(" "+line) |
|
497 | 498 | ## out_list.append(lines[-1]) |
|
498 | 499 | |
|
499 | 500 | # This means it was indenting everything but the last line by a little |
|
500 | 501 | # bit. I've disabled this for now, but if we see ugliness somewhre we |
|
501 | 502 | # can restore it. |
|
502 | 503 | |
|
503 | 504 | return out_list |
|
504 | 505 | |
|
505 | 506 | def _format_list(self, extracted_list): |
|
506 | 507 | """Format a list of traceback entry tuples for printing. |
|
507 | 508 | |
|
508 | 509 | Given a list of tuples as returned by extract_tb() or |
|
509 | 510 | extract_stack(), return a list of strings ready for printing. |
|
510 | 511 | Each string in the resulting list corresponds to the item with the |
|
511 | 512 | same index in the argument list. Each string ends in a newline; |
|
512 | 513 | the strings may contain internal newlines as well, for those items |
|
513 | 514 | whose source text line is not None. |
|
514 | 515 | |
|
515 | 516 | Lifted almost verbatim from traceback.py |
|
516 | 517 | """ |
|
517 | 518 | |
|
518 | 519 | Colors = self.Colors |
|
519 | 520 | list = [] |
|
520 | 521 | for filename, lineno, name, line in extracted_list[:-1]: |
|
521 | 522 | item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \ |
|
522 | 523 | (Colors.filename, filename, Colors.Normal, |
|
523 | 524 | Colors.lineno, lineno, Colors.Normal, |
|
524 | 525 | Colors.name, name, Colors.Normal) |
|
525 | 526 | if line: |
|
526 | 527 | item += ' %s\n' % line.strip() |
|
527 | 528 | list.append(item) |
|
528 | 529 | # Emphasize the last entry |
|
529 | 530 | filename, lineno, name, line = extracted_list[-1] |
|
530 | 531 | item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \ |
|
531 | 532 | (Colors.normalEm, |
|
532 | 533 | Colors.filenameEm, filename, Colors.normalEm, |
|
533 | 534 | Colors.linenoEm, lineno, Colors.normalEm, |
|
534 | 535 | Colors.nameEm, name, Colors.normalEm, |
|
535 | 536 | Colors.Normal) |
|
536 | 537 | if line: |
|
537 | 538 | item += '%s %s%s\n' % (Colors.line, line.strip(), |
|
538 | 539 | Colors.Normal) |
|
539 | 540 | list.append(item) |
|
540 | 541 | #from pprint import pformat; print 'LISTTB', pformat(list) # dbg |
|
541 | 542 | return list |
|
542 | 543 | |
|
543 | 544 | def _format_exception_only(self, etype, value): |
|
544 | 545 | """Format the exception part of a traceback. |
|
545 | 546 | |
|
546 | 547 | The arguments are the exception type and value such as given by |
|
547 | 548 | sys.exc_info()[:2]. The return value is a list of strings, each ending |
|
548 | 549 | in a newline. Normally, the list contains a single string; however, |
|
549 | 550 | for SyntaxError exceptions, it contains several lines that (when |
|
550 | 551 | printed) display detailed information about where the syntax error |
|
551 | 552 | occurred. The message indicating which exception occurred is the |
|
552 | 553 | always last string in the list. |
|
553 | 554 | |
|
554 | 555 | Also lifted nearly verbatim from traceback.py |
|
555 | 556 | """ |
|
556 | 557 | have_filedata = False |
|
557 | 558 | Colors = self.Colors |
|
558 | 559 | list = [] |
|
559 | 560 | stype = Colors.excName + etype.__name__ + Colors.Normal |
|
560 | 561 | if value is None: |
|
561 | 562 | # Not sure if this can still happen in Python 2.6 and above |
|
562 | 563 | list.append( py3compat.cast_unicode(stype) + '\n') |
|
563 | 564 | else: |
|
564 | 565 | if issubclass(etype, SyntaxError): |
|
565 | 566 | have_filedata = True |
|
566 | 567 | #print 'filename is',filename # dbg |
|
567 | 568 | if not value.filename: value.filename = "<string>" |
|
568 | 569 | if value.lineno: |
|
569 | 570 | lineno = value.lineno |
|
570 | 571 | textline = ulinecache.getline(value.filename, value.lineno) |
|
571 | 572 | else: |
|
572 | 573 | lineno = 'unknown' |
|
573 | 574 | textline = '' |
|
574 | 575 | list.append('%s File %s"%s"%s, line %s%s%s\n' % \ |
|
575 | 576 | (Colors.normalEm, |
|
576 | 577 | Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm, |
|
577 | 578 | Colors.linenoEm, lineno, Colors.Normal )) |
|
578 | 579 | if textline == '': |
|
579 | 580 | textline = py3compat.cast_unicode(value.text, "utf-8") |
|
580 | 581 | |
|
581 | 582 | if textline is not None: |
|
582 | 583 | i = 0 |
|
583 | 584 | while i < len(textline) and textline[i].isspace(): |
|
584 | 585 | i += 1 |
|
585 | 586 | list.append('%s %s%s\n' % (Colors.line, |
|
586 | 587 | textline.strip(), |
|
587 | 588 | Colors.Normal)) |
|
588 | 589 | if value.offset is not None: |
|
589 | 590 | s = ' ' |
|
590 | 591 | for c in textline[i:value.offset-1]: |
|
591 | 592 | if c.isspace(): |
|
592 | 593 | s += c |
|
593 | 594 | else: |
|
594 | 595 | s += ' ' |
|
595 | 596 | list.append('%s%s^%s\n' % (Colors.caret, s, |
|
596 | 597 | Colors.Normal) ) |
|
597 | 598 | |
|
598 | 599 | try: |
|
599 | 600 | s = value.msg |
|
600 | 601 | except Exception: |
|
601 | 602 | s = self._some_str(value) |
|
602 | 603 | if s: |
|
603 | 604 | list.append('%s%s:%s %s\n' % (str(stype), Colors.excName, |
|
604 | 605 | Colors.Normal, s)) |
|
605 | 606 | else: |
|
606 | 607 | list.append('%s\n' % str(stype)) |
|
607 | 608 | |
|
608 | 609 | # sync with user hooks |
|
609 | 610 | if have_filedata: |
|
610 | 611 | ipinst = get_ipython() |
|
611 | 612 | if ipinst is not None: |
|
612 | 613 | ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0) |
|
613 | 614 | |
|
614 | 615 | return list |
|
615 | 616 | |
|
616 | 617 | def get_exception_only(self, etype, value): |
|
617 | 618 | """Only print the exception type and message, without a traceback. |
|
618 | 619 | |
|
619 | 620 | Parameters |
|
620 | 621 | ---------- |
|
621 | 622 | etype : exception type |
|
622 | 623 | value : exception value |
|
623 | 624 | """ |
|
624 | 625 | return ListTB.structured_traceback(self, etype, value, []) |
|
625 | 626 | |
|
626 | 627 | |
|
627 | 628 | def show_exception_only(self, etype, evalue): |
|
628 | 629 | """Only print the exception type and message, without a traceback. |
|
629 | 630 | |
|
630 | 631 | Parameters |
|
631 | 632 | ---------- |
|
632 | 633 | etype : exception type |
|
633 | 634 | value : exception value |
|
634 | 635 | """ |
|
635 | 636 | # This method needs to use __call__ from *this* class, not the one from |
|
636 | 637 | # a subclass whose signature or behavior may be different |
|
637 | 638 | ostream = self.ostream |
|
638 | 639 | ostream.flush() |
|
639 | 640 | ostream.write('\n'.join(self.get_exception_only(etype, evalue))) |
|
640 | 641 | ostream.flush() |
|
641 | 642 | |
|
642 | 643 | def _some_str(self, value): |
|
643 | 644 | # Lifted from traceback.py |
|
644 | 645 | try: |
|
645 | 646 | return str(value) |
|
646 | 647 | except: |
|
647 | 648 | return '<unprintable %s object>' % type(value).__name__ |
|
648 | 649 | |
|
649 | 650 | #---------------------------------------------------------------------------- |
|
650 | 651 | class VerboseTB(TBTools): |
|
651 | 652 | """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead |
|
652 | 653 | of HTML. Requires inspect and pydoc. Crazy, man. |
|
653 | 654 | |
|
654 | 655 | Modified version which optionally strips the topmost entries from the |
|
655 | 656 | traceback, to be used with alternate interpreters (because their own code |
|
656 | 657 | would appear in the traceback).""" |
|
657 | 658 | |
|
658 | 659 | def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None, |
|
659 | 660 | tb_offset=0, long_header=False, include_vars=True, |
|
660 | 661 | check_cache=None): |
|
661 | 662 | """Specify traceback offset, headers and color scheme. |
|
662 | 663 | |
|
663 | 664 | Define how many frames to drop from the tracebacks. Calling it with |
|
664 | 665 | tb_offset=1 allows use of this handler in interpreters which will have |
|
665 | 666 | their own code at the top of the traceback (VerboseTB will first |
|
666 | 667 | remove that frame before printing the traceback info).""" |
|
667 | 668 | TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, |
|
668 | 669 | ostream=ostream) |
|
669 | 670 | self.tb_offset = tb_offset |
|
670 | 671 | self.long_header = long_header |
|
671 | 672 | self.include_vars = include_vars |
|
672 | 673 | # By default we use linecache.checkcache, but the user can provide a |
|
673 | 674 | # different check_cache implementation. This is used by the IPython |
|
674 | 675 | # kernel to provide tracebacks for interactive code that is cached, |
|
675 | 676 | # by a compiler instance that flushes the linecache but preserves its |
|
676 | 677 | # own code cache. |
|
677 | 678 | if check_cache is None: |
|
678 | 679 | check_cache = linecache.checkcache |
|
679 | 680 | self.check_cache = check_cache |
|
680 | 681 | |
|
681 | 682 | def structured_traceback(self, etype, evalue, etb, tb_offset=None, |
|
682 | 683 | context=5): |
|
683 | 684 | """Return a nice text document describing the traceback.""" |
|
684 | 685 | |
|
685 | 686 | tb_offset = self.tb_offset if tb_offset is None else tb_offset |
|
686 | 687 | |
|
687 | 688 | # some locals |
|
688 | 689 | try: |
|
689 | 690 | etype = etype.__name__ |
|
690 | 691 | except AttributeError: |
|
691 | 692 | pass |
|
692 | 693 | Colors = self.Colors # just a shorthand + quicker name lookup |
|
693 | 694 | ColorsNormal = Colors.Normal # used a lot |
|
694 | 695 | col_scheme = self.color_scheme_table.active_scheme_name |
|
695 | 696 | indent = ' '*INDENT_SIZE |
|
696 | 697 | em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal) |
|
697 | 698 | undefined = '%sundefined%s' % (Colors.em, ColorsNormal) |
|
698 | 699 | exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal) |
|
699 | 700 | |
|
700 | 701 | # some internal-use functions |
|
701 | 702 | def text_repr(value): |
|
702 | 703 | """Hopefully pretty robust repr equivalent.""" |
|
703 | 704 | # this is pretty horrible but should always return *something* |
|
704 | 705 | try: |
|
705 | 706 | return pydoc.text.repr(value) |
|
706 | 707 | except KeyboardInterrupt: |
|
707 | 708 | raise |
|
708 | 709 | except: |
|
709 | 710 | try: |
|
710 | 711 | return repr(value) |
|
711 | 712 | except KeyboardInterrupt: |
|
712 | 713 | raise |
|
713 | 714 | except: |
|
714 | 715 | try: |
|
715 | 716 | # all still in an except block so we catch |
|
716 | 717 | # getattr raising |
|
717 | 718 | name = getattr(value, '__name__', None) |
|
718 | 719 | if name: |
|
719 | 720 | # ick, recursion |
|
720 | 721 | return text_repr(name) |
|
721 | 722 | klass = getattr(value, '__class__', None) |
|
722 | 723 | if klass: |
|
723 | 724 | return '%s instance' % text_repr(klass) |
|
724 | 725 | except KeyboardInterrupt: |
|
725 | 726 | raise |
|
726 | 727 | except: |
|
727 | 728 | return 'UNRECOVERABLE REPR FAILURE' |
|
728 | 729 | def eqrepr(value, repr=text_repr): return '=%s' % repr(value) |
|
729 | 730 | def nullrepr(value, repr=text_repr): return '' |
|
730 | 731 | |
|
731 | 732 | # meat of the code begins |
|
732 | 733 | try: |
|
733 | 734 | etype = etype.__name__ |
|
734 | 735 | except AttributeError: |
|
735 | 736 | pass |
|
736 | 737 | |
|
737 | 738 | if self.long_header: |
|
738 | 739 | # Header with the exception type, python version, and date |
|
739 | 740 | pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable |
|
740 | 741 | date = time.ctime(time.time()) |
|
741 | 742 | |
|
742 | 743 | head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal, |
|
743 | 744 | exc, ' '*(75-len(str(etype))-len(pyver)), |
|
744 | 745 | pyver, date.rjust(75) ) |
|
745 | 746 | head += "\nA problem occured executing Python code. Here is the sequence of function"\ |
|
746 | 747 | "\ncalls leading up to the error, with the most recent (innermost) call last." |
|
747 | 748 | else: |
|
748 | 749 | # Simplified header |
|
749 | 750 | head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc, |
|
750 | 751 | 'Traceback (most recent call last)'.\ |
|
751 | 752 | rjust(75 - len(str(etype)) ) ) |
|
752 | 753 | frames = [] |
|
753 | 754 | # Flush cache before calling inspect. This helps alleviate some of the |
|
754 | 755 | # problems with python 2.3's inspect.py. |
|
755 | 756 | ##self.check_cache() |
|
756 | 757 | # Drop topmost frames if requested |
|
757 | 758 | try: |
|
758 | 759 | # Try the default getinnerframes and Alex's: Alex's fixes some |
|
759 | 760 | # problems, but it generates empty tracebacks for console errors |
|
760 | 761 | # (5 blanks lines) where none should be returned. |
|
761 | 762 | #records = inspect.getinnerframes(etb, context)[tb_offset:] |
|
762 | 763 | #print 'python records:', records # dbg |
|
763 | 764 | records = _fixed_getinnerframes(etb, context, tb_offset) |
|
764 | 765 | #print 'alex records:', records # dbg |
|
765 | 766 | except: |
|
766 | 767 | |
|
767 | 768 | # FIXME: I've been getting many crash reports from python 2.3 |
|
768 | 769 | # users, traceable to inspect.py. If I can find a small test-case |
|
769 | 770 | # to reproduce this, I should either write a better workaround or |
|
770 | 771 | # file a bug report against inspect (if that's the real problem). |
|
771 | 772 | # So far, I haven't been able to find an isolated example to |
|
772 | 773 | # reproduce the problem. |
|
773 | 774 | inspect_error() |
|
774 | 775 | traceback.print_exc(file=self.ostream) |
|
775 | 776 | info('\nUnfortunately, your original traceback can not be constructed.\n') |
|
776 | 777 | return '' |
|
777 | 778 | |
|
778 | 779 | # build some color string templates outside these nested loops |
|
779 | 780 | tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal) |
|
780 | 781 | tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, |
|
781 | 782 | ColorsNormal) |
|
782 | 783 | tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ |
|
783 | 784 | (Colors.vName, Colors.valEm, ColorsNormal) |
|
784 | 785 | tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal) |
|
785 | 786 | tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal, |
|
786 | 787 | Colors.vName, ColorsNormal) |
|
787 | 788 | tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) |
|
788 | 789 | tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal) |
|
789 | 790 | tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line, |
|
790 | 791 | ColorsNormal) |
|
791 | 792 | |
|
792 | 793 | # now, loop over all records printing context and info |
|
793 | 794 | abspath = os.path.abspath |
|
794 | 795 | for frame, file, lnum, func, lines, index in records: |
|
795 | 796 | #print '*** record:',file,lnum,func,lines,index # dbg |
|
796 | 797 | if not file: |
|
797 | 798 | file = '?' |
|
798 | 799 | elif not(file.startswith(str("<")) and file.endswith(str(">"))): |
|
799 | 800 | # Guess that filenames like <string> aren't real filenames, so |
|
800 | 801 | # don't call abspath on them. |
|
801 | 802 | try: |
|
802 | 803 | file = abspath(file) |
|
803 | 804 | except OSError: |
|
804 | 805 | # Not sure if this can still happen: abspath now works with |
|
805 | 806 | # file names like <string> |
|
806 | 807 | pass |
|
807 | 808 | file = py3compat.cast_unicode(file, util_path.fs_encoding) |
|
808 | 809 | link = tpl_link % file |
|
809 | 810 | args, varargs, varkw, locals = inspect.getargvalues(frame) |
|
810 | 811 | |
|
811 | 812 | if func == '?': |
|
812 | 813 | call = '' |
|
813 | 814 | else: |
|
814 | 815 | # Decide whether to include variable details or not |
|
815 | 816 | var_repr = self.include_vars and eqrepr or nullrepr |
|
816 | 817 | try: |
|
817 | 818 | call = tpl_call % (func,inspect.formatargvalues(args, |
|
818 | 819 | varargs, varkw, |
|
819 | 820 | locals,formatvalue=var_repr)) |
|
820 | 821 | except KeyError: |
|
821 | 822 | # This happens in situations like errors inside generator |
|
822 | 823 | # expressions, where local variables are listed in the |
|
823 | 824 | # line, but can't be extracted from the frame. I'm not |
|
824 | 825 | # 100% sure this isn't actually a bug in inspect itself, |
|
825 | 826 | # but since there's no info for us to compute with, the |
|
826 | 827 | # best we can do is report the failure and move on. Here |
|
827 | 828 | # we must *not* call any traceback construction again, |
|
828 | 829 | # because that would mess up use of %debug later on. So we |
|
829 | 830 | # simply report the failure and move on. The only |
|
830 | 831 | # limitation will be that this frame won't have locals |
|
831 | 832 | # listed in the call signature. Quite subtle problem... |
|
832 | 833 | # I can't think of a good way to validate this in a unit |
|
833 | 834 | # test, but running a script consisting of: |
|
834 | 835 | # dict( (k,v.strip()) for (k,v) in range(10) ) |
|
835 | 836 | # will illustrate the error, if this exception catch is |
|
836 | 837 | # disabled. |
|
837 | 838 | call = tpl_call_fail % func |
|
838 | 839 | |
|
839 | 840 | # Don't attempt to tokenize binary files. |
|
840 | 841 | if file.endswith(('.so', '.pyd', '.dll')): |
|
841 | 842 | frames.append('%s %s\n' % (link,call)) |
|
842 | 843 | continue |
|
843 | 844 | elif file.endswith(('.pyc','.pyo')): |
|
844 | 845 | # Look up the corresponding source file. |
|
845 | 846 | file = openpy.source_from_cache(file) |
|
846 | 847 | |
|
847 | 848 | def linereader(file=file, lnum=[lnum], getline=ulinecache.getline): |
|
848 | 849 | line = getline(file, lnum[0]) |
|
849 | 850 | lnum[0] += 1 |
|
850 | 851 | return line |
|
851 | 852 | |
|
852 | 853 | # Build the list of names on this line of code where the exception |
|
853 | 854 | # occurred. |
|
854 | 855 | try: |
|
855 | 856 | names = [] |
|
856 | 857 | name_cont = False |
|
857 | 858 | |
|
858 | 859 | for token_type, token, start, end, line in generate_tokens(linereader): |
|
859 | 860 | # build composite names |
|
860 | 861 | if token_type == tokenize.NAME and token not in keyword.kwlist: |
|
861 | 862 | if name_cont: |
|
862 | 863 | # Continuation of a dotted name |
|
863 | 864 | try: |
|
864 | 865 | names[-1].append(token) |
|
865 | 866 | except IndexError: |
|
866 | 867 | names.append([token]) |
|
867 | 868 | name_cont = False |
|
868 | 869 | else: |
|
869 | 870 | # Regular new names. We append everything, the caller |
|
870 | 871 | # will be responsible for pruning the list later. It's |
|
871 | 872 | # very tricky to try to prune as we go, b/c composite |
|
872 | 873 | # names can fool us. The pruning at the end is easy |
|
873 | 874 | # to do (or the caller can print a list with repeated |
|
874 | 875 | # names if so desired. |
|
875 | 876 | names.append([token]) |
|
876 | 877 | elif token == '.': |
|
877 | 878 | name_cont = True |
|
878 | 879 | elif token_type == tokenize.NEWLINE: |
|
879 | 880 | break |
|
880 | 881 | |
|
881 | 882 | except (IndexError, UnicodeDecodeError): |
|
882 | 883 | # signals exit of tokenizer |
|
883 | 884 | pass |
|
884 | 885 | except tokenize.TokenError as msg: |
|
885 | 886 | _m = ("An unexpected error occurred while tokenizing input\n" |
|
886 | 887 | "The following traceback may be corrupted or invalid\n" |
|
887 | 888 | "The error message is: %s\n" % msg) |
|
888 | 889 | error(_m) |
|
889 | 890 | |
|
890 | 891 | # Join composite names (e.g. "dict.fromkeys") |
|
891 | 892 | names = ['.'.join(n) for n in names] |
|
892 | 893 | # prune names list of duplicates, but keep the right order |
|
893 | 894 | unique_names = uniq_stable(names) |
|
894 | 895 | |
|
895 | 896 | # Start loop over vars |
|
896 | 897 | lvals = [] |
|
897 | 898 | if self.include_vars: |
|
898 | 899 | for name_full in unique_names: |
|
899 | 900 | name_base = name_full.split('.',1)[0] |
|
900 | 901 | if name_base in frame.f_code.co_varnames: |
|
901 | 902 | if name_base in locals: |
|
902 | 903 | try: |
|
903 | 904 | value = repr(eval(name_full,locals)) |
|
904 | 905 | except: |
|
905 | 906 | value = undefined |
|
906 | 907 | else: |
|
907 | 908 | value = undefined |
|
908 | 909 | name = tpl_local_var % name_full |
|
909 | 910 | else: |
|
910 | 911 | if name_base in frame.f_globals: |
|
911 | 912 | try: |
|
912 | 913 | value = repr(eval(name_full,frame.f_globals)) |
|
913 | 914 | except: |
|
914 | 915 | value = undefined |
|
915 | 916 | else: |
|
916 | 917 | value = undefined |
|
917 | 918 | name = tpl_global_var % name_full |
|
918 | 919 | lvals.append(tpl_name_val % (name,value)) |
|
919 | 920 | if lvals: |
|
920 | 921 | lvals = '%s%s' % (indent,em_normal.join(lvals)) |
|
921 | 922 | else: |
|
922 | 923 | lvals = '' |
|
923 | 924 | |
|
924 | 925 | level = '%s %s\n' % (link,call) |
|
925 | 926 | |
|
926 | 927 | if index is None: |
|
927 | 928 | frames.append(level) |
|
928 | 929 | else: |
|
929 | 930 | frames.append('%s%s' % (level,''.join( |
|
930 | 931 | _format_traceback_lines(lnum,index,lines,Colors,lvals, |
|
931 | 932 | col_scheme)))) |
|
932 | 933 | |
|
933 | 934 | # Get (safely) a string form of the exception info |
|
934 | 935 | try: |
|
935 | 936 | etype_str,evalue_str = map(str,(etype,evalue)) |
|
936 | 937 | except: |
|
937 | 938 | # User exception is improperly defined. |
|
938 | 939 | etype,evalue = str,sys.exc_info()[:2] |
|
939 | 940 | etype_str,evalue_str = map(str,(etype,evalue)) |
|
940 | 941 | # ... and format it |
|
941 | 942 | exception = ['%s%s%s: %s' % (Colors.excName, etype_str, |
|
942 | 943 | ColorsNormal, py3compat.cast_unicode(evalue_str))] |
|
943 | 944 | if (not py3compat.PY3) and type(evalue) is types.InstanceType: |
|
944 | 945 | try: |
|
945 | 946 | names = [w for w in dir(evalue) if isinstance(w, basestring)] |
|
946 | 947 | except: |
|
947 | 948 | # Every now and then, an object with funny inernals blows up |
|
948 | 949 | # when dir() is called on it. We do the best we can to report |
|
949 | 950 | # the problem and continue |
|
950 | 951 | _m = '%sException reporting error (object with broken dir())%s:' |
|
951 | 952 | exception.append(_m % (Colors.excName,ColorsNormal)) |
|
952 | 953 | etype_str,evalue_str = map(str,sys.exc_info()[:2]) |
|
953 | 954 | exception.append('%s%s%s: %s' % (Colors.excName,etype_str, |
|
954 | 955 | ColorsNormal, py3compat.cast_unicode(evalue_str))) |
|
955 | 956 | names = [] |
|
956 | 957 | for name in names: |
|
957 | 958 | value = text_repr(getattr(evalue, name)) |
|
958 | 959 | exception.append('\n%s%s = %s' % (indent, name, value)) |
|
959 | 960 | |
|
960 | 961 | # vds: >> |
|
961 | 962 | if records: |
|
962 | 963 | filepath, lnum = records[-1][1:3] |
|
963 | 964 | #print "file:", str(file), "linenb", str(lnum) # dbg |
|
964 | 965 | filepath = os.path.abspath(filepath) |
|
965 | 966 | ipinst = get_ipython() |
|
966 | 967 | if ipinst is not None: |
|
967 | 968 | ipinst.hooks.synchronize_with_editor(filepath, lnum, 0) |
|
968 | 969 | # vds: << |
|
969 | 970 | |
|
970 | 971 | # return all our info assembled as a single string |
|
971 | 972 | # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) ) |
|
972 | 973 | return [head] + frames + [''.join(exception[0])] |
|
973 | 974 | |
|
974 | 975 | def debugger(self,force=False): |
|
975 | 976 | """Call up the pdb debugger if desired, always clean up the tb |
|
976 | 977 | reference. |
|
977 | 978 | |
|
978 | 979 | Keywords: |
|
979 | 980 | |
|
980 | 981 | - force(False): by default, this routine checks the instance call_pdb |
|
981 | 982 | flag and does not actually invoke the debugger if the flag is false. |
|
982 | 983 | The 'force' option forces the debugger to activate even if the flag |
|
983 | 984 | is false. |
|
984 | 985 | |
|
985 | 986 | If the call_pdb flag is set, the pdb interactive debugger is |
|
986 | 987 | invoked. In all cases, the self.tb reference to the current traceback |
|
987 | 988 | is deleted to prevent lingering references which hamper memory |
|
988 | 989 | management. |
|
989 | 990 | |
|
990 | 991 | Note that each call to pdb() does an 'import readline', so if your app |
|
991 | 992 | requires a special setup for the readline completers, you'll have to |
|
992 | 993 | fix that by hand after invoking the exception handler.""" |
|
993 | 994 | |
|
994 | 995 | if force or self.call_pdb: |
|
995 | 996 | if self.pdb is None: |
|
996 | 997 | self.pdb = debugger.Pdb( |
|
997 | 998 | self.color_scheme_table.active_scheme_name) |
|
998 | 999 | # the system displayhook may have changed, restore the original |
|
999 | 1000 | # for pdb |
|
1000 | 1001 | display_trap = DisplayTrap(hook=sys.__displayhook__) |
|
1001 | 1002 | with display_trap: |
|
1002 | 1003 | self.pdb.reset() |
|
1003 | 1004 | # Find the right frame so we don't pop up inside ipython itself |
|
1004 | 1005 | if hasattr(self,'tb') and self.tb is not None: |
|
1005 | 1006 | etb = self.tb |
|
1006 | 1007 | else: |
|
1007 | 1008 | etb = self.tb = sys.last_traceback |
|
1008 | 1009 | while self.tb is not None and self.tb.tb_next is not None: |
|
1009 | 1010 | self.tb = self.tb.tb_next |
|
1010 | 1011 | if etb and etb.tb_next: |
|
1011 | 1012 | etb = etb.tb_next |
|
1012 | 1013 | self.pdb.botframe = etb.tb_frame |
|
1013 | 1014 | self.pdb.interaction(self.tb.tb_frame, self.tb) |
|
1014 | 1015 | |
|
1015 | 1016 | if hasattr(self,'tb'): |
|
1016 | 1017 | del self.tb |
|
1017 | 1018 | |
|
1018 | 1019 | def handler(self, info=None): |
|
1019 | 1020 | (etype, evalue, etb) = info or sys.exc_info() |
|
1020 | 1021 | self.tb = etb |
|
1021 | 1022 | ostream = self.ostream |
|
1022 | 1023 | ostream.flush() |
|
1023 | 1024 | ostream.write(self.text(etype, evalue, etb)) |
|
1024 | 1025 | ostream.write('\n') |
|
1025 | 1026 | ostream.flush() |
|
1026 | 1027 | |
|
1027 | 1028 | # Changed so an instance can just be called as VerboseTB_inst() and print |
|
1028 | 1029 | # out the right info on its own. |
|
1029 | 1030 | def __call__(self, etype=None, evalue=None, etb=None): |
|
1030 | 1031 | """This hook can replace sys.excepthook (for Python 2.1 or higher).""" |
|
1031 | 1032 | if etb is None: |
|
1032 | 1033 | self.handler() |
|
1033 | 1034 | else: |
|
1034 | 1035 | self.handler((etype, evalue, etb)) |
|
1035 | 1036 | try: |
|
1036 | 1037 | self.debugger() |
|
1037 | 1038 | except KeyboardInterrupt: |
|
1038 |
print |
|
|
1039 | print("\nKeyboardInterrupt") | |
|
1039 | 1040 | |
|
1040 | 1041 | #---------------------------------------------------------------------------- |
|
1041 | 1042 | class FormattedTB(VerboseTB, ListTB): |
|
1042 | 1043 | """Subclass ListTB but allow calling with a traceback. |
|
1043 | 1044 | |
|
1044 | 1045 | It can thus be used as a sys.excepthook for Python > 2.1. |
|
1045 | 1046 | |
|
1046 | 1047 | Also adds 'Context' and 'Verbose' modes, not available in ListTB. |
|
1047 | 1048 | |
|
1048 | 1049 | Allows a tb_offset to be specified. This is useful for situations where |
|
1049 | 1050 | one needs to remove a number of topmost frames from the traceback (such as |
|
1050 | 1051 | occurs with python programs that themselves execute other python code, |
|
1051 | 1052 | like Python shells). """ |
|
1052 | 1053 | |
|
1053 | 1054 | def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False, |
|
1054 | 1055 | ostream=None, |
|
1055 | 1056 | tb_offset=0, long_header=False, include_vars=False, |
|
1056 | 1057 | check_cache=None): |
|
1057 | 1058 | |
|
1058 | 1059 | # NEVER change the order of this list. Put new modes at the end: |
|
1059 | 1060 | self.valid_modes = ['Plain','Context','Verbose'] |
|
1060 | 1061 | self.verbose_modes = self.valid_modes[1:3] |
|
1061 | 1062 | |
|
1062 | 1063 | VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, |
|
1063 | 1064 | ostream=ostream, tb_offset=tb_offset, |
|
1064 | 1065 | long_header=long_header, include_vars=include_vars, |
|
1065 | 1066 | check_cache=check_cache) |
|
1066 | 1067 | |
|
1067 | 1068 | # Different types of tracebacks are joined with different separators to |
|
1068 | 1069 | # form a single string. They are taken from this dict |
|
1069 | 1070 | self._join_chars = dict(Plain='', Context='\n', Verbose='\n') |
|
1070 | 1071 | # set_mode also sets the tb_join_char attribute |
|
1071 | 1072 | self.set_mode(mode) |
|
1072 | 1073 | |
|
1073 | 1074 | def _extract_tb(self,tb): |
|
1074 | 1075 | if tb: |
|
1075 | 1076 | return traceback.extract_tb(tb) |
|
1076 | 1077 | else: |
|
1077 | 1078 | return None |
|
1078 | 1079 | |
|
1079 | 1080 | def structured_traceback(self, etype, value, tb, tb_offset=None, context=5): |
|
1080 | 1081 | tb_offset = self.tb_offset if tb_offset is None else tb_offset |
|
1081 | 1082 | mode = self.mode |
|
1082 | 1083 | if mode in self.verbose_modes: |
|
1083 | 1084 | # Verbose modes need a full traceback |
|
1084 | 1085 | return VerboseTB.structured_traceback( |
|
1085 | 1086 | self, etype, value, tb, tb_offset, context |
|
1086 | 1087 | ) |
|
1087 | 1088 | else: |
|
1088 | 1089 | # We must check the source cache because otherwise we can print |
|
1089 | 1090 | # out-of-date source code. |
|
1090 | 1091 | self.check_cache() |
|
1091 | 1092 | # Now we can extract and format the exception |
|
1092 | 1093 | elist = self._extract_tb(tb) |
|
1093 | 1094 | return ListTB.structured_traceback( |
|
1094 | 1095 | self, etype, value, elist, tb_offset, context |
|
1095 | 1096 | ) |
|
1096 | 1097 | |
|
1097 | 1098 | def stb2text(self, stb): |
|
1098 | 1099 | """Convert a structured traceback (a list) to a string.""" |
|
1099 | 1100 | return self.tb_join_char.join(stb) |
|
1100 | 1101 | |
|
1101 | 1102 | |
|
1102 | 1103 | def set_mode(self,mode=None): |
|
1103 | 1104 | """Switch to the desired mode. |
|
1104 | 1105 | |
|
1105 | 1106 | If mode is not specified, cycles through the available modes.""" |
|
1106 | 1107 | |
|
1107 | 1108 | if not mode: |
|
1108 | 1109 | new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \ |
|
1109 | 1110 | len(self.valid_modes) |
|
1110 | 1111 | self.mode = self.valid_modes[new_idx] |
|
1111 | 1112 | elif mode not in self.valid_modes: |
|
1112 | 1113 | raise ValueError('Unrecognized mode in FormattedTB: <'+mode+'>\n' |
|
1113 | 1114 | 'Valid modes: '+str(self.valid_modes)) |
|
1114 | 1115 | else: |
|
1115 | 1116 | self.mode = mode |
|
1116 | 1117 | # include variable details only in 'Verbose' mode |
|
1117 | 1118 | self.include_vars = (self.mode == self.valid_modes[2]) |
|
1118 | 1119 | # Set the join character for generating text tracebacks |
|
1119 | 1120 | self.tb_join_char = self._join_chars[self.mode] |
|
1120 | 1121 | |
|
1121 | 1122 | # some convenient shorcuts |
|
1122 | 1123 | def plain(self): |
|
1123 | 1124 | self.set_mode(self.valid_modes[0]) |
|
1124 | 1125 | |
|
1125 | 1126 | def context(self): |
|
1126 | 1127 | self.set_mode(self.valid_modes[1]) |
|
1127 | 1128 | |
|
1128 | 1129 | def verbose(self): |
|
1129 | 1130 | self.set_mode(self.valid_modes[2]) |
|
1130 | 1131 | |
|
1131 | 1132 | #---------------------------------------------------------------------------- |
|
1132 | 1133 | class AutoFormattedTB(FormattedTB): |
|
1133 | 1134 | """A traceback printer which can be called on the fly. |
|
1134 | 1135 | |
|
1135 | 1136 | It will find out about exceptions by itself. |
|
1136 | 1137 | |
|
1137 | 1138 | A brief example:: |
|
1138 | 1139 | |
|
1139 | 1140 | AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux') |
|
1140 | 1141 | try: |
|
1141 | 1142 | ... |
|
1142 | 1143 | except: |
|
1143 | 1144 | AutoTB() # or AutoTB(out=logfile) where logfile is an open file object |
|
1144 | 1145 | """ |
|
1145 | 1146 | |
|
1146 | 1147 | def __call__(self,etype=None,evalue=None,etb=None, |
|
1147 | 1148 | out=None,tb_offset=None): |
|
1148 | 1149 | """Print out a formatted exception traceback. |
|
1149 | 1150 | |
|
1150 | 1151 | Optional arguments: |
|
1151 | 1152 | - out: an open file-like object to direct output to. |
|
1152 | 1153 | |
|
1153 | 1154 | - tb_offset: the number of frames to skip over in the stack, on a |
|
1154 | 1155 | per-call basis (this overrides temporarily the instance's tb_offset |
|
1155 | 1156 | given at initialization time. """ |
|
1156 | 1157 | |
|
1157 | 1158 | |
|
1158 | 1159 | if out is None: |
|
1159 | 1160 | out = self.ostream |
|
1160 | 1161 | out.flush() |
|
1161 | 1162 | out.write(self.text(etype, evalue, etb, tb_offset)) |
|
1162 | 1163 | out.write('\n') |
|
1163 | 1164 | out.flush() |
|
1164 | 1165 | # FIXME: we should remove the auto pdb behavior from here and leave |
|
1165 | 1166 | # that to the clients. |
|
1166 | 1167 | try: |
|
1167 | 1168 | self.debugger() |
|
1168 | 1169 | except KeyboardInterrupt: |
|
1169 |
print |
|
|
1170 | print("\nKeyboardInterrupt") | |
|
1170 | 1171 | |
|
1171 | 1172 | def structured_traceback(self, etype=None, value=None, tb=None, |
|
1172 | 1173 | tb_offset=None, context=5): |
|
1173 | 1174 | if etype is None: |
|
1174 | 1175 | etype,value,tb = sys.exc_info() |
|
1175 | 1176 | self.tb = tb |
|
1176 | 1177 | return FormattedTB.structured_traceback( |
|
1177 | 1178 | self, etype, value, tb, tb_offset, context) |
|
1178 | 1179 | |
|
1179 | 1180 | #--------------------------------------------------------------------------- |
|
1180 | 1181 | |
|
1181 | 1182 | # A simple class to preserve Nathan's original functionality. |
|
1182 | 1183 | class ColorTB(FormattedTB): |
|
1183 | 1184 | """Shorthand to initialize a FormattedTB in Linux colors mode.""" |
|
1184 | 1185 | def __init__(self,color_scheme='Linux',call_pdb=0): |
|
1185 | 1186 | FormattedTB.__init__(self,color_scheme=color_scheme, |
|
1186 | 1187 | call_pdb=call_pdb) |
|
1187 | 1188 | |
|
1188 | 1189 | |
|
1189 | 1190 | class SyntaxTB(ListTB): |
|
1190 | 1191 | """Extension which holds some state: the last exception value""" |
|
1191 | 1192 | |
|
1192 | 1193 | def __init__(self,color_scheme = 'NoColor'): |
|
1193 | 1194 | ListTB.__init__(self,color_scheme) |
|
1194 | 1195 | self.last_syntax_error = None |
|
1195 | 1196 | |
|
1196 | 1197 | def __call__(self, etype, value, elist): |
|
1197 | 1198 | self.last_syntax_error = value |
|
1198 | 1199 | ListTB.__call__(self,etype,value,elist) |
|
1199 | 1200 | |
|
1200 | 1201 | def structured_traceback(self, etype, value, elist, tb_offset=None, |
|
1201 | 1202 | context=5): |
|
1202 | 1203 | # If the source file has been edited, the line in the syntax error can |
|
1203 | 1204 | # be wrong (retrieved from an outdated cache). This replaces it with |
|
1204 | 1205 | # the current value. |
|
1205 | 1206 | if isinstance(value, SyntaxError) \ |
|
1206 | 1207 | and isinstance(value.filename, py3compat.string_types) \ |
|
1207 | 1208 | and isinstance(value.lineno, int): |
|
1208 | 1209 | linecache.checkcache(value.filename) |
|
1209 | 1210 | newtext = ulinecache.getline(value.filename, value.lineno) |
|
1210 | 1211 | if newtext: |
|
1211 | 1212 | value.text = newtext |
|
1212 | 1213 | return super(SyntaxTB, self).structured_traceback(etype, value, elist, |
|
1213 | 1214 | tb_offset=tb_offset, context=context) |
|
1214 | 1215 | |
|
1215 | 1216 | def clear_err_state(self): |
|
1216 | 1217 | """Return the current error state and clear it""" |
|
1217 | 1218 | e = self.last_syntax_error |
|
1218 | 1219 | self.last_syntax_error = None |
|
1219 | 1220 | return e |
|
1220 | 1221 | |
|
1221 | 1222 | def stb2text(self, stb): |
|
1222 | 1223 | """Convert a structured traceback (a list) to a string.""" |
|
1223 | 1224 | return ''.join(stb) |
|
1224 | 1225 | |
|
1225 | 1226 | |
|
1226 | 1227 | #---------------------------------------------------------------------------- |
|
1227 | 1228 | # module testing (minimal) |
|
1228 | 1229 | if __name__ == "__main__": |
|
1229 | 1230 | def spam(c, d_e): |
|
1230 | 1231 | (d, e) = d_e |
|
1231 | 1232 | x = c + d |
|
1232 | 1233 | y = c * d |
|
1233 | 1234 | foo(x, y) |
|
1234 | 1235 | |
|
1235 | 1236 | def foo(a, b, bar=1): |
|
1236 | 1237 | eggs(a, b + bar) |
|
1237 | 1238 | |
|
1238 | 1239 | def eggs(f, g, z=globals()): |
|
1239 | 1240 | h = f + g |
|
1240 | 1241 | i = f - g |
|
1241 | 1242 | return h / i |
|
1242 | 1243 | |
|
1243 |
print |
|
|
1244 |
print |
|
|
1244 | print('') | |
|
1245 | print('*** Before ***') | |
|
1245 | 1246 | try: |
|
1246 |
print |
|
|
1247 | print(spam(1, (2, 3))) | |
|
1247 | 1248 | except: |
|
1248 | 1249 | traceback.print_exc() |
|
1249 |
print |
|
|
1250 | print('') | |
|
1250 | 1251 | |
|
1251 | 1252 | handler = ColorTB() |
|
1252 |
print |
|
|
1253 | print('*** ColorTB ***') | |
|
1253 | 1254 | try: |
|
1254 |
print |
|
|
1255 | print(spam(1, (2, 3))) | |
|
1255 | 1256 | except: |
|
1256 | 1257 | handler(*sys.exc_info()) |
|
1257 |
print |
|
|
1258 | print('') | |
|
1258 | 1259 | |
|
1259 | 1260 | handler = VerboseTB() |
|
1260 |
print |
|
|
1261 | print('*** VerboseTB ***') | |
|
1261 | 1262 | try: |
|
1262 |
print |
|
|
1263 | print(spam(1, (2, 3))) | |
|
1263 | 1264 | except: |
|
1264 | 1265 | handler(*sys.exc_info()) |
|
1265 |
print |
|
|
1266 | print('') | |
|
1266 | 1267 |
@@ -1,694 +1,695 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """ |
|
3 | 3 | ====== |
|
4 | 4 | Rmagic |
|
5 | 5 | ====== |
|
6 | 6 | |
|
7 | 7 | Magic command interface for interactive work with R via rpy2 |
|
8 | 8 | |
|
9 | 9 | .. note:: |
|
10 | 10 | |
|
11 | 11 | The ``rpy2`` package needs to be installed separately. It |
|
12 | 12 | can be obtained using ``easy_install`` or ``pip``. |
|
13 | 13 | |
|
14 | 14 | You will also need a working copy of R. |
|
15 | 15 | |
|
16 | 16 | Usage |
|
17 | 17 | ===== |
|
18 | 18 | |
|
19 | 19 | To enable the magics below, execute ``%load_ext rmagic``. |
|
20 | 20 | |
|
21 | 21 | ``%R`` |
|
22 | 22 | |
|
23 | 23 | {R_DOC} |
|
24 | 24 | |
|
25 | 25 | ``%Rpush`` |
|
26 | 26 | |
|
27 | 27 | {RPUSH_DOC} |
|
28 | 28 | |
|
29 | 29 | ``%Rpull`` |
|
30 | 30 | |
|
31 | 31 | {RPULL_DOC} |
|
32 | 32 | |
|
33 | 33 | ``%Rget`` |
|
34 | 34 | |
|
35 | 35 | {RGET_DOC} |
|
36 | 36 | |
|
37 | 37 | """ |
|
38 | from __future__ import print_function | |
|
38 | 39 | |
|
39 | 40 | #----------------------------------------------------------------------------- |
|
40 | 41 | # Copyright (C) 2012 The IPython Development Team |
|
41 | 42 | # |
|
42 | 43 | # Distributed under the terms of the BSD License. The full license is in |
|
43 | 44 | # the file COPYING, distributed as part of this software. |
|
44 | 45 | #----------------------------------------------------------------------------- |
|
45 | 46 | |
|
46 | 47 | import sys |
|
47 | 48 | import tempfile |
|
48 | 49 | from glob import glob |
|
49 | 50 | from shutil import rmtree |
|
50 | 51 | |
|
51 | 52 | # numpy and rpy2 imports |
|
52 | 53 | |
|
53 | 54 | import numpy as np |
|
54 | 55 | |
|
55 | 56 | import rpy2.rinterface as ri |
|
56 | 57 | import rpy2.robjects as ro |
|
57 | 58 | try: |
|
58 | 59 | from rpy2.robjects import pandas2ri |
|
59 | 60 | pandas2ri.activate() |
|
60 | 61 | except ImportError: |
|
61 | 62 | pandas2ri = None |
|
62 | 63 | from rpy2.robjects import numpy2ri |
|
63 | 64 | numpy2ri.activate() |
|
64 | 65 | |
|
65 | 66 | # IPython imports |
|
66 | 67 | |
|
67 | 68 | from IPython.core.displaypub import publish_display_data |
|
68 | 69 | from IPython.core.magic import (Magics, magics_class, line_magic, |
|
69 | 70 | line_cell_magic, needs_local_scope) |
|
70 | 71 | from IPython.testing.skipdoctest import skip_doctest |
|
71 | 72 | from IPython.core.magic_arguments import ( |
|
72 | 73 | argument, magic_arguments, parse_argstring |
|
73 | 74 | ) |
|
74 | 75 | from IPython.external.simplegeneric import generic |
|
75 | 76 | from IPython.utils.py3compat import (str_to_unicode, unicode_to_str, PY3, |
|
76 | 77 | unicode_type) |
|
77 | 78 | |
|
78 | 79 | class RInterpreterError(ri.RRuntimeError): |
|
79 | 80 | """An error when running R code in a %%R magic cell.""" |
|
80 | 81 | def __init__(self, line, err, stdout): |
|
81 | 82 | self.line = line |
|
82 | 83 | self.err = err.rstrip() |
|
83 | 84 | self.stdout = stdout.rstrip() |
|
84 | 85 | |
|
85 | 86 | def __unicode__(self): |
|
86 | 87 | s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \ |
|
87 | 88 | (self.line, self.err) |
|
88 | 89 | if self.stdout and (self.stdout != self.err): |
|
89 | 90 | s += '\nR stdout:\n' + self.stdout |
|
90 | 91 | return s |
|
91 | 92 | |
|
92 | 93 | if PY3: |
|
93 | 94 | __str__ = __unicode__ |
|
94 | 95 | else: |
|
95 | 96 | def __str__(self): |
|
96 | 97 | return unicode_to_str(unicode(self), 'utf-8') |
|
97 | 98 | |
|
98 | 99 | def Rconverter(Robj, dataframe=False): |
|
99 | 100 | """ |
|
100 | 101 | Convert an object in R's namespace to one suitable |
|
101 | 102 | for ipython's namespace. |
|
102 | 103 | |
|
103 | 104 | For a data.frame, it tries to return a structured array. |
|
104 | 105 | It first checks for colnames, then names. |
|
105 | 106 | If all are NULL, it returns np.asarray(Robj), else |
|
106 | 107 | it tries to construct a recarray |
|
107 | 108 | |
|
108 | 109 | Parameters |
|
109 | 110 | ---------- |
|
110 | 111 | |
|
111 | 112 | Robj: an R object returned from rpy2 |
|
112 | 113 | """ |
|
113 | 114 | is_data_frame = ro.r('is.data.frame') |
|
114 | 115 | colnames = ro.r('colnames') |
|
115 | 116 | rownames = ro.r('rownames') # with pandas, these could be used for the index |
|
116 | 117 | names = ro.r('names') |
|
117 | 118 | |
|
118 | 119 | if dataframe: |
|
119 | 120 | as_data_frame = ro.r('as.data.frame') |
|
120 | 121 | cols = colnames(Robj) |
|
121 | 122 | _names = names(Robj) |
|
122 | 123 | if cols != ri.NULL: |
|
123 | 124 | Robj = as_data_frame(Robj) |
|
124 | 125 | names = tuple(np.array(cols)) |
|
125 | 126 | elif _names != ri.NULL: |
|
126 | 127 | names = tuple(np.array(_names)) |
|
127 | 128 | else: # failed to find names |
|
128 | 129 | return np.asarray(Robj) |
|
129 | 130 | Robj = np.rec.fromarrays(Robj, names = names) |
|
130 | 131 | return np.asarray(Robj) |
|
131 | 132 | |
|
132 | 133 | @generic |
|
133 | 134 | def pyconverter(pyobj): |
|
134 | 135 | """Convert Python objects to R objects. Add types using the decorator: |
|
135 | 136 | |
|
136 | 137 | @pyconverter.when_type |
|
137 | 138 | """ |
|
138 | 139 | return pyobj |
|
139 | 140 | |
|
140 | 141 | # The default conversion for lists seems to make them a nested list. That has |
|
141 | 142 | # some advantages, but is rarely convenient, so for interactive use, we convert |
|
142 | 143 | # lists to a numpy array, which becomes an R vector. |
|
143 | 144 | @pyconverter.when_type(list) |
|
144 | 145 | def pyconverter_list(pyobj): |
|
145 | 146 | return np.asarray(pyobj) |
|
146 | 147 | |
|
147 | 148 | if pandas2ri is None: |
|
148 | 149 | # pandas2ri was new in rpy2 2.3.3, so for now we'll fallback to pandas' |
|
149 | 150 | # conversion function. |
|
150 | 151 | try: |
|
151 | 152 | from pandas import DataFrame |
|
152 | 153 | from pandas.rpy.common import convert_to_r_dataframe |
|
153 | 154 | @pyconverter.when_type(DataFrame) |
|
154 | 155 | def pyconverter_dataframe(pyobj): |
|
155 | 156 | return convert_to_r_dataframe(pyobj, strings_as_factors=True) |
|
156 | 157 | except ImportError: |
|
157 | 158 | pass |
|
158 | 159 | |
|
159 | 160 | @magics_class |
|
160 | 161 | class RMagics(Magics): |
|
161 | 162 | """A set of magics useful for interactive work with R via rpy2. |
|
162 | 163 | """ |
|
163 | 164 | |
|
164 | 165 | def __init__(self, shell, Rconverter=Rconverter, |
|
165 | 166 | pyconverter=pyconverter, |
|
166 | 167 | cache_display_data=False): |
|
167 | 168 | """ |
|
168 | 169 | Parameters |
|
169 | 170 | ---------- |
|
170 | 171 | |
|
171 | 172 | shell : IPython shell |
|
172 | 173 | |
|
173 | 174 | Rconverter : callable |
|
174 | 175 | To be called on values taken from R before putting them in the |
|
175 | 176 | IPython namespace. |
|
176 | 177 | |
|
177 | 178 | pyconverter : callable |
|
178 | 179 | To be called on values in ipython namespace before |
|
179 | 180 | assigning to variables in rpy2. |
|
180 | 181 | |
|
181 | 182 | cache_display_data : bool |
|
182 | 183 | If True, the published results of the final call to R are |
|
183 | 184 | cached in the variable 'display_cache'. |
|
184 | 185 | |
|
185 | 186 | """ |
|
186 | 187 | super(RMagics, self).__init__(shell) |
|
187 | 188 | self.cache_display_data = cache_display_data |
|
188 | 189 | |
|
189 | 190 | self.r = ro.R() |
|
190 | 191 | |
|
191 | 192 | self.Rstdout_cache = [] |
|
192 | 193 | self.pyconverter = pyconverter |
|
193 | 194 | self.Rconverter = Rconverter |
|
194 | 195 | |
|
195 | 196 | def eval(self, line): |
|
196 | 197 | ''' |
|
197 | 198 | Parse and evaluate a line of R code with rpy2. |
|
198 | 199 | Returns the output to R's stdout() connection, |
|
199 | 200 | the value generated by evaluating the code, and a |
|
200 | 201 | boolean indicating whether the return value would be |
|
201 | 202 | visible if the line of code were evaluated in an R REPL. |
|
202 | 203 | |
|
203 | 204 | R Code evaluation and visibility determination are |
|
204 | 205 | done via an R call of the form withVisible({<code>}) |
|
205 | 206 | |
|
206 | 207 | ''' |
|
207 | 208 | old_writeconsole = ri.get_writeconsole() |
|
208 | 209 | ri.set_writeconsole(self.write_console) |
|
209 | 210 | try: |
|
210 | 211 | res = ro.r("withVisible({%s})" % line) |
|
211 | 212 | value = res[0] #value (R object) |
|
212 | 213 | visible = ro.conversion.ri2py(res[1])[0] #visible (boolean) |
|
213 | 214 | except (ri.RRuntimeError, ValueError) as exception: |
|
214 | 215 | warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error |
|
215 | 216 | raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg) |
|
216 | 217 | text_output = self.flush() |
|
217 | 218 | ri.set_writeconsole(old_writeconsole) |
|
218 | 219 | return text_output, value, visible |
|
219 | 220 | |
|
220 | 221 | def write_console(self, output): |
|
221 | 222 | ''' |
|
222 | 223 | A hook to capture R's stdout in a cache. |
|
223 | 224 | ''' |
|
224 | 225 | self.Rstdout_cache.append(output) |
|
225 | 226 | |
|
226 | 227 | def flush(self): |
|
227 | 228 | ''' |
|
228 | 229 | Flush R's stdout cache to a string, returning the string. |
|
229 | 230 | ''' |
|
230 | 231 | value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache]) |
|
231 | 232 | self.Rstdout_cache = [] |
|
232 | 233 | return value |
|
233 | 234 | |
|
234 | 235 | @skip_doctest |
|
235 | 236 | @needs_local_scope |
|
236 | 237 | @line_magic |
|
237 | 238 | def Rpush(self, line, local_ns=None): |
|
238 | 239 | ''' |
|
239 | 240 | A line-level magic for R that pushes |
|
240 | 241 | variables from python to rpy2. The line should be made up |
|
241 | 242 | of whitespace separated variable names in the IPython |
|
242 | 243 | namespace:: |
|
243 | 244 | |
|
244 | 245 | In [7]: import numpy as np |
|
245 | 246 | |
|
246 | 247 | In [8]: X = np.array([4.5,6.3,7.9]) |
|
247 | 248 | |
|
248 | 249 | In [9]: X.mean() |
|
249 | 250 | Out[9]: 6.2333333333333343 |
|
250 | 251 | |
|
251 | 252 | In [10]: %Rpush X |
|
252 | 253 | |
|
253 | 254 | In [11]: %R mean(X) |
|
254 | 255 | Out[11]: array([ 6.23333333]) |
|
255 | 256 | |
|
256 | 257 | ''' |
|
257 | 258 | if local_ns is None: |
|
258 | 259 | local_ns = {} |
|
259 | 260 | |
|
260 | 261 | inputs = line.split(' ') |
|
261 | 262 | for input in inputs: |
|
262 | 263 | try: |
|
263 | 264 | val = local_ns[input] |
|
264 | 265 | except KeyError: |
|
265 | 266 | try: |
|
266 | 267 | val = self.shell.user_ns[input] |
|
267 | 268 | except KeyError: |
|
268 | 269 | # reraise the KeyError as a NameError so that it looks like |
|
269 | 270 | # the standard python behavior when you use an unnamed |
|
270 | 271 | # variable |
|
271 | 272 | raise NameError("name '%s' is not defined" % input) |
|
272 | 273 | |
|
273 | 274 | self.r.assign(input, self.pyconverter(val)) |
|
274 | 275 | |
|
275 | 276 | @skip_doctest |
|
276 | 277 | @magic_arguments() |
|
277 | 278 | @argument( |
|
278 | 279 | '-d', '--as_dataframe', action='store_true', |
|
279 | 280 | default=False, |
|
280 | 281 | help='Convert objects to data.frames before returning to ipython.' |
|
281 | 282 | ) |
|
282 | 283 | @argument( |
|
283 | 284 | 'outputs', |
|
284 | 285 | nargs='*', |
|
285 | 286 | ) |
|
286 | 287 | @line_magic |
|
287 | 288 | def Rpull(self, line): |
|
288 | 289 | ''' |
|
289 | 290 | A line-level magic for R that pulls |
|
290 | 291 | variables from python to rpy2:: |
|
291 | 292 | |
|
292 | 293 | In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4) |
|
293 | 294 | |
|
294 | 295 | In [19]: %Rpull x y z |
|
295 | 296 | |
|
296 | 297 | In [20]: x |
|
297 | 298 | Out[20]: array([ 3. , 4. , 6.7]) |
|
298 | 299 | |
|
299 | 300 | In [21]: y |
|
300 | 301 | Out[21]: array([ 4., 6., 7.]) |
|
301 | 302 | |
|
302 | 303 | In [22]: z |
|
303 | 304 | Out[22]: |
|
304 | 305 | array(['a', '3', '4'], |
|
305 | 306 | dtype='|S1') |
|
306 | 307 | |
|
307 | 308 | |
|
308 | 309 | If --as_dataframe, then each object is returned as a structured array |
|
309 | 310 | after first passed through "as.data.frame" in R before |
|
310 | 311 | being calling self.Rconverter. |
|
311 | 312 | This is useful when a structured array is desired as output, or |
|
312 | 313 | when the object in R has mixed data types. |
|
313 | 314 | See the %%R docstring for more examples. |
|
314 | 315 | |
|
315 | 316 | Notes |
|
316 | 317 | ----- |
|
317 | 318 | |
|
318 | 319 | Beware that R names can have '.' so this is not fool proof. |
|
319 | 320 | To avoid this, don't name your R objects with '.'s... |
|
320 | 321 | |
|
321 | 322 | ''' |
|
322 | 323 | args = parse_argstring(self.Rpull, line) |
|
323 | 324 | outputs = args.outputs |
|
324 | 325 | for output in outputs: |
|
325 | 326 | self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)}) |
|
326 | 327 | |
|
327 | 328 | @skip_doctest |
|
328 | 329 | @magic_arguments() |
|
329 | 330 | @argument( |
|
330 | 331 | '-d', '--as_dataframe', action='store_true', |
|
331 | 332 | default=False, |
|
332 | 333 | help='Convert objects to data.frames before returning to ipython.' |
|
333 | 334 | ) |
|
334 | 335 | @argument( |
|
335 | 336 | 'output', |
|
336 | 337 | nargs=1, |
|
337 | 338 | type=str, |
|
338 | 339 | ) |
|
339 | 340 | @line_magic |
|
340 | 341 | def Rget(self, line): |
|
341 | 342 | ''' |
|
342 | 343 | Return an object from rpy2, possibly as a structured array (if possible). |
|
343 | 344 | Similar to Rpull except only one argument is accepted and the value is |
|
344 | 345 | returned rather than pushed to self.shell.user_ns:: |
|
345 | 346 | |
|
346 | 347 | In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')] |
|
347 | 348 | |
|
348 | 349 | In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype) |
|
349 | 350 | |
|
350 | 351 | In [5]: %R -i datapy |
|
351 | 352 | |
|
352 | 353 | In [6]: %Rget datapy |
|
353 | 354 | Out[6]: |
|
354 | 355 | array([['1', '2', '3', '4'], |
|
355 | 356 | ['2', '3', '2', '5'], |
|
356 | 357 | ['a', 'b', 'c', 'e']], |
|
357 | 358 | dtype='|S1') |
|
358 | 359 | |
|
359 | 360 | In [7]: %Rget -d datapy |
|
360 | 361 | Out[7]: |
|
361 | 362 | array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')], |
|
362 | 363 | dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]) |
|
363 | 364 | |
|
364 | 365 | ''' |
|
365 | 366 | args = parse_argstring(self.Rget, line) |
|
366 | 367 | output = args.output |
|
367 | 368 | return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe) |
|
368 | 369 | |
|
369 | 370 | |
|
370 | 371 | @skip_doctest |
|
371 | 372 | @magic_arguments() |
|
372 | 373 | @argument( |
|
373 | 374 | '-i', '--input', action='append', |
|
374 | 375 | help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.' |
|
375 | 376 | ) |
|
376 | 377 | @argument( |
|
377 | 378 | '-o', '--output', action='append', |
|
378 | 379 | help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.' |
|
379 | 380 | ) |
|
380 | 381 | @argument( |
|
381 | 382 | '-w', '--width', type=int, |
|
382 | 383 | help='Width of png plotting device sent as an argument to *png* in R.' |
|
383 | 384 | ) |
|
384 | 385 | @argument( |
|
385 | 386 | '-h', '--height', type=int, |
|
386 | 387 | help='Height of png plotting device sent as an argument to *png* in R.' |
|
387 | 388 | ) |
|
388 | 389 | |
|
389 | 390 | @argument( |
|
390 | 391 | '-d', '--dataframe', action='append', |
|
391 | 392 | help='Convert these objects to data.frames and return as structured arrays.' |
|
392 | 393 | ) |
|
393 | 394 | @argument( |
|
394 | 395 | '-u', '--units', type=unicode_type, choices=["px", "in", "cm", "mm"], |
|
395 | 396 | help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].' |
|
396 | 397 | ) |
|
397 | 398 | @argument( |
|
398 | 399 | '-r', '--res', type=int, |
|
399 | 400 | help='Resolution of png plotting device sent as an argument to *png* in R. Defaults to 72 if *units* is one of ["in", "cm", "mm"].' |
|
400 | 401 | ) |
|
401 | 402 | @argument( |
|
402 | 403 | '-p', '--pointsize', type=int, |
|
403 | 404 | help='Pointsize of png plotting device sent as an argument to *png* in R.' |
|
404 | 405 | ) |
|
405 | 406 | @argument( |
|
406 | 407 | '-b', '--bg', |
|
407 | 408 | help='Background of png plotting device sent as an argument to *png* in R.' |
|
408 | 409 | ) |
|
409 | 410 | @argument( |
|
410 | 411 | '-n', '--noreturn', |
|
411 | 412 | help='Force the magic to not return anything.', |
|
412 | 413 | action='store_true', |
|
413 | 414 | default=False |
|
414 | 415 | ) |
|
415 | 416 | @argument( |
|
416 | 417 | 'code', |
|
417 | 418 | nargs='*', |
|
418 | 419 | ) |
|
419 | 420 | @needs_local_scope |
|
420 | 421 | @line_cell_magic |
|
421 | 422 | def R(self, line, cell=None, local_ns=None): |
|
422 | 423 | ''' |
|
423 | 424 | Execute code in R, and pull some of the results back into the Python namespace. |
|
424 | 425 | |
|
425 | 426 | In line mode, this will evaluate an expression and convert the returned value to a Python object. |
|
426 | 427 | The return value is determined by rpy2's behaviour of returning the result of evaluating the |
|
427 | 428 | final line. |
|
428 | 429 | |
|
429 | 430 | Multiple R lines can be executed by joining them with semicolons:: |
|
430 | 431 | |
|
431 | 432 | In [9]: %R X=c(1,4,5,7); sd(X); mean(X) |
|
432 | 433 | Out[9]: array([ 4.25]) |
|
433 | 434 | |
|
434 | 435 | In cell mode, this will run a block of R code. The resulting value |
|
435 | 436 | is printed if it would printed be when evaluating the same code |
|
436 | 437 | within a standard R REPL. |
|
437 | 438 | |
|
438 | 439 | Nothing is returned to python by default in cell mode:: |
|
439 | 440 | |
|
440 | 441 | In [10]: %%R |
|
441 | 442 | ....: Y = c(2,4,3,9) |
|
442 | 443 | ....: summary(lm(Y~X)) |
|
443 | 444 | |
|
444 | 445 | Call: |
|
445 | 446 | lm(formula = Y ~ X) |
|
446 | 447 | |
|
447 | 448 | Residuals: |
|
448 | 449 | 1 2 3 4 |
|
449 | 450 | 0.88 -0.24 -2.28 1.64 |
|
450 | 451 | |
|
451 | 452 | Coefficients: |
|
452 | 453 | Estimate Std. Error t value Pr(>|t|) |
|
453 | 454 | (Intercept) 0.0800 2.3000 0.035 0.975 |
|
454 | 455 | X 1.0400 0.4822 2.157 0.164 |
|
455 | 456 | |
|
456 | 457 | Residual standard error: 2.088 on 2 degrees of freedom |
|
457 | 458 | Multiple R-squared: 0.6993,Adjusted R-squared: 0.549 |
|
458 | 459 | F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638 |
|
459 | 460 | |
|
460 | 461 | In the notebook, plots are published as the output of the cell:: |
|
461 | 462 | |
|
462 | 463 | %R plot(X, Y) |
|
463 | 464 | |
|
464 | 465 | will create a scatter plot of X bs Y. |
|
465 | 466 | |
|
466 | 467 | If cell is not None and line has some R code, it is prepended to |
|
467 | 468 | the R code in cell. |
|
468 | 469 | |
|
469 | 470 | Objects can be passed back and forth between rpy2 and python via the -i -o flags in line:: |
|
470 | 471 | |
|
471 | 472 | In [14]: Z = np.array([1,4,5,10]) |
|
472 | 473 | |
|
473 | 474 | In [15]: %R -i Z mean(Z) |
|
474 | 475 | Out[15]: array([ 5.]) |
|
475 | 476 | |
|
476 | 477 | |
|
477 | 478 | In [16]: %R -o W W=Z*mean(Z) |
|
478 | 479 | Out[16]: array([ 5., 20., 25., 50.]) |
|
479 | 480 | |
|
480 | 481 | In [17]: W |
|
481 | 482 | Out[17]: array([ 5., 20., 25., 50.]) |
|
482 | 483 | |
|
483 | 484 | The return value is determined by these rules: |
|
484 | 485 | |
|
485 | 486 | * If the cell is not None, the magic returns None. |
|
486 | 487 | |
|
487 | 488 | * If the cell evaluates as False, the resulting value is returned |
|
488 | 489 | unless the final line prints something to the console, in |
|
489 | 490 | which case None is returned. |
|
490 | 491 | |
|
491 | 492 | * If the final line results in a NULL value when evaluated |
|
492 | 493 | by rpy2, then None is returned. |
|
493 | 494 | |
|
494 | 495 | * No attempt is made to convert the final value to a structured array. |
|
495 | 496 | Use the --dataframe flag or %Rget to push / return a structured array. |
|
496 | 497 | |
|
497 | 498 | * If the -n flag is present, there is no return value. |
|
498 | 499 | |
|
499 | 500 | * A trailing ';' will also result in no return value as the last |
|
500 | 501 | value in the line is an empty string. |
|
501 | 502 | |
|
502 | 503 | The --dataframe argument will attempt to return structured arrays. |
|
503 | 504 | This is useful for dataframes with |
|
504 | 505 | mixed data types. Note also that for a data.frame, |
|
505 | 506 | if it is returned as an ndarray, it is transposed:: |
|
506 | 507 | |
|
507 | 508 | In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')] |
|
508 | 509 | |
|
509 | 510 | In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype) |
|
510 | 511 | |
|
511 | 512 | In [20]: %%R -o datar |
|
512 | 513 | datar = datapy |
|
513 | 514 | ....: |
|
514 | 515 | |
|
515 | 516 | In [21]: datar |
|
516 | 517 | Out[21]: |
|
517 | 518 | array([['1', '2', '3', '4'], |
|
518 | 519 | ['2', '3', '2', '5'], |
|
519 | 520 | ['a', 'b', 'c', 'e']], |
|
520 | 521 | dtype='|S1') |
|
521 | 522 | |
|
522 | 523 | In [22]: %%R -d datar |
|
523 | 524 | datar = datapy |
|
524 | 525 | ....: |
|
525 | 526 | |
|
526 | 527 | In [23]: datar |
|
527 | 528 | Out[23]: |
|
528 | 529 | array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')], |
|
529 | 530 | dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]) |
|
530 | 531 | |
|
531 | 532 | The --dataframe argument first tries colnames, then names. |
|
532 | 533 | If both are NULL, it returns an ndarray (i.e. unstructured):: |
|
533 | 534 | |
|
534 | 535 | In [1]: %R mydata=c(4,6,8.3); NULL |
|
535 | 536 | |
|
536 | 537 | In [2]: %R -d mydata |
|
537 | 538 | |
|
538 | 539 | In [3]: mydata |
|
539 | 540 | Out[3]: array([ 4. , 6. , 8.3]) |
|
540 | 541 | |
|
541 | 542 | In [4]: %R names(mydata) = c('a','b','c'); NULL |
|
542 | 543 | |
|
543 | 544 | In [5]: %R -d mydata |
|
544 | 545 | |
|
545 | 546 | In [6]: mydata |
|
546 | 547 | Out[6]: |
|
547 | 548 | array((4.0, 6.0, 8.3), |
|
548 | 549 | dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')]) |
|
549 | 550 | |
|
550 | 551 | In [7]: %R -o mydata |
|
551 | 552 | |
|
552 | 553 | In [8]: mydata |
|
553 | 554 | Out[8]: array([ 4. , 6. , 8.3]) |
|
554 | 555 | |
|
555 | 556 | ''' |
|
556 | 557 | |
|
557 | 558 | args = parse_argstring(self.R, line) |
|
558 | 559 | |
|
559 | 560 | # arguments 'code' in line are prepended to |
|
560 | 561 | # the cell lines |
|
561 | 562 | |
|
562 | 563 | if cell is None: |
|
563 | 564 | code = '' |
|
564 | 565 | return_output = True |
|
565 | 566 | line_mode = True |
|
566 | 567 | else: |
|
567 | 568 | code = cell |
|
568 | 569 | return_output = False |
|
569 | 570 | line_mode = False |
|
570 | 571 | |
|
571 | 572 | code = ' '.join(args.code) + code |
|
572 | 573 | |
|
573 | 574 | # if there is no local namespace then default to an empty dict |
|
574 | 575 | if local_ns is None: |
|
575 | 576 | local_ns = {} |
|
576 | 577 | |
|
577 | 578 | if args.input: |
|
578 | 579 | for input in ','.join(args.input).split(','): |
|
579 | 580 | try: |
|
580 | 581 | val = local_ns[input] |
|
581 | 582 | except KeyError: |
|
582 | 583 | try: |
|
583 | 584 | val = self.shell.user_ns[input] |
|
584 | 585 | except KeyError: |
|
585 | 586 | raise NameError("name '%s' is not defined" % input) |
|
586 | 587 | self.r.assign(input, self.pyconverter(val)) |
|
587 | 588 | |
|
588 | 589 | if getattr(args, 'units') is not None: |
|
589 | 590 | if args.units != "px" and getattr(args, 'res') is None: |
|
590 | 591 | args.res = 72 |
|
591 | 592 | args.units = '"%s"' % args.units |
|
592 | 593 | |
|
593 | 594 | png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']]) |
|
594 | 595 | png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None]) |
|
595 | 596 | # execute the R code in a temporary directory |
|
596 | 597 | |
|
597 | 598 | tmpd = tempfile.mkdtemp() |
|
598 | 599 | self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args)) |
|
599 | 600 | |
|
600 | 601 | text_output = '' |
|
601 | 602 | try: |
|
602 | 603 | if line_mode: |
|
603 | 604 | for line in code.split(';'): |
|
604 | 605 | text_result, result, visible = self.eval(line) |
|
605 | 606 | text_output += text_result |
|
606 | 607 | if text_result: |
|
607 | 608 | # the last line printed something to the console so we won't return it |
|
608 | 609 | return_output = False |
|
609 | 610 | else: |
|
610 | 611 | text_result, result, visible = self.eval(code) |
|
611 | 612 | text_output += text_result |
|
612 | 613 | if visible: |
|
613 | 614 | old_writeconsole = ri.get_writeconsole() |
|
614 | 615 | ri.set_writeconsole(self.write_console) |
|
615 | 616 | ro.r.show(result) |
|
616 | 617 | text_output += self.flush() |
|
617 | 618 | ri.set_writeconsole(old_writeconsole) |
|
618 | 619 | |
|
619 | 620 | except RInterpreterError as e: |
|
620 | print(e.stdout) | |
|
621 | print((e.stdout)) | |
|
621 | 622 | if not e.stdout.endswith(e.err): |
|
622 | print(e.err) | |
|
623 | print((e.err)) | |
|
623 | 624 | rmtree(tmpd) |
|
624 | 625 | return |
|
625 | 626 | |
|
626 | 627 | self.r('dev.off()') |
|
627 | 628 | |
|
628 | 629 | # read out all the saved .png files |
|
629 | 630 | |
|
630 | 631 | images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)] |
|
631 | 632 | |
|
632 | 633 | # now publish the images |
|
633 | 634 | # mimicking IPython/zmq/pylab/backend_inline.py |
|
634 | 635 | fmt = 'png' |
|
635 | 636 | mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' } |
|
636 | 637 | mime = mimetypes[fmt] |
|
637 | 638 | |
|
638 | 639 | # publish the printed R objects, if any |
|
639 | 640 | |
|
640 | 641 | display_data = [] |
|
641 | 642 | if text_output: |
|
642 | 643 | display_data.append(('RMagic.R', {'text/plain':text_output})) |
|
643 | 644 | |
|
644 | 645 | # flush text streams before sending figures, helps a little with output |
|
645 | 646 | for image in images: |
|
646 | 647 | # synchronization in the console (though it's a bandaid, not a real sln) |
|
647 | 648 | sys.stdout.flush(); sys.stderr.flush() |
|
648 | 649 | display_data.append(('RMagic.R', {mime: image})) |
|
649 | 650 | |
|
650 | 651 | # kill the temporary directory |
|
651 | 652 | rmtree(tmpd) |
|
652 | 653 | |
|
653 | 654 | # try to turn every output into a numpy array |
|
654 | 655 | # this means that output are assumed to be castable |
|
655 | 656 | # as numpy arrays |
|
656 | 657 | |
|
657 | 658 | if args.output: |
|
658 | 659 | for output in ','.join(args.output).split(','): |
|
659 | 660 | self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)}) |
|
660 | 661 | |
|
661 | 662 | if args.dataframe: |
|
662 | 663 | for output in ','.join(args.dataframe).split(','): |
|
663 | 664 | self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)}) |
|
664 | 665 | |
|
665 | 666 | for tag, disp_d in display_data: |
|
666 | 667 | publish_display_data(tag, disp_d) |
|
667 | 668 | |
|
668 | 669 | # this will keep a reference to the display_data |
|
669 | 670 | # which might be useful to other objects who happen to use |
|
670 | 671 | # this method |
|
671 | 672 | |
|
672 | 673 | if self.cache_display_data: |
|
673 | 674 | self.display_cache = display_data |
|
674 | 675 | |
|
675 | 676 | # if in line mode and return_output, return the result as an ndarray |
|
676 | 677 | if return_output and not args.noreturn: |
|
677 | 678 | if result != ri.NULL: |
|
678 | 679 | return self.Rconverter(result, dataframe=False) |
|
679 | 680 | |
|
680 | 681 | __doc__ = __doc__.format( |
|
681 | 682 | R_DOC = ' '*8 + RMagics.R.__doc__, |
|
682 | 683 | RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__, |
|
683 | 684 | RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__, |
|
684 | 685 | RGET_DOC = ' '*8 + RMagics.Rget.__doc__ |
|
685 | 686 | ) |
|
686 | 687 | |
|
687 | 688 | |
|
688 | 689 | def load_ipython_extension(ip): |
|
689 | 690 | """Load the extension in IPython.""" |
|
690 | 691 | ip.register_magics(RMagics) |
|
691 | 692 | # Initialising rpy2 interferes with readline. Since, at this point, we've |
|
692 | 693 | # probably just loaded rpy2, we reset the delimiters. See issue gh-2759. |
|
693 | 694 | if ip.has_readline: |
|
694 | 695 | ip.readline.set_completer_delims(ip.readline_delims) |
@@ -1,241 +1,242 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """ |
|
3 | 3 | %store magic for lightweight persistence. |
|
4 | 4 | |
|
5 | 5 | Stores variables, aliases and macros in IPython's database. |
|
6 | 6 | |
|
7 | 7 | To automatically restore stored variables at startup, add this to your |
|
8 | 8 | :file:`ipython_config.py` file:: |
|
9 | 9 | |
|
10 | 10 | c.StoreMagic.autorestore = True |
|
11 | 11 | """ |
|
12 | from __future__ import print_function | |
|
12 | 13 | #----------------------------------------------------------------------------- |
|
13 | 14 | # Copyright (c) 2012, The IPython Development Team. |
|
14 | 15 | # |
|
15 | 16 | # Distributed under the terms of the Modified BSD License. |
|
16 | 17 | # |
|
17 | 18 | # The full license is in the file COPYING.txt, distributed with this software. |
|
18 | 19 | #----------------------------------------------------------------------------- |
|
19 | 20 | |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | # Imports |
|
22 | 23 | #----------------------------------------------------------------------------- |
|
23 | 24 | |
|
24 | 25 | # Stdlib |
|
25 | 26 | import inspect, os, sys, textwrap |
|
26 | 27 | |
|
27 | 28 | # Our own |
|
28 | 29 | from IPython.core.error import UsageError |
|
29 | 30 | from IPython.core.magic import Magics, magics_class, line_magic |
|
30 | 31 | from IPython.testing.skipdoctest import skip_doctest |
|
31 | 32 | from IPython.utils.traitlets import Bool |
|
32 | 33 | |
|
33 | 34 | #----------------------------------------------------------------------------- |
|
34 | 35 | # Functions and classes |
|
35 | 36 | #----------------------------------------------------------------------------- |
|
36 | 37 | |
|
37 | 38 | def restore_aliases(ip): |
|
38 | 39 | staliases = ip.db.get('stored_aliases', {}) |
|
39 | 40 | for k,v in staliases.items(): |
|
40 | 41 | #print "restore alias",k,v # dbg |
|
41 | 42 | #self.alias_table[k] = v |
|
42 | 43 | ip.alias_manager.define_alias(k,v) |
|
43 | 44 | |
|
44 | 45 | |
|
45 | 46 | def refresh_variables(ip): |
|
46 | 47 | db = ip.db |
|
47 | 48 | for key in db.keys('autorestore/*'): |
|
48 | 49 | # strip autorestore |
|
49 | 50 | justkey = os.path.basename(key) |
|
50 | 51 | try: |
|
51 | 52 | obj = db[key] |
|
52 | 53 | except KeyError: |
|
53 |
print |
|
|
54 |
print |
|
|
54 | print("Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey) | |
|
55 | print("The error was:", sys.exc_info()[0]) | |
|
55 | 56 | else: |
|
56 | 57 | #print "restored",justkey,"=",obj #dbg |
|
57 | 58 | ip.user_ns[justkey] = obj |
|
58 | 59 | |
|
59 | 60 | |
|
60 | 61 | def restore_dhist(ip): |
|
61 | 62 | ip.user_ns['_dh'] = ip.db.get('dhist',[]) |
|
62 | 63 | |
|
63 | 64 | |
|
64 | 65 | def restore_data(ip): |
|
65 | 66 | refresh_variables(ip) |
|
66 | 67 | restore_aliases(ip) |
|
67 | 68 | restore_dhist(ip) |
|
68 | 69 | |
|
69 | 70 | |
|
70 | 71 | @magics_class |
|
71 | 72 | class StoreMagics(Magics): |
|
72 | 73 | """Lightweight persistence for python variables. |
|
73 | 74 | |
|
74 | 75 | Provides the %store magic.""" |
|
75 | 76 | |
|
76 | 77 | autorestore = Bool(False, config=True, help= |
|
77 | 78 | """If True, any %store-d variables will be automatically restored |
|
78 | 79 | when IPython starts. |
|
79 | 80 | """ |
|
80 | 81 | ) |
|
81 | 82 | |
|
82 | 83 | def __init__(self, shell): |
|
83 | 84 | super(StoreMagics, self).__init__(shell=shell) |
|
84 | 85 | self.shell.configurables.append(self) |
|
85 | 86 | if self.autorestore: |
|
86 | 87 | restore_data(self.shell) |
|
87 | 88 | |
|
88 | 89 | @skip_doctest |
|
89 | 90 | @line_magic |
|
90 | 91 | def store(self, parameter_s=''): |
|
91 | 92 | """Lightweight persistence for python variables. |
|
92 | 93 | |
|
93 | 94 | Example:: |
|
94 | 95 | |
|
95 | 96 | In [1]: l = ['hello',10,'world'] |
|
96 | 97 | In [2]: %store l |
|
97 | 98 | In [3]: exit |
|
98 | 99 | |
|
99 | 100 | (IPython session is closed and started again...) |
|
100 | 101 | |
|
101 | 102 | ville@badger:~$ ipython |
|
102 | 103 | In [1]: l |
|
103 | 104 | NameError: name 'l' is not defined |
|
104 | 105 | In [2]: %store -r |
|
105 | 106 | In [3]: l |
|
106 | 107 | Out[3]: ['hello', 10, 'world'] |
|
107 | 108 | |
|
108 | 109 | Usage: |
|
109 | 110 | |
|
110 | 111 | * ``%store`` - Show list of all variables and their current |
|
111 | 112 | values |
|
112 | 113 | * ``%store spam`` - Store the *current* value of the variable spam |
|
113 | 114 | to disk |
|
114 | 115 | * ``%store -d spam`` - Remove the variable and its value from storage |
|
115 | 116 | * ``%store -z`` - Remove all variables from storage |
|
116 | 117 | * ``%store -r`` - Refresh all variables from store (overwrite |
|
117 | 118 | current vals) |
|
118 | 119 | * ``%store -r spam bar`` - Refresh specified variables from store |
|
119 | 120 | (delete current val) |
|
120 | 121 | * ``%store foo >a.txt`` - Store value of foo to new file a.txt |
|
121 | 122 | * ``%store foo >>a.txt`` - Append value of foo to file a.txt |
|
122 | 123 | |
|
123 | 124 | It should be noted that if you change the value of a variable, you |
|
124 | 125 | need to %store it again if you want to persist the new value. |
|
125 | 126 | |
|
126 | 127 | Note also that the variables will need to be pickleable; most basic |
|
127 | 128 | python types can be safely %store'd. |
|
128 | 129 | |
|
129 | 130 | Also aliases can be %store'd across sessions. |
|
130 | 131 | """ |
|
131 | 132 | |
|
132 | 133 | opts,argsl = self.parse_options(parameter_s,'drz',mode='string') |
|
133 | 134 | args = argsl.split(None,1) |
|
134 | 135 | ip = self.shell |
|
135 | 136 | db = ip.db |
|
136 | 137 | # delete |
|
137 | 138 | if 'd' in opts: |
|
138 | 139 | try: |
|
139 | 140 | todel = args[0] |
|
140 | 141 | except IndexError: |
|
141 | 142 | raise UsageError('You must provide the variable to forget') |
|
142 | 143 | else: |
|
143 | 144 | try: |
|
144 | 145 | del db['autorestore/' + todel] |
|
145 | 146 | except: |
|
146 | 147 | raise UsageError("Can't delete variable '%s'" % todel) |
|
147 | 148 | # reset |
|
148 | 149 | elif 'z' in opts: |
|
149 | 150 | for k in db.keys('autorestore/*'): |
|
150 | 151 | del db[k] |
|
151 | 152 | |
|
152 | 153 | elif 'r' in opts: |
|
153 | 154 | if args: |
|
154 | 155 | for arg in args: |
|
155 | 156 | try: |
|
156 | 157 | obj = db['autorestore/' + arg] |
|
157 | 158 | except KeyError: |
|
158 |
print |
|
|
159 | print("no stored variable %s" % arg) | |
|
159 | 160 | else: |
|
160 | 161 | ip.user_ns[arg] = obj |
|
161 | 162 | else: |
|
162 | 163 | restore_data(ip) |
|
163 | 164 | |
|
164 | 165 | # run without arguments -> list variables & values |
|
165 | 166 | elif not args: |
|
166 | 167 | vars = db.keys('autorestore/*') |
|
167 | 168 | vars.sort() |
|
168 | 169 | if vars: |
|
169 | 170 | size = max(map(len, vars)) |
|
170 | 171 | else: |
|
171 | 172 | size = 0 |
|
172 | 173 | |
|
173 |
print |
|
|
174 | print('Stored variables and their in-db values:') | |
|
174 | 175 | fmt = '%-'+str(size)+'s -> %s' |
|
175 | 176 | get = db.get |
|
176 | 177 | for var in vars: |
|
177 | 178 | justkey = os.path.basename(var) |
|
178 | 179 | # print 30 first characters from every var |
|
179 |
print |
|
|
180 | print(fmt % (justkey, repr(get(var, '<unavailable>'))[:50])) | |
|
180 | 181 | |
|
181 | 182 | # default action - store the variable |
|
182 | 183 | else: |
|
183 | 184 | # %store foo >file.txt or >>file.txt |
|
184 | 185 | if len(args) > 1 and args[1].startswith('>'): |
|
185 | 186 | fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) |
|
186 | 187 | if args[1].startswith('>>'): |
|
187 | 188 | fil = open(fnam, 'a') |
|
188 | 189 | else: |
|
189 | 190 | fil = open(fnam, 'w') |
|
190 | 191 | obj = ip.ev(args[0]) |
|
191 |
print |
|
|
192 | obj.__class__.__name__, fnam) | |
|
192 | print("Writing '%s' (%s) to file '%s'." % (args[0], | |
|
193 | obj.__class__.__name__, fnam)) | |
|
193 | 194 | |
|
194 | 195 | |
|
195 | 196 | if not isinstance (obj, basestring): |
|
196 | 197 | from pprint import pprint |
|
197 | 198 | pprint(obj, fil) |
|
198 | 199 | else: |
|
199 | 200 | fil.write(obj) |
|
200 | 201 | if not obj.endswith('\n'): |
|
201 | 202 | fil.write('\n') |
|
202 | 203 | |
|
203 | 204 | fil.close() |
|
204 | 205 | return |
|
205 | 206 | |
|
206 | 207 | # %store foo |
|
207 | 208 | try: |
|
208 | 209 | obj = ip.user_ns[args[0]] |
|
209 | 210 | except KeyError: |
|
210 | 211 | # it might be an alias |
|
211 | 212 | name = args[0] |
|
212 | 213 | try: |
|
213 | 214 | cmd = ip.alias_manager.retrieve_alias(name) |
|
214 | 215 | except ValueError: |
|
215 | 216 | raise UsageError("Unknown variable '%s'" % name) |
|
216 | 217 | |
|
217 | 218 | staliases = db.get('stored_aliases',{}) |
|
218 | 219 | staliases[name] = cmd |
|
219 | 220 | db['stored_aliases'] = staliases |
|
220 |
print |
|
|
221 | print("Alias stored: %s (%s)" % (name, cmd)) | |
|
221 | 222 | return |
|
222 | 223 | |
|
223 | 224 | else: |
|
224 | 225 | modname = getattr(inspect.getmodule(obj), '__name__', '') |
|
225 | 226 | if modname == '__main__': |
|
226 |
print |
|
|
227 | print(textwrap.dedent("""\ | |
|
227 | 228 | Warning:%s is %s |
|
228 | 229 | Proper storage of interactively declared classes (or instances |
|
229 | 230 | of those classes) is not possible! Only instances |
|
230 | 231 | of classes in real modules on file system can be %%store'd. |
|
231 | """ % (args[0], obj) ) | |
|
232 | """ % (args[0], obj) )) | |
|
232 | 233 | return |
|
233 | 234 | #pickled = pickle.dumps(obj) |
|
234 | 235 | db[ 'autorestore/' + args[0] ] = obj |
|
235 |
print |
|
|
236 | print("Stored '%s' (%s)" % (args[0], obj.__class__.__name__)) | |
|
236 | 237 | |
|
237 | 238 | |
|
238 | 239 | def load_ipython_extension(ip): |
|
239 | 240 | """Load the extension in IPython.""" |
|
240 | 241 | ip.register_magics(StoreMagics) |
|
241 | 242 |
@@ -1,220 +1,221 b'' | |||
|
1 | 1 | ########################## LICENCE ############################### |
|
2 | 2 | |
|
3 | 3 | # Copyright (c) 2005-2012, Michele Simionato |
|
4 | 4 | # All rights reserved. |
|
5 | 5 | |
|
6 | 6 | # Redistribution and use in source and binary forms, with or without |
|
7 | 7 | # modification, are permitted provided that the following conditions are |
|
8 | 8 | # met: |
|
9 | 9 | |
|
10 | 10 | # Redistributions of source code must retain the above copyright |
|
11 | 11 | # notice, this list of conditions and the following disclaimer. |
|
12 | 12 | # Redistributions in bytecode form must reproduce the above copyright |
|
13 | 13 | # notice, this list of conditions and the following disclaimer in |
|
14 | 14 | # the documentation and/or other materials provided with the |
|
15 | 15 | # distribution. |
|
16 | 16 | |
|
17 | 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
18 | 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
19 | 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
20 | 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
21 | 21 | # HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
22 | 22 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
23 | 23 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|
24 | 24 | # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
25 | 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
26 | 26 | # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
|
27 | 27 | # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
|
28 | 28 | # DAMAGE. |
|
29 | 29 | |
|
30 | 30 | """ |
|
31 | 31 | Decorator module, see http://pypi.python.org/pypi/decorator |
|
32 | 32 | for the documentation. |
|
33 | 33 | """ |
|
34 | from __future__ import print_function | |
|
34 | 35 | |
|
35 | 36 | __version__ = '3.3.3' |
|
36 | 37 | |
|
37 | 38 | __all__ = ["decorator", "FunctionMaker", "partial"] |
|
38 | 39 | |
|
39 | 40 | import sys, re, inspect |
|
40 | 41 | |
|
41 | 42 | try: |
|
42 | 43 | from functools import partial |
|
43 | 44 | except ImportError: # for Python version < 2.5 |
|
44 | 45 | class partial(object): |
|
45 | 46 | "A simple replacement of functools.partial" |
|
46 | 47 | def __init__(self, func, *args, **kw): |
|
47 | 48 | self.func = func |
|
48 | 49 | self.args = args |
|
49 | 50 | self.keywords = kw |
|
50 | 51 | def __call__(self, *otherargs, **otherkw): |
|
51 | 52 | kw = self.keywords.copy() |
|
52 | 53 | kw.update(otherkw) |
|
53 | 54 | return self.func(*(self.args + otherargs), **kw) |
|
54 | 55 | |
|
55 | 56 | if sys.version >= '3': |
|
56 | 57 | from inspect import getfullargspec |
|
57 | 58 | else: |
|
58 | 59 | class getfullargspec(object): |
|
59 | 60 | "A quick and dirty replacement for getfullargspec for Python 2.X" |
|
60 | 61 | def __init__(self, f): |
|
61 | 62 | self.args, self.varargs, self.varkw, self.defaults = \ |
|
62 | 63 | inspect.getargspec(f) |
|
63 | 64 | self.kwonlyargs = [] |
|
64 | 65 | self.kwonlydefaults = None |
|
65 | 66 | def __iter__(self): |
|
66 | 67 | yield self.args |
|
67 | 68 | yield self.varargs |
|
68 | 69 | yield self.varkw |
|
69 | 70 | yield self.defaults |
|
70 | 71 | |
|
71 | 72 | DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') |
|
72 | 73 | |
|
73 | 74 | # basic functionality |
|
74 | 75 | class FunctionMaker(object): |
|
75 | 76 | """ |
|
76 | 77 | An object with the ability to create functions with a given signature. |
|
77 | 78 | It has attributes name, doc, module, signature, defaults, dict and |
|
78 | 79 | methods update and make. |
|
79 | 80 | """ |
|
80 | 81 | def __init__(self, func=None, name=None, signature=None, |
|
81 | 82 | defaults=None, doc=None, module=None, funcdict=None): |
|
82 | 83 | self.shortsignature = signature |
|
83 | 84 | if func: |
|
84 | 85 | # func can be a class or a callable, but not an instance method |
|
85 | 86 | self.name = func.__name__ |
|
86 | 87 | if self.name == '<lambda>': # small hack for lambda functions |
|
87 | 88 | self.name = '_lambda_' |
|
88 | 89 | self.doc = func.__doc__ |
|
89 | 90 | self.module = func.__module__ |
|
90 | 91 | if inspect.isfunction(func): |
|
91 | 92 | argspec = getfullargspec(func) |
|
92 | 93 | self.annotations = getattr(func, '__annotations__', {}) |
|
93 | 94 | for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', |
|
94 | 95 | 'kwonlydefaults'): |
|
95 | 96 | setattr(self, a, getattr(argspec, a)) |
|
96 | 97 | for i, arg in enumerate(self.args): |
|
97 | 98 | setattr(self, 'arg%d' % i, arg) |
|
98 | 99 | if sys.version < '3': # easy way |
|
99 | 100 | self.shortsignature = self.signature = \ |
|
100 | 101 | inspect.formatargspec( |
|
101 | 102 | formatvalue=lambda val: "", *argspec)[1:-1] |
|
102 | 103 | else: # Python 3 way |
|
103 | 104 | self.signature = self.shortsignature = ', '.join(self.args) |
|
104 | 105 | if self.varargs: |
|
105 | 106 | self.signature += ', *' + self.varargs |
|
106 | 107 | self.shortsignature += ', *' + self.varargs |
|
107 | 108 | if self.kwonlyargs: |
|
108 | 109 | for a in self.kwonlyargs: |
|
109 | 110 | self.signature += ', %s=None' % a |
|
110 | 111 | self.shortsignature += ', %s=%s' % (a, a) |
|
111 | 112 | if self.varkw: |
|
112 | 113 | self.signature += ', **' + self.varkw |
|
113 | 114 | self.shortsignature += ', **' + self.varkw |
|
114 | 115 | self.dict = func.__dict__.copy() |
|
115 | 116 | # func=None happens when decorating a caller |
|
116 | 117 | if name: |
|
117 | 118 | self.name = name |
|
118 | 119 | if signature is not None: |
|
119 | 120 | self.signature = signature |
|
120 | 121 | if defaults: |
|
121 | 122 | self.defaults = defaults |
|
122 | 123 | if doc: |
|
123 | 124 | self.doc = doc |
|
124 | 125 | if module: |
|
125 | 126 | self.module = module |
|
126 | 127 | if funcdict: |
|
127 | 128 | self.dict = funcdict |
|
128 | 129 | # check existence required attributes |
|
129 | 130 | assert hasattr(self, 'name') |
|
130 | 131 | if not hasattr(self, 'signature'): |
|
131 | 132 | raise TypeError('You are decorating a non function: %s' % func) |
|
132 | 133 | |
|
133 | 134 | def update(self, func, **kw): |
|
134 | 135 | "Update the signature of func with the data in self" |
|
135 | 136 | func.__name__ = self.name |
|
136 | 137 | func.__doc__ = getattr(self, 'doc', None) |
|
137 | 138 | func.__dict__ = getattr(self, 'dict', {}) |
|
138 | 139 | func.func_defaults = getattr(self, 'defaults', ()) |
|
139 | 140 | func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None) |
|
140 | 141 | func.__annotations__ = getattr(self, 'annotations', None) |
|
141 | 142 | callermodule = sys._getframe(3).f_globals.get('__name__', '?') |
|
142 | 143 | func.__module__ = getattr(self, 'module', callermodule) |
|
143 | 144 | func.__dict__.update(kw) |
|
144 | 145 | |
|
145 | 146 | def make(self, src_templ, evaldict=None, addsource=False, **attrs): |
|
146 | 147 | "Make a new function from a given template and update the signature" |
|
147 | 148 | src = src_templ % vars(self) # expand name and signature |
|
148 | 149 | evaldict = evaldict or {} |
|
149 | 150 | mo = DEF.match(src) |
|
150 | 151 | if mo is None: |
|
151 | 152 | raise SyntaxError('not a valid function template\n%s' % src) |
|
152 | 153 | name = mo.group(1) # extract the function name |
|
153 | 154 | names = set([name] + [arg.strip(' *') for arg in |
|
154 | 155 | self.shortsignature.split(',')]) |
|
155 | 156 | for n in names: |
|
156 | 157 | if n in ('_func_', '_call_'): |
|
157 | 158 | raise NameError('%s is overridden in\n%s' % (n, src)) |
|
158 | 159 | if not src.endswith('\n'): # add a newline just for safety |
|
159 | 160 | src += '\n' # this is needed in old versions of Python |
|
160 | 161 | try: |
|
161 | 162 | code = compile(src, '<string>', 'single') |
|
162 | 163 | # print >> sys.stderr, 'Compiling %s' % src |
|
163 | 164 | exec code in evaldict |
|
164 | 165 | except: |
|
165 |
print |
|
|
166 |
print |
|
|
166 | print('Error in generated code:', file=sys.stderr) | |
|
167 | print(src, file=sys.stderr) | |
|
167 | 168 | raise |
|
168 | 169 | func = evaldict[name] |
|
169 | 170 | if addsource: |
|
170 | 171 | attrs['__source__'] = src |
|
171 | 172 | self.update(func, **attrs) |
|
172 | 173 | return func |
|
173 | 174 | |
|
174 | 175 | @classmethod |
|
175 | 176 | def create(cls, obj, body, evaldict, defaults=None, |
|
176 | 177 | doc=None, module=None, addsource=True, **attrs): |
|
177 | 178 | """ |
|
178 | 179 | Create a function from the strings name, signature and body. |
|
179 | 180 | evaldict is the evaluation dictionary. If addsource is true an attribute |
|
180 | 181 | __source__ is added to the result. The attributes attrs are added, |
|
181 | 182 | if any. |
|
182 | 183 | """ |
|
183 | 184 | if isinstance(obj, str): # "name(signature)" |
|
184 | 185 | name, rest = obj.strip().split('(', 1) |
|
185 | 186 | signature = rest[:-1] #strip a right parens |
|
186 | 187 | func = None |
|
187 | 188 | else: # a function |
|
188 | 189 | name = None |
|
189 | 190 | signature = None |
|
190 | 191 | func = obj |
|
191 | 192 | self = cls(func, name, signature, defaults, doc, module) |
|
192 | 193 | ibody = '\n'.join(' ' + line for line in body.splitlines()) |
|
193 | 194 | return self.make('def %(name)s(%(signature)s):\n' + ibody, |
|
194 | 195 | evaldict, addsource, **attrs) |
|
195 | 196 | |
|
196 | 197 | def decorator(caller, func=None): |
|
197 | 198 | """ |
|
198 | 199 | decorator(caller) converts a caller function into a decorator; |
|
199 | 200 | decorator(caller, func) decorates a function using a caller. |
|
200 | 201 | """ |
|
201 | 202 | if func is not None: # returns a decorated function |
|
202 | 203 | evaldict = func.func_globals.copy() |
|
203 | 204 | evaldict['_call_'] = caller |
|
204 | 205 | evaldict['_func_'] = func |
|
205 | 206 | return FunctionMaker.create( |
|
206 | 207 | func, "return _call_(_func_, %(shortsignature)s)", |
|
207 | 208 | evaldict, undecorated=func, __wrapped__=func) |
|
208 | 209 | else: # returns a decorator |
|
209 | 210 | if isinstance(caller, partial): |
|
210 | 211 | return partial(decorator, caller) |
|
211 | 212 | # otherwise assume caller is a function |
|
212 | 213 | first = inspect.getargspec(caller)[0][0] # first arg |
|
213 | 214 | evaldict = caller.func_globals.copy() |
|
214 | 215 | evaldict['_call_'] = caller |
|
215 | 216 | evaldict['decorator'] = decorator |
|
216 | 217 | return FunctionMaker.create( |
|
217 | 218 | '%s(%s)' % (caller.__name__, first), |
|
218 | 219 | 'return decorator(_call_, %s)' % first, |
|
219 | 220 | evaldict, undecorated=caller, __wrapped__=caller, |
|
220 | 221 | doc=caller.__doc__, module=caller.__module__) |
@@ -1,233 +1,234 b'' | |||
|
1 | 1 | #!/usr/bin/python |
|
2 | 2 | """Utility function for installing MathJax javascript library into |
|
3 | 3 | your IPython nbextensions directory, for offline use. |
|
4 | 4 | |
|
5 | 5 | Authors: |
|
6 | 6 | |
|
7 | 7 | * Min RK |
|
8 | 8 | * Mark Sienkiewicz |
|
9 | 9 | * Matthias Bussonnier |
|
10 | 10 | |
|
11 | 11 | To download and install MathJax: |
|
12 | 12 | |
|
13 | 13 | From Python: |
|
14 | 14 | |
|
15 | 15 | >>> from IPython.external.mathjax import install_mathjax |
|
16 | 16 | >>> install_mathjax() |
|
17 | 17 | |
|
18 | 18 | From the command line: |
|
19 | 19 | |
|
20 | 20 | $ python -m IPython.external.mathjax |
|
21 | 21 | |
|
22 | 22 | To a specific location: |
|
23 | 23 | |
|
24 | 24 | $ python -m IPython.external.mathjax -i /usr/share/ |
|
25 | 25 | |
|
26 | 26 | will install mathjax to /usr/share/mathjax |
|
27 | 27 | |
|
28 | 28 | To install MathJax from a file you have already downloaded: |
|
29 | 29 | |
|
30 | 30 | $ python -m IPython.external.mathjax mathjax-xxx.tar.gz |
|
31 | 31 | $ python -m IPython.external.mathjax mathjax-xxx.zip |
|
32 | 32 | |
|
33 | 33 | It will not install MathJax if it is already there. Use -r to |
|
34 | 34 | replace the existing copy of MathJax. |
|
35 | 35 | |
|
36 | 36 | To find the directory where IPython would like MathJax installed: |
|
37 | 37 | |
|
38 | 38 | $ python -m IPython.external.mathjax -d |
|
39 | 39 | |
|
40 | 40 | """ |
|
41 | from __future__ import print_function | |
|
41 | 42 | |
|
42 | 43 | |
|
43 | 44 | #----------------------------------------------------------------------------- |
|
44 | 45 | # Copyright (C) 2011 The IPython Development Team |
|
45 | 46 | # |
|
46 | 47 | # Distributed under the terms of the BSD License. The full license is in |
|
47 | 48 | # the file COPYING, distributed as part of this software. |
|
48 | 49 | #----------------------------------------------------------------------------- |
|
49 | 50 | |
|
50 | 51 | |
|
51 | 52 | #----------------------------------------------------------------------------- |
|
52 | 53 | # Imports |
|
53 | 54 | #----------------------------------------------------------------------------- |
|
54 | 55 | |
|
55 | 56 | import argparse |
|
56 | 57 | import os |
|
57 | 58 | import shutil |
|
58 | 59 | import sys |
|
59 | 60 | import tarfile |
|
60 | 61 | import urllib2 |
|
61 | 62 | import zipfile |
|
62 | 63 | |
|
63 | 64 | from IPython.utils.path import get_ipython_dir |
|
64 | 65 | |
|
65 | 66 | #----------------------------------------------------------------------------- |
|
66 | 67 | # |
|
67 | 68 | #----------------------------------------------------------------------------- |
|
68 | 69 | |
|
69 | 70 | # Where mathjax will be installed |
|
70 | 71 | |
|
71 | 72 | nbextensions = os.path.join(get_ipython_dir(), 'nbextensions') |
|
72 | 73 | default_dest = os.path.join(nbextensions, 'mathjax') |
|
73 | 74 | |
|
74 | 75 | # Test for access to install mathjax |
|
75 | 76 | |
|
76 | 77 | def prepare_dest(dest, replace=False): |
|
77 | 78 | """prepare the destination folder for mathjax install |
|
78 | 79 | |
|
79 | 80 | Returns False if mathjax appears to already be installed and there is nothing to do, |
|
80 | 81 | True otherwise. |
|
81 | 82 | """ |
|
82 | 83 | |
|
83 | 84 | parent = os.path.abspath(os.path.join(dest, os.path.pardir)) |
|
84 | 85 | if not os.path.exists(parent): |
|
85 | 86 | os.makedirs(parent) |
|
86 | 87 | |
|
87 | 88 | if os.path.exists(dest): |
|
88 | 89 | if replace: |
|
89 |
print |
|
|
90 | print("removing existing MathJax at %s" % dest) | |
|
90 | 91 | shutil.rmtree(dest) |
|
91 | 92 | return True |
|
92 | 93 | else: |
|
93 | 94 | mathjax_js = os.path.join(dest, 'MathJax.js') |
|
94 | 95 | if not os.path.exists(mathjax_js): |
|
95 | 96 | raise IOError("%s exists, but does not contain MathJax.js" % dest) |
|
96 |
print |
|
|
97 | print("%s already exists" % mathjax_js) | |
|
97 | 98 | return False |
|
98 | 99 | else: |
|
99 | 100 | return True |
|
100 | 101 | |
|
101 | 102 | |
|
102 | 103 | def extract_tar(fd, dest): |
|
103 | 104 | """extract a tarball from filelike `fd` to destination `dest`""" |
|
104 | 105 | # use 'r|gz' stream mode, because socket file-like objects can't seek: |
|
105 | 106 | tar = tarfile.open(fileobj=fd, mode='r|gz') |
|
106 | 107 | |
|
107 | 108 | # The first entry in the archive is the top-level dir |
|
108 | 109 | topdir = tar.firstmember.path |
|
109 | 110 | |
|
110 | 111 | # extract the archive (contains a single directory) to the destination directory |
|
111 | 112 | parent = os.path.abspath(os.path.join(dest, os.path.pardir)) |
|
112 | 113 | tar.extractall(parent) |
|
113 | 114 | |
|
114 | 115 | # it will be mathjax-MathJax-<sha>, rename to just mathjax |
|
115 | 116 | os.rename(os.path.join(parent, topdir), dest) |
|
116 | 117 | |
|
117 | 118 | |
|
118 | 119 | def extract_zip(fd, dest): |
|
119 | 120 | """extract a zip file from filelike `fd` to destination `dest`""" |
|
120 | 121 | z = zipfile.ZipFile(fd, 'r') |
|
121 | 122 | |
|
122 | 123 | # The first entry in the archive is the top-level dir |
|
123 | 124 | topdir = z.namelist()[0] |
|
124 | 125 | |
|
125 | 126 | # extract the archive (contains a single directory) to the static/ directory |
|
126 | 127 | parent = os.path.abspath(os.path.join(dest, os.path.pardir)) |
|
127 | 128 | z.extractall(parent) |
|
128 | 129 | |
|
129 | 130 | # it will be mathjax-MathJax-<sha>, rename to just mathjax |
|
130 | 131 | d = os.path.join(parent, topdir) |
|
131 | 132 | os.rename(os.path.join(parent, topdir), dest) |
|
132 | 133 | |
|
133 | 134 | |
|
134 | 135 | def install_mathjax(tag='v2.2', dest=default_dest, replace=False, file=None, extractor=extract_tar): |
|
135 | 136 | """Download and/or install MathJax for offline use. |
|
136 | 137 | |
|
137 | 138 | This will install mathjax to the nbextensions dir in your IPYTHONDIR. |
|
138 | 139 | |
|
139 | 140 | MathJax is a ~15MB download, and ~150MB installed. |
|
140 | 141 | |
|
141 | 142 | Parameters |
|
142 | 143 | ---------- |
|
143 | 144 | |
|
144 | 145 | replace : bool [False] |
|
145 | 146 | Whether to remove and replace an existing install. |
|
146 | 147 | dest : str [IPYTHONDIR/nbextensions/mathjax] |
|
147 | 148 | Where to install mathjax |
|
148 | 149 | tag : str ['v2.2'] |
|
149 | 150 | Which tag to download. Default is 'v2.2', the current stable release, |
|
150 | 151 | but alternatives include 'v1.1a' and 'master'. |
|
151 | 152 | file : file like object [ defualt to content of https://github.com/mathjax/MathJax/tarball/#{tag}] |
|
152 | 153 | File handle from which to untar/unzip/... mathjax |
|
153 | 154 | extractor : function |
|
154 | 155 | Method to use to untar/unzip/... `file` |
|
155 | 156 | """ |
|
156 | 157 | try: |
|
157 | 158 | anything_to_do = prepare_dest(dest, replace) |
|
158 | 159 | except OSError as e: |
|
159 | print("ERROR %s, require write access to %s" % (e, dest)) | |
|
160 | print(("ERROR %s, require write access to %s" % (e, dest))) | |
|
160 | 161 | return 1 |
|
161 | 162 | else: |
|
162 | 163 | if not anything_to_do: |
|
163 | 164 | return 0 |
|
164 | 165 | |
|
165 | 166 | if file is None: |
|
166 | 167 | # download mathjax |
|
167 | 168 | mathjax_url = "https://github.com/mathjax/MathJax/archive/%s.tar.gz" %tag |
|
168 |
print |
|
|
169 | print("Downloading mathjax source from %s" % mathjax_url) | |
|
169 | 170 | response = urllib2.urlopen(mathjax_url) |
|
170 | 171 | file = response.fp |
|
171 | 172 | |
|
172 |
print |
|
|
173 | print("Extracting to %s" % dest) | |
|
173 | 174 | extractor(file, dest) |
|
174 | 175 | return 0 |
|
175 | 176 | |
|
176 | 177 | |
|
177 | 178 | def main(): |
|
178 | 179 | parser = argparse.ArgumentParser( |
|
179 | 180 | description="""Install mathjax from internet or local archive""", |
|
180 | 181 | ) |
|
181 | 182 | |
|
182 | 183 | parser.add_argument( |
|
183 | 184 | '-i', |
|
184 | 185 | '--install-dir', |
|
185 | 186 | default=nbextensions, |
|
186 | 187 | help='custom installation directory. Mathjax will be installed in here/mathjax') |
|
187 | 188 | |
|
188 | 189 | parser.add_argument( |
|
189 | 190 | '-d', |
|
190 | 191 | '--print-dest', |
|
191 | 192 | action='store_true', |
|
192 | 193 | help='print where mathjax would be installed and exit') |
|
193 | 194 | parser.add_argument( |
|
194 | 195 | '-r', |
|
195 | 196 | '--replace', |
|
196 | 197 | action='store_true', |
|
197 | 198 | help='Whether to replace current mathjax if it already exists') |
|
198 | 199 | parser.add_argument('filename', |
|
199 | 200 | help="the local tar/zip-ball filename containing mathjax", |
|
200 | 201 | nargs='?', |
|
201 | 202 | metavar='filename') |
|
202 | 203 | |
|
203 | 204 | pargs = parser.parse_args() |
|
204 | 205 | |
|
205 | 206 | dest = os.path.join(pargs.install_dir, 'mathjax') |
|
206 | 207 | |
|
207 | 208 | if pargs.print_dest: |
|
208 |
print |
|
|
209 | print(dest) | |
|
209 | 210 | return |
|
210 | 211 | |
|
211 | 212 | # remove/replace existing mathjax? |
|
212 | 213 | replace = pargs.replace |
|
213 | 214 | |
|
214 | 215 | # do it |
|
215 | 216 | if pargs.filename: |
|
216 | 217 | fname = pargs.filename |
|
217 | 218 | |
|
218 | 219 | # automatically detect zip/tar - could do something based |
|
219 | 220 | # on file content, but really not cost-effective here. |
|
220 | 221 | if fname.endswith('.zip'): |
|
221 | 222 | extractor = extract_zip |
|
222 | 223 | else : |
|
223 | 224 | extractor = extract_tar |
|
224 | 225 | # do it |
|
225 | 226 | return install_mathjax(file=open(fname, "rb"), replace=replace, extractor=extractor, dest=dest) |
|
226 | 227 | else: |
|
227 | 228 | return install_mathjax(replace=replace, dest=dest) |
|
228 | 229 | |
|
229 | 230 | |
|
230 | 231 | if __name__ == '__main__' : |
|
231 | 232 | sys.exit(main()) |
|
232 | 233 | |
|
233 | 234 | __all__ = ['install_mathjax', 'main', 'default_dest'] |
@@ -1,747 +1,748 b'' | |||
|
1 | 1 | # coding: utf-8 |
|
2 | 2 | """A tornado based IPython notebook server. |
|
3 | 3 | |
|
4 | 4 | Authors: |
|
5 | 5 | |
|
6 | 6 | * Brian Granger |
|
7 | 7 | """ |
|
8 | from __future__ import print_function | |
|
8 | 9 | #----------------------------------------------------------------------------- |
|
9 | 10 | # Copyright (C) 2013 The IPython Development Team |
|
10 | 11 | # |
|
11 | 12 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | 13 | # the file COPYING, distributed as part of this software. |
|
13 | 14 | #----------------------------------------------------------------------------- |
|
14 | 15 | |
|
15 | 16 | #----------------------------------------------------------------------------- |
|
16 | 17 | # Imports |
|
17 | 18 | #----------------------------------------------------------------------------- |
|
18 | 19 | |
|
19 | 20 | # stdlib |
|
20 | 21 | import errno |
|
21 | 22 | import logging |
|
22 | 23 | import os |
|
23 | 24 | import random |
|
24 | 25 | import select |
|
25 | 26 | import signal |
|
26 | 27 | import socket |
|
27 | 28 | import sys |
|
28 | 29 | import threading |
|
29 | 30 | import time |
|
30 | 31 | import webbrowser |
|
31 | 32 | |
|
32 | 33 | |
|
33 | 34 | # Third party |
|
34 | 35 | # check for pyzmq 2.1.11 |
|
35 | 36 | from IPython.utils.zmqrelated import check_for_zmq |
|
36 | 37 | check_for_zmq('2.1.11', 'IPython.html') |
|
37 | 38 | |
|
38 | 39 | from jinja2 import Environment, FileSystemLoader |
|
39 | 40 | |
|
40 | 41 | # Install the pyzmq ioloop. This has to be done before anything else from |
|
41 | 42 | # tornado is imported. |
|
42 | 43 | from zmq.eventloop import ioloop |
|
43 | 44 | ioloop.install() |
|
44 | 45 | |
|
45 | 46 | # check for tornado 3.1.0 |
|
46 | 47 | msg = "The IPython Notebook requires tornado >= 3.1.0" |
|
47 | 48 | try: |
|
48 | 49 | import tornado |
|
49 | 50 | except ImportError: |
|
50 | 51 | raise ImportError(msg) |
|
51 | 52 | try: |
|
52 | 53 | version_info = tornado.version_info |
|
53 | 54 | except AttributeError: |
|
54 | 55 | raise ImportError(msg + ", but you have < 1.1.0") |
|
55 | 56 | if version_info < (3,1,0): |
|
56 | 57 | raise ImportError(msg + ", but you have %s" % tornado.version) |
|
57 | 58 | |
|
58 | 59 | from tornado import httpserver |
|
59 | 60 | from tornado import web |
|
60 | 61 | |
|
61 | 62 | # Our own libraries |
|
62 | 63 | from IPython.html import DEFAULT_STATIC_FILES_PATH |
|
63 | 64 | |
|
64 | 65 | from .services.kernels.kernelmanager import MappingKernelManager |
|
65 | 66 | from .services.notebooks.nbmanager import NotebookManager |
|
66 | 67 | from .services.notebooks.filenbmanager import FileNotebookManager |
|
67 | 68 | from .services.clusters.clustermanager import ClusterManager |
|
68 | 69 | from .services.sessions.sessionmanager import SessionManager |
|
69 | 70 | |
|
70 | 71 | from .base.handlers import AuthenticatedFileHandler, FileFindHandler |
|
71 | 72 | |
|
72 | 73 | from IPython.config.application import catch_config_error, boolean_flag |
|
73 | 74 | from IPython.core.application import BaseIPythonApplication |
|
74 | 75 | from IPython.consoleapp import IPythonConsoleApp |
|
75 | 76 | from IPython.kernel import swallow_argv |
|
76 | 77 | from IPython.kernel.zmq.session import default_secure |
|
77 | 78 | from IPython.kernel.zmq.kernelapp import ( |
|
78 | 79 | kernel_flags, |
|
79 | 80 | kernel_aliases, |
|
80 | 81 | ) |
|
81 | 82 | from IPython.utils.importstring import import_item |
|
82 | 83 | from IPython.utils.localinterfaces import localhost |
|
83 | 84 | from IPython.utils import submodule |
|
84 | 85 | from IPython.utils.traitlets import ( |
|
85 | 86 | Dict, Unicode, Integer, List, Bool, Bytes, |
|
86 | 87 | DottedObjectName |
|
87 | 88 | ) |
|
88 | 89 | from IPython.utils import py3compat |
|
89 | 90 | from IPython.utils.path import filefind, get_ipython_dir |
|
90 | 91 | |
|
91 | 92 | from .utils import url_path_join |
|
92 | 93 | |
|
93 | 94 | #----------------------------------------------------------------------------- |
|
94 | 95 | # Module globals |
|
95 | 96 | #----------------------------------------------------------------------------- |
|
96 | 97 | |
|
97 | 98 | _examples = """ |
|
98 | 99 | ipython notebook # start the notebook |
|
99 | 100 | ipython notebook --profile=sympy # use the sympy profile |
|
100 | 101 | ipython notebook --certfile=mycert.pem # use SSL/TLS certificate |
|
101 | 102 | """ |
|
102 | 103 | |
|
103 | 104 | #----------------------------------------------------------------------------- |
|
104 | 105 | # Helper functions |
|
105 | 106 | #----------------------------------------------------------------------------- |
|
106 | 107 | |
|
107 | 108 | def random_ports(port, n): |
|
108 | 109 | """Generate a list of n random ports near the given port. |
|
109 | 110 | |
|
110 | 111 | The first 5 ports will be sequential, and the remaining n-5 will be |
|
111 | 112 | randomly selected in the range [port-2*n, port+2*n]. |
|
112 | 113 | """ |
|
113 | 114 | for i in range(min(5, n)): |
|
114 | 115 | yield port + i |
|
115 | 116 | for i in range(n-5): |
|
116 | 117 | yield max(1, port + random.randint(-2*n, 2*n)) |
|
117 | 118 | |
|
118 | 119 | def load_handlers(name): |
|
119 | 120 | """Load the (URL pattern, handler) tuples for each component.""" |
|
120 | 121 | name = 'IPython.html.' + name |
|
121 | 122 | mod = __import__(name, fromlist=['default_handlers']) |
|
122 | 123 | return mod.default_handlers |
|
123 | 124 | |
|
124 | 125 | #----------------------------------------------------------------------------- |
|
125 | 126 | # The Tornado web application |
|
126 | 127 | #----------------------------------------------------------------------------- |
|
127 | 128 | |
|
128 | 129 | class NotebookWebApplication(web.Application): |
|
129 | 130 | |
|
130 | 131 | def __init__(self, ipython_app, kernel_manager, notebook_manager, |
|
131 | 132 | cluster_manager, session_manager, log, base_project_url, |
|
132 | 133 | settings_overrides): |
|
133 | 134 | |
|
134 | 135 | settings = self.init_settings( |
|
135 | 136 | ipython_app, kernel_manager, notebook_manager, cluster_manager, |
|
136 | 137 | session_manager, log, base_project_url, settings_overrides) |
|
137 | 138 | handlers = self.init_handlers(settings) |
|
138 | 139 | |
|
139 | 140 | super(NotebookWebApplication, self).__init__(handlers, **settings) |
|
140 | 141 | |
|
141 | 142 | def init_settings(self, ipython_app, kernel_manager, notebook_manager, |
|
142 | 143 | cluster_manager, session_manager, log, base_project_url, |
|
143 | 144 | settings_overrides): |
|
144 | 145 | # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and |
|
145 | 146 | # base_project_url will always be unicode, which will in turn |
|
146 | 147 | # make the patterns unicode, and ultimately result in unicode |
|
147 | 148 | # keys in kwargs to handler._execute(**kwargs) in tornado. |
|
148 | 149 | # This enforces that base_project_url be ascii in that situation. |
|
149 | 150 | # |
|
150 | 151 | # Note that the URLs these patterns check against are escaped, |
|
151 | 152 | # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'. |
|
152 | 153 | base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii') |
|
153 | 154 | template_path = settings_overrides.get("template_path", os.path.join(os.path.dirname(__file__), "templates")) |
|
154 | 155 | settings = dict( |
|
155 | 156 | # basics |
|
156 | 157 | base_project_url=base_project_url, |
|
157 | 158 | base_kernel_url=ipython_app.base_kernel_url, |
|
158 | 159 | template_path=template_path, |
|
159 | 160 | static_path=ipython_app.static_file_path, |
|
160 | 161 | static_handler_class = FileFindHandler, |
|
161 | 162 | static_url_prefix = url_path_join(base_project_url,'/static/'), |
|
162 | 163 | |
|
163 | 164 | # authentication |
|
164 | 165 | cookie_secret=ipython_app.cookie_secret, |
|
165 | 166 | login_url=url_path_join(base_project_url,'/login'), |
|
166 | 167 | password=ipython_app.password, |
|
167 | 168 | |
|
168 | 169 | # managers |
|
169 | 170 | kernel_manager=kernel_manager, |
|
170 | 171 | notebook_manager=notebook_manager, |
|
171 | 172 | cluster_manager=cluster_manager, |
|
172 | 173 | session_manager=session_manager, |
|
173 | 174 | |
|
174 | 175 | # IPython stuff |
|
175 | 176 | nbextensions_path = ipython_app.nbextensions_path, |
|
176 | 177 | mathjax_url=ipython_app.mathjax_url, |
|
177 | 178 | config=ipython_app.config, |
|
178 | 179 | use_less=ipython_app.use_less, |
|
179 | 180 | jinja2_env=Environment(loader=FileSystemLoader(template_path)), |
|
180 | 181 | ) |
|
181 | 182 | |
|
182 | 183 | # allow custom overrides for the tornado web app. |
|
183 | 184 | settings.update(settings_overrides) |
|
184 | 185 | return settings |
|
185 | 186 | |
|
186 | 187 | def init_handlers(self, settings): |
|
187 | 188 | # Load the (URL pattern, handler) tuples for each component. |
|
188 | 189 | handlers = [] |
|
189 | 190 | handlers.extend(load_handlers('base.handlers')) |
|
190 | 191 | handlers.extend(load_handlers('tree.handlers')) |
|
191 | 192 | handlers.extend(load_handlers('auth.login')) |
|
192 | 193 | handlers.extend(load_handlers('auth.logout')) |
|
193 | 194 | handlers.extend(load_handlers('notebook.handlers')) |
|
194 | 195 | handlers.extend(load_handlers('services.kernels.handlers')) |
|
195 | 196 | handlers.extend(load_handlers('services.notebooks.handlers')) |
|
196 | 197 | handlers.extend(load_handlers('services.clusters.handlers')) |
|
197 | 198 | handlers.extend(load_handlers('services.sessions.handlers')) |
|
198 | 199 | handlers.extend([ |
|
199 | 200 | (r"/files/(.*)", AuthenticatedFileHandler, {'path' : settings['notebook_manager'].notebook_dir}), |
|
200 | 201 | (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}), |
|
201 | 202 | ]) |
|
202 | 203 | # prepend base_project_url onto the patterns that we match |
|
203 | 204 | new_handlers = [] |
|
204 | 205 | for handler in handlers: |
|
205 | 206 | pattern = url_path_join(settings['base_project_url'], handler[0]) |
|
206 | 207 | new_handler = tuple([pattern] + list(handler[1:])) |
|
207 | 208 | new_handlers.append(new_handler) |
|
208 | 209 | return new_handlers |
|
209 | 210 | |
|
210 | 211 | |
|
211 | 212 | |
|
212 | 213 | #----------------------------------------------------------------------------- |
|
213 | 214 | # Aliases and Flags |
|
214 | 215 | #----------------------------------------------------------------------------- |
|
215 | 216 | |
|
216 | 217 | flags = dict(kernel_flags) |
|
217 | 218 | flags['no-browser']=( |
|
218 | 219 | {'NotebookApp' : {'open_browser' : False}}, |
|
219 | 220 | "Don't open the notebook in a browser after startup." |
|
220 | 221 | ) |
|
221 | 222 | flags['no-mathjax']=( |
|
222 | 223 | {'NotebookApp' : {'enable_mathjax' : False}}, |
|
223 | 224 | """Disable MathJax |
|
224 | 225 | |
|
225 | 226 | MathJax is the javascript library IPython uses to render math/LaTeX. It is |
|
226 | 227 | very large, so you may want to disable it if you have a slow internet |
|
227 | 228 | connection, or for offline use of the notebook. |
|
228 | 229 | |
|
229 | 230 | When disabled, equations etc. will appear as their untransformed TeX source. |
|
230 | 231 | """ |
|
231 | 232 | ) |
|
232 | 233 | |
|
233 | 234 | # Add notebook manager flags |
|
234 | 235 | flags.update(boolean_flag('script', 'FileNotebookManager.save_script', |
|
235 | 236 | 'Auto-save a .py script everytime the .ipynb notebook is saved', |
|
236 | 237 | 'Do not auto-save .py scripts for every notebook')) |
|
237 | 238 | |
|
238 | 239 | # the flags that are specific to the frontend |
|
239 | 240 | # these must be scrubbed before being passed to the kernel, |
|
240 | 241 | # or it will raise an error on unrecognized flags |
|
241 | 242 | notebook_flags = ['no-browser', 'no-mathjax', 'script', 'no-script'] |
|
242 | 243 | |
|
243 | 244 | aliases = dict(kernel_aliases) |
|
244 | 245 | |
|
245 | 246 | aliases.update({ |
|
246 | 247 | 'ip': 'NotebookApp.ip', |
|
247 | 248 | 'port': 'NotebookApp.port', |
|
248 | 249 | 'port-retries': 'NotebookApp.port_retries', |
|
249 | 250 | 'transport': 'KernelManager.transport', |
|
250 | 251 | 'keyfile': 'NotebookApp.keyfile', |
|
251 | 252 | 'certfile': 'NotebookApp.certfile', |
|
252 | 253 | 'notebook-dir': 'NotebookManager.notebook_dir', |
|
253 | 254 | 'browser': 'NotebookApp.browser', |
|
254 | 255 | }) |
|
255 | 256 | |
|
256 | 257 | # remove ipkernel flags that are singletons, and don't make sense in |
|
257 | 258 | # multi-kernel evironment: |
|
258 | 259 | aliases.pop('f', None) |
|
259 | 260 | |
|
260 | 261 | notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile', |
|
261 | 262 | u'notebook-dir', u'profile', u'profile-dir'] |
|
262 | 263 | |
|
263 | 264 | #----------------------------------------------------------------------------- |
|
264 | 265 | # NotebookApp |
|
265 | 266 | #----------------------------------------------------------------------------- |
|
266 | 267 | |
|
267 | 268 | class NotebookApp(BaseIPythonApplication): |
|
268 | 269 | |
|
269 | 270 | name = 'ipython-notebook' |
|
270 | 271 | |
|
271 | 272 | description = """ |
|
272 | 273 | The IPython HTML Notebook. |
|
273 | 274 | |
|
274 | 275 | This launches a Tornado based HTML Notebook Server that serves up an |
|
275 | 276 | HTML5/Javascript Notebook client. |
|
276 | 277 | """ |
|
277 | 278 | examples = _examples |
|
278 | 279 | |
|
279 | 280 | classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager, |
|
280 | 281 | FileNotebookManager] |
|
281 | 282 | flags = Dict(flags) |
|
282 | 283 | aliases = Dict(aliases) |
|
283 | 284 | |
|
284 | 285 | kernel_argv = List(Unicode) |
|
285 | 286 | |
|
286 | 287 | def _log_level_default(self): |
|
287 | 288 | return logging.INFO |
|
288 | 289 | |
|
289 | 290 | def _log_format_default(self): |
|
290 | 291 | """override default log format to include time""" |
|
291 | 292 | return u"%(asctime)s.%(msecs).03d [%(name)s]%(highlevel)s %(message)s" |
|
292 | 293 | |
|
293 | 294 | # create requested profiles by default, if they don't exist: |
|
294 | 295 | auto_create = Bool(True) |
|
295 | 296 | |
|
296 | 297 | # file to be opened in the notebook server |
|
297 | 298 | file_to_run = Unicode('') |
|
298 | 299 | |
|
299 | 300 | # Network related information. |
|
300 | 301 | |
|
301 | 302 | ip = Unicode(config=True, |
|
302 | 303 | help="The IP address the notebook server will listen on." |
|
303 | 304 | ) |
|
304 | 305 | def _ip_default(self): |
|
305 | 306 | return localhost() |
|
306 | 307 | |
|
307 | 308 | def _ip_changed(self, name, old, new): |
|
308 | 309 | if new == u'*': self.ip = u'' |
|
309 | 310 | |
|
310 | 311 | port = Integer(8888, config=True, |
|
311 | 312 | help="The port the notebook server will listen on." |
|
312 | 313 | ) |
|
313 | 314 | port_retries = Integer(50, config=True, |
|
314 | 315 | help="The number of additional ports to try if the specified port is not available." |
|
315 | 316 | ) |
|
316 | 317 | |
|
317 | 318 | certfile = Unicode(u'', config=True, |
|
318 | 319 | help="""The full path to an SSL/TLS certificate file.""" |
|
319 | 320 | ) |
|
320 | 321 | |
|
321 | 322 | keyfile = Unicode(u'', config=True, |
|
322 | 323 | help="""The full path to a private key file for usage with SSL/TLS.""" |
|
323 | 324 | ) |
|
324 | 325 | |
|
325 | 326 | cookie_secret = Bytes(b'', config=True, |
|
326 | 327 | help="""The random bytes used to secure cookies. |
|
327 | 328 | By default this is a new random number every time you start the Notebook. |
|
328 | 329 | Set it to a value in a config file to enable logins to persist across server sessions. |
|
329 | 330 | |
|
330 | 331 | Note: Cookie secrets should be kept private, do not share config files with |
|
331 | 332 | cookie_secret stored in plaintext (you can read the value from a file). |
|
332 | 333 | """ |
|
333 | 334 | ) |
|
334 | 335 | def _cookie_secret_default(self): |
|
335 | 336 | return os.urandom(1024) |
|
336 | 337 | |
|
337 | 338 | password = Unicode(u'', config=True, |
|
338 | 339 | help="""Hashed password to use for web authentication. |
|
339 | 340 | |
|
340 | 341 | To generate, type in a python/IPython shell: |
|
341 | 342 | |
|
342 | 343 | from IPython.lib import passwd; passwd() |
|
343 | 344 | |
|
344 | 345 | The string should be of the form type:salt:hashed-password. |
|
345 | 346 | """ |
|
346 | 347 | ) |
|
347 | 348 | |
|
348 | 349 | open_browser = Bool(True, config=True, |
|
349 | 350 | help="""Whether to open in a browser after starting. |
|
350 | 351 | The specific browser used is platform dependent and |
|
351 | 352 | determined by the python standard library `webbrowser` |
|
352 | 353 | module, unless it is overridden using the --browser |
|
353 | 354 | (NotebookApp.browser) configuration option. |
|
354 | 355 | """) |
|
355 | 356 | |
|
356 | 357 | browser = Unicode(u'', config=True, |
|
357 | 358 | help="""Specify what command to use to invoke a web |
|
358 | 359 | browser when opening the notebook. If not specified, the |
|
359 | 360 | default browser will be determined by the `webbrowser` |
|
360 | 361 | standard library module, which allows setting of the |
|
361 | 362 | BROWSER environment variable to override it. |
|
362 | 363 | """) |
|
363 | 364 | |
|
364 | 365 | use_less = Bool(False, config=True, |
|
365 | 366 | help="""Wether to use Browser Side less-css parsing |
|
366 | 367 | instead of compiled css version in templates that allows |
|
367 | 368 | it. This is mainly convenient when working on the less |
|
368 | 369 | file to avoid a build step, or if user want to overwrite |
|
369 | 370 | some of the less variables without having to recompile |
|
370 | 371 | everything. |
|
371 | 372 | |
|
372 | 373 | You will need to install the less.js component in the static directory |
|
373 | 374 | either in the source tree or in your profile folder. |
|
374 | 375 | """) |
|
375 | 376 | |
|
376 | 377 | webapp_settings = Dict(config=True, |
|
377 | 378 | help="Supply overrides for the tornado.web.Application that the " |
|
378 | 379 | "IPython notebook uses.") |
|
379 | 380 | |
|
380 | 381 | enable_mathjax = Bool(True, config=True, |
|
381 | 382 | help="""Whether to enable MathJax for typesetting math/TeX |
|
382 | 383 | |
|
383 | 384 | MathJax is the javascript library IPython uses to render math/LaTeX. It is |
|
384 | 385 | very large, so you may want to disable it if you have a slow internet |
|
385 | 386 | connection, or for offline use of the notebook. |
|
386 | 387 | |
|
387 | 388 | When disabled, equations etc. will appear as their untransformed TeX source. |
|
388 | 389 | """ |
|
389 | 390 | ) |
|
390 | 391 | def _enable_mathjax_changed(self, name, old, new): |
|
391 | 392 | """set mathjax url to empty if mathjax is disabled""" |
|
392 | 393 | if not new: |
|
393 | 394 | self.mathjax_url = u'' |
|
394 | 395 | |
|
395 | 396 | base_project_url = Unicode('/', config=True, |
|
396 | 397 | help='''The base URL for the notebook server. |
|
397 | 398 | |
|
398 | 399 | Leading and trailing slashes can be omitted, |
|
399 | 400 | and will automatically be added. |
|
400 | 401 | ''') |
|
401 | 402 | def _base_project_url_changed(self, name, old, new): |
|
402 | 403 | if not new.startswith('/'): |
|
403 | 404 | self.base_project_url = '/'+new |
|
404 | 405 | elif not new.endswith('/'): |
|
405 | 406 | self.base_project_url = new+'/' |
|
406 | 407 | |
|
407 | 408 | base_kernel_url = Unicode('/', config=True, |
|
408 | 409 | help='''The base URL for the kernel server |
|
409 | 410 | |
|
410 | 411 | Leading and trailing slashes can be omitted, |
|
411 | 412 | and will automatically be added. |
|
412 | 413 | ''') |
|
413 | 414 | def _base_kernel_url_changed(self, name, old, new): |
|
414 | 415 | if not new.startswith('/'): |
|
415 | 416 | self.base_kernel_url = '/'+new |
|
416 | 417 | elif not new.endswith('/'): |
|
417 | 418 | self.base_kernel_url = new+'/' |
|
418 | 419 | |
|
419 | 420 | websocket_url = Unicode("", config=True, |
|
420 | 421 | help="""The base URL for the websocket server, |
|
421 | 422 | if it differs from the HTTP server (hint: it almost certainly doesn't). |
|
422 | 423 | |
|
423 | 424 | Should be in the form of an HTTP origin: ws[s]://hostname[:port] |
|
424 | 425 | """ |
|
425 | 426 | ) |
|
426 | 427 | |
|
427 | 428 | extra_static_paths = List(Unicode, config=True, |
|
428 | 429 | help="""Extra paths to search for serving static files. |
|
429 | 430 | |
|
430 | 431 | This allows adding javascript/css to be available from the notebook server machine, |
|
431 | 432 | or overriding individual files in the IPython""" |
|
432 | 433 | ) |
|
433 | 434 | def _extra_static_paths_default(self): |
|
434 | 435 | return [os.path.join(self.profile_dir.location, 'static')] |
|
435 | 436 | |
|
436 | 437 | @property |
|
437 | 438 | def static_file_path(self): |
|
438 | 439 | """return extra paths + the default location""" |
|
439 | 440 | return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH] |
|
440 | 441 | |
|
441 | 442 | nbextensions_path = List(Unicode, config=True, |
|
442 | 443 | help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions""" |
|
443 | 444 | ) |
|
444 | 445 | def _nbextensions_path_default(self): |
|
445 | 446 | return [os.path.join(get_ipython_dir(), 'nbextensions')] |
|
446 | 447 | |
|
447 | 448 | mathjax_url = Unicode("", config=True, |
|
448 | 449 | help="""The url for MathJax.js.""" |
|
449 | 450 | ) |
|
450 | 451 | def _mathjax_url_default(self): |
|
451 | 452 | if not self.enable_mathjax: |
|
452 | 453 | return u'' |
|
453 | 454 | static_url_prefix = self.webapp_settings.get("static_url_prefix", |
|
454 | 455 | url_path_join(self.base_project_url, "static") |
|
455 | 456 | ) |
|
456 | 457 | |
|
457 | 458 | # try local mathjax, either in nbextensions/mathjax or static/mathjax |
|
458 | 459 | for (url_prefix, search_path) in [ |
|
459 | 460 | (url_path_join(self.base_project_url, "nbextensions"), self.nbextensions_path), |
|
460 | 461 | (static_url_prefix, self.static_file_path), |
|
461 | 462 | ]: |
|
462 | 463 | self.log.debug("searching for local mathjax in %s", search_path) |
|
463 | 464 | try: |
|
464 | 465 | mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), search_path) |
|
465 | 466 | except IOError: |
|
466 | 467 | continue |
|
467 | 468 | else: |
|
468 | 469 | url = url_path_join(url_prefix, u"mathjax/MathJax.js") |
|
469 | 470 | self.log.info("Serving local MathJax from %s at %s", mathjax, url) |
|
470 | 471 | return url |
|
471 | 472 | |
|
472 | 473 | # no local mathjax, serve from CDN |
|
473 | 474 | if self.certfile: |
|
474 | 475 | # HTTPS: load from Rackspace CDN, because SSL certificate requires it |
|
475 | 476 | host = u"https://c328740.ssl.cf1.rackcdn.com" |
|
476 | 477 | else: |
|
477 | 478 | host = u"http://cdn.mathjax.org" |
|
478 | 479 | |
|
479 | 480 | url = host + u"/mathjax/latest/MathJax.js" |
|
480 | 481 | self.log.info("Using MathJax from CDN: %s", url) |
|
481 | 482 | return url |
|
482 | 483 | |
|
483 | 484 | def _mathjax_url_changed(self, name, old, new): |
|
484 | 485 | if new and not self.enable_mathjax: |
|
485 | 486 | # enable_mathjax=False overrides mathjax_url |
|
486 | 487 | self.mathjax_url = u'' |
|
487 | 488 | else: |
|
488 | 489 | self.log.info("Using MathJax: %s", new) |
|
489 | 490 | |
|
490 | 491 | notebook_manager_class = DottedObjectName('IPython.html.services.notebooks.filenbmanager.FileNotebookManager', |
|
491 | 492 | config=True, |
|
492 | 493 | help='The notebook manager class to use.') |
|
493 | 494 | |
|
494 | 495 | trust_xheaders = Bool(False, config=True, |
|
495 | 496 | help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers" |
|
496 | 497 | "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL") |
|
497 | 498 | ) |
|
498 | 499 | |
|
499 | 500 | def parse_command_line(self, argv=None): |
|
500 | 501 | super(NotebookApp, self).parse_command_line(argv) |
|
501 | 502 | |
|
502 | 503 | if self.extra_args: |
|
503 | 504 | arg0 = self.extra_args[0] |
|
504 | 505 | f = os.path.abspath(arg0) |
|
505 | 506 | self.argv.remove(arg0) |
|
506 | 507 | if not os.path.exists(f): |
|
507 | 508 | self.log.critical("No such file or directory: %s", f) |
|
508 | 509 | self.exit(1) |
|
509 | 510 | if os.path.isdir(f): |
|
510 | 511 | self.config.FileNotebookManager.notebook_dir = f |
|
511 | 512 | elif os.path.isfile(f): |
|
512 | 513 | self.file_to_run = f |
|
513 | 514 | |
|
514 | 515 | def init_kernel_argv(self): |
|
515 | 516 | """construct the kernel arguments""" |
|
516 | 517 | # Scrub frontend-specific flags |
|
517 | 518 | self.kernel_argv = swallow_argv(self.argv, notebook_aliases, notebook_flags) |
|
518 | 519 | # Kernel should inherit default config file from frontend |
|
519 | 520 | self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name) |
|
520 | 521 | # Kernel should get *absolute* path to profile directory |
|
521 | 522 | self.kernel_argv.extend(["--profile-dir", self.profile_dir.location]) |
|
522 | 523 | |
|
523 | 524 | def init_configurables(self): |
|
524 | 525 | # force Session default to be secure |
|
525 | 526 | default_secure(self.config) |
|
526 | 527 | self.kernel_manager = MappingKernelManager( |
|
527 | 528 | parent=self, log=self.log, kernel_argv=self.kernel_argv, |
|
528 | 529 | connection_dir = self.profile_dir.security_dir, |
|
529 | 530 | ) |
|
530 | 531 | kls = import_item(self.notebook_manager_class) |
|
531 | 532 | self.notebook_manager = kls(parent=self, log=self.log) |
|
532 | 533 | self.session_manager = SessionManager(parent=self, log=self.log) |
|
533 | 534 | self.cluster_manager = ClusterManager(parent=self, log=self.log) |
|
534 | 535 | self.cluster_manager.update_profiles() |
|
535 | 536 | |
|
536 | 537 | def init_logging(self): |
|
537 | 538 | # This prevents double log messages because tornado use a root logger that |
|
538 | 539 | # self.log is a child of. The logging module dipatches log messages to a log |
|
539 | 540 | # and all of its ancenstors until propagate is set to False. |
|
540 | 541 | self.log.propagate = False |
|
541 | 542 | |
|
542 | 543 | # hook up tornado 3's loggers to our app handlers |
|
543 | 544 | for name in ('access', 'application', 'general'): |
|
544 | 545 | logger = logging.getLogger('tornado.%s' % name) |
|
545 | 546 | logger.parent = self.log |
|
546 | 547 | logger.setLevel(self.log.level) |
|
547 | 548 | |
|
548 | 549 | def init_webapp(self): |
|
549 | 550 | """initialize tornado webapp and httpserver""" |
|
550 | 551 | self.web_app = NotebookWebApplication( |
|
551 | 552 | self, self.kernel_manager, self.notebook_manager, |
|
552 | 553 | self.cluster_manager, self.session_manager, |
|
553 | 554 | self.log, self.base_project_url, self.webapp_settings |
|
554 | 555 | ) |
|
555 | 556 | if self.certfile: |
|
556 | 557 | ssl_options = dict(certfile=self.certfile) |
|
557 | 558 | if self.keyfile: |
|
558 | 559 | ssl_options['keyfile'] = self.keyfile |
|
559 | 560 | else: |
|
560 | 561 | ssl_options = None |
|
561 | 562 | self.web_app.password = self.password |
|
562 | 563 | self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options, |
|
563 | 564 | xheaders=self.trust_xheaders) |
|
564 | 565 | if not self.ip: |
|
565 | 566 | warning = "WARNING: The notebook server is listening on all IP addresses" |
|
566 | 567 | if ssl_options is None: |
|
567 | 568 | self.log.critical(warning + " and not using encryption. This " |
|
568 | 569 | "is not recommended.") |
|
569 | 570 | if not self.password: |
|
570 | 571 | self.log.critical(warning + " and not using authentication. " |
|
571 | 572 | "This is highly insecure and not recommended.") |
|
572 | 573 | success = None |
|
573 | 574 | for port in random_ports(self.port, self.port_retries+1): |
|
574 | 575 | try: |
|
575 | 576 | self.http_server.listen(port, self.ip) |
|
576 | 577 | except socket.error as e: |
|
577 | 578 | if e.errno == errno.EADDRINUSE: |
|
578 | 579 | self.log.info('The port %i is already in use, trying another random port.' % port) |
|
579 | 580 | continue |
|
580 | 581 | elif e.errno in (errno.EACCES, getattr(errno, 'WSAEACCES', errno.EACCES)): |
|
581 | 582 | self.log.warn("Permission to listen on port %i denied" % port) |
|
582 | 583 | continue |
|
583 | 584 | else: |
|
584 | 585 | raise |
|
585 | 586 | else: |
|
586 | 587 | self.port = port |
|
587 | 588 | success = True |
|
588 | 589 | break |
|
589 | 590 | if not success: |
|
590 | 591 | self.log.critical('ERROR: the notebook server could not be started because ' |
|
591 | 592 | 'no available port could be found.') |
|
592 | 593 | self.exit(1) |
|
593 | 594 | |
|
594 | 595 | def init_signal(self): |
|
595 | 596 | if not sys.platform.startswith('win'): |
|
596 | 597 | signal.signal(signal.SIGINT, self._handle_sigint) |
|
597 | 598 | signal.signal(signal.SIGTERM, self._signal_stop) |
|
598 | 599 | if hasattr(signal, 'SIGUSR1'): |
|
599 | 600 | # Windows doesn't support SIGUSR1 |
|
600 | 601 | signal.signal(signal.SIGUSR1, self._signal_info) |
|
601 | 602 | if hasattr(signal, 'SIGINFO'): |
|
602 | 603 | # only on BSD-based systems |
|
603 | 604 | signal.signal(signal.SIGINFO, self._signal_info) |
|
604 | 605 | |
|
605 | 606 | def _handle_sigint(self, sig, frame): |
|
606 | 607 | """SIGINT handler spawns confirmation dialog""" |
|
607 | 608 | # register more forceful signal handler for ^C^C case |
|
608 | 609 | signal.signal(signal.SIGINT, self._signal_stop) |
|
609 | 610 | # request confirmation dialog in bg thread, to avoid |
|
610 | 611 | # blocking the App |
|
611 | 612 | thread = threading.Thread(target=self._confirm_exit) |
|
612 | 613 | thread.daemon = True |
|
613 | 614 | thread.start() |
|
614 | 615 | |
|
615 | 616 | def _restore_sigint_handler(self): |
|
616 | 617 | """callback for restoring original SIGINT handler""" |
|
617 | 618 | signal.signal(signal.SIGINT, self._handle_sigint) |
|
618 | 619 | |
|
619 | 620 | def _confirm_exit(self): |
|
620 | 621 | """confirm shutdown on ^C |
|
621 | 622 | |
|
622 | 623 | A second ^C, or answering 'y' within 5s will cause shutdown, |
|
623 | 624 | otherwise original SIGINT handler will be restored. |
|
624 | 625 | |
|
625 | 626 | This doesn't work on Windows. |
|
626 | 627 | """ |
|
627 | 628 | # FIXME: remove this delay when pyzmq dependency is >= 2.1.11 |
|
628 | 629 | time.sleep(0.1) |
|
629 | 630 | info = self.log.info |
|
630 | 631 | info('interrupted') |
|
631 |
print |
|
|
632 | print(self.notebook_info()) | |
|
632 | 633 | sys.stdout.write("Shutdown this notebook server (y/[n])? ") |
|
633 | 634 | sys.stdout.flush() |
|
634 | 635 | r,w,x = select.select([sys.stdin], [], [], 5) |
|
635 | 636 | if r: |
|
636 | 637 | line = sys.stdin.readline() |
|
637 | 638 | if line.lower().startswith('y'): |
|
638 | 639 | self.log.critical("Shutdown confirmed") |
|
639 | 640 | ioloop.IOLoop.instance().stop() |
|
640 | 641 | return |
|
641 | 642 | else: |
|
642 |
print |
|
|
643 |
print |
|
|
643 | print("No answer for 5s:", end=' ') | |
|
644 | print("resuming operation...") | |
|
644 | 645 | # no answer, or answer is no: |
|
645 | 646 | # set it back to original SIGINT handler |
|
646 | 647 | # use IOLoop.add_callback because signal.signal must be called |
|
647 | 648 | # from main thread |
|
648 | 649 | ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler) |
|
649 | 650 | |
|
650 | 651 | def _signal_stop(self, sig, frame): |
|
651 | 652 | self.log.critical("received signal %s, stopping", sig) |
|
652 | 653 | ioloop.IOLoop.instance().stop() |
|
653 | 654 | |
|
654 | 655 | def _signal_info(self, sig, frame): |
|
655 |
print |
|
|
656 | print(self.notebook_info()) | |
|
656 | 657 | |
|
657 | 658 | def init_components(self): |
|
658 | 659 | """Check the components submodule, and warn if it's unclean""" |
|
659 | 660 | status = submodule.check_submodule_status() |
|
660 | 661 | if status == 'missing': |
|
661 | 662 | self.log.warn("components submodule missing, running `git submodule update`") |
|
662 | 663 | submodule.update_submodules(submodule.ipython_parent()) |
|
663 | 664 | elif status == 'unclean': |
|
664 | 665 | self.log.warn("components submodule unclean, you may see 404s on static/components") |
|
665 | 666 | self.log.warn("run `setup.py submodule` or `git submodule update` to update") |
|
666 | 667 | |
|
667 | 668 | |
|
668 | 669 | @catch_config_error |
|
669 | 670 | def initialize(self, argv=None): |
|
670 | 671 | super(NotebookApp, self).initialize(argv) |
|
671 | 672 | self.init_logging() |
|
672 | 673 | self.init_kernel_argv() |
|
673 | 674 | self.init_configurables() |
|
674 | 675 | self.init_components() |
|
675 | 676 | self.init_webapp() |
|
676 | 677 | self.init_signal() |
|
677 | 678 | |
|
678 | 679 | def cleanup_kernels(self): |
|
679 | 680 | """Shutdown all kernels. |
|
680 | 681 | |
|
681 | 682 | The kernels will shutdown themselves when this process no longer exists, |
|
682 | 683 | but explicit shutdown allows the KernelManagers to cleanup the connection files. |
|
683 | 684 | """ |
|
684 | 685 | self.log.info('Shutting down kernels') |
|
685 | 686 | self.kernel_manager.shutdown_all() |
|
686 | 687 | |
|
687 | 688 | def notebook_info(self): |
|
688 | 689 | "Return the current working directory and the server url information" |
|
689 | 690 | info = self.notebook_manager.info_string() + "\n" |
|
690 | 691 | info += "%d active kernels \n" % len(self.kernel_manager._kernels) |
|
691 | 692 | return info + "The IPython Notebook is running at: %s" % self._url |
|
692 | 693 | |
|
693 | 694 | def start(self): |
|
694 | 695 | """ Start the IPython Notebook server app, after initialization |
|
695 | 696 | |
|
696 | 697 | This method takes no arguments so all configuration and initialization |
|
697 | 698 | must be done prior to calling this method.""" |
|
698 | 699 | ip = self.ip if self.ip else '[all ip addresses on your system]' |
|
699 | 700 | proto = 'https' if self.certfile else 'http' |
|
700 | 701 | info = self.log.info |
|
701 | 702 | self._url = "%s://%s:%i%s" % (proto, ip, self.port, |
|
702 | 703 | self.base_project_url) |
|
703 | 704 | for line in self.notebook_info().split("\n"): |
|
704 | 705 | info(line) |
|
705 | 706 | info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).") |
|
706 | 707 | |
|
707 | 708 | if self.open_browser or self.file_to_run: |
|
708 | 709 | ip = self.ip or localhost() |
|
709 | 710 | try: |
|
710 | 711 | browser = webbrowser.get(self.browser or None) |
|
711 | 712 | except webbrowser.Error as e: |
|
712 | 713 | self.log.warn('No web browser found: %s.' % e) |
|
713 | 714 | browser = None |
|
714 | 715 | |
|
715 | 716 | nbdir = os.path.abspath(self.notebook_manager.notebook_dir) |
|
716 | 717 | f = self.file_to_run |
|
717 | 718 | if f and f.startswith(nbdir): |
|
718 | 719 | f = f[len(nbdir):] |
|
719 | 720 | else: |
|
720 | 721 | self.log.warn( |
|
721 | 722 | "Probably won't be able to open notebook %s " |
|
722 | 723 | "because it is not in notebook_dir %s", |
|
723 | 724 | f, nbdir, |
|
724 | 725 | ) |
|
725 | 726 | |
|
726 | 727 | if os.path.isfile(self.file_to_run): |
|
727 | 728 | url = url_path_join('notebooks', f) |
|
728 | 729 | else: |
|
729 | 730 | url = url_path_join('tree', f) |
|
730 | 731 | if browser: |
|
731 | 732 | b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip, |
|
732 | 733 | self.port, self.base_project_url, url), new=2) |
|
733 | 734 | threading.Thread(target=b).start() |
|
734 | 735 | try: |
|
735 | 736 | ioloop.IOLoop.instance().start() |
|
736 | 737 | except KeyboardInterrupt: |
|
737 | 738 | info("Interrupted...") |
|
738 | 739 | finally: |
|
739 | 740 | self.cleanup_kernels() |
|
740 | 741 | |
|
741 | 742 | |
|
742 | 743 | #----------------------------------------------------------------------------- |
|
743 | 744 | # Main entry point |
|
744 | 745 | #----------------------------------------------------------------------------- |
|
745 | 746 | |
|
746 | 747 | launch_new_instance = NotebookApp.launch_instance |
|
747 | 748 |
@@ -1,230 +1,231 b'' | |||
|
1 | 1 | # coding: utf-8 |
|
2 | 2 | """Tests for the notebook manager.""" |
|
3 | from __future__ import print_function | |
|
3 | 4 | |
|
4 | 5 | import os |
|
5 | 6 | |
|
6 | 7 | from tornado.web import HTTPError |
|
7 | 8 | from unittest import TestCase |
|
8 | 9 | from tempfile import NamedTemporaryFile |
|
9 | 10 | |
|
10 | 11 | from IPython.utils.tempdir import TemporaryDirectory |
|
11 | 12 | from IPython.utils.traitlets import TraitError |
|
12 | 13 | from IPython.html.utils import url_path_join |
|
13 | 14 | |
|
14 | 15 | from ..filenbmanager import FileNotebookManager |
|
15 | 16 | from ..nbmanager import NotebookManager |
|
16 | 17 | |
|
17 | 18 | class TestFileNotebookManager(TestCase): |
|
18 | 19 | |
|
19 | 20 | def test_nb_dir(self): |
|
20 | 21 | with TemporaryDirectory() as td: |
|
21 | 22 | fm = FileNotebookManager(notebook_dir=td) |
|
22 | 23 | self.assertEqual(fm.notebook_dir, td) |
|
23 | 24 | |
|
24 | 25 | def test_create_nb_dir(self): |
|
25 | 26 | with TemporaryDirectory() as td: |
|
26 | 27 | nbdir = os.path.join(td, 'notebooks') |
|
27 | 28 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
28 | 29 | self.assertEqual(fm.notebook_dir, nbdir) |
|
29 | 30 | |
|
30 | 31 | def test_missing_nb_dir(self): |
|
31 | 32 | with TemporaryDirectory() as td: |
|
32 | 33 | nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') |
|
33 | 34 | self.assertRaises(TraitError, FileNotebookManager, notebook_dir=nbdir) |
|
34 | 35 | |
|
35 | 36 | def test_invalid_nb_dir(self): |
|
36 | 37 | with NamedTemporaryFile() as tf: |
|
37 | 38 | self.assertRaises(TraitError, FileNotebookManager, notebook_dir=tf.name) |
|
38 | 39 | |
|
39 | 40 | def test_get_os_path(self): |
|
40 | 41 | # full filesystem path should be returned with correct operating system |
|
41 | 42 | # separators. |
|
42 | 43 | with TemporaryDirectory() as td: |
|
43 | 44 | nbdir = os.path.join(td, 'notebooks') |
|
44 | 45 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
45 | 46 | path = fm.get_os_path('test.ipynb', '/path/to/notebook/') |
|
46 | 47 | rel_path_list = '/path/to/notebook/test.ipynb'.split('/') |
|
47 | 48 | fs_path = os.path.join(fm.notebook_dir, *rel_path_list) |
|
48 | 49 | self.assertEqual(path, fs_path) |
|
49 | 50 | |
|
50 | 51 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
51 | 52 | path = fm.get_os_path('test.ipynb') |
|
52 | 53 | fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') |
|
53 | 54 | self.assertEqual(path, fs_path) |
|
54 | 55 | |
|
55 | 56 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
56 | 57 | path = fm.get_os_path('test.ipynb', '////') |
|
57 | 58 | fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') |
|
58 | 59 | self.assertEqual(path, fs_path) |
|
59 | 60 | |
|
60 | 61 | class TestNotebookManager(TestCase): |
|
61 | 62 | |
|
62 | 63 | def make_dir(self, abs_path, rel_path): |
|
63 | 64 | """make subdirectory, rel_path is the relative path |
|
64 | 65 | to that directory from the location where the server started""" |
|
65 | 66 | os_path = os.path.join(abs_path, rel_path) |
|
66 | 67 | try: |
|
67 | 68 | os.makedirs(os_path) |
|
68 | 69 | except OSError: |
|
69 |
print |
|
|
70 | print("Directory already exists.") | |
|
70 | 71 | |
|
71 | 72 | def test_create_notebook_model(self): |
|
72 | 73 | with TemporaryDirectory() as td: |
|
73 | 74 | # Test in root directory |
|
74 | 75 | nm = FileNotebookManager(notebook_dir=td) |
|
75 | 76 | model = nm.create_notebook_model() |
|
76 | 77 | assert isinstance(model, dict) |
|
77 | 78 | self.assertIn('name', model) |
|
78 | 79 | self.assertIn('path', model) |
|
79 | 80 | self.assertEqual(model['name'], 'Untitled0.ipynb') |
|
80 | 81 | self.assertEqual(model['path'], '') |
|
81 | 82 | |
|
82 | 83 | # Test in sub-directory |
|
83 | 84 | sub_dir = '/foo/' |
|
84 | 85 | self.make_dir(nm.notebook_dir, 'foo') |
|
85 | 86 | model = nm.create_notebook_model(None, sub_dir) |
|
86 | 87 | assert isinstance(model, dict) |
|
87 | 88 | self.assertIn('name', model) |
|
88 | 89 | self.assertIn('path', model) |
|
89 | 90 | self.assertEqual(model['name'], 'Untitled0.ipynb') |
|
90 | 91 | self.assertEqual(model['path'], sub_dir.strip('/')) |
|
91 | 92 | |
|
92 | 93 | def test_get_notebook_model(self): |
|
93 | 94 | with TemporaryDirectory() as td: |
|
94 | 95 | # Test in root directory |
|
95 | 96 | # Create a notebook |
|
96 | 97 | nm = FileNotebookManager(notebook_dir=td) |
|
97 | 98 | model = nm.create_notebook_model() |
|
98 | 99 | name = model['name'] |
|
99 | 100 | path = model['path'] |
|
100 | 101 | |
|
101 | 102 | # Check that we 'get' on the notebook we just created |
|
102 | 103 | model2 = nm.get_notebook_model(name, path) |
|
103 | 104 | assert isinstance(model2, dict) |
|
104 | 105 | self.assertIn('name', model2) |
|
105 | 106 | self.assertIn('path', model2) |
|
106 | 107 | self.assertEqual(model['name'], name) |
|
107 | 108 | self.assertEqual(model['path'], path) |
|
108 | 109 | |
|
109 | 110 | # Test in sub-directory |
|
110 | 111 | sub_dir = '/foo/' |
|
111 | 112 | self.make_dir(nm.notebook_dir, 'foo') |
|
112 | 113 | model = nm.create_notebook_model(None, sub_dir) |
|
113 | 114 | model2 = nm.get_notebook_model(name, sub_dir) |
|
114 | 115 | assert isinstance(model2, dict) |
|
115 | 116 | self.assertIn('name', model2) |
|
116 | 117 | self.assertIn('path', model2) |
|
117 | 118 | self.assertIn('content', model2) |
|
118 | 119 | self.assertEqual(model2['name'], 'Untitled0.ipynb') |
|
119 | 120 | self.assertEqual(model2['path'], sub_dir.strip('/')) |
|
120 | 121 | |
|
121 | 122 | def test_update_notebook_model(self): |
|
122 | 123 | with TemporaryDirectory() as td: |
|
123 | 124 | # Test in root directory |
|
124 | 125 | # Create a notebook |
|
125 | 126 | nm = FileNotebookManager(notebook_dir=td) |
|
126 | 127 | model = nm.create_notebook_model() |
|
127 | 128 | name = model['name'] |
|
128 | 129 | path = model['path'] |
|
129 | 130 | |
|
130 | 131 | # Change the name in the model for rename |
|
131 | 132 | model['name'] = 'test.ipynb' |
|
132 | 133 | model = nm.update_notebook_model(model, name, path) |
|
133 | 134 | assert isinstance(model, dict) |
|
134 | 135 | self.assertIn('name', model) |
|
135 | 136 | self.assertIn('path', model) |
|
136 | 137 | self.assertEqual(model['name'], 'test.ipynb') |
|
137 | 138 | |
|
138 | 139 | # Make sure the old name is gone |
|
139 | 140 | self.assertRaises(HTTPError, nm.get_notebook_model, name, path) |
|
140 | 141 | |
|
141 | 142 | # Test in sub-directory |
|
142 | 143 | # Create a directory and notebook in that directory |
|
143 | 144 | sub_dir = '/foo/' |
|
144 | 145 | self.make_dir(nm.notebook_dir, 'foo') |
|
145 | 146 | model = nm.create_notebook_model(None, sub_dir) |
|
146 | 147 | name = model['name'] |
|
147 | 148 | path = model['path'] |
|
148 | 149 | |
|
149 | 150 | # Change the name in the model for rename |
|
150 | 151 | model['name'] = 'test_in_sub.ipynb' |
|
151 | 152 | model = nm.update_notebook_model(model, name, path) |
|
152 | 153 | assert isinstance(model, dict) |
|
153 | 154 | self.assertIn('name', model) |
|
154 | 155 | self.assertIn('path', model) |
|
155 | 156 | self.assertEqual(model['name'], 'test_in_sub.ipynb') |
|
156 | 157 | self.assertEqual(model['path'], sub_dir.strip('/')) |
|
157 | 158 | |
|
158 | 159 | # Make sure the old name is gone |
|
159 | 160 | self.assertRaises(HTTPError, nm.get_notebook_model, name, path) |
|
160 | 161 | |
|
161 | 162 | def test_save_notebook_model(self): |
|
162 | 163 | with TemporaryDirectory() as td: |
|
163 | 164 | # Test in the root directory |
|
164 | 165 | # Create a notebook |
|
165 | 166 | nm = FileNotebookManager(notebook_dir=td) |
|
166 | 167 | model = nm.create_notebook_model() |
|
167 | 168 | name = model['name'] |
|
168 | 169 | path = model['path'] |
|
169 | 170 | |
|
170 | 171 | # Get the model with 'content' |
|
171 | 172 | full_model = nm.get_notebook_model(name, path) |
|
172 | 173 | |
|
173 | 174 | # Save the notebook |
|
174 | 175 | model = nm.save_notebook_model(full_model, name, path) |
|
175 | 176 | assert isinstance(model, dict) |
|
176 | 177 | self.assertIn('name', model) |
|
177 | 178 | self.assertIn('path', model) |
|
178 | 179 | self.assertEqual(model['name'], name) |
|
179 | 180 | self.assertEqual(model['path'], path) |
|
180 | 181 | |
|
181 | 182 | # Test in sub-directory |
|
182 | 183 | # Create a directory and notebook in that directory |
|
183 | 184 | sub_dir = '/foo/' |
|
184 | 185 | self.make_dir(nm.notebook_dir, 'foo') |
|
185 | 186 | model = nm.create_notebook_model(None, sub_dir) |
|
186 | 187 | name = model['name'] |
|
187 | 188 | path = model['path'] |
|
188 | 189 | model = nm.get_notebook_model(name, path) |
|
189 | 190 | |
|
190 | 191 | # Change the name in the model for rename |
|
191 | 192 | model = nm.save_notebook_model(model, name, path) |
|
192 | 193 | assert isinstance(model, dict) |
|
193 | 194 | self.assertIn('name', model) |
|
194 | 195 | self.assertIn('path', model) |
|
195 | 196 | self.assertEqual(model['name'], 'Untitled0.ipynb') |
|
196 | 197 | self.assertEqual(model['path'], sub_dir.strip('/')) |
|
197 | 198 | |
|
198 | 199 | def test_delete_notebook_model(self): |
|
199 | 200 | with TemporaryDirectory() as td: |
|
200 | 201 | # Test in the root directory |
|
201 | 202 | # Create a notebook |
|
202 | 203 | nm = FileNotebookManager(notebook_dir=td) |
|
203 | 204 | model = nm.create_notebook_model() |
|
204 | 205 | name = model['name'] |
|
205 | 206 | path = model['path'] |
|
206 | 207 | |
|
207 | 208 | # Delete the notebook |
|
208 | 209 | nm.delete_notebook_model(name, path) |
|
209 | 210 | |
|
210 | 211 | # Check that a 'get' on the deleted notebook raises and error |
|
211 | 212 | self.assertRaises(HTTPError, nm.get_notebook_model, name, path) |
|
212 | 213 | |
|
213 | 214 | def test_copy_notebook(self): |
|
214 | 215 | with TemporaryDirectory() as td: |
|
215 | 216 | # Test in the root directory |
|
216 | 217 | # Create a notebook |
|
217 | 218 | nm = FileNotebookManager(notebook_dir=td) |
|
218 | 219 | path = u'å b' |
|
219 | 220 | name = u'nb √.ipynb' |
|
220 | 221 | os.mkdir(os.path.join(td, path)) |
|
221 | 222 | orig = nm.create_notebook_model({'name' : name}, path=path) |
|
222 | 223 | |
|
223 | 224 | # copy with unspecified name |
|
224 | 225 | copy = nm.copy_notebook(name, path=path) |
|
225 | 226 | self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy0.ipynb')) |
|
226 | 227 | |
|
227 | 228 | # copy with specified name |
|
228 | 229 | copy2 = nm.copy_notebook(name, u'copy 2.ipynb', path=path) |
|
229 | 230 | self.assertEqual(copy2['name'], u'copy 2.ipynb') |
|
230 | 231 |
@@ -1,484 +1,485 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Manage background (threaded) jobs conveniently from an interactive shell. |
|
3 | 3 | |
|
4 | 4 | This module provides a BackgroundJobManager class. This is the main class |
|
5 | 5 | meant for public usage, it implements an object which can create and manage |
|
6 | 6 | new background jobs. |
|
7 | 7 | |
|
8 | 8 | It also provides the actual job classes managed by these BackgroundJobManager |
|
9 | 9 | objects, see their docstrings below. |
|
10 | 10 | |
|
11 | 11 | |
|
12 | 12 | This system was inspired by discussions with B. Granger and the |
|
13 | 13 | BackgroundCommand class described in the book Python Scripting for |
|
14 | 14 | Computational Science, by H. P. Langtangen: |
|
15 | 15 | |
|
16 | 16 | http://folk.uio.no/hpl/scripting |
|
17 | 17 | |
|
18 | 18 | (although ultimately no code from this text was used, as IPython's system is a |
|
19 | 19 | separate implementation). |
|
20 | 20 | |
|
21 | 21 | An example notebook is provided in our documentation illustrating interactive |
|
22 | 22 | use of the system. |
|
23 | 23 | """ |
|
24 | from __future__ import print_function | |
|
24 | 25 | |
|
25 | 26 | #***************************************************************************** |
|
26 | 27 | # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu> |
|
27 | 28 | # |
|
28 | 29 | # Distributed under the terms of the BSD License. The full license is in |
|
29 | 30 | # the file COPYING, distributed as part of this software. |
|
30 | 31 | #***************************************************************************** |
|
31 | 32 | |
|
32 | 33 | # Code begins |
|
33 | 34 | import sys |
|
34 | 35 | import threading |
|
35 | 36 | |
|
36 | 37 | from IPython import get_ipython |
|
37 | 38 | from IPython.core.ultratb import AutoFormattedTB |
|
38 | 39 | from IPython.utils.warn import error |
|
39 | 40 | |
|
40 | 41 | |
|
41 | 42 | class BackgroundJobManager(object): |
|
42 | 43 | """Class to manage a pool of backgrounded threaded jobs. |
|
43 | 44 | |
|
44 | 45 | Below, we assume that 'jobs' is a BackgroundJobManager instance. |
|
45 | 46 | |
|
46 | 47 | Usage summary (see the method docstrings for details): |
|
47 | 48 | |
|
48 | 49 | jobs.new(...) -> start a new job |
|
49 | 50 | |
|
50 | 51 | jobs() or jobs.status() -> print status summary of all jobs |
|
51 | 52 | |
|
52 | 53 | jobs[N] -> returns job number N. |
|
53 | 54 | |
|
54 | 55 | foo = jobs[N].result -> assign to variable foo the result of job N |
|
55 | 56 | |
|
56 | 57 | jobs[N].traceback() -> print the traceback of dead job N |
|
57 | 58 | |
|
58 | 59 | jobs.remove(N) -> remove (finished) job N |
|
59 | 60 | |
|
60 | 61 | jobs.flush() -> remove all finished jobs |
|
61 | 62 | |
|
62 | 63 | As a convenience feature, BackgroundJobManager instances provide the |
|
63 | 64 | utility result and traceback methods which retrieve the corresponding |
|
64 | 65 | information from the jobs list: |
|
65 | 66 | |
|
66 | 67 | jobs.result(N) <--> jobs[N].result |
|
67 | 68 | jobs.traceback(N) <--> jobs[N].traceback() |
|
68 | 69 | |
|
69 | 70 | While this appears minor, it allows you to use tab completion |
|
70 | 71 | interactively on the job manager instance. |
|
71 | 72 | """ |
|
72 | 73 | |
|
73 | 74 | def __init__(self): |
|
74 | 75 | # Lists for job management, accessed via a property to ensure they're |
|
75 | 76 | # up to date.x |
|
76 | 77 | self._running = [] |
|
77 | 78 | self._completed = [] |
|
78 | 79 | self._dead = [] |
|
79 | 80 | # A dict of all jobs, so users can easily access any of them |
|
80 | 81 | self.all = {} |
|
81 | 82 | # For reporting |
|
82 | 83 | self._comp_report = [] |
|
83 | 84 | self._dead_report = [] |
|
84 | 85 | # Store status codes locally for fast lookups |
|
85 | 86 | self._s_created = BackgroundJobBase.stat_created_c |
|
86 | 87 | self._s_running = BackgroundJobBase.stat_running_c |
|
87 | 88 | self._s_completed = BackgroundJobBase.stat_completed_c |
|
88 | 89 | self._s_dead = BackgroundJobBase.stat_dead_c |
|
89 | 90 | |
|
90 | 91 | @property |
|
91 | 92 | def running(self): |
|
92 | 93 | self._update_status() |
|
93 | 94 | return self._running |
|
94 | 95 | |
|
95 | 96 | @property |
|
96 | 97 | def dead(self): |
|
97 | 98 | self._update_status() |
|
98 | 99 | return self._dead |
|
99 | 100 | |
|
100 | 101 | @property |
|
101 | 102 | def completed(self): |
|
102 | 103 | self._update_status() |
|
103 | 104 | return self._completed |
|
104 | 105 | |
|
105 | 106 | def new(self, func_or_exp, *args, **kwargs): |
|
106 | 107 | """Add a new background job and start it in a separate thread. |
|
107 | 108 | |
|
108 | 109 | There are two types of jobs which can be created: |
|
109 | 110 | |
|
110 | 111 | 1. Jobs based on expressions which can be passed to an eval() call. |
|
111 | 112 | The expression must be given as a string. For example: |
|
112 | 113 | |
|
113 | 114 | job_manager.new('myfunc(x,y,z=1)'[,glob[,loc]]) |
|
114 | 115 | |
|
115 | 116 | The given expression is passed to eval(), along with the optional |
|
116 | 117 | global/local dicts provided. If no dicts are given, they are |
|
117 | 118 | extracted automatically from the caller's frame. |
|
118 | 119 | |
|
119 | 120 | A Python statement is NOT a valid eval() expression. Basically, you |
|
120 | 121 | can only use as an eval() argument something which can go on the right |
|
121 | 122 | of an '=' sign and be assigned to a variable. |
|
122 | 123 | |
|
123 | 124 | For example,"print 'hello'" is not valid, but '2+3' is. |
|
124 | 125 | |
|
125 | 126 | 2. Jobs given a function object, optionally passing additional |
|
126 | 127 | positional arguments: |
|
127 | 128 | |
|
128 | 129 | job_manager.new(myfunc, x, y) |
|
129 | 130 | |
|
130 | 131 | The function is called with the given arguments. |
|
131 | 132 | |
|
132 | 133 | If you need to pass keyword arguments to your function, you must |
|
133 | 134 | supply them as a dict named kw: |
|
134 | 135 | |
|
135 | 136 | job_manager.new(myfunc, x, y, kw=dict(z=1)) |
|
136 | 137 | |
|
137 | 138 | The reason for this assymmetry is that the new() method needs to |
|
138 | 139 | maintain access to its own keywords, and this prevents name collisions |
|
139 | 140 | between arguments to new() and arguments to your own functions. |
|
140 | 141 | |
|
141 | 142 | In both cases, the result is stored in the job.result field of the |
|
142 | 143 | background job object. |
|
143 | 144 | |
|
144 | 145 | You can set `daemon` attribute of the thread by giving the keyword |
|
145 | 146 | argument `daemon`. |
|
146 | 147 | |
|
147 | 148 | Notes and caveats: |
|
148 | 149 | |
|
149 | 150 | 1. All threads running share the same standard output. Thus, if your |
|
150 | 151 | background jobs generate output, it will come out on top of whatever |
|
151 | 152 | you are currently writing. For this reason, background jobs are best |
|
152 | 153 | used with silent functions which simply return their output. |
|
153 | 154 | |
|
154 | 155 | 2. Threads also all work within the same global namespace, and this |
|
155 | 156 | system does not lock interactive variables. So if you send job to the |
|
156 | 157 | background which operates on a mutable object for a long time, and |
|
157 | 158 | start modifying that same mutable object interactively (or in another |
|
158 | 159 | backgrounded job), all sorts of bizarre behaviour will occur. |
|
159 | 160 | |
|
160 | 161 | 3. If a background job is spending a lot of time inside a C extension |
|
161 | 162 | module which does not release the Python Global Interpreter Lock |
|
162 | 163 | (GIL), this will block the IPython prompt. This is simply because the |
|
163 | 164 | Python interpreter can only switch between threads at Python |
|
164 | 165 | bytecodes. While the execution is inside C code, the interpreter must |
|
165 | 166 | simply wait unless the extension module releases the GIL. |
|
166 | 167 | |
|
167 | 168 | 4. There is no way, due to limitations in the Python threads library, |
|
168 | 169 | to kill a thread once it has started.""" |
|
169 | 170 | |
|
170 | 171 | if callable(func_or_exp): |
|
171 | 172 | kw = kwargs.get('kw',{}) |
|
172 | 173 | job = BackgroundJobFunc(func_or_exp,*args,**kw) |
|
173 | 174 | elif isinstance(func_or_exp, basestring): |
|
174 | 175 | if not args: |
|
175 | 176 | frame = sys._getframe(1) |
|
176 | 177 | glob, loc = frame.f_globals, frame.f_locals |
|
177 | 178 | elif len(args)==1: |
|
178 | 179 | glob = loc = args[0] |
|
179 | 180 | elif len(args)==2: |
|
180 | 181 | glob,loc = args |
|
181 | 182 | else: |
|
182 | 183 | raise ValueError( |
|
183 | 184 | 'Expression jobs take at most 2 args (globals,locals)') |
|
184 | 185 | job = BackgroundJobExpr(func_or_exp, glob, loc) |
|
185 | 186 | else: |
|
186 | 187 | raise TypeError('invalid args for new job') |
|
187 | 188 | |
|
188 | 189 | if kwargs.get('daemon', False): |
|
189 | 190 | job.daemon = True |
|
190 | 191 | job.num = len(self.all)+1 if self.all else 0 |
|
191 | 192 | self.running.append(job) |
|
192 | 193 | self.all[job.num] = job |
|
193 |
print |
|
|
194 | print('Starting job # %s in a separate thread.' % job.num) | |
|
194 | 195 | job.start() |
|
195 | 196 | return job |
|
196 | 197 | |
|
197 | 198 | def __getitem__(self, job_key): |
|
198 | 199 | num = job_key if isinstance(job_key, int) else job_key.num |
|
199 | 200 | return self.all[num] |
|
200 | 201 | |
|
201 | 202 | def __call__(self): |
|
202 | 203 | """An alias to self.status(), |
|
203 | 204 | |
|
204 | 205 | This allows you to simply call a job manager instance much like the |
|
205 | 206 | Unix `jobs` shell command.""" |
|
206 | 207 | |
|
207 | 208 | return self.status() |
|
208 | 209 | |
|
209 | 210 | def _update_status(self): |
|
210 | 211 | """Update the status of the job lists. |
|
211 | 212 | |
|
212 | 213 | This method moves finished jobs to one of two lists: |
|
213 | 214 | - self.completed: jobs which completed successfully |
|
214 | 215 | - self.dead: jobs which finished but died. |
|
215 | 216 | |
|
216 | 217 | It also copies those jobs to corresponding _report lists. These lists |
|
217 | 218 | are used to report jobs completed/dead since the last update, and are |
|
218 | 219 | then cleared by the reporting function after each call.""" |
|
219 | 220 | |
|
220 | 221 | # Status codes |
|
221 | 222 | srun, scomp, sdead = self._s_running, self._s_completed, self._s_dead |
|
222 | 223 | # State lists, use the actual lists b/c the public names are properties |
|
223 | 224 | # that call this very function on access |
|
224 | 225 | running, completed, dead = self._running, self._completed, self._dead |
|
225 | 226 | |
|
226 | 227 | # Now, update all state lists |
|
227 | 228 | for num, job in enumerate(running): |
|
228 | 229 | stat = job.stat_code |
|
229 | 230 | if stat == srun: |
|
230 | 231 | continue |
|
231 | 232 | elif stat == scomp: |
|
232 | 233 | completed.append(job) |
|
233 | 234 | self._comp_report.append(job) |
|
234 | 235 | running[num] = False |
|
235 | 236 | elif stat == sdead: |
|
236 | 237 | dead.append(job) |
|
237 | 238 | self._dead_report.append(job) |
|
238 | 239 | running[num] = False |
|
239 | 240 | # Remove dead/completed jobs from running list |
|
240 | 241 | running[:] = filter(None, running) |
|
241 | 242 | |
|
242 | 243 | def _group_report(self,group,name): |
|
243 | 244 | """Report summary for a given job group. |
|
244 | 245 | |
|
245 | 246 | Return True if the group had any elements.""" |
|
246 | 247 | |
|
247 | 248 | if group: |
|
248 |
print |
|
|
249 | print('%s jobs:' % name) | |
|
249 | 250 | for job in group: |
|
250 |
print |
|
|
251 | ||
|
251 | print('%s : %s' % (job.num,job)) | |
|
252 | print() | |
|
252 | 253 | return True |
|
253 | 254 | |
|
254 | 255 | def _group_flush(self,group,name): |
|
255 | 256 | """Flush a given job group |
|
256 | 257 | |
|
257 | 258 | Return True if the group had any elements.""" |
|
258 | 259 | |
|
259 | 260 | njobs = len(group) |
|
260 | 261 | if njobs: |
|
261 | 262 | plural = {1:''}.setdefault(njobs,'s') |
|
262 |
print |
|
|
263 | print('Flushing %s %s job%s.' % (njobs,name,plural)) | |
|
263 | 264 | group[:] = [] |
|
264 | 265 | return True |
|
265 | 266 | |
|
266 | 267 | def _status_new(self): |
|
267 | 268 | """Print the status of newly finished jobs. |
|
268 | 269 | |
|
269 | 270 | Return True if any new jobs are reported. |
|
270 | 271 | |
|
271 | 272 | This call resets its own state every time, so it only reports jobs |
|
272 | 273 | which have finished since the last time it was called.""" |
|
273 | 274 | |
|
274 | 275 | self._update_status() |
|
275 | 276 | new_comp = self._group_report(self._comp_report, 'Completed') |
|
276 | 277 | new_dead = self._group_report(self._dead_report, |
|
277 | 278 | 'Dead, call jobs.traceback() for details') |
|
278 | 279 | self._comp_report[:] = [] |
|
279 | 280 | self._dead_report[:] = [] |
|
280 | 281 | return new_comp or new_dead |
|
281 | 282 | |
|
282 | 283 | def status(self,verbose=0): |
|
283 | 284 | """Print a status of all jobs currently being managed.""" |
|
284 | 285 | |
|
285 | 286 | self._update_status() |
|
286 | 287 | self._group_report(self.running,'Running') |
|
287 | 288 | self._group_report(self.completed,'Completed') |
|
288 | 289 | self._group_report(self.dead,'Dead') |
|
289 | 290 | # Also flush the report queues |
|
290 | 291 | self._comp_report[:] = [] |
|
291 | 292 | self._dead_report[:] = [] |
|
292 | 293 | |
|
293 | 294 | def remove(self,num): |
|
294 | 295 | """Remove a finished (completed or dead) job.""" |
|
295 | 296 | |
|
296 | 297 | try: |
|
297 | 298 | job = self.all[num] |
|
298 | 299 | except KeyError: |
|
299 | 300 | error('Job #%s not found' % num) |
|
300 | 301 | else: |
|
301 | 302 | stat_code = job.stat_code |
|
302 | 303 | if stat_code == self._s_running: |
|
303 | 304 | error('Job #%s is still running, it can not be removed.' % num) |
|
304 | 305 | return |
|
305 | 306 | elif stat_code == self._s_completed: |
|
306 | 307 | self.completed.remove(job) |
|
307 | 308 | elif stat_code == self._s_dead: |
|
308 | 309 | self.dead.remove(job) |
|
309 | 310 | |
|
310 | 311 | def flush(self): |
|
311 | 312 | """Flush all finished jobs (completed and dead) from lists. |
|
312 | 313 | |
|
313 | 314 | Running jobs are never flushed. |
|
314 | 315 | |
|
315 | 316 | It first calls _status_new(), to update info. If any jobs have |
|
316 | 317 | completed since the last _status_new() call, the flush operation |
|
317 | 318 | aborts.""" |
|
318 | 319 | |
|
319 | 320 | # Remove the finished jobs from the master dict |
|
320 | 321 | alljobs = self.all |
|
321 | 322 | for job in self.completed+self.dead: |
|
322 | 323 | del(alljobs[job.num]) |
|
323 | 324 | |
|
324 | 325 | # Now flush these lists completely |
|
325 | 326 | fl_comp = self._group_flush(self.completed, 'Completed') |
|
326 | 327 | fl_dead = self._group_flush(self.dead, 'Dead') |
|
327 | 328 | if not (fl_comp or fl_dead): |
|
328 |
print |
|
|
329 | print('No jobs to flush.') | |
|
329 | 330 | |
|
330 | 331 | def result(self,num): |
|
331 | 332 | """result(N) -> return the result of job N.""" |
|
332 | 333 | try: |
|
333 | 334 | return self.all[num].result |
|
334 | 335 | except KeyError: |
|
335 | 336 | error('Job #%s not found' % num) |
|
336 | 337 | |
|
337 | 338 | def _traceback(self, job): |
|
338 | 339 | num = job if isinstance(job, int) else job.num |
|
339 | 340 | try: |
|
340 | 341 | self.all[num].traceback() |
|
341 | 342 | except KeyError: |
|
342 | 343 | error('Job #%s not found' % num) |
|
343 | 344 | |
|
344 | 345 | def traceback(self, job=None): |
|
345 | 346 | if job is None: |
|
346 | 347 | self._update_status() |
|
347 | 348 | for deadjob in self.dead: |
|
348 |
print |
|
|
349 | print("Traceback for: %r" % deadjob) | |
|
349 | 350 | self._traceback(deadjob) |
|
350 | ||
|
351 | print() | |
|
351 | 352 | else: |
|
352 | 353 | self._traceback(job) |
|
353 | 354 | |
|
354 | 355 | |
|
355 | 356 | class BackgroundJobBase(threading.Thread): |
|
356 | 357 | """Base class to build BackgroundJob classes. |
|
357 | 358 | |
|
358 | 359 | The derived classes must implement: |
|
359 | 360 | |
|
360 | 361 | - Their own __init__, since the one here raises NotImplementedError. The |
|
361 | 362 | derived constructor must call self._init() at the end, to provide common |
|
362 | 363 | initialization. |
|
363 | 364 | |
|
364 | 365 | - A strform attribute used in calls to __str__. |
|
365 | 366 | |
|
366 | 367 | - A call() method, which will make the actual execution call and must |
|
367 | 368 | return a value to be held in the 'result' field of the job object.""" |
|
368 | 369 | |
|
369 | 370 | # Class constants for status, in string and as numerical codes (when |
|
370 | 371 | # updating jobs lists, we don't want to do string comparisons). This will |
|
371 | 372 | # be done at every user prompt, so it has to be as fast as possible |
|
372 | 373 | stat_created = 'Created'; stat_created_c = 0 |
|
373 | 374 | stat_running = 'Running'; stat_running_c = 1 |
|
374 | 375 | stat_completed = 'Completed'; stat_completed_c = 2 |
|
375 | 376 | stat_dead = 'Dead (Exception), call jobs.traceback() for details' |
|
376 | 377 | stat_dead_c = -1 |
|
377 | 378 | |
|
378 | 379 | def __init__(self): |
|
379 | 380 | raise NotImplementedError("This class can not be instantiated directly.") |
|
380 | 381 | |
|
381 | 382 | def _init(self): |
|
382 | 383 | """Common initialization for all BackgroundJob objects""" |
|
383 | 384 | |
|
384 | 385 | for attr in ['call','strform']: |
|
385 | 386 | assert hasattr(self,attr), "Missing attribute <%s>" % attr |
|
386 | 387 | |
|
387 | 388 | # The num tag can be set by an external job manager |
|
388 | 389 | self.num = None |
|
389 | 390 | |
|
390 | 391 | self.status = BackgroundJobBase.stat_created |
|
391 | 392 | self.stat_code = BackgroundJobBase.stat_created_c |
|
392 | 393 | self.finished = False |
|
393 | 394 | self.result = '<BackgroundJob has not completed>' |
|
394 | 395 | |
|
395 | 396 | # reuse the ipython traceback handler if we can get to it, otherwise |
|
396 | 397 | # make a new one |
|
397 | 398 | try: |
|
398 | 399 | make_tb = get_ipython().InteractiveTB.text |
|
399 | 400 | except: |
|
400 | 401 | make_tb = AutoFormattedTB(mode = 'Context', |
|
401 | 402 | color_scheme='NoColor', |
|
402 | 403 | tb_offset = 1).text |
|
403 | 404 | # Note that the actual API for text() requires the three args to be |
|
404 | 405 | # passed in, so we wrap it in a simple lambda. |
|
405 | 406 | self._make_tb = lambda : make_tb(None, None, None) |
|
406 | 407 | |
|
407 | 408 | # Hold a formatted traceback if one is generated. |
|
408 | 409 | self._tb = None |
|
409 | 410 | |
|
410 | 411 | threading.Thread.__init__(self) |
|
411 | 412 | |
|
412 | 413 | def __str__(self): |
|
413 | 414 | return self.strform |
|
414 | 415 | |
|
415 | 416 | def __repr__(self): |
|
416 | 417 | return '<BackgroundJob #%d: %s>' % (self.num, self.strform) |
|
417 | 418 | |
|
418 | 419 | def traceback(self): |
|
419 |
print |
|
|
420 | print(self._tb) | |
|
420 | 421 | |
|
421 | 422 | def run(self): |
|
422 | 423 | try: |
|
423 | 424 | self.status = BackgroundJobBase.stat_running |
|
424 | 425 | self.stat_code = BackgroundJobBase.stat_running_c |
|
425 | 426 | self.result = self.call() |
|
426 | 427 | except: |
|
427 | 428 | self.status = BackgroundJobBase.stat_dead |
|
428 | 429 | self.stat_code = BackgroundJobBase.stat_dead_c |
|
429 | 430 | self.finished = None |
|
430 | 431 | self.result = ('<BackgroundJob died, call jobs.traceback() for details>') |
|
431 | 432 | self._tb = self._make_tb() |
|
432 | 433 | else: |
|
433 | 434 | self.status = BackgroundJobBase.stat_completed |
|
434 | 435 | self.stat_code = BackgroundJobBase.stat_completed_c |
|
435 | 436 | self.finished = True |
|
436 | 437 | |
|
437 | 438 | |
|
438 | 439 | class BackgroundJobExpr(BackgroundJobBase): |
|
439 | 440 | """Evaluate an expression as a background job (uses a separate thread).""" |
|
440 | 441 | |
|
441 | 442 | def __init__(self, expression, glob=None, loc=None): |
|
442 | 443 | """Create a new job from a string which can be fed to eval(). |
|
443 | 444 | |
|
444 | 445 | global/locals dicts can be provided, which will be passed to the eval |
|
445 | 446 | call.""" |
|
446 | 447 | |
|
447 | 448 | # fail immediately if the given expression can't be compiled |
|
448 | 449 | self.code = compile(expression,'<BackgroundJob compilation>','eval') |
|
449 | 450 | |
|
450 | 451 | glob = {} if glob is None else glob |
|
451 | 452 | loc = {} if loc is None else loc |
|
452 | 453 | self.expression = self.strform = expression |
|
453 | 454 | self.glob = glob |
|
454 | 455 | self.loc = loc |
|
455 | 456 | self._init() |
|
456 | 457 | |
|
457 | 458 | def call(self): |
|
458 | 459 | return eval(self.code,self.glob,self.loc) |
|
459 | 460 | |
|
460 | 461 | |
|
461 | 462 | class BackgroundJobFunc(BackgroundJobBase): |
|
462 | 463 | """Run a function call as a background job (uses a separate thread).""" |
|
463 | 464 | |
|
464 | 465 | def __init__(self, func, *args, **kwargs): |
|
465 | 466 | """Create a new job from a callable object. |
|
466 | 467 | |
|
467 | 468 | Any positional arguments and keyword args given to this constructor |
|
468 | 469 | after the initial callable are passed directly to it.""" |
|
469 | 470 | |
|
470 | 471 | if not callable(func): |
|
471 | 472 | raise TypeError( |
|
472 | 473 | 'first argument to BackgroundJobFunc must be callable') |
|
473 | 474 | |
|
474 | 475 | self.func = func |
|
475 | 476 | self.args = args |
|
476 | 477 | self.kwargs = kwargs |
|
477 | 478 | # The string form will only include the function passed, because |
|
478 | 479 | # generating string representations of the arguments is a potentially |
|
479 | 480 | # _very_ expensive operation (e.g. with large arrays). |
|
480 | 481 | self.strform = str(func) |
|
481 | 482 | self._init() |
|
482 | 483 | |
|
483 | 484 | def call(self): |
|
484 | 485 | return self.func(*self.args, **self.kwargs) |
@@ -1,337 +1,338 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """ |
|
3 | 3 | A module to change reload() so that it acts recursively. |
|
4 | 4 | To enable it type:: |
|
5 | 5 | |
|
6 | 6 | import __builtin__, deepreload |
|
7 | 7 | __builtin__.reload = deepreload.reload |
|
8 | 8 | |
|
9 | 9 | You can then disable it with:: |
|
10 | 10 | |
|
11 | 11 | __builtin__.reload = deepreload.original_reload |
|
12 | 12 | |
|
13 | 13 | Alternatively, you can add a dreload builtin alongside normal reload with:: |
|
14 | 14 | |
|
15 | 15 | __builtin__.dreload = deepreload.reload |
|
16 | 16 | |
|
17 | 17 | This code is almost entirely based on knee.py, which is a Python |
|
18 | 18 | re-implementation of hierarchical module import. |
|
19 | 19 | """ |
|
20 | from __future__ import print_function | |
|
20 | 21 | #***************************************************************************** |
|
21 | 22 | # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu> |
|
22 | 23 | # |
|
23 | 24 | # Distributed under the terms of the BSD License. The full license is in |
|
24 | 25 | # the file COPYING, distributed as part of this software. |
|
25 | 26 | #***************************************************************************** |
|
26 | 27 | |
|
27 | 28 | import __builtin__ |
|
28 | 29 | from contextlib import contextmanager |
|
29 | 30 | import imp |
|
30 | 31 | import sys |
|
31 | 32 | |
|
32 | 33 | from types import ModuleType |
|
33 | 34 | from warnings import warn |
|
34 | 35 | |
|
35 | 36 | original_import = __builtin__.__import__ |
|
36 | 37 | |
|
37 | 38 | @contextmanager |
|
38 | 39 | def replace_import_hook(new_import): |
|
39 | 40 | saved_import = __builtin__.__import__ |
|
40 | 41 | __builtin__.__import__ = new_import |
|
41 | 42 | try: |
|
42 | 43 | yield |
|
43 | 44 | finally: |
|
44 | 45 | __builtin__.__import__ = saved_import |
|
45 | 46 | |
|
46 | 47 | def get_parent(globals, level): |
|
47 | 48 | """ |
|
48 | 49 | parent, name = get_parent(globals, level) |
|
49 | 50 | |
|
50 | 51 | Return the package that an import is being performed in. If globals comes |
|
51 | 52 | from the module foo.bar.bat (not itself a package), this returns the |
|
52 | 53 | sys.modules entry for foo.bar. If globals is from a package's __init__.py, |
|
53 | 54 | the package's entry in sys.modules is returned. |
|
54 | 55 | |
|
55 | 56 | If globals doesn't come from a package or a module in a package, or a |
|
56 | 57 | corresponding entry is not found in sys.modules, None is returned. |
|
57 | 58 | """ |
|
58 | 59 | orig_level = level |
|
59 | 60 | |
|
60 | 61 | if not level or not isinstance(globals, dict): |
|
61 | 62 | return None, '' |
|
62 | 63 | |
|
63 | 64 | pkgname = globals.get('__package__', None) |
|
64 | 65 | |
|
65 | 66 | if pkgname is not None: |
|
66 | 67 | # __package__ is set, so use it |
|
67 | 68 | if not hasattr(pkgname, 'rindex'): |
|
68 | 69 | raise ValueError('__package__ set to non-string') |
|
69 | 70 | if len(pkgname) == 0: |
|
70 | 71 | if level > 0: |
|
71 | 72 | raise ValueError('Attempted relative import in non-package') |
|
72 | 73 | return None, '' |
|
73 | 74 | name = pkgname |
|
74 | 75 | else: |
|
75 | 76 | # __package__ not set, so figure it out and set it |
|
76 | 77 | if '__name__' not in globals: |
|
77 | 78 | return None, '' |
|
78 | 79 | modname = globals['__name__'] |
|
79 | 80 | |
|
80 | 81 | if '__path__' in globals: |
|
81 | 82 | # __path__ is set, so modname is already the package name |
|
82 | 83 | globals['__package__'] = name = modname |
|
83 | 84 | else: |
|
84 | 85 | # Normal module, so work out the package name if any |
|
85 | 86 | lastdot = modname.rfind('.') |
|
86 | 87 | if lastdot < 0 and level > 0: |
|
87 | 88 | raise ValueError("Attempted relative import in non-package") |
|
88 | 89 | if lastdot < 0: |
|
89 | 90 | globals['__package__'] = None |
|
90 | 91 | return None, '' |
|
91 | 92 | globals['__package__'] = name = modname[:lastdot] |
|
92 | 93 | |
|
93 | 94 | dot = len(name) |
|
94 | 95 | for x in xrange(level, 1, -1): |
|
95 | 96 | try: |
|
96 | 97 | dot = name.rindex('.', 0, dot) |
|
97 | 98 | except ValueError: |
|
98 | 99 | raise ValueError("attempted relative import beyond top-level " |
|
99 | 100 | "package") |
|
100 | 101 | name = name[:dot] |
|
101 | 102 | |
|
102 | 103 | try: |
|
103 | 104 | parent = sys.modules[name] |
|
104 | 105 | except: |
|
105 | 106 | if orig_level < 1: |
|
106 | 107 | warn("Parent module '%.200s' not found while handling absolute " |
|
107 | 108 | "import" % name) |
|
108 | 109 | parent = None |
|
109 | 110 | else: |
|
110 | 111 | raise SystemError("Parent module '%.200s' not loaded, cannot " |
|
111 | 112 | "perform relative import" % name) |
|
112 | 113 | |
|
113 | 114 | # We expect, but can't guarantee, if parent != None, that: |
|
114 | 115 | # - parent.__name__ == name |
|
115 | 116 | # - parent.__dict__ is globals |
|
116 | 117 | # If this is violated... Who cares? |
|
117 | 118 | return parent, name |
|
118 | 119 | |
|
119 | 120 | def load_next(mod, altmod, name, buf): |
|
120 | 121 | """ |
|
121 | 122 | mod, name, buf = load_next(mod, altmod, name, buf) |
|
122 | 123 | |
|
123 | 124 | altmod is either None or same as mod |
|
124 | 125 | """ |
|
125 | 126 | |
|
126 | 127 | if len(name) == 0: |
|
127 | 128 | # completely empty module name should only happen in |
|
128 | 129 | # 'from . import' (or '__import__("")') |
|
129 | 130 | return mod, None, buf |
|
130 | 131 | |
|
131 | 132 | dot = name.find('.') |
|
132 | 133 | if dot == 0: |
|
133 | 134 | raise ValueError('Empty module name') |
|
134 | 135 | |
|
135 | 136 | if dot < 0: |
|
136 | 137 | subname = name |
|
137 | 138 | next = None |
|
138 | 139 | else: |
|
139 | 140 | subname = name[:dot] |
|
140 | 141 | next = name[dot+1:] |
|
141 | 142 | |
|
142 | 143 | if buf != '': |
|
143 | 144 | buf += '.' |
|
144 | 145 | buf += subname |
|
145 | 146 | |
|
146 | 147 | result = import_submodule(mod, subname, buf) |
|
147 | 148 | if result is None and mod != altmod: |
|
148 | 149 | result = import_submodule(altmod, subname, subname) |
|
149 | 150 | if result is not None: |
|
150 | 151 | buf = subname |
|
151 | 152 | |
|
152 | 153 | if result is None: |
|
153 | 154 | raise ImportError("No module named %.200s" % name) |
|
154 | 155 | |
|
155 | 156 | return result, next, buf |
|
156 | 157 | |
|
157 | 158 | # Need to keep track of what we've already reloaded to prevent cyclic evil |
|
158 | 159 | found_now = {} |
|
159 | 160 | |
|
160 | 161 | def import_submodule(mod, subname, fullname): |
|
161 | 162 | """m = import_submodule(mod, subname, fullname)""" |
|
162 | 163 | # Require: |
|
163 | 164 | # if mod == None: subname == fullname |
|
164 | 165 | # else: mod.__name__ + "." + subname == fullname |
|
165 | 166 | |
|
166 | 167 | global found_now |
|
167 | 168 | if fullname in found_now and fullname in sys.modules: |
|
168 | 169 | m = sys.modules[fullname] |
|
169 | 170 | else: |
|
170 |
print |
|
|
171 | print('Reloading', fullname) | |
|
171 | 172 | found_now[fullname] = 1 |
|
172 | 173 | oldm = sys.modules.get(fullname, None) |
|
173 | 174 | |
|
174 | 175 | if mod is None: |
|
175 | 176 | path = None |
|
176 | 177 | elif hasattr(mod, '__path__'): |
|
177 | 178 | path = mod.__path__ |
|
178 | 179 | else: |
|
179 | 180 | return None |
|
180 | 181 | |
|
181 | 182 | try: |
|
182 | 183 | # This appears to be necessary on Python 3, because imp.find_module() |
|
183 | 184 | # tries to import standard libraries (like io) itself, and we don't |
|
184 | 185 | # want them to be processed by our deep_import_hook. |
|
185 | 186 | with replace_import_hook(original_import): |
|
186 | 187 | fp, filename, stuff = imp.find_module(subname, path) |
|
187 | 188 | except ImportError: |
|
188 | 189 | return None |
|
189 | 190 | |
|
190 | 191 | try: |
|
191 | 192 | m = imp.load_module(fullname, fp, filename, stuff) |
|
192 | 193 | except: |
|
193 | 194 | # load_module probably removed name from modules because of |
|
194 | 195 | # the error. Put back the original module object. |
|
195 | 196 | if oldm: |
|
196 | 197 | sys.modules[fullname] = oldm |
|
197 | 198 | raise |
|
198 | 199 | finally: |
|
199 | 200 | if fp: fp.close() |
|
200 | 201 | |
|
201 | 202 | add_submodule(mod, m, fullname, subname) |
|
202 | 203 | |
|
203 | 204 | return m |
|
204 | 205 | |
|
205 | 206 | def add_submodule(mod, submod, fullname, subname): |
|
206 | 207 | """mod.{subname} = submod""" |
|
207 | 208 | if mod is None: |
|
208 | 209 | return #Nothing to do here. |
|
209 | 210 | |
|
210 | 211 | if submod is None: |
|
211 | 212 | submod = sys.modules[fullname] |
|
212 | 213 | |
|
213 | 214 | setattr(mod, subname, submod) |
|
214 | 215 | |
|
215 | 216 | return |
|
216 | 217 | |
|
217 | 218 | def ensure_fromlist(mod, fromlist, buf, recursive): |
|
218 | 219 | """Handle 'from module import a, b, c' imports.""" |
|
219 | 220 | if not hasattr(mod, '__path__'): |
|
220 | 221 | return |
|
221 | 222 | for item in fromlist: |
|
222 | 223 | if not hasattr(item, 'rindex'): |
|
223 | 224 | raise TypeError("Item in ``from list'' not a string") |
|
224 | 225 | if item == '*': |
|
225 | 226 | if recursive: |
|
226 | 227 | continue # avoid endless recursion |
|
227 | 228 | try: |
|
228 | 229 | all = mod.__all__ |
|
229 | 230 | except AttributeError: |
|
230 | 231 | pass |
|
231 | 232 | else: |
|
232 | 233 | ret = ensure_fromlist(mod, all, buf, 1) |
|
233 | 234 | if not ret: |
|
234 | 235 | return 0 |
|
235 | 236 | elif not hasattr(mod, item): |
|
236 | 237 | import_submodule(mod, item, buf + '.' + item) |
|
237 | 238 | |
|
238 | 239 | def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1): |
|
239 | 240 | """Replacement for __import__()""" |
|
240 | 241 | parent, buf = get_parent(globals, level) |
|
241 | 242 | |
|
242 | 243 | head, name, buf = load_next(parent, None if level < 0 else parent, name, buf) |
|
243 | 244 | |
|
244 | 245 | tail = head |
|
245 | 246 | while name: |
|
246 | 247 | tail, name, buf = load_next(tail, tail, name, buf) |
|
247 | 248 | |
|
248 | 249 | # If tail is None, both get_parent and load_next found |
|
249 | 250 | # an empty module name: someone called __import__("") or |
|
250 | 251 | # doctored faulty bytecode |
|
251 | 252 | if tail is None: |
|
252 | 253 | raise ValueError('Empty module name') |
|
253 | 254 | |
|
254 | 255 | if not fromlist: |
|
255 | 256 | return head |
|
256 | 257 | |
|
257 | 258 | ensure_fromlist(tail, fromlist, buf, 0) |
|
258 | 259 | return tail |
|
259 | 260 | |
|
260 | 261 | modules_reloading = {} |
|
261 | 262 | |
|
262 | 263 | def deep_reload_hook(m): |
|
263 | 264 | """Replacement for reload().""" |
|
264 | 265 | if not isinstance(m, ModuleType): |
|
265 | 266 | raise TypeError("reload() argument must be module") |
|
266 | 267 | |
|
267 | 268 | name = m.__name__ |
|
268 | 269 | |
|
269 | 270 | if name not in sys.modules: |
|
270 | 271 | raise ImportError("reload(): module %.200s not in sys.modules" % name) |
|
271 | 272 | |
|
272 | 273 | global modules_reloading |
|
273 | 274 | try: |
|
274 | 275 | return modules_reloading[name] |
|
275 | 276 | except: |
|
276 | 277 | modules_reloading[name] = m |
|
277 | 278 | |
|
278 | 279 | dot = name.rfind('.') |
|
279 | 280 | if dot < 0: |
|
280 | 281 | subname = name |
|
281 | 282 | path = None |
|
282 | 283 | else: |
|
283 | 284 | try: |
|
284 | 285 | parent = sys.modules[name[:dot]] |
|
285 | 286 | except KeyError: |
|
286 | 287 | modules_reloading.clear() |
|
287 | 288 | raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot]) |
|
288 | 289 | subname = name[dot+1:] |
|
289 | 290 | path = getattr(parent, "__path__", None) |
|
290 | 291 | |
|
291 | 292 | try: |
|
292 | 293 | # This appears to be necessary on Python 3, because imp.find_module() |
|
293 | 294 | # tries to import standard libraries (like io) itself, and we don't |
|
294 | 295 | # want them to be processed by our deep_import_hook. |
|
295 | 296 | with replace_import_hook(original_import): |
|
296 | 297 | fp, filename, stuff = imp.find_module(subname, path) |
|
297 | 298 | finally: |
|
298 | 299 | modules_reloading.clear() |
|
299 | 300 | |
|
300 | 301 | try: |
|
301 | 302 | newm = imp.load_module(name, fp, filename, stuff) |
|
302 | 303 | except: |
|
303 | 304 | # load_module probably removed name from modules because of |
|
304 | 305 | # the error. Put back the original module object. |
|
305 | 306 | sys.modules[name] = m |
|
306 | 307 | raise |
|
307 | 308 | finally: |
|
308 | 309 | if fp: fp.close() |
|
309 | 310 | |
|
310 | 311 | modules_reloading.clear() |
|
311 | 312 | return newm |
|
312 | 313 | |
|
313 | 314 | # Save the original hooks |
|
314 | 315 | try: |
|
315 | 316 | original_reload = __builtin__.reload |
|
316 | 317 | except AttributeError: |
|
317 | 318 | original_reload = imp.reload # Python 3 |
|
318 | 319 | |
|
319 | 320 | # Replacement for reload() |
|
320 | 321 | def reload(module, exclude=['sys', 'os.path', '__builtin__', '__main__']): |
|
321 | 322 | """Recursively reload all modules used in the given module. Optionally |
|
322 | 323 | takes a list of modules to exclude from reloading. The default exclude |
|
323 | 324 | list contains sys, __main__, and __builtin__, to prevent, e.g., resetting |
|
324 | 325 | display, exception, and io hooks. |
|
325 | 326 | """ |
|
326 | 327 | global found_now |
|
327 | 328 | for i in exclude: |
|
328 | 329 | found_now[i] = 1 |
|
329 | 330 | try: |
|
330 | 331 | with replace_import_hook(deep_import_hook): |
|
331 | 332 | return deep_reload_hook(module) |
|
332 | 333 | finally: |
|
333 | 334 | found_now = {} |
|
334 | 335 | |
|
335 | 336 | # Uncomment the following to automatically activate deep reloading whenever |
|
336 | 337 | # this module is imported |
|
337 | 338 | #__builtin__.reload = reload |
@@ -1,122 +1,123 b'' | |||
|
1 | 1 | """ 'editor' hooks for common editors that work well with ipython |
|
2 | 2 | |
|
3 | 3 | They should honor the line number argument, at least. |
|
4 | 4 | |
|
5 | 5 | Contributions are *very* welcome. |
|
6 | 6 | """ |
|
7 | from __future__ import print_function | |
|
7 | 8 | |
|
8 | 9 | import os |
|
9 | 10 | import pipes |
|
10 | 11 | import subprocess |
|
11 | 12 | |
|
12 | 13 | from IPython import get_ipython |
|
13 | 14 | from IPython.core.error import TryNext |
|
14 | 15 | |
|
15 | 16 | |
|
16 | 17 | def install_editor(template, wait=False): |
|
17 | 18 | """Installs the editor that is called by IPython for the %edit magic. |
|
18 | 19 | |
|
19 | 20 | This overrides the default editor, which is generally set by your EDITOR |
|
20 | 21 | environment variable or is notepad (windows) or vi (linux). By supplying a |
|
21 | 22 | template string `run_template`, you can control how the editor is invoked |
|
22 | 23 | by IPython -- (e.g. the format in which it accepts command line options) |
|
23 | 24 | |
|
24 | 25 | Parameters |
|
25 | 26 | ---------- |
|
26 | 27 | template : basestring |
|
27 | 28 | run_template acts as a template for how your editor is invoked by |
|
28 | 29 | the shell. It should contain '{filename}', which will be replaced on |
|
29 | 30 | invokation with the file name, and '{line}', $line by line number |
|
30 | 31 | (or 0) to invoke the file with. |
|
31 | 32 | wait : bool |
|
32 | 33 | If `wait` is true, wait until the user presses enter before returning, |
|
33 | 34 | to facilitate non-blocking editors that exit immediately after |
|
34 | 35 | the call. |
|
35 | 36 | """ |
|
36 | 37 | |
|
37 | 38 | # not all editors support $line, so we'll leave out this check |
|
38 | 39 | # for substitution in ['$file', '$line']: |
|
39 | 40 | # if not substitution in run_template: |
|
40 | 41 | # raise ValueError(('run_template should contain %s' |
|
41 | 42 | # ' for string substitution. You supplied "%s"' % (substitution, |
|
42 | 43 | # run_template))) |
|
43 | 44 | |
|
44 | 45 | def call_editor(self, filename, line=0): |
|
45 | 46 | if line is None: |
|
46 | 47 | line = 0 |
|
47 | 48 | cmd = template.format(filename=pipes.quote(filename), line=line) |
|
48 |
print |
|
|
49 | print(">", cmd) | |
|
49 | 50 | proc = subprocess.Popen(cmd, shell=True) |
|
50 | 51 | if wait and proc.wait() != 0: |
|
51 | 52 | raise TryNext() |
|
52 | 53 | if wait: |
|
53 | 54 | raw_input("Press Enter when done editing:") |
|
54 | 55 | |
|
55 | 56 | get_ipython().set_hook('editor', call_editor) |
|
56 | 57 | get_ipython().editor = template |
|
57 | 58 | |
|
58 | 59 | |
|
59 | 60 | # in these, exe is always the path/name of the executable. Useful |
|
60 | 61 | # if you don't have the editor directory in your path |
|
61 | 62 | def komodo(exe=u'komodo'): |
|
62 | 63 | """ Activestate Komodo [Edit] """ |
|
63 | 64 | install_editor(exe + u' -l {line} {filename}', wait=True) |
|
64 | 65 | |
|
65 | 66 | |
|
66 | 67 | def scite(exe=u"scite"): |
|
67 | 68 | """ SciTE or Sc1 """ |
|
68 | 69 | install_editor(exe + u' {filename} -goto:{line}') |
|
69 | 70 | |
|
70 | 71 | |
|
71 | 72 | def notepadplusplus(exe=u'notepad++'): |
|
72 | 73 | """ Notepad++ http://notepad-plus.sourceforge.net """ |
|
73 | 74 | install_editor(exe + u' -n{line} {filename}') |
|
74 | 75 | |
|
75 | 76 | |
|
76 | 77 | def jed(exe=u'jed'): |
|
77 | 78 | """ JED, the lightweight emacsish editor """ |
|
78 | 79 | install_editor(exe + u' +{line} {filename}') |
|
79 | 80 | |
|
80 | 81 | |
|
81 | 82 | def idle(exe=u'idle'): |
|
82 | 83 | """ Idle, the editor bundled with python |
|
83 | 84 | |
|
84 | 85 | Parameters |
|
85 | 86 | ---------- |
|
86 | 87 | exe : str, None |
|
87 | 88 | If none, should be pretty smart about finding the executable. |
|
88 | 89 | """ |
|
89 | 90 | if exe is None: |
|
90 | 91 | import idlelib |
|
91 | 92 | p = os.path.dirname(idlelib.__filename__) |
|
92 | 93 | # i'm not sure if this actually works. Is this idle.py script |
|
93 | 94 | # guarenteed to be executable? |
|
94 | 95 | exe = os.path.join(p, 'idle.py') |
|
95 | 96 | install_editor(exe + u' {filename}') |
|
96 | 97 | |
|
97 | 98 | |
|
98 | 99 | def mate(exe=u'mate'): |
|
99 | 100 | """ TextMate, the missing editor""" |
|
100 | 101 | # wait=True is not required since we're using the -w flag to mate |
|
101 | 102 | install_editor(exe + u' -w -l {line} {filename}') |
|
102 | 103 | |
|
103 | 104 | |
|
104 | 105 | # ########################################## |
|
105 | 106 | # these are untested, report any problems |
|
106 | 107 | # ########################################## |
|
107 | 108 | |
|
108 | 109 | |
|
109 | 110 | def emacs(exe=u'emacs'): |
|
110 | 111 | install_editor(exe + u' +{line} {filename}') |
|
111 | 112 | |
|
112 | 113 | |
|
113 | 114 | def gnuclient(exe=u'gnuclient'): |
|
114 | 115 | install_editor(exe + u' -nw +{line} {filename}') |
|
115 | 116 | |
|
116 | 117 | |
|
117 | 118 | def crimson_editor(exe=u'cedt.exe'): |
|
118 | 119 | install_editor(exe + u' /L:{line} {filename}') |
|
119 | 120 | |
|
120 | 121 | |
|
121 | 122 | def kate(exe=u'kate'): |
|
122 | 123 | install_editor(exe + u' -u -l {line} {filename}') |
@@ -1,172 +1,173 b'' | |||
|
1 | 1 | # coding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | GLUT Inputhook support functions |
|
4 | 4 | """ |
|
5 | from __future__ import print_function | |
|
5 | 6 | |
|
6 | 7 | #----------------------------------------------------------------------------- |
|
7 | 8 | # Copyright (C) 2008-2011 The IPython Development Team |
|
8 | 9 | # |
|
9 | 10 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | 11 | # the file COPYING, distributed as part of this software. |
|
11 | 12 | #----------------------------------------------------------------------------- |
|
12 | 13 | |
|
13 | 14 | # GLUT is quite an old library and it is difficult to ensure proper |
|
14 | 15 | # integration within IPython since original GLUT does not allow to handle |
|
15 | 16 | # events one by one. Instead, it requires for the mainloop to be entered |
|
16 | 17 | # and never returned (there is not even a function to exit he |
|
17 | 18 | # mainloop). Fortunately, there are alternatives such as freeglut |
|
18 | 19 | # (available for linux and windows) and the OSX implementation gives |
|
19 | 20 | # access to a glutCheckLoop() function that blocks itself until a new |
|
20 | 21 | # event is received. This means we have to setup the idle callback to |
|
21 | 22 | # ensure we got at least one event that will unblock the function. |
|
22 | 23 | # |
|
23 | 24 | # Furthermore, it is not possible to install these handlers without a window |
|
24 | 25 | # being first created. We choose to make this window invisible. This means that |
|
25 | 26 | # display mode options are set at this level and user won't be able to change |
|
26 | 27 | # them later without modifying the code. This should probably be made available |
|
27 | 28 | # via IPython options system. |
|
28 | 29 | |
|
29 | 30 | #----------------------------------------------------------------------------- |
|
30 | 31 | # Imports |
|
31 | 32 | #----------------------------------------------------------------------------- |
|
32 | 33 | import os |
|
33 | 34 | import sys |
|
34 | 35 | import time |
|
35 | 36 | import signal |
|
36 | 37 | import OpenGL.GLUT as glut |
|
37 | 38 | import OpenGL.platform as platform |
|
38 | 39 | from timeit import default_timer as clock |
|
39 | 40 | |
|
40 | 41 | #----------------------------------------------------------------------------- |
|
41 | 42 | # Constants |
|
42 | 43 | #----------------------------------------------------------------------------- |
|
43 | 44 | |
|
44 | 45 | # Frame per second : 60 |
|
45 | 46 | # Should probably be an IPython option |
|
46 | 47 | glut_fps = 60 |
|
47 | 48 | |
|
48 | 49 | |
|
49 | 50 | # Display mode : double buffeed + rgba + depth |
|
50 | 51 | # Should probably be an IPython option |
|
51 | 52 | glut_display_mode = (glut.GLUT_DOUBLE | |
|
52 | 53 | glut.GLUT_RGBA | |
|
53 | 54 | glut.GLUT_DEPTH) |
|
54 | 55 | |
|
55 | 56 | glutMainLoopEvent = None |
|
56 | 57 | if sys.platform == 'darwin': |
|
57 | 58 | try: |
|
58 | 59 | glutCheckLoop = platform.createBaseFunction( |
|
59 | 60 | 'glutCheckLoop', dll=platform.GLUT, resultType=None, |
|
60 | 61 | argTypes=[], |
|
61 | 62 | doc='glutCheckLoop( ) -> None', |
|
62 | 63 | argNames=(), |
|
63 | 64 | ) |
|
64 | 65 | except AttributeError: |
|
65 | 66 | raise RuntimeError( |
|
66 | 67 | '''Your glut implementation does not allow interactive sessions''' |
|
67 | 68 | '''Consider installing freeglut.''') |
|
68 | 69 | glutMainLoopEvent = glutCheckLoop |
|
69 | 70 | elif glut.HAVE_FREEGLUT: |
|
70 | 71 | glutMainLoopEvent = glut.glutMainLoopEvent |
|
71 | 72 | else: |
|
72 | 73 | raise RuntimeError( |
|
73 | 74 | '''Your glut implementation does not allow interactive sessions. ''' |
|
74 | 75 | '''Consider installing freeglut.''') |
|
75 | 76 | |
|
76 | 77 | |
|
77 | 78 | #----------------------------------------------------------------------------- |
|
78 | 79 | # Platform-dependent imports and functions |
|
79 | 80 | #----------------------------------------------------------------------------- |
|
80 | 81 | |
|
81 | 82 | if os.name == 'posix': |
|
82 | 83 | import select |
|
83 | 84 | |
|
84 | 85 | def stdin_ready(): |
|
85 | 86 | infds, outfds, erfds = select.select([sys.stdin],[],[],0) |
|
86 | 87 | if infds: |
|
87 | 88 | return True |
|
88 | 89 | else: |
|
89 | 90 | return False |
|
90 | 91 | |
|
91 | 92 | elif sys.platform == 'win32': |
|
92 | 93 | import msvcrt |
|
93 | 94 | |
|
94 | 95 | def stdin_ready(): |
|
95 | 96 | return msvcrt.kbhit() |
|
96 | 97 | |
|
97 | 98 | #----------------------------------------------------------------------------- |
|
98 | 99 | # Callback functions |
|
99 | 100 | #----------------------------------------------------------------------------- |
|
100 | 101 | |
|
101 | 102 | def glut_display(): |
|
102 | 103 | # Dummy display function |
|
103 | 104 | pass |
|
104 | 105 | |
|
105 | 106 | def glut_idle(): |
|
106 | 107 | # Dummy idle function |
|
107 | 108 | pass |
|
108 | 109 | |
|
109 | 110 | def glut_close(): |
|
110 | 111 | # Close function only hides the current window |
|
111 | 112 | glut.glutHideWindow() |
|
112 | 113 | glutMainLoopEvent() |
|
113 | 114 | |
|
114 | 115 | def glut_int_handler(signum, frame): |
|
115 | 116 | # Catch sigint and print the defautl message |
|
116 | 117 | signal.signal(signal.SIGINT, signal.default_int_handler) |
|
117 |
print |
|
|
118 | print('\nKeyboardInterrupt') | |
|
118 | 119 | # Need to reprint the prompt at this stage |
|
119 | 120 | |
|
120 | 121 | |
|
121 | 122 | |
|
122 | 123 | #----------------------------------------------------------------------------- |
|
123 | 124 | # Code |
|
124 | 125 | #----------------------------------------------------------------------------- |
|
125 | 126 | def inputhook_glut(): |
|
126 | 127 | """Run the pyglet event loop by processing pending events only. |
|
127 | 128 | |
|
128 | 129 | This keeps processing pending events until stdin is ready. After |
|
129 | 130 | processing all pending events, a call to time.sleep is inserted. This is |
|
130 | 131 | needed, otherwise, CPU usage is at 100%. This sleep time should be tuned |
|
131 | 132 | though for best performance. |
|
132 | 133 | """ |
|
133 | 134 | # We need to protect against a user pressing Control-C when IPython is |
|
134 | 135 | # idle and this is running. We trap KeyboardInterrupt and pass. |
|
135 | 136 | |
|
136 | 137 | signal.signal(signal.SIGINT, glut_int_handler) |
|
137 | 138 | |
|
138 | 139 | try: |
|
139 | 140 | t = clock() |
|
140 | 141 | |
|
141 | 142 | # Make sure the default window is set after a window has been closed |
|
142 | 143 | if glut.glutGetWindow() == 0: |
|
143 | 144 | glut.glutSetWindow( 1 ) |
|
144 | 145 | glutMainLoopEvent() |
|
145 | 146 | return 0 |
|
146 | 147 | |
|
147 | 148 | while not stdin_ready(): |
|
148 | 149 | glutMainLoopEvent() |
|
149 | 150 | # We need to sleep at this point to keep the idle CPU load |
|
150 | 151 | # low. However, if sleep to long, GUI response is poor. As |
|
151 | 152 | # a compromise, we watch how often GUI events are being processed |
|
152 | 153 | # and switch between a short and long sleep time. Here are some |
|
153 | 154 | # stats useful in helping to tune this. |
|
154 | 155 | # time CPU load |
|
155 | 156 | # 0.001 13% |
|
156 | 157 | # 0.005 3% |
|
157 | 158 | # 0.01 1.5% |
|
158 | 159 | # 0.05 0.5% |
|
159 | 160 | used_time = clock() - t |
|
160 | 161 | if used_time > 10.0: |
|
161 | 162 | # print 'Sleep for 1 s' # dbg |
|
162 | 163 | time.sleep(1.0) |
|
163 | 164 | elif used_time > 0.1: |
|
164 | 165 | # Few GUI events coming in, so we can sleep longer |
|
165 | 166 | # print 'Sleep for 0.05 s' # dbg |
|
166 | 167 | time.sleep(0.05) |
|
167 | 168 | else: |
|
168 | 169 | # Many GUI events coming in, so sleep only very little |
|
169 | 170 | time.sleep(0.001) |
|
170 | 171 | except KeyboardInterrupt: |
|
171 | 172 | pass |
|
172 | 173 | return 0 |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now