##// END OF EJS Templates
Convert print statements to print function calls...
Thomas Kluyver -
Show More
@@ -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 os.linesep.join(lines)
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 os.linesep.join(lines)
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 os.linesep.join(lines)
314 print(os.linesep.join(lines))
314 315 self.print_flag_help()
315 316 self.print_alias_help()
316 print
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 os.linesep.join(lines)
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 "Class parameters"
348 print "----------------"
349 print
348 print("Class parameters")
349 print("----------------")
350 print()
350 351 for p in wrap_paragraphs(self.keyvalue_description):
351 print p
352 print
352 print(p)
353 print()
353 354
354 355 for cls in self.classes:
355 356 cls.class_print_help()
356 print
357 print()
357 358 else:
358 print "To see all available configurables, use `--help-all`"
359 print
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 p
368 print
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 "Examples"
378 print "--------"
379 print
380 print indent(dedent(self.examples.strip()))
381 print
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 self.version
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 cls.class_get_help(inst)
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 'Logging is already',label[val]
150 print('Logging is already',label[val])
150 151 else:
151 print 'Switching logging',label[val]
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 'Logging has not been activated.'
159 print('Logging has not been activated.')
159 160 else:
160 161 state = self.log_active and 'active' or 'temporarily suspended'
161 print 'Filename :',self.logfname
162 print 'Mode :',self.logmode
163 print 'Output logging :',self.log_output
164 print 'Raw input log :',self.log_raw_input
165 print 'Timestamping :',self.timestamp
166 print 'State :',state
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 "Logging hadn't been started."
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 'Error in arguments:'
548 print oinspect.getdoc(func)
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 '\n' + self.shell.magics_manager.auto_status()
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 "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall]
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 "File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s)
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 'Operation cancelled.'
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 e.args[0]
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 'The following commands were written to file `%s`:' % fname
211 print cmds
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 e.args[0]
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 'Operation cancelled.'
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 'IPython will make a temporary file named:',filename
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 "Editing In[%i]" % e.index
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 'Editing...',
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 print
669 print()
669 670 else:
670 print 'done. Executing edited code...'
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 "Available objects for config:"
120 print("Available objects for config:")
120 121 for name in classnames:
121 print " ", name
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 help
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 '\n'.join([
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 '\n'.join([
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 sys_exit,
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 '\n*** Profile stats marshalled to file',\
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 '\n*** Profile printout saved to text file',\
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 'Automatic pdb calling has been turned',on_off(new_pdb)
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 '\n%run:\n', oinspect.getdoc(self.run)
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 "NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt
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 "\nIPython CPU timings (estimated):"
813 print " User : %10.2f s." % t_usr
814 print " System : %10.2f s." % t_sys
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 "\nIPython CPU timings (estimated):"
824 print "Total runs performed:", nruns
825 print " Times : %10s %10s" % ('Total', 'Per run')
826 print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)
827 print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)
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 "Wall time: %10.2f s." % (twall1 - twall0)
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 u"%d loops, best of %d: %s per loop" % (number, repeat,
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 "Compiler time: %.2f s" % tc
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 "CPU times: user %s, sys: %s, total: %s" % \
1114 (_format_time(cpu_user),_format_time(cpu_sys),_format_time(cpu_tot))
1115 print "Wall time: %s" % _format_time(wall_time)
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 "Compiler : %s" % _format_time(tc)
1118 print("Compiler : %s" % _format_time(tc))
1118 1119 if tp > tp_min:
1119 print "Parser : %s" % _format_time(tp)
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 e.args[0]
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 'Macro `%s` created. To execute, type its name (without quotes).' % name
1204 print '=== Macro contents: ==='
1205 print macro,
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 e
50 print(e)
50 51 return
51 52
52 53 filename = os.path.basename(filename)
53 print "Installed %s. To use it, type:" % filename
54 print " %%load_ext %s" % os.path.splitext(filename)[0]
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 "The %s extension is already loaded. To reload it, use:" % module_str
66 print " %reload_ext", module_str
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 "The %s module is not an IPython extension." % module_str
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 "The %s extension doesn't define how to unload it." % module_str
84 print("The %s extension doesn't define how to unload it." % module_str)
84 85 elif res == "not loaded":
85 print "The %s extension is not loaded." % module_str
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 msg
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 'Python identifiers can only contain ascii characters.'
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 'No variables match your requested type.'
331 print('No variables match your requested type.')
331 332 else:
332 print 'Interactive namespace is empty.'
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 i+'\t',
339 print(i+'\t', end=' ')
339 340 count += 1
340 341 if count > 8:
341 342 count = 0
342 print
343 print
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 'No variables match your requested type.'
382 print('No variables match your requested type.')
382 383 else:
383 print 'Interactive namespace is empty.'
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 varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
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 vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth),
442 print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ')
442 443 if vtype in seq_types:
443 print "n="+str(len(var))
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 aformat % (vshape, vsize, vdtype, vbytes)
454 print(aformat % (vshape, vsize, vdtype, vbytes))
454 455 else:
455 print aformat % (vshape, vsize, vdtype, vbytes),
456 print(aformat % (vshape, vsize, vdtype, vbytes), end=' ')
456 457 if vbytes < Mb:
457 print '(%s kb)' % (vbytes/kb,)
458 print('(%s kb)' % (vbytes/kb,))
458 459 else:
459 print '(%s Mb)' % (vbytes/Mb,)
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 vstr
471 print(vstr)
471 472 else:
472 print vstr[:25] + "<...>" + vstr[-25:]
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 'Nothing done.'
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 "Flushing output cache (%d entries)" % len(user_ns['_oh'])
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 "Flushing input history"
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 "reset array only works if Numpy is available."
589 print("reset array only works if Numpy is available.")
589 590
590 591 elif target == 'dhist':
591 print "Flushing directory history"
592 print("Flushing directory history")
592 593 del user_ns['_dh'][:]
593 594
594 595 else:
595 print "Don't know how to reset ",
596 print target + ", please run `%reset?` for details"
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 'Nothing done.'
674 print('Nothing done.')
674 675 return
675 676 user_ns = self.shell.user_ns
676 677 if not regex:
677 print 'No regex pattern specified. Nothing done.'
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 type(e).__name__ +": "+ str(e)
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 "Total number of aliases:", len(aliases)
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 "Removing %stored alias",aname
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 'The requested directory does not exist in history.'
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 "No matching entry in directory history"
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 '(bookmark:%s) -> %s' % (ps, target)
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 sys.exc_info()[1]
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 self.shell.user_ns['_dh'][-1]
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 "popd ->",top
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 'Directory history (kept in _dh)'
445 print('Directory history (kept in _dh)')
445 446 for i in range(ini, fin):
446 print "%d: %s" % (i, dh[i])
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 '%s ==\n%s' % (var, pformat(out))
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 'Current bookmarks:'
671 print('Current bookmarks:')
671 672 for bk in bks:
672 print fmt % (bk, bkms[bk])
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 "Error: no such file, variable, URL, history range or macro"
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 "Appending to %s" % filename
731 print("Appending to %s" % filename)
731 732 else:
732 print "Overwriting %s" % filename
733 print("Overwriting %s" % filename)
733 734 else:
734 print "Writing %s" % filename
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 ("Using matplotlib backend: %s" % backend)
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 "Couldn't find program: %r" % cmd[0]
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 "Process is interrupted."
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 "Process is terminated."
220 print("Process is terminated.")
220 221 return
221 222 p.kill()
222 print "Process is killed."
223 print("Process is killed.")
223 224 except OSError:
224 225 pass
225 226 except Exception as e:
226 print "Error while terminating subprocess (pid=%i): %s" \
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 "All background processes were killed."
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 self.profile_dir.location
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 ' %s' % profile
164 print(' %s' % profile)
164 165
165 166 def list_profile_dirs(self):
166 167 profiles = list_bundled_profiles()
167 168 if profiles:
168 print
169 print "Available profiles in IPython:"
169 print()
170 print("Available profiles in IPython:")
170 171 self._print_profiles(profiles)
171 print
172 print " The first request for a bundled profile will copy it"
173 print " into your IPython directory (%s)," % self.ipython_dir
174 print " where you can customize it."
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 print
179 print "Available profiles in %s:" % self.ipython_dir
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 print
185 print "Available profiles in current directory (%s):" % os.getcwdu()
185 print()
186 print("Available profiles in current directory (%s):" % os.getcwdu())
186 187 self._print_profiles(profiles)
187 188
188 print
189 print "To use any of the above profiles, start IPython with:"
190 print " ipython --profile=<name>"
191 print
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 "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
308 print
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 ("Using matplotlib backend: %s" % backend)
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 sys.argv[1:]
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 'lowercased:',func().lower()
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 line
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 'Input source was:\n', src
501 print 'Raw source was:\n', raw
501 print('Input source was:\n', src)
502 print('Raw source was:\n', raw)
502 503 except EOFError:
503 print 'Bye'
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 "\nKeyboardInterrupt"
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 "\nKeyboardInterrupt"
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 '*** Before ***'
1244 print('')
1245 print('*** Before ***')
1245 1246 try:
1246 print spam(1, (2, 3))
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 '*** ColorTB ***'
1253 print('*** ColorTB ***')
1253 1254 try:
1254 print spam(1, (2, 3))
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 '*** VerboseTB ***'
1261 print('*** VerboseTB ***')
1261 1262 try:
1262 print spam(1, (2, 3))
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 "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
54 print "The error was:", sys.exc_info()[0]
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 "no stored variable %s" % arg
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 'Stored variables and their in-db values:'
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 fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
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 "Writing '%s' (%s) to file '%s'." % (args[0],
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 "Alias stored: %s (%s)" % (name, cmd)
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 textwrap.dedent("""\
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 "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
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 >> sys.stderr, 'Error in generated code:'
166 print >> sys.stderr, src
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 "removing existing MathJax at %s" % dest
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 "%s already exists" % mathjax_js
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 "Downloading mathjax source from %s" % mathjax_url
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 "Extracting to %s" % dest
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 dest
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 self.notebook_info()
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 "No answer for 5s:",
643 print "resuming operation..."
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 self.notebook_info()
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 "Directory already exists."
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 'Starting job # %s in a separate thread.' % job.num
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 '%s jobs:' % name
249 print('%s jobs:' % name)
249 250 for job in group:
250 print '%s : %s' % (job.num,job)
251 print
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 'Flushing %s %s job%s.' % (njobs,name,plural)
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 'No jobs to flush.'
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 "Traceback for: %r" % deadjob
349 print("Traceback for: %r" % deadjob)
349 350 self._traceback(deadjob)
350 print
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 self._tb
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 'Reloading', fullname
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 ">", cmd
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 '\nKeyboardInterrupt'
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,789 +1,790 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Python advanced pretty printer. This pretty printer is intended to
4 4 replace the old `pprint` python module which does not allow developers
5 5 to provide their own pretty print callbacks.
6 6
7 7 This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`.
8 8
9 9
10 10 Example Usage
11 11 -------------
12 12
13 13 To directly print the representation of an object use `pprint`::
14 14
15 15 from pretty import pprint
16 16 pprint(complex_object)
17 17
18 18 To get a string of the output use `pretty`::
19 19
20 20 from pretty import pretty
21 21 string = pretty(complex_object)
22 22
23 23
24 24 Extending
25 25 ---------
26 26
27 27 The pretty library allows developers to add pretty printing rules for their
28 28 own objects. This process is straightforward. All you have to do is to
29 29 add a `_repr_pretty_` method to your object and call the methods on the
30 30 pretty printer passed::
31 31
32 32 class MyObject(object):
33 33
34 34 def _repr_pretty_(self, p, cycle):
35 35 ...
36 36
37 37 Depending on the python version you want to support you have two
38 38 possibilities. The following list shows the python 2.5 version and the
39 39 compatibility one.
40 40
41 41
42 42 Here the example implementation of a `_repr_pretty_` method for a list
43 43 subclass for python 2.5 and higher (python 2.5 requires the with statement
44 44 __future__ import)::
45 45
46 46 class MyList(list):
47 47
48 48 def _repr_pretty_(self, p, cycle):
49 49 if cycle:
50 50 p.text('MyList(...)')
51 51 else:
52 52 with p.group(8, 'MyList([', '])'):
53 53 for idx, item in enumerate(self):
54 54 if idx:
55 55 p.text(',')
56 56 p.breakable()
57 57 p.pretty(item)
58 58
59 59 The `cycle` parameter is `True` if pretty detected a cycle. You *have* to
60 60 react to that or the result is an infinite loop. `p.text()` just adds
61 61 non breaking text to the output, `p.breakable()` either adds a whitespace
62 62 or breaks here. If you pass it an argument it's used instead of the
63 63 default space. `p.pretty` prettyprints another object using the pretty print
64 64 method.
65 65
66 66 The first parameter to the `group` function specifies the extra indentation
67 67 of the next line. In this example the next item will either be not
68 68 breaked (if the items are short enough) or aligned with the right edge of
69 69 the opening bracked of `MyList`.
70 70
71 71 If you want to support python 2.4 and lower you can use this code::
72 72
73 73 class MyList(list):
74 74
75 75 def _repr_pretty_(self, p, cycle):
76 76 if cycle:
77 77 p.text('MyList(...)')
78 78 else:
79 79 p.begin_group(8, 'MyList([')
80 80 for idx, item in enumerate(self):
81 81 if idx:
82 82 p.text(',')
83 83 p.breakable()
84 84 p.pretty(item)
85 85 p.end_group(8, '])')
86 86
87 87 If you just want to indent something you can use the group function
88 88 without open / close parameters. Under python 2.5 you can also use this
89 89 code::
90 90
91 91 with p.indent(2):
92 92 ...
93 93
94 94 Or under python2.4 you might want to modify ``p.indentation`` by hand but
95 95 this is rather ugly.
96 96
97 97 Inheritance diagram:
98 98
99 99 .. inheritance-diagram:: IPython.lib.pretty
100 100 :parts: 3
101 101
102 102 :copyright: 2007 by Armin Ronacher.
103 103 Portions (c) 2009 by Robert Kern.
104 104 :license: BSD License.
105 105 """
106 from __future__ import print_function
106 107 from contextlib import contextmanager
107 108 import sys
108 109 import types
109 110 import re
110 111 import datetime
111 112 from StringIO import StringIO
112 113 from collections import deque
113 114
114 115
115 116 __all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter',
116 117 'for_type', 'for_type_by_name']
117 118
118 119
119 120 _re_pattern_type = type(re.compile(''))
120 121
121 122
122 123 def pretty(obj, verbose=False, max_width=79, newline='\n'):
123 124 """
124 125 Pretty print the object's representation.
125 126 """
126 127 stream = StringIO()
127 128 printer = RepresentationPrinter(stream, verbose, max_width, newline)
128 129 printer.pretty(obj)
129 130 printer.flush()
130 131 return stream.getvalue()
131 132
132 133
133 134 def pprint(obj, verbose=False, max_width=79, newline='\n'):
134 135 """
135 136 Like `pretty` but print to stdout.
136 137 """
137 138 printer = RepresentationPrinter(sys.stdout, verbose, max_width, newline)
138 139 printer.pretty(obj)
139 140 printer.flush()
140 141 sys.stdout.write(newline)
141 142 sys.stdout.flush()
142 143
143 144 class _PrettyPrinterBase(object):
144 145
145 146 @contextmanager
146 147 def indent(self, indent):
147 148 """with statement support for indenting/dedenting."""
148 149 self.indentation += indent
149 150 try:
150 151 yield
151 152 finally:
152 153 self.indentation -= indent
153 154
154 155 @contextmanager
155 156 def group(self, indent=0, open='', close=''):
156 157 """like begin_group / end_group but for the with statement."""
157 158 self.begin_group(indent, open)
158 159 try:
159 160 yield
160 161 finally:
161 162 self.end_group(indent, close)
162 163
163 164 class PrettyPrinter(_PrettyPrinterBase):
164 165 """
165 166 Baseclass for the `RepresentationPrinter` prettyprinter that is used to
166 167 generate pretty reprs of objects. Contrary to the `RepresentationPrinter`
167 168 this printer knows nothing about the default pprinters or the `_repr_pretty_`
168 169 callback method.
169 170 """
170 171
171 172 def __init__(self, output, max_width=79, newline='\n'):
172 173 self.output = output
173 174 self.max_width = max_width
174 175 self.newline = newline
175 176 self.output_width = 0
176 177 self.buffer_width = 0
177 178 self.buffer = deque()
178 179
179 180 root_group = Group(0)
180 181 self.group_stack = [root_group]
181 182 self.group_queue = GroupQueue(root_group)
182 183 self.indentation = 0
183 184
184 185 def _break_outer_groups(self):
185 186 while self.max_width < self.output_width + self.buffer_width:
186 187 group = self.group_queue.deq()
187 188 if not group:
188 189 return
189 190 while group.breakables:
190 191 x = self.buffer.popleft()
191 192 self.output_width = x.output(self.output, self.output_width)
192 193 self.buffer_width -= x.width
193 194 while self.buffer and isinstance(self.buffer[0], Text):
194 195 x = self.buffer.popleft()
195 196 self.output_width = x.output(self.output, self.output_width)
196 197 self.buffer_width -= x.width
197 198
198 199 def text(self, obj):
199 200 """Add literal text to the output."""
200 201 width = len(obj)
201 202 if self.buffer:
202 203 text = self.buffer[-1]
203 204 if not isinstance(text, Text):
204 205 text = Text()
205 206 self.buffer.append(text)
206 207 text.add(obj, width)
207 208 self.buffer_width += width
208 209 self._break_outer_groups()
209 210 else:
210 211 self.output.write(obj)
211 212 self.output_width += width
212 213
213 214 def breakable(self, sep=' '):
214 215 """
215 216 Add a breakable separator to the output. This does not mean that it
216 217 will automatically break here. If no breaking on this position takes
217 218 place the `sep` is inserted which default to one space.
218 219 """
219 220 width = len(sep)
220 221 group = self.group_stack[-1]
221 222 if group.want_break:
222 223 self.flush()
223 224 self.output.write(self.newline)
224 225 self.output.write(' ' * self.indentation)
225 226 self.output_width = self.indentation
226 227 self.buffer_width = 0
227 228 else:
228 229 self.buffer.append(Breakable(sep, width, self))
229 230 self.buffer_width += width
230 231 self._break_outer_groups()
231 232
232 233 def break_(self):
233 234 """
234 235 Explicitly insert a newline into the output, maintaining correct indentation.
235 236 """
236 237 self.flush()
237 238 self.output.write(self.newline)
238 239 self.output.write(' ' * self.indentation)
239 240 self.output_width = self.indentation
240 241 self.buffer_width = 0
241 242
242 243
243 244 def begin_group(self, indent=0, open=''):
244 245 """
245 246 Begin a group. If you want support for python < 2.5 which doesn't has
246 247 the with statement this is the preferred way:
247 248
248 249 p.begin_group(1, '{')
249 250 ...
250 251 p.end_group(1, '}')
251 252
252 253 The python 2.5 expression would be this:
253 254
254 255 with p.group(1, '{', '}'):
255 256 ...
256 257
257 258 The first parameter specifies the indentation for the next line (usually
258 259 the width of the opening text), the second the opening text. All
259 260 parameters are optional.
260 261 """
261 262 if open:
262 263 self.text(open)
263 264 group = Group(self.group_stack[-1].depth + 1)
264 265 self.group_stack.append(group)
265 266 self.group_queue.enq(group)
266 267 self.indentation += indent
267 268
268 269 def end_group(self, dedent=0, close=''):
269 270 """End a group. See `begin_group` for more details."""
270 271 self.indentation -= dedent
271 272 group = self.group_stack.pop()
272 273 if not group.breakables:
273 274 self.group_queue.remove(group)
274 275 if close:
275 276 self.text(close)
276 277
277 278 def flush(self):
278 279 """Flush data that is left in the buffer."""
279 280 for data in self.buffer:
280 281 self.output_width += data.output(self.output, self.output_width)
281 282 self.buffer.clear()
282 283 self.buffer_width = 0
283 284
284 285
285 286 def _get_mro(obj_class):
286 287 """ Get a reasonable method resolution order of a class and its superclasses
287 288 for both old-style and new-style classes.
288 289 """
289 290 if not hasattr(obj_class, '__mro__'):
290 291 # Old-style class. Mix in object to make a fake new-style class.
291 292 try:
292 293 obj_class = type(obj_class.__name__, (obj_class, object), {})
293 294 except TypeError:
294 295 # Old-style extension type that does not descend from object.
295 296 # FIXME: try to construct a more thorough MRO.
296 297 mro = [obj_class]
297 298 else:
298 299 mro = obj_class.__mro__[1:-1]
299 300 else:
300 301 mro = obj_class.__mro__
301 302 return mro
302 303
303 304
304 305 class RepresentationPrinter(PrettyPrinter):
305 306 """
306 307 Special pretty printer that has a `pretty` method that calls the pretty
307 308 printer for a python object.
308 309
309 310 This class stores processing data on `self` so you must *never* use
310 311 this class in a threaded environment. Always lock it or reinstanciate
311 312 it.
312 313
313 314 Instances also have a verbose flag callbacks can access to control their
314 315 output. For example the default instance repr prints all attributes and
315 316 methods that are not prefixed by an underscore if the printer is in
316 317 verbose mode.
317 318 """
318 319
319 320 def __init__(self, output, verbose=False, max_width=79, newline='\n',
320 321 singleton_pprinters=None, type_pprinters=None, deferred_pprinters=None):
321 322
322 323 PrettyPrinter.__init__(self, output, max_width, newline)
323 324 self.verbose = verbose
324 325 self.stack = []
325 326 if singleton_pprinters is None:
326 327 singleton_pprinters = _singleton_pprinters.copy()
327 328 self.singleton_pprinters = singleton_pprinters
328 329 if type_pprinters is None:
329 330 type_pprinters = _type_pprinters.copy()
330 331 self.type_pprinters = type_pprinters
331 332 if deferred_pprinters is None:
332 333 deferred_pprinters = _deferred_type_pprinters.copy()
333 334 self.deferred_pprinters = deferred_pprinters
334 335
335 336 def pretty(self, obj):
336 337 """Pretty print the given object."""
337 338 obj_id = id(obj)
338 339 cycle = obj_id in self.stack
339 340 self.stack.append(obj_id)
340 341 self.begin_group()
341 342 try:
342 343 obj_class = getattr(obj, '__class__', None) or type(obj)
343 344 # First try to find registered singleton printers for the type.
344 345 try:
345 346 printer = self.singleton_pprinters[obj_id]
346 347 except (TypeError, KeyError):
347 348 pass
348 349 else:
349 350 return printer(obj, self, cycle)
350 351 # Next walk the mro and check for either:
351 352 # 1) a registered printer
352 353 # 2) a _repr_pretty_ method
353 354 for cls in _get_mro(obj_class):
354 355 if cls in self.type_pprinters:
355 356 # printer registered in self.type_pprinters
356 357 return self.type_pprinters[cls](obj, self, cycle)
357 358 else:
358 359 # deferred printer
359 360 printer = self._in_deferred_types(cls)
360 361 if printer is not None:
361 362 return printer(obj, self, cycle)
362 363 else:
363 364 # Finally look for special method names.
364 365 # Some objects automatically create any requested
365 366 # attribute. Try to ignore most of them by checking for
366 367 # callability.
367 368 if '_repr_pretty_' in cls.__dict__:
368 369 meth = cls._repr_pretty_
369 370 if callable(meth):
370 371 return meth(obj, self, cycle)
371 372 return _default_pprint(obj, self, cycle)
372 373 finally:
373 374 self.end_group()
374 375 self.stack.pop()
375 376
376 377 def _in_deferred_types(self, cls):
377 378 """
378 379 Check if the given class is specified in the deferred type registry.
379 380
380 381 Returns the printer from the registry if it exists, and None if the
381 382 class is not in the registry. Successful matches will be moved to the
382 383 regular type registry for future use.
383 384 """
384 385 mod = getattr(cls, '__module__', None)
385 386 name = getattr(cls, '__name__', None)
386 387 key = (mod, name)
387 388 printer = None
388 389 if key in self.deferred_pprinters:
389 390 # Move the printer over to the regular registry.
390 391 printer = self.deferred_pprinters.pop(key)
391 392 self.type_pprinters[cls] = printer
392 393 return printer
393 394
394 395
395 396 class Printable(object):
396 397
397 398 def output(self, stream, output_width):
398 399 return output_width
399 400
400 401
401 402 class Text(Printable):
402 403
403 404 def __init__(self):
404 405 self.objs = []
405 406 self.width = 0
406 407
407 408 def output(self, stream, output_width):
408 409 for obj in self.objs:
409 410 stream.write(obj)
410 411 return output_width + self.width
411 412
412 413 def add(self, obj, width):
413 414 self.objs.append(obj)
414 415 self.width += width
415 416
416 417
417 418 class Breakable(Printable):
418 419
419 420 def __init__(self, seq, width, pretty):
420 421 self.obj = seq
421 422 self.width = width
422 423 self.pretty = pretty
423 424 self.indentation = pretty.indentation
424 425 self.group = pretty.group_stack[-1]
425 426 self.group.breakables.append(self)
426 427
427 428 def output(self, stream, output_width):
428 429 self.group.breakables.popleft()
429 430 if self.group.want_break:
430 431 stream.write(self.pretty.newline)
431 432 stream.write(' ' * self.indentation)
432 433 return self.indentation
433 434 if not self.group.breakables:
434 435 self.pretty.group_queue.remove(self.group)
435 436 stream.write(self.obj)
436 437 return output_width + self.width
437 438
438 439
439 440 class Group(Printable):
440 441
441 442 def __init__(self, depth):
442 443 self.depth = depth
443 444 self.breakables = deque()
444 445 self.want_break = False
445 446
446 447
447 448 class GroupQueue(object):
448 449
449 450 def __init__(self, *groups):
450 451 self.queue = []
451 452 for group in groups:
452 453 self.enq(group)
453 454
454 455 def enq(self, group):
455 456 depth = group.depth
456 457 while depth > len(self.queue) - 1:
457 458 self.queue.append([])
458 459 self.queue[depth].append(group)
459 460
460 461 def deq(self):
461 462 for stack in self.queue:
462 463 for idx, group in enumerate(reversed(stack)):
463 464 if group.breakables:
464 465 del stack[idx]
465 466 group.want_break = True
466 467 return group
467 468 for group in stack:
468 469 group.want_break = True
469 470 del stack[:]
470 471
471 472 def remove(self, group):
472 473 try:
473 474 self.queue[group.depth].remove(group)
474 475 except ValueError:
475 476 pass
476 477
477 478 try:
478 479 _baseclass_reprs = (object.__repr__, types.InstanceType.__repr__)
479 480 except AttributeError: # Python 3
480 481 _baseclass_reprs = (object.__repr__,)
481 482
482 483
483 484 def _default_pprint(obj, p, cycle):
484 485 """
485 486 The default print function. Used if an object does not provide one and
486 487 it's none of the builtin objects.
487 488 """
488 489 klass = getattr(obj, '__class__', None) or type(obj)
489 490 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
490 491 # A user-provided repr. Find newlines and replace them with p.break_()
491 492 output = repr(obj)
492 493 for idx,output_line in enumerate(output.splitlines()):
493 494 if idx:
494 495 p.break_()
495 496 p.text(output_line)
496 497 return
497 498 p.begin_group(1, '<')
498 499 p.pretty(klass)
499 500 p.text(' at 0x%x' % id(obj))
500 501 if cycle:
501 502 p.text(' ...')
502 503 elif p.verbose:
503 504 first = True
504 505 for key in dir(obj):
505 506 if not key.startswith('_'):
506 507 try:
507 508 value = getattr(obj, key)
508 509 except AttributeError:
509 510 continue
510 511 if isinstance(value, types.MethodType):
511 512 continue
512 513 if not first:
513 514 p.text(',')
514 515 p.breakable()
515 516 p.text(key)
516 517 p.text('=')
517 518 step = len(key) + 1
518 519 p.indentation += step
519 520 p.pretty(value)
520 521 p.indentation -= step
521 522 first = False
522 523 p.end_group(1, '>')
523 524
524 525
525 526 def _seq_pprinter_factory(start, end, basetype):
526 527 """
527 528 Factory that returns a pprint function useful for sequences. Used by
528 529 the default pprint for tuples, dicts, and lists.
529 530 """
530 531 def inner(obj, p, cycle):
531 532 typ = type(obj)
532 533 if basetype is not None and typ is not basetype and typ.__repr__ != basetype.__repr__:
533 534 # If the subclass provides its own repr, use it instead.
534 535 return p.text(typ.__repr__(obj))
535 536
536 537 if cycle:
537 538 return p.text(start + '...' + end)
538 539 step = len(start)
539 540 p.begin_group(step, start)
540 541 for idx, x in enumerate(obj):
541 542 if idx:
542 543 p.text(',')
543 544 p.breakable()
544 545 p.pretty(x)
545 546 if len(obj) == 1 and type(obj) is tuple:
546 547 # Special case for 1-item tuples.
547 548 p.text(',')
548 549 p.end_group(step, end)
549 550 return inner
550 551
551 552
552 553 def _set_pprinter_factory(start, end, basetype):
553 554 """
554 555 Factory that returns a pprint function useful for sets and frozensets.
555 556 """
556 557 def inner(obj, p, cycle):
557 558 typ = type(obj)
558 559 if basetype is not None and typ is not basetype and typ.__repr__ != basetype.__repr__:
559 560 # If the subclass provides its own repr, use it instead.
560 561 return p.text(typ.__repr__(obj))
561 562
562 563 if cycle:
563 564 return p.text(start + '...' + end)
564 565 if len(obj) == 0:
565 566 # Special case.
566 567 p.text(basetype.__name__ + '()')
567 568 else:
568 569 step = len(start)
569 570 p.begin_group(step, start)
570 571 # Like dictionary keys, we will try to sort the items.
571 572 items = list(obj)
572 573 try:
573 574 items.sort()
574 575 except Exception:
575 576 # Sometimes the items don't sort.
576 577 pass
577 578 for idx, x in enumerate(items):
578 579 if idx:
579 580 p.text(',')
580 581 p.breakable()
581 582 p.pretty(x)
582 583 p.end_group(step, end)
583 584 return inner
584 585
585 586
586 587 def _dict_pprinter_factory(start, end, basetype=None):
587 588 """
588 589 Factory that returns a pprint function used by the default pprint of
589 590 dicts and dict proxies.
590 591 """
591 592 def inner(obj, p, cycle):
592 593 typ = type(obj)
593 594 if basetype is not None and typ is not basetype and typ.__repr__ != basetype.__repr__:
594 595 # If the subclass provides its own repr, use it instead.
595 596 return p.text(typ.__repr__(obj))
596 597
597 598 if cycle:
598 599 return p.text('{...}')
599 600 p.begin_group(1, start)
600 601 keys = obj.keys()
601 602 try:
602 603 keys.sort()
603 604 except Exception as e:
604 605 # Sometimes the keys don't sort.
605 606 pass
606 607 for idx, key in enumerate(keys):
607 608 if idx:
608 609 p.text(',')
609 610 p.breakable()
610 611 p.pretty(key)
611 612 p.text(': ')
612 613 p.pretty(obj[key])
613 614 p.end_group(1, end)
614 615 return inner
615 616
616 617
617 618 def _super_pprint(obj, p, cycle):
618 619 """The pprint for the super type."""
619 620 p.begin_group(8, '<super: ')
620 621 p.pretty(obj.__self_class__)
621 622 p.text(',')
622 623 p.breakable()
623 624 p.pretty(obj.__self__)
624 625 p.end_group(8, '>')
625 626
626 627
627 628 def _re_pattern_pprint(obj, p, cycle):
628 629 """The pprint function for regular expression patterns."""
629 630 p.text('re.compile(')
630 631 pattern = repr(obj.pattern)
631 632 if pattern[:1] in 'uU':
632 633 pattern = pattern[1:]
633 634 prefix = 'ur'
634 635 else:
635 636 prefix = 'r'
636 637 pattern = prefix + pattern.replace('\\\\', '\\')
637 638 p.text(pattern)
638 639 if obj.flags:
639 640 p.text(',')
640 641 p.breakable()
641 642 done_one = False
642 643 for flag in ('TEMPLATE', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL',
643 644 'UNICODE', 'VERBOSE', 'DEBUG'):
644 645 if obj.flags & getattr(re, flag):
645 646 if done_one:
646 647 p.text('|')
647 648 p.text('re.' + flag)
648 649 done_one = True
649 650 p.text(')')
650 651
651 652
652 653 def _type_pprint(obj, p, cycle):
653 654 """The pprint for classes and types."""
654 655 mod = getattr(obj, '__module__', None)
655 656 if mod is None:
656 657 # Heap allocated types might not have the module attribute,
657 658 # and others may set it to None.
658 659 return p.text(obj.__name__)
659 660
660 661 if mod in ('__builtin__', 'builtins', 'exceptions'):
661 662 name = obj.__name__
662 663 else:
663 664 name = mod + '.' + obj.__name__
664 665 p.text(name)
665 666
666 667
667 668 def _repr_pprint(obj, p, cycle):
668 669 """A pprint that just redirects to the normal repr function."""
669 670 p.text(repr(obj))
670 671
671 672
672 673 def _function_pprint(obj, p, cycle):
673 674 """Base pprint for all functions and builtin functions."""
674 675 if obj.__module__ in ('__builtin__', 'builtins', 'exceptions') or not obj.__module__:
675 676 name = obj.__name__
676 677 else:
677 678 name = obj.__module__ + '.' + obj.__name__
678 679 p.text('<function %s>' % name)
679 680
680 681
681 682 def _exception_pprint(obj, p, cycle):
682 683 """Base pprint for all exceptions."""
683 684 if obj.__class__.__module__ in ('exceptions', 'builtins'):
684 685 name = obj.__class__.__name__
685 686 else:
686 687 name = '%s.%s' % (
687 688 obj.__class__.__module__,
688 689 obj.__class__.__name__
689 690 )
690 691 step = len(name) + 1
691 692 p.begin_group(step, name + '(')
692 693 for idx, arg in enumerate(getattr(obj, 'args', ())):
693 694 if idx:
694 695 p.text(',')
695 696 p.breakable()
696 697 p.pretty(arg)
697 698 p.end_group(step, ')')
698 699
699 700
700 701 #: the exception base
701 702 try:
702 703 _exception_base = BaseException
703 704 except NameError:
704 705 _exception_base = Exception
705 706
706 707
707 708 #: printers for builtin types
708 709 _type_pprinters = {
709 710 int: _repr_pprint,
710 711 long: _repr_pprint,
711 712 float: _repr_pprint,
712 713 str: _repr_pprint,
713 714 unicode: _repr_pprint,
714 715 tuple: _seq_pprinter_factory('(', ')', tuple),
715 716 list: _seq_pprinter_factory('[', ']', list),
716 717 dict: _dict_pprinter_factory('{', '}', dict),
717 718
718 719 set: _set_pprinter_factory('{', '}', set),
719 720 frozenset: _set_pprinter_factory('frozenset({', '})', frozenset),
720 721 super: _super_pprint,
721 722 _re_pattern_type: _re_pattern_pprint,
722 723 type: _type_pprint,
723 724 types.FunctionType: _function_pprint,
724 725 types.BuiltinFunctionType: _function_pprint,
725 726 types.SliceType: _repr_pprint,
726 727 types.MethodType: _repr_pprint,
727 728
728 729 datetime.datetime: _repr_pprint,
729 730 datetime.timedelta: _repr_pprint,
730 731 _exception_base: _exception_pprint
731 732 }
732 733
733 734 try:
734 735 _type_pprinters[types.DictProxyType] = _dict_pprinter_factory('<dictproxy {', '}>')
735 736 _type_pprinters[types.ClassType] = _type_pprint
736 737 except AttributeError: # Python 3
737 738 pass
738 739
739 740 try:
740 741 _type_pprinters[xrange] = _repr_pprint
741 742 except NameError:
742 743 _type_pprinters[range] = _repr_pprint
743 744
744 745 #: printers for types specified by name
745 746 _deferred_type_pprinters = {
746 747 }
747 748
748 749 def for_type(typ, func):
749 750 """
750 751 Add a pretty printer for a given type.
751 752 """
752 753 oldfunc = _type_pprinters.get(typ, None)
753 754 if func is not None:
754 755 # To support easy restoration of old pprinters, we need to ignore Nones.
755 756 _type_pprinters[typ] = func
756 757 return oldfunc
757 758
758 759 def for_type_by_name(type_module, type_name, func):
759 760 """
760 761 Add a pretty printer for a type specified by the module and name of a type
761 762 rather than the type object itself.
762 763 """
763 764 key = (type_module, type_name)
764 765 oldfunc = _deferred_type_pprinters.get(key, None)
765 766 if func is not None:
766 767 # To support easy restoration of old pprinters, we need to ignore Nones.
767 768 _deferred_type_pprinters[key] = func
768 769 return oldfunc
769 770
770 771
771 772 #: printers for the default singletons
772 773 _singleton_pprinters = dict.fromkeys(map(id, [None, True, False, Ellipsis,
773 774 NotImplemented]), _repr_pprint)
774 775
775 776
776 777 if __name__ == '__main__':
777 778 from random import randrange
778 779 class Foo(object):
779 780 def __init__(self):
780 781 self.foo = 1
781 782 self.bar = re.compile(r'\s+')
782 783 self.blub = dict.fromkeys(range(30), randrange(1, 40))
783 784 self.hehe = 23424.234234
784 785 self.list = ["blub", "blah", self]
785 786
786 787 def get_foo(self):
787 print "foo"
788 print("foo")
788 789
789 790 pprint(Foo(), verbose=True)
@@ -1,111 +1,112 b''
1 1 """PostProcessor for serving reveal.js HTML slideshows."""
2 from __future__ import print_function
2 3 #-----------------------------------------------------------------------------
3 4 #Copyright (c) 2013, 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 import os
15 16 import webbrowser
16 17
17 18 from tornado import web, ioloop, httpserver
18 19 from tornado.httpclient import AsyncHTTPClient
19 20
20 21 from IPython.utils.traitlets import Bool, Unicode, Int
21 22
22 23 from .base import PostProcessorBase
23 24
24 25 #-----------------------------------------------------------------------------
25 26 # Classes
26 27 #-----------------------------------------------------------------------------
27 28
28 29 class ProxyHandler(web.RequestHandler):
29 30 """handler the proxies requests from a local prefix to a CDN"""
30 31 @web.asynchronous
31 32 def get(self, prefix, url):
32 33 """proxy a request to a CDN"""
33 34 proxy_url = "/".join([self.settings['cdn'], url])
34 35 client = self.settings['client']
35 36 client.fetch(proxy_url, callback=self.finish_get)
36 37
37 38 def finish_get(self, response):
38 39 """finish the request"""
39 40 # copy potentially relevant headers
40 41 for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]:
41 42 if header in response.headers:
42 43 self.set_header(header, response.headers[header])
43 44 self.finish(response.body)
44 45
45 46 class ServePostProcessor(PostProcessorBase):
46 47 """Post processor designed to serve files
47 48
48 49 Proxies reveal.js requests to a CDN if no local reveal.js is present
49 50 """
50 51
51 52
52 53 open_in_browser = Bool(True, config=True,
53 54 help="""Should the browser be opened automatically?"""
54 55 )
55 56 reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.4.0", config=True,
56 57 help="""URL for reveal.js CDN."""
57 58 )
58 59 reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js")
59 60 ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.")
60 61 port = Int(8000, config=True, help="port for the server to listen on.")
61 62
62 63 def postprocess(self, input):
63 64 """Serve the build directory with a webserver."""
64 65 dirname, filename = os.path.split(input)
65 66 handlers = [
66 67 (r"/(.+)", web.StaticFileHandler, {'path' : dirname}),
67 68 (r"/", web.RedirectHandler, {"url": "/%s" % filename})
68 69 ]
69 70
70 71 if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")):
71 72 # reveal specifically from CDN, nothing to do
72 73 pass
73 74 elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)):
74 75 # reveal prefix exists
75 76 self.log.info("Serving local %s", self.reveal_prefix)
76 77 else:
77 78 self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn)
78 79 handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler))
79 80
80 81 app = web.Application(handlers,
81 82 cdn=self.reveal_cdn,
82 83 client=AsyncHTTPClient(),
83 84 )
84 85 # hook up tornado logging to our logger
85 86 try:
86 87 from tornado import log
87 88 log.app_log = self.log
88 89 except ImportError:
89 90 # old tornado (<= 3), ignore
90 91 pass
91 92
92 93 http_server = httpserver.HTTPServer(app)
93 94 http_server.listen(self.port, address=self.ip)
94 95 url = "http://%s:%i/%s" % (self.ip, self.port, filename)
95 print("Serving your slides at %s" % url)
96 print(("Serving your slides at %s" % url))
96 97 print("Use Control-C to stop this server")
97 98 if self.open_in_browser:
98 99 webbrowser.open(url, new=2)
99 100 try:
100 101 ioloop.IOLoop.instance().start()
101 102 except KeyboardInterrupt:
102 103 print("\nInterrupted")
103 104
104 105 def main(path):
105 106 """allow running this module to serve the slides"""
106 107 server = ServePostProcessor()
107 108 server(path)
108 109
109 110 if __name__ == '__main__':
110 111 import sys
111 112 main(sys.argv[1])
@@ -1,42 +1,43 b''
1 1 """
2 2 Contains debug writer.
3 3 """
4 from __future__ import print_function
4 5 #-----------------------------------------------------------------------------
5 6 #Copyright (c) 2013, 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 from .base import WriterBase
17 18 from pprint import pprint
18 19
19 20 #-----------------------------------------------------------------------------
20 21 # Classes
21 22 #-----------------------------------------------------------------------------
22 23
23 24 class DebugWriter(WriterBase):
24 25 """Consumes output from nbconvert export...() methods and writes usefull
25 26 debugging information to the stdout. The information includes a list of
26 27 resources that were extracted from the notebook(s) during export."""
27 28
28 29
29 30 def write(self, output, resources, notebook_name='notebook', **kw):
30 31 """
31 32 Consume and write Jinja output.
32 33
33 34 See base for more...
34 35 """
35 36
36 37 if isinstance(resources['outputs'], dict):
37 print("outputs extracted from %s" % notebook_name)
38 print('-' * 80)
38 print(("outputs extracted from %s" % notebook_name))
39 print(('-' * 80))
39 40 pprint(resources['outputs'], indent=2, width=70)
40 41 else:
41 print("no outputs extracted from %s" % notebook_name)
42 print('=' * 80)
42 print(("no outputs extracted from %s" % notebook_name))
43 print(('=' * 80))
@@ -1,88 +1,89 b''
1 from __future__ import print_function
1 2 #!/usr/bin/env python
2 3 # -*- coding: utf8 -*-
3 4 import argparse
4 5 import traceback
5 6 import json
6 7
7 8 from IPython.external.jsonschema import Draft3Validator, validate, ValidationError
8 9 import IPython.external.jsonpointer as jsonpointer
9 10
10 11 def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True):
11 12 v3schema = resolve_ref(json.load(open(schema,'r')))
12 13 if key :
13 14 v3schema = jsonpointer.resolve_pointer(v3schema,key)
14 15 errors = 0
15 16 v = Draft3Validator(v3schema);
16 17 for error in v.iter_errors(nbjson):
17 18 errors = errors + 1
18 19 if verbose:
19 20 print(error)
20 21 return errors
21 22
22 23 def resolve_ref(json, base=None):
23 24 """return a json with resolved internal references
24 25
25 26 only support local reference to the same json
26 27 """
27 28 if not base :
28 29 base = json
29 30
30 31 temp = None
31 32 if type(json) is list:
32 33 temp = [];
33 34 for item in json:
34 35 temp.append(resolve_ref(item, base=base))
35 36 elif type(json) is dict:
36 37 temp = {};
37 38 for key,value in json.iteritems():
38 39 if key == '$ref':
39 40 return resolve_ref(jsonpointer.resolve_pointer(base,value), base=base)
40 41 else :
41 42 temp[key]=resolve_ref(value, base=base)
42 43 else :
43 44 return json
44 45 return temp
45 46
46 47 def convert(namein, nameout, indent=2):
47 48 """resolve the references of namein, save the result in nameout"""
48 49 jsn = None
49 50 with open(namein) as file :
50 51 jsn = json.load(file)
51 52 v = resolve_ref(jsn, base=jsn)
52 53 x = jsonpointer.resolve_pointer(v, '/notebook')
53 54 with open(nameout,'w') as file:
54 55 json.dump(x,file,indent=indent)
55 56
56 57
57 58 if __name__ == '__main__':
58 59 parser = argparse.ArgumentParser()
59 60 parser.add_argument('-s', '--schema',
60 61 type=str, default='v3.withref.json')
61 62
62 63 parser.add_argument('-k', '--key',
63 64 type=str, default='/notebook',
64 65 help='subkey to extract json schema from json file')
65 66
66 67 parser.add_argument("-v", "--verbose", action="store_true",
67 68 help="increase output verbosity")
68 69
69 70 parser.add_argument('filename',
70 71 type=str,
71 72 help="file to validate",
72 73 nargs='*',
73 74 metavar='names')
74 75
75 76 args = parser.parse_args()
76 77 for name in args.filename :
77 78 nerror = nbvalidate(json.load(open(name,'r')),
78 79 schema=args.schema,
79 80 key=args.key,
80 81 verbose=args.verbose)
81 82 if nerror is 0:
82 print u"[Pass]",name
83 print(u"[Pass]",name)
83 84 else :
84 print u"[ ]",name,'(%d)'%(nerror)
85 print(u"[ ]",name,'(%d)'%(nerror))
85 86 if args.verbose :
86 print '=================================================='
87 print('==================================================')
87 88
88 89
@@ -1,610 +1,611 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The ipcluster application.
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * MinRK
10 10
11 11 """
12 from __future__ import print_function
12 13
13 14 #-----------------------------------------------------------------------------
14 15 # Copyright (C) 2008-2011 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 errno
25 26 import logging
26 27 import os
27 28 import re
28 29 import signal
29 30
30 31 from subprocess import check_call, CalledProcessError, PIPE
31 32 import zmq
32 33 from zmq.eventloop import ioloop
33 34
34 35 from IPython.config.application import Application, boolean_flag, catch_config_error
35 36 from IPython.config.loader import Config
36 37 from IPython.core.application import BaseIPythonApplication
37 38 from IPython.core.profiledir import ProfileDir
38 39 from IPython.utils.daemonize import daemonize
39 40 from IPython.utils.importstring import import_item
40 41 from IPython.utils.sysinfo import num_cpus
41 42 from IPython.utils.traitlets import (Integer, Unicode, Bool, CFloat, Dict, List, Any,
42 43 DottedObjectName)
43 44
44 45 from IPython.parallel.apps.baseapp import (
45 46 BaseParallelApplication,
46 47 PIDFileError,
47 48 base_flags, base_aliases
48 49 )
49 50
50 51
51 52 #-----------------------------------------------------------------------------
52 53 # Module level variables
53 54 #-----------------------------------------------------------------------------
54 55
55 56
56 57 _description = """Start an IPython cluster for parallel computing.
57 58
58 59 An IPython cluster consists of 1 controller and 1 or more engines.
59 60 This command automates the startup of these processes using a wide range of
60 61 startup methods (SSH, local processes, PBS, mpiexec, SGE, LSF, HTCondor,
61 62 Windows HPC Server 2008). To start a cluster with 4 engines on your
62 63 local host simply do 'ipcluster start --n=4'. For more complex usage
63 64 you will typically do 'ipython profile create mycluster --parallel', then edit
64 65 configuration files, followed by 'ipcluster start --profile=mycluster --n=4'.
65 66 """
66 67
67 68 _main_examples = """
68 69 ipcluster start --n=4 # start a 4 node cluster on localhost
69 70 ipcluster start -h # show the help string for the start subcmd
70 71
71 72 ipcluster stop -h # show the help string for the stop subcmd
72 73 ipcluster engines -h # show the help string for the engines subcmd
73 74 """
74 75
75 76 _start_examples = """
76 77 ipython profile create mycluster --parallel # create mycluster profile
77 78 ipcluster start --profile=mycluster --n=4 # start mycluster with 4 nodes
78 79 """
79 80
80 81 _stop_examples = """
81 82 ipcluster stop --profile=mycluster # stop a running cluster by profile name
82 83 """
83 84
84 85 _engines_examples = """
85 86 ipcluster engines --profile=mycluster --n=4 # start 4 engines only
86 87 """
87 88
88 89
89 90 # Exit codes for ipcluster
90 91
91 92 # This will be the exit code if the ipcluster appears to be running because
92 93 # a .pid file exists
93 94 ALREADY_STARTED = 10
94 95
95 96
96 97 # This will be the exit code if ipcluster stop is run, but there is not .pid
97 98 # file to be found.
98 99 ALREADY_STOPPED = 11
99 100
100 101 # This will be the exit code if ipcluster engines is run, but there is not .pid
101 102 # file to be found.
102 103 NO_CLUSTER = 12
103 104
104 105
105 106 #-----------------------------------------------------------------------------
106 107 # Utilities
107 108 #-----------------------------------------------------------------------------
108 109
109 110 def find_launcher_class(clsname, kind):
110 111 """Return a launcher for a given clsname and kind.
111 112
112 113 Parameters
113 114 ==========
114 115 clsname : str
115 116 The full name of the launcher class, either with or without the
116 117 module path, or an abbreviation (MPI, SSH, SGE, PBS, LSF, HTCondor
117 118 WindowsHPC).
118 119 kind : str
119 120 Either 'EngineSet' or 'Controller'.
120 121 """
121 122 if '.' not in clsname:
122 123 # not a module, presume it's the raw name in apps.launcher
123 124 if kind and kind not in clsname:
124 125 # doesn't match necessary full class name, assume it's
125 126 # just 'PBS' or 'MPI' etc prefix:
126 127 clsname = clsname + kind + 'Launcher'
127 128 clsname = 'IPython.parallel.apps.launcher.'+clsname
128 129 klass = import_item(clsname)
129 130 return klass
130 131
131 132 #-----------------------------------------------------------------------------
132 133 # Main application
133 134 #-----------------------------------------------------------------------------
134 135
135 136 start_help = """Start an IPython cluster for parallel computing
136 137
137 138 Start an ipython cluster by its profile name or cluster
138 139 directory. Cluster directories contain configuration, log and
139 140 security related files and are named using the convention
140 141 'profile_<name>' and should be creating using the 'start'
141 142 subcommand of 'ipcluster'. If your cluster directory is in
142 143 the cwd or the ipython directory, you can simply refer to it
143 144 using its profile name, 'ipcluster start --n=4 --profile=<profile>`,
144 145 otherwise use the 'profile-dir' option.
145 146 """
146 147 stop_help = """Stop a running IPython cluster
147 148
148 149 Stop a running ipython cluster by its profile name or cluster
149 150 directory. Cluster directories are named using the convention
150 151 'profile_<name>'. If your cluster directory is in
151 152 the cwd or the ipython directory, you can simply refer to it
152 153 using its profile name, 'ipcluster stop --profile=<profile>`, otherwise
153 154 use the '--profile-dir' option.
154 155 """
155 156 engines_help = """Start engines connected to an existing IPython cluster
156 157
157 158 Start one or more engines to connect to an existing Cluster
158 159 by profile name or cluster directory.
159 160 Cluster directories contain configuration, log and
160 161 security related files and are named using the convention
161 162 'profile_<name>' and should be creating using the 'start'
162 163 subcommand of 'ipcluster'. If your cluster directory is in
163 164 the cwd or the ipython directory, you can simply refer to it
164 165 using its profile name, 'ipcluster engines --n=4 --profile=<profile>`,
165 166 otherwise use the 'profile-dir' option.
166 167 """
167 168 stop_aliases = dict(
168 169 signal='IPClusterStop.signal',
169 170 )
170 171 stop_aliases.update(base_aliases)
171 172
172 173 class IPClusterStop(BaseParallelApplication):
173 174 name = u'ipcluster'
174 175 description = stop_help
175 176 examples = _stop_examples
176 177
177 178 signal = Integer(signal.SIGINT, config=True,
178 179 help="signal to use for stopping processes.")
179 180
180 181 aliases = Dict(stop_aliases)
181 182
182 183 def start(self):
183 184 """Start the app for the stop subcommand."""
184 185 try:
185 186 pid = self.get_pid_from_file()
186 187 except PIDFileError:
187 188 self.log.critical(
188 189 'Could not read pid file, cluster is probably not running.'
189 190 )
190 191 # Here I exit with a unusual exit status that other processes
191 192 # can watch for to learn how I existed.
192 193 self.remove_pid_file()
193 194 self.exit(ALREADY_STOPPED)
194 195
195 196 if not self.check_pid(pid):
196 197 self.log.critical(
197 198 'Cluster [pid=%r] is not running.' % pid
198 199 )
199 200 self.remove_pid_file()
200 201 # Here I exit with a unusual exit status that other processes
201 202 # can watch for to learn how I existed.
202 203 self.exit(ALREADY_STOPPED)
203 204
204 205 elif os.name=='posix':
205 206 sig = self.signal
206 207 self.log.info(
207 208 "Stopping cluster [pid=%r] with [signal=%r]" % (pid, sig)
208 209 )
209 210 try:
210 211 os.kill(pid, sig)
211 212 except OSError:
212 213 self.log.error("Stopping cluster failed, assuming already dead.",
213 214 exc_info=True)
214 215 self.remove_pid_file()
215 216 elif os.name=='nt':
216 217 try:
217 218 # kill the whole tree
218 219 p = check_call(['taskkill', '-pid', str(pid), '-t', '-f'], stdout=PIPE,stderr=PIPE)
219 220 except (CalledProcessError, OSError):
220 221 self.log.error("Stopping cluster failed, assuming already dead.",
221 222 exc_info=True)
222 223 self.remove_pid_file()
223 224
224 225 engine_aliases = {}
225 226 engine_aliases.update(base_aliases)
226 227 engine_aliases.update(dict(
227 228 n='IPClusterEngines.n',
228 229 engines = 'IPClusterEngines.engine_launcher_class',
229 230 daemonize = 'IPClusterEngines.daemonize',
230 231 ))
231 232 engine_flags = {}
232 233 engine_flags.update(base_flags)
233 234
234 235 engine_flags.update(dict(
235 236 daemonize=(
236 237 {'IPClusterEngines' : {'daemonize' : True}},
237 238 """run the cluster into the background (not available on Windows)""",
238 239 )
239 240 ))
240 241 class IPClusterEngines(BaseParallelApplication):
241 242
242 243 name = u'ipcluster'
243 244 description = engines_help
244 245 examples = _engines_examples
245 246 usage = None
246 247 default_log_level = logging.INFO
247 248 classes = List()
248 249 def _classes_default(self):
249 250 from IPython.parallel.apps import launcher
250 251 launchers = launcher.all_launchers
251 252 eslaunchers = [ l for l in launchers if 'EngineSet' in l.__name__]
252 253 return [ProfileDir]+eslaunchers
253 254
254 255 n = Integer(num_cpus(), config=True,
255 256 help="""The number of engines to start. The default is to use one for each
256 257 CPU on your machine""")
257 258
258 259 engine_launcher = Any(config=True, help="Deprecated, use engine_launcher_class")
259 260 def _engine_launcher_changed(self, name, old, new):
260 261 if isinstance(new, basestring):
261 262 self.log.warn("WARNING: %s.engine_launcher is deprecated as of 0.12,"
262 263 " use engine_launcher_class" % self.__class__.__name__)
263 264 self.engine_launcher_class = new
264 265 engine_launcher_class = DottedObjectName('LocalEngineSetLauncher',
265 266 config=True,
266 267 help="""The class for launching a set of Engines. Change this value
267 268 to use various batch systems to launch your engines, such as PBS,SGE,MPI,etc.
268 269 Each launcher class has its own set of configuration options, for making sure
269 270 it will work in your environment.
270 271
271 272 You can also write your own launcher, and specify it's absolute import path,
272 273 as in 'mymodule.launcher.FTLEnginesLauncher`.
273 274
274 275 IPython's bundled examples include:
275 276
276 277 Local : start engines locally as subprocesses [default]
277 278 MPI : use mpiexec to launch engines in an MPI environment
278 279 PBS : use PBS (qsub) to submit engines to a batch queue
279 280 SGE : use SGE (qsub) to submit engines to a batch queue
280 281 LSF : use LSF (bsub) to submit engines to a batch queue
281 282 SSH : use SSH to start the controller
282 283 Note that SSH does *not* move the connection files
283 284 around, so you will likely have to do this manually
284 285 unless the machines are on a shared file system.
285 286 HTCondor : use HTCondor to submit engines to a batch queue
286 287 WindowsHPC : use Windows HPC
287 288
288 289 If you are using one of IPython's builtin launchers, you can specify just the
289 290 prefix, e.g:
290 291
291 292 c.IPClusterEngines.engine_launcher_class = 'SSH'
292 293
293 294 or:
294 295
295 296 ipcluster start --engines=MPI
296 297
297 298 """
298 299 )
299 300 daemonize = Bool(False, config=True,
300 301 help="""Daemonize the ipcluster program. This implies --log-to-file.
301 302 Not available on Windows.
302 303 """)
303 304
304 305 def _daemonize_changed(self, name, old, new):
305 306 if new:
306 307 self.log_to_file = True
307 308
308 309 early_shutdown = Integer(30, config=True, help="The timeout (in seconds)")
309 310 _stopping = False
310 311
311 312 aliases = Dict(engine_aliases)
312 313 flags = Dict(engine_flags)
313 314
314 315 @catch_config_error
315 316 def initialize(self, argv=None):
316 317 super(IPClusterEngines, self).initialize(argv)
317 318 self.init_signal()
318 319 self.init_launchers()
319 320
320 321 def init_launchers(self):
321 322 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
322 323
323 324 def init_signal(self):
324 325 # Setup signals
325 326 signal.signal(signal.SIGINT, self.sigint_handler)
326 327
327 328 def build_launcher(self, clsname, kind=None):
328 329 """import and instantiate a Launcher based on importstring"""
329 330 try:
330 331 klass = find_launcher_class(clsname, kind)
331 332 except (ImportError, KeyError):
332 333 self.log.fatal("Could not import launcher class: %r"%clsname)
333 334 self.exit(1)
334 335
335 336 launcher = klass(
336 337 work_dir=u'.', parent=self, log=self.log,
337 338 profile_dir=self.profile_dir.location, cluster_id=self.cluster_id,
338 339 )
339 340 return launcher
340 341
341 342 def engines_started_ok(self):
342 343 self.log.info("Engines appear to have started successfully")
343 344 self.early_shutdown = 0
344 345
345 346 def start_engines(self):
346 347 # Some EngineSetLaunchers ignore `n` and use their own engine count, such as SSH:
347 348 n = getattr(self.engine_launcher, 'engine_count', self.n)
348 349 self.log.info("Starting %s Engines with %s", n, self.engine_launcher_class)
349 350 self.engine_launcher.start(self.n)
350 351 self.engine_launcher.on_stop(self.engines_stopped_early)
351 352 if self.early_shutdown:
352 353 ioloop.DelayedCallback(self.engines_started_ok, self.early_shutdown*1000, self.loop).start()
353 354
354 355 def engines_stopped_early(self, r):
355 356 if self.early_shutdown and not self._stopping:
356 357 self.log.error("""
357 358 Engines shutdown early, they probably failed to connect.
358 359
359 360 Check the engine log files for output.
360 361
361 362 If your controller and engines are not on the same machine, you probably
362 363 have to instruct the controller to listen on an interface other than localhost.
363 364
364 365 You can set this by adding "--ip='*'" to your ControllerLauncher.controller_args.
365 366
366 367 Be sure to read our security docs before instructing your controller to listen on
367 368 a public interface.
368 369 """)
369 370 self.stop_launchers()
370 371
371 372 return self.engines_stopped(r)
372 373
373 374 def engines_stopped(self, r):
374 375 return self.loop.stop()
375 376
376 377 def stop_engines(self):
377 378 if self.engine_launcher.running:
378 379 self.log.info("Stopping Engines...")
379 380 d = self.engine_launcher.stop()
380 381 return d
381 382 else:
382 383 return None
383 384
384 385 def stop_launchers(self, r=None):
385 386 if not self._stopping:
386 387 self._stopping = True
387 388 self.log.error("IPython cluster: stopping")
388 389 self.stop_engines()
389 390 # Wait a few seconds to let things shut down.
390 391 dc = ioloop.DelayedCallback(self.loop.stop, 3000, self.loop)
391 392 dc.start()
392 393
393 394 def sigint_handler(self, signum, frame):
394 395 self.log.debug("SIGINT received, stopping launchers...")
395 396 self.stop_launchers()
396 397
397 398 def start_logging(self):
398 399 # Remove old log files of the controller and engine
399 400 if self.clean_logs:
400 401 log_dir = self.profile_dir.log_dir
401 402 for f in os.listdir(log_dir):
402 403 if re.match(r'ip(engine|controller)z-\d+\.(log|err|out)',f):
403 404 os.remove(os.path.join(log_dir, f))
404 405 # This will remove old log files for ipcluster itself
405 406 # super(IPBaseParallelApplication, self).start_logging()
406 407
407 408 def start(self):
408 409 """Start the app for the engines subcommand."""
409 410 self.log.info("IPython cluster: started")
410 411 # First see if the cluster is already running
411 412
412 413 # Now log and daemonize
413 414 self.log.info(
414 415 'Starting engines with [daemon=%r]' % self.daemonize
415 416 )
416 417 # TODO: Get daemonize working on Windows or as a Windows Server.
417 418 if self.daemonize:
418 419 if os.name=='posix':
419 420 daemonize()
420 421
421 422 dc = ioloop.DelayedCallback(self.start_engines, 0, self.loop)
422 423 dc.start()
423 424 # Now write the new pid file AFTER our new forked pid is active.
424 425 # self.write_pid_file()
425 426 try:
426 427 self.loop.start()
427 428 except KeyboardInterrupt:
428 429 pass
429 430 except zmq.ZMQError as e:
430 431 if e.errno == errno.EINTR:
431 432 pass
432 433 else:
433 434 raise
434 435
435 436 start_aliases = {}
436 437 start_aliases.update(engine_aliases)
437 438 start_aliases.update(dict(
438 439 delay='IPClusterStart.delay',
439 440 controller = 'IPClusterStart.controller_launcher_class',
440 441 ))
441 442 start_aliases['clean-logs'] = 'IPClusterStart.clean_logs'
442 443
443 444 class IPClusterStart(IPClusterEngines):
444 445
445 446 name = u'ipcluster'
446 447 description = start_help
447 448 examples = _start_examples
448 449 default_log_level = logging.INFO
449 450 auto_create = Bool(True, config=True,
450 451 help="whether to create the profile_dir if it doesn't exist")
451 452 classes = List()
452 453 def _classes_default(self,):
453 454 from IPython.parallel.apps import launcher
454 455 return [ProfileDir] + [IPClusterEngines] + launcher.all_launchers
455 456
456 457 clean_logs = Bool(True, config=True,
457 458 help="whether to cleanup old logs before starting")
458 459
459 460 delay = CFloat(1., config=True,
460 461 help="delay (in s) between starting the controller and the engines")
461 462
462 463 controller_launcher = Any(config=True, help="Deprecated, use controller_launcher_class")
463 464 def _controller_launcher_changed(self, name, old, new):
464 465 if isinstance(new, basestring):
465 466 # old 0.11-style config
466 467 self.log.warn("WARNING: %s.controller_launcher is deprecated as of 0.12,"
467 468 " use controller_launcher_class" % self.__class__.__name__)
468 469 self.controller_launcher_class = new
469 470 controller_launcher_class = DottedObjectName('LocalControllerLauncher',
470 471 config=True,
471 472 help="""The class for launching a Controller. Change this value if you want
472 473 your controller to also be launched by a batch system, such as PBS,SGE,MPI,etc.
473 474
474 475 Each launcher class has its own set of configuration options, for making sure
475 476 it will work in your environment.
476 477
477 478 Note that using a batch launcher for the controller *does not* put it
478 479 in the same batch job as the engines, so they will still start separately.
479 480
480 481 IPython's bundled examples include:
481 482
482 483 Local : start engines locally as subprocesses
483 484 MPI : use mpiexec to launch the controller in an MPI universe
484 485 PBS : use PBS (qsub) to submit the controller to a batch queue
485 486 SGE : use SGE (qsub) to submit the controller to a batch queue
486 487 LSF : use LSF (bsub) to submit the controller to a batch queue
487 488 HTCondor : use HTCondor to submit the controller to a batch queue
488 489 SSH : use SSH to start the controller
489 490 WindowsHPC : use Windows HPC
490 491
491 492 If you are using one of IPython's builtin launchers, you can specify just the
492 493 prefix, e.g:
493 494
494 495 c.IPClusterStart.controller_launcher_class = 'SSH'
495 496
496 497 or:
497 498
498 499 ipcluster start --controller=MPI
499 500
500 501 """
501 502 )
502 503 reset = Bool(False, config=True,
503 504 help="Whether to reset config files as part of '--create'."
504 505 )
505 506
506 507 # flags = Dict(flags)
507 508 aliases = Dict(start_aliases)
508 509
509 510 def init_launchers(self):
510 511 self.controller_launcher = self.build_launcher(self.controller_launcher_class, 'Controller')
511 512 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
512 513
513 514 def engines_stopped(self, r):
514 515 """prevent parent.engines_stopped from stopping everything on engine shutdown"""
515 516 pass
516 517
517 518 def start_controller(self):
518 519 self.log.info("Starting Controller with %s", self.controller_launcher_class)
519 520 self.controller_launcher.on_stop(self.stop_launchers)
520 521 self.controller_launcher.start()
521 522
522 523 def stop_controller(self):
523 524 # self.log.info("In stop_controller")
524 525 if self.controller_launcher and self.controller_launcher.running:
525 526 return self.controller_launcher.stop()
526 527
527 528 def stop_launchers(self, r=None):
528 529 if not self._stopping:
529 530 self.stop_controller()
530 531 super(IPClusterStart, self).stop_launchers()
531 532
532 533 def start(self):
533 534 """Start the app for the start subcommand."""
534 535 # First see if the cluster is already running
535 536 try:
536 537 pid = self.get_pid_from_file()
537 538 except PIDFileError:
538 539 pass
539 540 else:
540 541 if self.check_pid(pid):
541 542 self.log.critical(
542 543 'Cluster is already running with [pid=%s]. '
543 544 'use "ipcluster stop" to stop the cluster.' % pid
544 545 )
545 546 # Here I exit with a unusual exit status that other processes
546 547 # can watch for to learn how I existed.
547 548 self.exit(ALREADY_STARTED)
548 549 else:
549 550 self.remove_pid_file()
550 551
551 552
552 553 # Now log and daemonize
553 554 self.log.info(
554 555 'Starting ipcluster with [daemon=%r]' % self.daemonize
555 556 )
556 557 # TODO: Get daemonize working on Windows or as a Windows Server.
557 558 if self.daemonize:
558 559 if os.name=='posix':
559 560 daemonize()
560 561
561 562 dc = ioloop.DelayedCallback(self.start_controller, 0, self.loop)
562 563 dc.start()
563 564 dc = ioloop.DelayedCallback(self.start_engines, 1000*self.delay, self.loop)
564 565 dc.start()
565 566 # Now write the new pid file AFTER our new forked pid is active.
566 567 self.write_pid_file()
567 568 try:
568 569 self.loop.start()
569 570 except KeyboardInterrupt:
570 571 pass
571 572 except zmq.ZMQError as e:
572 573 if e.errno == errno.EINTR:
573 574 pass
574 575 else:
575 576 raise
576 577 finally:
577 578 self.remove_pid_file()
578 579
579 580 base='IPython.parallel.apps.ipclusterapp.IPCluster'
580 581
581 582 class IPClusterApp(BaseIPythonApplication):
582 583 name = u'ipcluster'
583 584 description = _description
584 585 examples = _main_examples
585 586
586 587 subcommands = {
587 588 'start' : (base+'Start', start_help),
588 589 'stop' : (base+'Stop', stop_help),
589 590 'engines' : (base+'Engines', engines_help),
590 591 }
591 592
592 593 # no aliases or flags for parent App
593 594 aliases = Dict()
594 595 flags = Dict()
595 596
596 597 def start(self):
597 598 if self.subapp is None:
598 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
599 print
599 print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys()))
600 print()
600 601 self.print_description()
601 602 self.print_subcommands()
602 603 self.exit(1)
603 604 else:
604 605 return self.subapp.start()
605 606
606 607 launch_new_instance = IPClusterApp.launch_instance
607 608
608 609 if __name__ == '__main__':
609 610 launch_new_instance()
610 611
@@ -1,1858 +1,1859 b''
1 1 """A semi-synchronous Client for the ZMQ cluster
2 2
3 3 Authors:
4 4
5 5 * MinRK
6 6 """
7 from __future__ import print_function
7 8 #-----------------------------------------------------------------------------
8 9 # Copyright (C) 2010-2011 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
18 19 import os
19 20 import json
20 21 import sys
21 22 from threading import Thread, Event
22 23 import time
23 24 import warnings
24 25 from datetime import datetime
25 26 from getpass import getpass
26 27 from pprint import pprint
27 28
28 29 pjoin = os.path.join
29 30
30 31 import zmq
31 32 # from zmq.eventloop import ioloop, zmqstream
32 33
33 34 from IPython.config.configurable import MultipleInstanceError
34 35 from IPython.core.application import BaseIPythonApplication
35 36 from IPython.core.profiledir import ProfileDir, ProfileDirError
36 37
37 38 from IPython.utils.capture import RichOutput
38 39 from IPython.utils.coloransi import TermColors
39 40 from IPython.utils.jsonutil import rekey
40 41 from IPython.utils.localinterfaces import localhost, is_local_ip
41 42 from IPython.utils.path import get_ipython_dir
42 43 from IPython.utils.py3compat import cast_bytes
43 44 from IPython.utils.traitlets import (HasTraits, Integer, Instance, Unicode,
44 45 Dict, List, Bool, Set, Any)
45 46 from IPython.external.decorator import decorator
46 47 from IPython.external.ssh import tunnel
47 48
48 49 from IPython.parallel import Reference
49 50 from IPython.parallel import error
50 51 from IPython.parallel import util
51 52
52 53 from IPython.kernel.zmq.session import Session, Message
53 54 from IPython.kernel.zmq import serialize
54 55
55 56 from .asyncresult import AsyncResult, AsyncHubResult
56 57 from .view import DirectView, LoadBalancedView
57 58
58 59 if sys.version_info[0] >= 3:
59 60 # xrange is used in a couple 'isinstance' tests in py2
60 61 # should be just 'range' in 3k
61 62 xrange = range
62 63
63 64 #--------------------------------------------------------------------------
64 65 # Decorators for Client methods
65 66 #--------------------------------------------------------------------------
66 67
67 68 @decorator
68 69 def spin_first(f, self, *args, **kwargs):
69 70 """Call spin() to sync state prior to calling the method."""
70 71 self.spin()
71 72 return f(self, *args, **kwargs)
72 73
73 74
74 75 #--------------------------------------------------------------------------
75 76 # Classes
76 77 #--------------------------------------------------------------------------
77 78
78 79
79 80 class ExecuteReply(RichOutput):
80 81 """wrapper for finished Execute results"""
81 82 def __init__(self, msg_id, content, metadata):
82 83 self.msg_id = msg_id
83 84 self._content = content
84 85 self.execution_count = content['execution_count']
85 86 self.metadata = metadata
86 87
87 88 # RichOutput overrides
88 89
89 90 @property
90 91 def source(self):
91 92 pyout = self.metadata['pyout']
92 93 if pyout:
93 94 return pyout.get('source', '')
94 95
95 96 @property
96 97 def data(self):
97 98 pyout = self.metadata['pyout']
98 99 if pyout:
99 100 return pyout.get('data', {})
100 101
101 102 @property
102 103 def _metadata(self):
103 104 pyout = self.metadata['pyout']
104 105 if pyout:
105 106 return pyout.get('metadata', {})
106 107
107 108 def display(self):
108 109 from IPython.display import publish_display_data
109 110 publish_display_data(self.source, self.data, self.metadata)
110 111
111 112 def _repr_mime_(self, mime):
112 113 if mime not in self.data:
113 114 return
114 115 data = self.data[mime]
115 116 if mime in self._metadata:
116 117 return data, self._metadata[mime]
117 118 else:
118 119 return data
119 120
120 121 def __getitem__(self, key):
121 122 return self.metadata[key]
122 123
123 124 def __getattr__(self, key):
124 125 if key not in self.metadata:
125 126 raise AttributeError(key)
126 127 return self.metadata[key]
127 128
128 129 def __repr__(self):
129 130 pyout = self.metadata['pyout'] or {'data':{}}
130 131 text_out = pyout['data'].get('text/plain', '')
131 132 if len(text_out) > 32:
132 133 text_out = text_out[:29] + '...'
133 134
134 135 return "<ExecuteReply[%i]: %s>" % (self.execution_count, text_out)
135 136
136 137 def _repr_pretty_(self, p, cycle):
137 138 pyout = self.metadata['pyout'] or {'data':{}}
138 139 text_out = pyout['data'].get('text/plain', '')
139 140
140 141 if not text_out:
141 142 return
142 143
143 144 try:
144 145 ip = get_ipython()
145 146 except NameError:
146 147 colors = "NoColor"
147 148 else:
148 149 colors = ip.colors
149 150
150 151 if colors == "NoColor":
151 152 out = normal = ""
152 153 else:
153 154 out = TermColors.Red
154 155 normal = TermColors.Normal
155 156
156 157 if '\n' in text_out and not text_out.startswith('\n'):
157 158 # add newline for multiline reprs
158 159 text_out = '\n' + text_out
159 160
160 161 p.text(
161 162 out + u'Out[%i:%i]: ' % (
162 163 self.metadata['engine_id'], self.execution_count
163 164 ) + normal + text_out
164 165 )
165 166
166 167
167 168 class Metadata(dict):
168 169 """Subclass of dict for initializing metadata values.
169 170
170 171 Attribute access works on keys.
171 172
172 173 These objects have a strict set of keys - errors will raise if you try
173 174 to add new keys.
174 175 """
175 176 def __init__(self, *args, **kwargs):
176 177 dict.__init__(self)
177 178 md = {'msg_id' : None,
178 179 'submitted' : None,
179 180 'started' : None,
180 181 'completed' : None,
181 182 'received' : None,
182 183 'engine_uuid' : None,
183 184 'engine_id' : None,
184 185 'follow' : None,
185 186 'after' : None,
186 187 'status' : None,
187 188
188 189 'pyin' : None,
189 190 'pyout' : None,
190 191 'pyerr' : None,
191 192 'stdout' : '',
192 193 'stderr' : '',
193 194 'outputs' : [],
194 195 'data': {},
195 196 'outputs_ready' : False,
196 197 }
197 198 self.update(md)
198 199 self.update(dict(*args, **kwargs))
199 200
200 201 def __getattr__(self, key):
201 202 """getattr aliased to getitem"""
202 203 if key in self.iterkeys():
203 204 return self[key]
204 205 else:
205 206 raise AttributeError(key)
206 207
207 208 def __setattr__(self, key, value):
208 209 """setattr aliased to setitem, with strict"""
209 210 if key in self.iterkeys():
210 211 self[key] = value
211 212 else:
212 213 raise AttributeError(key)
213 214
214 215 def __setitem__(self, key, value):
215 216 """strict static key enforcement"""
216 217 if key in self.iterkeys():
217 218 dict.__setitem__(self, key, value)
218 219 else:
219 220 raise KeyError(key)
220 221
221 222
222 223 class Client(HasTraits):
223 224 """A semi-synchronous client to the IPython ZMQ cluster
224 225
225 226 Parameters
226 227 ----------
227 228
228 229 url_file : str/unicode; path to ipcontroller-client.json
229 230 This JSON file should contain all the information needed to connect to a cluster,
230 231 and is likely the only argument needed.
231 232 Connection information for the Hub's registration. If a json connector
232 233 file is given, then likely no further configuration is necessary.
233 234 [Default: use profile]
234 235 profile : bytes
235 236 The name of the Cluster profile to be used to find connector information.
236 237 If run from an IPython application, the default profile will be the same
237 238 as the running application, otherwise it will be 'default'.
238 239 cluster_id : str
239 240 String id to added to runtime files, to prevent name collisions when using
240 241 multiple clusters with a single profile simultaneously.
241 242 When set, will look for files named like: 'ipcontroller-<cluster_id>-client.json'
242 243 Since this is text inserted into filenames, typical recommendations apply:
243 244 Simple character strings are ideal, and spaces are not recommended (but
244 245 should generally work)
245 246 context : zmq.Context
246 247 Pass an existing zmq.Context instance, otherwise the client will create its own.
247 248 debug : bool
248 249 flag for lots of message printing for debug purposes
249 250 timeout : int/float
250 251 time (in seconds) to wait for connection replies from the Hub
251 252 [Default: 10]
252 253
253 254 #-------------- session related args ----------------
254 255
255 256 config : Config object
256 257 If specified, this will be relayed to the Session for configuration
257 258 username : str
258 259 set username for the session object
259 260
260 261 #-------------- ssh related args ----------------
261 262 # These are args for configuring the ssh tunnel to be used
262 263 # credentials are used to forward connections over ssh to the Controller
263 264 # Note that the ip given in `addr` needs to be relative to sshserver
264 265 # The most basic case is to leave addr as pointing to localhost (127.0.0.1),
265 266 # and set sshserver as the same machine the Controller is on. However,
266 267 # the only requirement is that sshserver is able to see the Controller
267 268 # (i.e. is within the same trusted network).
268 269
269 270 sshserver : str
270 271 A string of the form passed to ssh, i.e. 'server.tld' or 'user@server.tld:port'
271 272 If keyfile or password is specified, and this is not, it will default to
272 273 the ip given in addr.
273 274 sshkey : str; path to ssh private key file
274 275 This specifies a key to be used in ssh login, default None.
275 276 Regular default ssh keys will be used without specifying this argument.
276 277 password : str
277 278 Your ssh password to sshserver. Note that if this is left None,
278 279 you will be prompted for it if passwordless key based login is unavailable.
279 280 paramiko : bool
280 281 flag for whether to use paramiko instead of shell ssh for tunneling.
281 282 [default: True on win32, False else]
282 283
283 284
284 285 Attributes
285 286 ----------
286 287
287 288 ids : list of int engine IDs
288 289 requesting the ids attribute always synchronizes
289 290 the registration state. To request ids without synchronization,
290 291 use semi-private _ids attributes.
291 292
292 293 history : list of msg_ids
293 294 a list of msg_ids, keeping track of all the execution
294 295 messages you have submitted in order.
295 296
296 297 outstanding : set of msg_ids
297 298 a set of msg_ids that have been submitted, but whose
298 299 results have not yet been received.
299 300
300 301 results : dict
301 302 a dict of all our results, keyed by msg_id
302 303
303 304 block : bool
304 305 determines default behavior when block not specified
305 306 in execution methods
306 307
307 308 Methods
308 309 -------
309 310
310 311 spin
311 312 flushes incoming results and registration state changes
312 313 control methods spin, and requesting `ids` also ensures up to date
313 314
314 315 wait
315 316 wait on one or more msg_ids
316 317
317 318 execution methods
318 319 apply
319 320 legacy: execute, run
320 321
321 322 data movement
322 323 push, pull, scatter, gather
323 324
324 325 query methods
325 326 queue_status, get_result, purge, result_status
326 327
327 328 control methods
328 329 abort, shutdown
329 330
330 331 """
331 332
332 333
333 334 block = Bool(False)
334 335 outstanding = Set()
335 336 results = Instance('collections.defaultdict', (dict,))
336 337 metadata = Instance('collections.defaultdict', (Metadata,))
337 338 history = List()
338 339 debug = Bool(False)
339 340 _spin_thread = Any()
340 341 _stop_spinning = Any()
341 342
342 343 profile=Unicode()
343 344 def _profile_default(self):
344 345 if BaseIPythonApplication.initialized():
345 346 # an IPython app *might* be running, try to get its profile
346 347 try:
347 348 return BaseIPythonApplication.instance().profile
348 349 except (AttributeError, MultipleInstanceError):
349 350 # could be a *different* subclass of config.Application,
350 351 # which would raise one of these two errors.
351 352 return u'default'
352 353 else:
353 354 return u'default'
354 355
355 356
356 357 _outstanding_dict = Instance('collections.defaultdict', (set,))
357 358 _ids = List()
358 359 _connected=Bool(False)
359 360 _ssh=Bool(False)
360 361 _context = Instance('zmq.Context')
361 362 _config = Dict()
362 363 _engines=Instance(util.ReverseDict, (), {})
363 364 # _hub_socket=Instance('zmq.Socket')
364 365 _query_socket=Instance('zmq.Socket')
365 366 _control_socket=Instance('zmq.Socket')
366 367 _iopub_socket=Instance('zmq.Socket')
367 368 _notification_socket=Instance('zmq.Socket')
368 369 _mux_socket=Instance('zmq.Socket')
369 370 _task_socket=Instance('zmq.Socket')
370 371 _task_scheme=Unicode()
371 372 _closed = False
372 373 _ignored_control_replies=Integer(0)
373 374 _ignored_hub_replies=Integer(0)
374 375
375 376 def __new__(self, *args, **kw):
376 377 # don't raise on positional args
377 378 return HasTraits.__new__(self, **kw)
378 379
379 380 def __init__(self, url_file=None, profile=None, profile_dir=None, ipython_dir=None,
380 381 context=None, debug=False,
381 382 sshserver=None, sshkey=None, password=None, paramiko=None,
382 383 timeout=10, cluster_id=None, **extra_args
383 384 ):
384 385 if profile:
385 386 super(Client, self).__init__(debug=debug, profile=profile)
386 387 else:
387 388 super(Client, self).__init__(debug=debug)
388 389 if context is None:
389 390 context = zmq.Context.instance()
390 391 self._context = context
391 392 self._stop_spinning = Event()
392 393
393 394 if 'url_or_file' in extra_args:
394 395 url_file = extra_args['url_or_file']
395 396 warnings.warn("url_or_file arg no longer supported, use url_file", DeprecationWarning)
396 397
397 398 if url_file and util.is_url(url_file):
398 399 raise ValueError("single urls cannot be specified, url-files must be used.")
399 400
400 401 self._setup_profile_dir(self.profile, profile_dir, ipython_dir)
401 402
402 403 if self._cd is not None:
403 404 if url_file is None:
404 405 if not cluster_id:
405 406 client_json = 'ipcontroller-client.json'
406 407 else:
407 408 client_json = 'ipcontroller-%s-client.json' % cluster_id
408 409 url_file = pjoin(self._cd.security_dir, client_json)
409 410 if url_file is None:
410 411 raise ValueError(
411 412 "I can't find enough information to connect to a hub!"
412 413 " Please specify at least one of url_file or profile."
413 414 )
414 415
415 416 with open(url_file) as f:
416 417 cfg = json.load(f)
417 418
418 419 self._task_scheme = cfg['task_scheme']
419 420
420 421 # sync defaults from args, json:
421 422 if sshserver:
422 423 cfg['ssh'] = sshserver
423 424
424 425 location = cfg.setdefault('location', None)
425 426
426 427 proto,addr = cfg['interface'].split('://')
427 428 addr = util.disambiguate_ip_address(addr, location)
428 429 cfg['interface'] = "%s://%s" % (proto, addr)
429 430
430 431 # turn interface,port into full urls:
431 432 for key in ('control', 'task', 'mux', 'iopub', 'notification', 'registration'):
432 433 cfg[key] = cfg['interface'] + ':%i' % cfg[key]
433 434
434 435 url = cfg['registration']
435 436
436 437 if location is not None and addr == localhost():
437 438 # location specified, and connection is expected to be local
438 439 if not is_local_ip(location) and not sshserver:
439 440 # load ssh from JSON *only* if the controller is not on
440 441 # this machine
441 442 sshserver=cfg['ssh']
442 443 if not is_local_ip(location) and not sshserver:
443 444 # warn if no ssh specified, but SSH is probably needed
444 445 # This is only a warning, because the most likely cause
445 446 # is a local Controller on a laptop whose IP is dynamic
446 447 warnings.warn("""
447 448 Controller appears to be listening on localhost, but not on this machine.
448 449 If this is true, you should specify Client(...,sshserver='you@%s')
449 450 or instruct your controller to listen on an external IP."""%location,
450 451 RuntimeWarning)
451 452 elif not sshserver:
452 453 # otherwise sync with cfg
453 454 sshserver = cfg['ssh']
454 455
455 456 self._config = cfg
456 457
457 458 self._ssh = bool(sshserver or sshkey or password)
458 459 if self._ssh and sshserver is None:
459 460 # default to ssh via localhost
460 461 sshserver = addr
461 462 if self._ssh and password is None:
462 463 if tunnel.try_passwordless_ssh(sshserver, sshkey, paramiko):
463 464 password=False
464 465 else:
465 466 password = getpass("SSH Password for %s: "%sshserver)
466 467 ssh_kwargs = dict(keyfile=sshkey, password=password, paramiko=paramiko)
467 468
468 469 # configure and construct the session
469 470 try:
470 471 extra_args['packer'] = cfg['pack']
471 472 extra_args['unpacker'] = cfg['unpack']
472 473 extra_args['key'] = cast_bytes(cfg['key'])
473 474 extra_args['signature_scheme'] = cfg['signature_scheme']
474 475 except KeyError as exc:
475 476 msg = '\n'.join([
476 477 "Connection file is invalid (missing '{}'), possibly from an old version of IPython.",
477 478 "If you are reusing connection files, remove them and start ipcontroller again."
478 479 ])
479 480 raise ValueError(msg.format(exc.message))
480 481
481 482 self.session = Session(**extra_args)
482 483
483 484 self._query_socket = self._context.socket(zmq.DEALER)
484 485
485 486 if self._ssh:
486 487 tunnel.tunnel_connection(self._query_socket, cfg['registration'], sshserver, **ssh_kwargs)
487 488 else:
488 489 self._query_socket.connect(cfg['registration'])
489 490
490 491 self.session.debug = self.debug
491 492
492 493 self._notification_handlers = {'registration_notification' : self._register_engine,
493 494 'unregistration_notification' : self._unregister_engine,
494 495 'shutdown_notification' : lambda msg: self.close(),
495 496 }
496 497 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
497 498 'apply_reply' : self._handle_apply_reply}
498 499
499 500 try:
500 501 self._connect(sshserver, ssh_kwargs, timeout)
501 502 except:
502 503 self.close(linger=0)
503 504 raise
504 505
505 506 # last step: setup magics, if we are in IPython:
506 507
507 508 try:
508 509 ip = get_ipython()
509 510 except NameError:
510 511 return
511 512 else:
512 513 if 'px' not in ip.magics_manager.magics:
513 514 # in IPython but we are the first Client.
514 515 # activate a default view for parallel magics.
515 516 self.activate()
516 517
517 518 def __del__(self):
518 519 """cleanup sockets, but _not_ context."""
519 520 self.close()
520 521
521 522 def _setup_profile_dir(self, profile, profile_dir, ipython_dir):
522 523 if ipython_dir is None:
523 524 ipython_dir = get_ipython_dir()
524 525 if profile_dir is not None:
525 526 try:
526 527 self._cd = ProfileDir.find_profile_dir(profile_dir)
527 528 return
528 529 except ProfileDirError:
529 530 pass
530 531 elif profile is not None:
531 532 try:
532 533 self._cd = ProfileDir.find_profile_dir_by_name(
533 534 ipython_dir, profile)
534 535 return
535 536 except ProfileDirError:
536 537 pass
537 538 self._cd = None
538 539
539 540 def _update_engines(self, engines):
540 541 """Update our engines dict and _ids from a dict of the form: {id:uuid}."""
541 542 for k,v in engines.iteritems():
542 543 eid = int(k)
543 544 if eid not in self._engines:
544 545 self._ids.append(eid)
545 546 self._engines[eid] = v
546 547 self._ids = sorted(self._ids)
547 548 if sorted(self._engines.keys()) != range(len(self._engines)) and \
548 549 self._task_scheme == 'pure' and self._task_socket:
549 550 self._stop_scheduling_tasks()
550 551
551 552 def _stop_scheduling_tasks(self):
552 553 """Stop scheduling tasks because an engine has been unregistered
553 554 from a pure ZMQ scheduler.
554 555 """
555 556 self._task_socket.close()
556 557 self._task_socket = None
557 558 msg = "An engine has been unregistered, and we are using pure " +\
558 559 "ZMQ task scheduling. Task farming will be disabled."
559 560 if self.outstanding:
560 561 msg += " If you were running tasks when this happened, " +\
561 562 "some `outstanding` msg_ids may never resolve."
562 563 warnings.warn(msg, RuntimeWarning)
563 564
564 565 def _build_targets(self, targets):
565 566 """Turn valid target IDs or 'all' into two lists:
566 567 (int_ids, uuids).
567 568 """
568 569 if not self._ids:
569 570 # flush notification socket if no engines yet, just in case
570 571 if not self.ids:
571 572 raise error.NoEnginesRegistered("Can't build targets without any engines")
572 573
573 574 if targets is None:
574 575 targets = self._ids
575 576 elif isinstance(targets, basestring):
576 577 if targets.lower() == 'all':
577 578 targets = self._ids
578 579 else:
579 580 raise TypeError("%r not valid str target, must be 'all'"%(targets))
580 581 elif isinstance(targets, int):
581 582 if targets < 0:
582 583 targets = self.ids[targets]
583 584 if targets not in self._ids:
584 585 raise IndexError("No such engine: %i"%targets)
585 586 targets = [targets]
586 587
587 588 if isinstance(targets, slice):
588 589 indices = range(len(self._ids))[targets]
589 590 ids = self.ids
590 591 targets = [ ids[i] for i in indices ]
591 592
592 593 if not isinstance(targets, (tuple, list, xrange)):
593 594 raise TypeError("targets by int/slice/collection of ints only, not %s"%(type(targets)))
594 595
595 596 return [cast_bytes(self._engines[t]) for t in targets], list(targets)
596 597
597 598 def _connect(self, sshserver, ssh_kwargs, timeout):
598 599 """setup all our socket connections to the cluster. This is called from
599 600 __init__."""
600 601
601 602 # Maybe allow reconnecting?
602 603 if self._connected:
603 604 return
604 605 self._connected=True
605 606
606 607 def connect_socket(s, url):
607 608 if self._ssh:
608 609 return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs)
609 610 else:
610 611 return s.connect(url)
611 612
612 613 self.session.send(self._query_socket, 'connection_request')
613 614 # use Poller because zmq.select has wrong units in pyzmq 2.1.7
614 615 poller = zmq.Poller()
615 616 poller.register(self._query_socket, zmq.POLLIN)
616 617 # poll expects milliseconds, timeout is seconds
617 618 evts = poller.poll(timeout*1000)
618 619 if not evts:
619 620 raise error.TimeoutError("Hub connection request timed out")
620 621 idents,msg = self.session.recv(self._query_socket,mode=0)
621 622 if self.debug:
622 623 pprint(msg)
623 624 content = msg['content']
624 625 # self._config['registration'] = dict(content)
625 626 cfg = self._config
626 627 if content['status'] == 'ok':
627 628 self._mux_socket = self._context.socket(zmq.DEALER)
628 629 connect_socket(self._mux_socket, cfg['mux'])
629 630
630 631 self._task_socket = self._context.socket(zmq.DEALER)
631 632 connect_socket(self._task_socket, cfg['task'])
632 633
633 634 self._notification_socket = self._context.socket(zmq.SUB)
634 635 self._notification_socket.setsockopt(zmq.SUBSCRIBE, b'')
635 636 connect_socket(self._notification_socket, cfg['notification'])
636 637
637 638 self._control_socket = self._context.socket(zmq.DEALER)
638 639 connect_socket(self._control_socket, cfg['control'])
639 640
640 641 self._iopub_socket = self._context.socket(zmq.SUB)
641 642 self._iopub_socket.setsockopt(zmq.SUBSCRIBE, b'')
642 643 connect_socket(self._iopub_socket, cfg['iopub'])
643 644
644 645 self._update_engines(dict(content['engines']))
645 646 else:
646 647 self._connected = False
647 648 raise Exception("Failed to connect!")
648 649
649 650 #--------------------------------------------------------------------------
650 651 # handlers and callbacks for incoming messages
651 652 #--------------------------------------------------------------------------
652 653
653 654 def _unwrap_exception(self, content):
654 655 """unwrap exception, and remap engine_id to int."""
655 656 e = error.unwrap_exception(content)
656 657 # print e.traceback
657 658 if e.engine_info:
658 659 e_uuid = e.engine_info['engine_uuid']
659 660 eid = self._engines[e_uuid]
660 661 e.engine_info['engine_id'] = eid
661 662 return e
662 663
663 664 def _extract_metadata(self, msg):
664 665 header = msg['header']
665 666 parent = msg['parent_header']
666 667 msg_meta = msg['metadata']
667 668 content = msg['content']
668 669 md = {'msg_id' : parent['msg_id'],
669 670 'received' : datetime.now(),
670 671 'engine_uuid' : msg_meta.get('engine', None),
671 672 'follow' : msg_meta.get('follow', []),
672 673 'after' : msg_meta.get('after', []),
673 674 'status' : content['status'],
674 675 }
675 676
676 677 if md['engine_uuid'] is not None:
677 678 md['engine_id'] = self._engines.get(md['engine_uuid'], None)
678 679
679 680 if 'date' in parent:
680 681 md['submitted'] = parent['date']
681 682 if 'started' in msg_meta:
682 683 md['started'] = msg_meta['started']
683 684 if 'date' in header:
684 685 md['completed'] = header['date']
685 686 return md
686 687
687 688 def _register_engine(self, msg):
688 689 """Register a new engine, and update our connection info."""
689 690 content = msg['content']
690 691 eid = content['id']
691 692 d = {eid : content['uuid']}
692 693 self._update_engines(d)
693 694
694 695 def _unregister_engine(self, msg):
695 696 """Unregister an engine that has died."""
696 697 content = msg['content']
697 698 eid = int(content['id'])
698 699 if eid in self._ids:
699 700 self._ids.remove(eid)
700 701 uuid = self._engines.pop(eid)
701 702
702 703 self._handle_stranded_msgs(eid, uuid)
703 704
704 705 if self._task_socket and self._task_scheme == 'pure':
705 706 self._stop_scheduling_tasks()
706 707
707 708 def _handle_stranded_msgs(self, eid, uuid):
708 709 """Handle messages known to be on an engine when the engine unregisters.
709 710
710 711 It is possible that this will fire prematurely - that is, an engine will
711 712 go down after completing a result, and the client will be notified
712 713 of the unregistration and later receive the successful result.
713 714 """
714 715
715 716 outstanding = self._outstanding_dict[uuid]
716 717
717 718 for msg_id in list(outstanding):
718 719 if msg_id in self.results:
719 720 # we already
720 721 continue
721 722 try:
722 723 raise error.EngineError("Engine %r died while running task %r"%(eid, msg_id))
723 724 except:
724 725 content = error.wrap_exception()
725 726 # build a fake message:
726 727 msg = self.session.msg('apply_reply', content=content)
727 728 msg['parent_header']['msg_id'] = msg_id
728 729 msg['metadata']['engine'] = uuid
729 730 self._handle_apply_reply(msg)
730 731
731 732 def _handle_execute_reply(self, msg):
732 733 """Save the reply to an execute_request into our results.
733 734
734 735 execute messages are never actually used. apply is used instead.
735 736 """
736 737
737 738 parent = msg['parent_header']
738 739 msg_id = parent['msg_id']
739 740 if msg_id not in self.outstanding:
740 741 if msg_id in self.history:
741 print ("got stale result: %s"%msg_id)
742 print(("got stale result: %s"%msg_id))
742 743 else:
743 print ("got unknown result: %s"%msg_id)
744 print(("got unknown result: %s"%msg_id))
744 745 else:
745 746 self.outstanding.remove(msg_id)
746 747
747 748 content = msg['content']
748 749 header = msg['header']
749 750
750 751 # construct metadata:
751 752 md = self.metadata[msg_id]
752 753 md.update(self._extract_metadata(msg))
753 754 # is this redundant?
754 755 self.metadata[msg_id] = md
755 756
756 757 e_outstanding = self._outstanding_dict[md['engine_uuid']]
757 758 if msg_id in e_outstanding:
758 759 e_outstanding.remove(msg_id)
759 760
760 761 # construct result:
761 762 if content['status'] == 'ok':
762 763 self.results[msg_id] = ExecuteReply(msg_id, content, md)
763 764 elif content['status'] == 'aborted':
764 765 self.results[msg_id] = error.TaskAborted(msg_id)
765 766 elif content['status'] == 'resubmitted':
766 767 # TODO: handle resubmission
767 768 pass
768 769 else:
769 770 self.results[msg_id] = self._unwrap_exception(content)
770 771
771 772 def _handle_apply_reply(self, msg):
772 773 """Save the reply to an apply_request into our results."""
773 774 parent = msg['parent_header']
774 775 msg_id = parent['msg_id']
775 776 if msg_id not in self.outstanding:
776 777 if msg_id in self.history:
777 print ("got stale result: %s"%msg_id)
778 print self.results[msg_id]
779 print msg
778 print(("got stale result: %s"%msg_id))
779 print(self.results[msg_id])
780 print(msg)
780 781 else:
781 print ("got unknown result: %s"%msg_id)
782 print(("got unknown result: %s"%msg_id))
782 783 else:
783 784 self.outstanding.remove(msg_id)
784 785 content = msg['content']
785 786 header = msg['header']
786 787
787 788 # construct metadata:
788 789 md = self.metadata[msg_id]
789 790 md.update(self._extract_metadata(msg))
790 791 # is this redundant?
791 792 self.metadata[msg_id] = md
792 793
793 794 e_outstanding = self._outstanding_dict[md['engine_uuid']]
794 795 if msg_id in e_outstanding:
795 796 e_outstanding.remove(msg_id)
796 797
797 798 # construct result:
798 799 if content['status'] == 'ok':
799 800 self.results[msg_id] = serialize.unserialize_object(msg['buffers'])[0]
800 801 elif content['status'] == 'aborted':
801 802 self.results[msg_id] = error.TaskAborted(msg_id)
802 803 elif content['status'] == 'resubmitted':
803 804 # TODO: handle resubmission
804 805 pass
805 806 else:
806 807 self.results[msg_id] = self._unwrap_exception(content)
807 808
808 809 def _flush_notifications(self):
809 810 """Flush notifications of engine registrations waiting
810 811 in ZMQ queue."""
811 812 idents,msg = self.session.recv(self._notification_socket, mode=zmq.NOBLOCK)
812 813 while msg is not None:
813 814 if self.debug:
814 815 pprint(msg)
815 816 msg_type = msg['header']['msg_type']
816 817 handler = self._notification_handlers.get(msg_type, None)
817 818 if handler is None:
818 819 raise Exception("Unhandled message type: %s" % msg_type)
819 820 else:
820 821 handler(msg)
821 822 idents,msg = self.session.recv(self._notification_socket, mode=zmq.NOBLOCK)
822 823
823 824 def _flush_results(self, sock):
824 825 """Flush task or queue results waiting in ZMQ queue."""
825 826 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
826 827 while msg is not None:
827 828 if self.debug:
828 829 pprint(msg)
829 830 msg_type = msg['header']['msg_type']
830 831 handler = self._queue_handlers.get(msg_type, None)
831 832 if handler is None:
832 833 raise Exception("Unhandled message type: %s" % msg_type)
833 834 else:
834 835 handler(msg)
835 836 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
836 837
837 838 def _flush_control(self, sock):
838 839 """Flush replies from the control channel waiting
839 840 in the ZMQ queue.
840 841
841 842 Currently: ignore them."""
842 843 if self._ignored_control_replies <= 0:
843 844 return
844 845 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
845 846 while msg is not None:
846 847 self._ignored_control_replies -= 1
847 848 if self.debug:
848 849 pprint(msg)
849 850 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
850 851
851 852 def _flush_ignored_control(self):
852 853 """flush ignored control replies"""
853 854 while self._ignored_control_replies > 0:
854 855 self.session.recv(self._control_socket)
855 856 self._ignored_control_replies -= 1
856 857
857 858 def _flush_ignored_hub_replies(self):
858 859 ident,msg = self.session.recv(self._query_socket, mode=zmq.NOBLOCK)
859 860 while msg is not None:
860 861 ident,msg = self.session.recv(self._query_socket, mode=zmq.NOBLOCK)
861 862
862 863 def _flush_iopub(self, sock):
863 864 """Flush replies from the iopub channel waiting
864 865 in the ZMQ queue.
865 866 """
866 867 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
867 868 while msg is not None:
868 869 if self.debug:
869 870 pprint(msg)
870 871 parent = msg['parent_header']
871 872 # ignore IOPub messages with no parent.
872 873 # Caused by print statements or warnings from before the first execution.
873 874 if not parent:
874 875 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
875 876 continue
876 877 msg_id = parent['msg_id']
877 878 content = msg['content']
878 879 header = msg['header']
879 880 msg_type = msg['header']['msg_type']
880 881
881 882 # init metadata:
882 883 md = self.metadata[msg_id]
883 884
884 885 if msg_type == 'stream':
885 886 name = content['name']
886 887 s = md[name] or ''
887 888 md[name] = s + content['data']
888 889 elif msg_type == 'pyerr':
889 890 md.update({'pyerr' : self._unwrap_exception(content)})
890 891 elif msg_type == 'pyin':
891 892 md.update({'pyin' : content['code']})
892 893 elif msg_type == 'display_data':
893 894 md['outputs'].append(content)
894 895 elif msg_type == 'pyout':
895 896 md['pyout'] = content
896 897 elif msg_type == 'data_message':
897 898 data, remainder = serialize.unserialize_object(msg['buffers'])
898 899 md['data'].update(data)
899 900 elif msg_type == 'status':
900 901 # idle message comes after all outputs
901 902 if content['execution_state'] == 'idle':
902 903 md['outputs_ready'] = True
903 904 else:
904 905 # unhandled msg_type (status, etc.)
905 906 pass
906 907
907 908 # reduntant?
908 909 self.metadata[msg_id] = md
909 910
910 911 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
911 912
912 913 #--------------------------------------------------------------------------
913 914 # len, getitem
914 915 #--------------------------------------------------------------------------
915 916
916 917 def __len__(self):
917 918 """len(client) returns # of engines."""
918 919 return len(self.ids)
919 920
920 921 def __getitem__(self, key):
921 922 """index access returns DirectView multiplexer objects
922 923
923 924 Must be int, slice, or list/tuple/xrange of ints"""
924 925 if not isinstance(key, (int, slice, tuple, list, xrange)):
925 926 raise TypeError("key by int/slice/iterable of ints only, not %s"%(type(key)))
926 927 else:
927 928 return self.direct_view(key)
928 929
929 930 #--------------------------------------------------------------------------
930 931 # Begin public methods
931 932 #--------------------------------------------------------------------------
932 933
933 934 @property
934 935 def ids(self):
935 936 """Always up-to-date ids property."""
936 937 self._flush_notifications()
937 938 # always copy:
938 939 return list(self._ids)
939 940
940 941 def activate(self, targets='all', suffix=''):
941 942 """Create a DirectView and register it with IPython magics
942 943
943 944 Defines the magics `%px, %autopx, %pxresult, %%px`
944 945
945 946 Parameters
946 947 ----------
947 948
948 949 targets: int, list of ints, or 'all'
949 950 The engines on which the view's magics will run
950 951 suffix: str [default: '']
951 952 The suffix, if any, for the magics. This allows you to have
952 953 multiple views associated with parallel magics at the same time.
953 954
954 955 e.g. ``rc.activate(targets=0, suffix='0')`` will give you
955 956 the magics ``%px0``, ``%pxresult0``, etc. for running magics just
956 957 on engine 0.
957 958 """
958 959 view = self.direct_view(targets)
959 960 view.block = True
960 961 view.activate(suffix)
961 962 return view
962 963
963 964 def close(self, linger=None):
964 965 """Close my zmq Sockets
965 966
966 967 If `linger`, set the zmq LINGER socket option,
967 968 which allows discarding of messages.
968 969 """
969 970 if self._closed:
970 971 return
971 972 self.stop_spin_thread()
972 973 snames = [ trait for trait in self.trait_names() if trait.endswith("socket") ]
973 974 for name in snames:
974 975 socket = getattr(self, name)
975 976 if socket is not None and not socket.closed:
976 977 if linger is not None:
977 978 socket.close(linger=linger)
978 979 else:
979 980 socket.close()
980 981 self._closed = True
981 982
982 983 def _spin_every(self, interval=1):
983 984 """target func for use in spin_thread"""
984 985 while True:
985 986 if self._stop_spinning.is_set():
986 987 return
987 988 time.sleep(interval)
988 989 self.spin()
989 990
990 991 def spin_thread(self, interval=1):
991 992 """call Client.spin() in a background thread on some regular interval
992 993
993 994 This helps ensure that messages don't pile up too much in the zmq queue
994 995 while you are working on other things, or just leaving an idle terminal.
995 996
996 997 It also helps limit potential padding of the `received` timestamp
997 998 on AsyncResult objects, used for timings.
998 999
999 1000 Parameters
1000 1001 ----------
1001 1002
1002 1003 interval : float, optional
1003 1004 The interval on which to spin the client in the background thread
1004 1005 (simply passed to time.sleep).
1005 1006
1006 1007 Notes
1007 1008 -----
1008 1009
1009 1010 For precision timing, you may want to use this method to put a bound
1010 1011 on the jitter (in seconds) in `received` timestamps used
1011 1012 in AsyncResult.wall_time.
1012 1013
1013 1014 """
1014 1015 if self._spin_thread is not None:
1015 1016 self.stop_spin_thread()
1016 1017 self._stop_spinning.clear()
1017 1018 self._spin_thread = Thread(target=self._spin_every, args=(interval,))
1018 1019 self._spin_thread.daemon = True
1019 1020 self._spin_thread.start()
1020 1021
1021 1022 def stop_spin_thread(self):
1022 1023 """stop background spin_thread, if any"""
1023 1024 if self._spin_thread is not None:
1024 1025 self._stop_spinning.set()
1025 1026 self._spin_thread.join()
1026 1027 self._spin_thread = None
1027 1028
1028 1029 def spin(self):
1029 1030 """Flush any registration notifications and execution results
1030 1031 waiting in the ZMQ queue.
1031 1032 """
1032 1033 if self._notification_socket:
1033 1034 self._flush_notifications()
1034 1035 if self._iopub_socket:
1035 1036 self._flush_iopub(self._iopub_socket)
1036 1037 if self._mux_socket:
1037 1038 self._flush_results(self._mux_socket)
1038 1039 if self._task_socket:
1039 1040 self._flush_results(self._task_socket)
1040 1041 if self._control_socket:
1041 1042 self._flush_control(self._control_socket)
1042 1043 if self._query_socket:
1043 1044 self._flush_ignored_hub_replies()
1044 1045
1045 1046 def wait(self, jobs=None, timeout=-1):
1046 1047 """waits on one or more `jobs`, for up to `timeout` seconds.
1047 1048
1048 1049 Parameters
1049 1050 ----------
1050 1051
1051 1052 jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects
1052 1053 ints are indices to self.history
1053 1054 strs are msg_ids
1054 1055 default: wait on all outstanding messages
1055 1056 timeout : float
1056 1057 a time in seconds, after which to give up.
1057 1058 default is -1, which means no timeout
1058 1059
1059 1060 Returns
1060 1061 -------
1061 1062
1062 1063 True : when all msg_ids are done
1063 1064 False : timeout reached, some msg_ids still outstanding
1064 1065 """
1065 1066 tic = time.time()
1066 1067 if jobs is None:
1067 1068 theids = self.outstanding
1068 1069 else:
1069 1070 if isinstance(jobs, (int, basestring, AsyncResult)):
1070 1071 jobs = [jobs]
1071 1072 theids = set()
1072 1073 for job in jobs:
1073 1074 if isinstance(job, int):
1074 1075 # index access
1075 1076 job = self.history[job]
1076 1077 elif isinstance(job, AsyncResult):
1077 1078 map(theids.add, job.msg_ids)
1078 1079 continue
1079 1080 theids.add(job)
1080 1081 if not theids.intersection(self.outstanding):
1081 1082 return True
1082 1083 self.spin()
1083 1084 while theids.intersection(self.outstanding):
1084 1085 if timeout >= 0 and ( time.time()-tic ) > timeout:
1085 1086 break
1086 1087 time.sleep(1e-3)
1087 1088 self.spin()
1088 1089 return len(theids.intersection(self.outstanding)) == 0
1089 1090
1090 1091 #--------------------------------------------------------------------------
1091 1092 # Control methods
1092 1093 #--------------------------------------------------------------------------
1093 1094
1094 1095 @spin_first
1095 1096 def clear(self, targets=None, block=None):
1096 1097 """Clear the namespace in target(s)."""
1097 1098 block = self.block if block is None else block
1098 1099 targets = self._build_targets(targets)[0]
1099 1100 for t in targets:
1100 1101 self.session.send(self._control_socket, 'clear_request', content={}, ident=t)
1101 1102 error = False
1102 1103 if block:
1103 1104 self._flush_ignored_control()
1104 1105 for i in range(len(targets)):
1105 1106 idents,msg = self.session.recv(self._control_socket,0)
1106 1107 if self.debug:
1107 1108 pprint(msg)
1108 1109 if msg['content']['status'] != 'ok':
1109 1110 error = self._unwrap_exception(msg['content'])
1110 1111 else:
1111 1112 self._ignored_control_replies += len(targets)
1112 1113 if error:
1113 1114 raise error
1114 1115
1115 1116
1116 1117 @spin_first
1117 1118 def abort(self, jobs=None, targets=None, block=None):
1118 1119 """Abort specific jobs from the execution queues of target(s).
1119 1120
1120 1121 This is a mechanism to prevent jobs that have already been submitted
1121 1122 from executing.
1122 1123
1123 1124 Parameters
1124 1125 ----------
1125 1126
1126 1127 jobs : msg_id, list of msg_ids, or AsyncResult
1127 1128 The jobs to be aborted
1128 1129
1129 1130 If unspecified/None: abort all outstanding jobs.
1130 1131
1131 1132 """
1132 1133 block = self.block if block is None else block
1133 1134 jobs = jobs if jobs is not None else list(self.outstanding)
1134 1135 targets = self._build_targets(targets)[0]
1135 1136
1136 1137 msg_ids = []
1137 1138 if isinstance(jobs, (basestring,AsyncResult)):
1138 1139 jobs = [jobs]
1139 1140 bad_ids = filter(lambda obj: not isinstance(obj, (basestring, AsyncResult)), jobs)
1140 1141 if bad_ids:
1141 1142 raise TypeError("Invalid msg_id type %r, expected str or AsyncResult"%bad_ids[0])
1142 1143 for j in jobs:
1143 1144 if isinstance(j, AsyncResult):
1144 1145 msg_ids.extend(j.msg_ids)
1145 1146 else:
1146 1147 msg_ids.append(j)
1147 1148 content = dict(msg_ids=msg_ids)
1148 1149 for t in targets:
1149 1150 self.session.send(self._control_socket, 'abort_request',
1150 1151 content=content, ident=t)
1151 1152 error = False
1152 1153 if block:
1153 1154 self._flush_ignored_control()
1154 1155 for i in range(len(targets)):
1155 1156 idents,msg = self.session.recv(self._control_socket,0)
1156 1157 if self.debug:
1157 1158 pprint(msg)
1158 1159 if msg['content']['status'] != 'ok':
1159 1160 error = self._unwrap_exception(msg['content'])
1160 1161 else:
1161 1162 self._ignored_control_replies += len(targets)
1162 1163 if error:
1163 1164 raise error
1164 1165
1165 1166 @spin_first
1166 1167 def shutdown(self, targets='all', restart=False, hub=False, block=None):
1167 1168 """Terminates one or more engine processes, optionally including the hub.
1168 1169
1169 1170 Parameters
1170 1171 ----------
1171 1172
1172 1173 targets: list of ints or 'all' [default: all]
1173 1174 Which engines to shutdown.
1174 1175 hub: bool [default: False]
1175 1176 Whether to include the Hub. hub=True implies targets='all'.
1176 1177 block: bool [default: self.block]
1177 1178 Whether to wait for clean shutdown replies or not.
1178 1179 restart: bool [default: False]
1179 1180 NOT IMPLEMENTED
1180 1181 whether to restart engines after shutting them down.
1181 1182 """
1182 1183 from IPython.parallel.error import NoEnginesRegistered
1183 1184 if restart:
1184 1185 raise NotImplementedError("Engine restart is not yet implemented")
1185 1186
1186 1187 block = self.block if block is None else block
1187 1188 if hub:
1188 1189 targets = 'all'
1189 1190 try:
1190 1191 targets = self._build_targets(targets)[0]
1191 1192 except NoEnginesRegistered:
1192 1193 targets = []
1193 1194 for t in targets:
1194 1195 self.session.send(self._control_socket, 'shutdown_request',
1195 1196 content={'restart':restart},ident=t)
1196 1197 error = False
1197 1198 if block or hub:
1198 1199 self._flush_ignored_control()
1199 1200 for i in range(len(targets)):
1200 1201 idents,msg = self.session.recv(self._control_socket, 0)
1201 1202 if self.debug:
1202 1203 pprint(msg)
1203 1204 if msg['content']['status'] != 'ok':
1204 1205 error = self._unwrap_exception(msg['content'])
1205 1206 else:
1206 1207 self._ignored_control_replies += len(targets)
1207 1208
1208 1209 if hub:
1209 1210 time.sleep(0.25)
1210 1211 self.session.send(self._query_socket, 'shutdown_request')
1211 1212 idents,msg = self.session.recv(self._query_socket, 0)
1212 1213 if self.debug:
1213 1214 pprint(msg)
1214 1215 if msg['content']['status'] != 'ok':
1215 1216 error = self._unwrap_exception(msg['content'])
1216 1217
1217 1218 if error:
1218 1219 raise error
1219 1220
1220 1221 #--------------------------------------------------------------------------
1221 1222 # Execution related methods
1222 1223 #--------------------------------------------------------------------------
1223 1224
1224 1225 def _maybe_raise(self, result):
1225 1226 """wrapper for maybe raising an exception if apply failed."""
1226 1227 if isinstance(result, error.RemoteError):
1227 1228 raise result
1228 1229
1229 1230 return result
1230 1231
1231 1232 def send_apply_request(self, socket, f, args=None, kwargs=None, metadata=None, track=False,
1232 1233 ident=None):
1233 1234 """construct and send an apply message via a socket.
1234 1235
1235 1236 This is the principal method with which all engine execution is performed by views.
1236 1237 """
1237 1238
1238 1239 if self._closed:
1239 1240 raise RuntimeError("Client cannot be used after its sockets have been closed")
1240 1241
1241 1242 # defaults:
1242 1243 args = args if args is not None else []
1243 1244 kwargs = kwargs if kwargs is not None else {}
1244 1245 metadata = metadata if metadata is not None else {}
1245 1246
1246 1247 # validate arguments
1247 1248 if not callable(f) and not isinstance(f, Reference):
1248 1249 raise TypeError("f must be callable, not %s"%type(f))
1249 1250 if not isinstance(args, (tuple, list)):
1250 1251 raise TypeError("args must be tuple or list, not %s"%type(args))
1251 1252 if not isinstance(kwargs, dict):
1252 1253 raise TypeError("kwargs must be dict, not %s"%type(kwargs))
1253 1254 if not isinstance(metadata, dict):
1254 1255 raise TypeError("metadata must be dict, not %s"%type(metadata))
1255 1256
1256 1257 bufs = serialize.pack_apply_message(f, args, kwargs,
1257 1258 buffer_threshold=self.session.buffer_threshold,
1258 1259 item_threshold=self.session.item_threshold,
1259 1260 )
1260 1261
1261 1262 msg = self.session.send(socket, "apply_request", buffers=bufs, ident=ident,
1262 1263 metadata=metadata, track=track)
1263 1264
1264 1265 msg_id = msg['header']['msg_id']
1265 1266 self.outstanding.add(msg_id)
1266 1267 if ident:
1267 1268 # possibly routed to a specific engine
1268 1269 if isinstance(ident, list):
1269 1270 ident = ident[-1]
1270 1271 if ident in self._engines.values():
1271 1272 # save for later, in case of engine death
1272 1273 self._outstanding_dict[ident].add(msg_id)
1273 1274 self.history.append(msg_id)
1274 1275 self.metadata[msg_id]['submitted'] = datetime.now()
1275 1276
1276 1277 return msg
1277 1278
1278 1279 def send_execute_request(self, socket, code, silent=True, metadata=None, ident=None):
1279 1280 """construct and send an execute request via a socket.
1280 1281
1281 1282 """
1282 1283
1283 1284 if self._closed:
1284 1285 raise RuntimeError("Client cannot be used after its sockets have been closed")
1285 1286
1286 1287 # defaults:
1287 1288 metadata = metadata if metadata is not None else {}
1288 1289
1289 1290 # validate arguments
1290 1291 if not isinstance(code, basestring):
1291 1292 raise TypeError("code must be text, not %s" % type(code))
1292 1293 if not isinstance(metadata, dict):
1293 1294 raise TypeError("metadata must be dict, not %s" % type(metadata))
1294 1295
1295 1296 content = dict(code=code, silent=bool(silent), user_variables=[], user_expressions={})
1296 1297
1297 1298
1298 1299 msg = self.session.send(socket, "execute_request", content=content, ident=ident,
1299 1300 metadata=metadata)
1300 1301
1301 1302 msg_id = msg['header']['msg_id']
1302 1303 self.outstanding.add(msg_id)
1303 1304 if ident:
1304 1305 # possibly routed to a specific engine
1305 1306 if isinstance(ident, list):
1306 1307 ident = ident[-1]
1307 1308 if ident in self._engines.values():
1308 1309 # save for later, in case of engine death
1309 1310 self._outstanding_dict[ident].add(msg_id)
1310 1311 self.history.append(msg_id)
1311 1312 self.metadata[msg_id]['submitted'] = datetime.now()
1312 1313
1313 1314 return msg
1314 1315
1315 1316 #--------------------------------------------------------------------------
1316 1317 # construct a View object
1317 1318 #--------------------------------------------------------------------------
1318 1319
1319 1320 def load_balanced_view(self, targets=None):
1320 1321 """construct a DirectView object.
1321 1322
1322 1323 If no arguments are specified, create a LoadBalancedView
1323 1324 using all engines.
1324 1325
1325 1326 Parameters
1326 1327 ----------
1327 1328
1328 1329 targets: list,slice,int,etc. [default: use all engines]
1329 1330 The subset of engines across which to load-balance
1330 1331 """
1331 1332 if targets == 'all':
1332 1333 targets = None
1333 1334 if targets is not None:
1334 1335 targets = self._build_targets(targets)[1]
1335 1336 return LoadBalancedView(client=self, socket=self._task_socket, targets=targets)
1336 1337
1337 1338 def direct_view(self, targets='all'):
1338 1339 """construct a DirectView object.
1339 1340
1340 1341 If no targets are specified, create a DirectView using all engines.
1341 1342
1342 1343 rc.direct_view('all') is distinguished from rc[:] in that 'all' will
1343 1344 evaluate the target engines at each execution, whereas rc[:] will connect to
1344 1345 all *current* engines, and that list will not change.
1345 1346
1346 1347 That is, 'all' will always use all engines, whereas rc[:] will not use
1347 1348 engines added after the DirectView is constructed.
1348 1349
1349 1350 Parameters
1350 1351 ----------
1351 1352
1352 1353 targets: list,slice,int,etc. [default: use all engines]
1353 1354 The engines to use for the View
1354 1355 """
1355 1356 single = isinstance(targets, int)
1356 1357 # allow 'all' to be lazily evaluated at each execution
1357 1358 if targets != 'all':
1358 1359 targets = self._build_targets(targets)[1]
1359 1360 if single:
1360 1361 targets = targets[0]
1361 1362 return DirectView(client=self, socket=self._mux_socket, targets=targets)
1362 1363
1363 1364 #--------------------------------------------------------------------------
1364 1365 # Query methods
1365 1366 #--------------------------------------------------------------------------
1366 1367
1367 1368 @spin_first
1368 1369 def get_result(self, indices_or_msg_ids=None, block=None):
1369 1370 """Retrieve a result by msg_id or history index, wrapped in an AsyncResult object.
1370 1371
1371 1372 If the client already has the results, no request to the Hub will be made.
1372 1373
1373 1374 This is a convenient way to construct AsyncResult objects, which are wrappers
1374 1375 that include metadata about execution, and allow for awaiting results that
1375 1376 were not submitted by this Client.
1376 1377
1377 1378 It can also be a convenient way to retrieve the metadata associated with
1378 1379 blocking execution, since it always retrieves
1379 1380
1380 1381 Examples
1381 1382 --------
1382 1383 ::
1383 1384
1384 1385 In [10]: r = client.apply()
1385 1386
1386 1387 Parameters
1387 1388 ----------
1388 1389
1389 1390 indices_or_msg_ids : integer history index, str msg_id, or list of either
1390 1391 The indices or msg_ids of indices to be retrieved
1391 1392
1392 1393 block : bool
1393 1394 Whether to wait for the result to be done
1394 1395
1395 1396 Returns
1396 1397 -------
1397 1398
1398 1399 AsyncResult
1399 1400 A single AsyncResult object will always be returned.
1400 1401
1401 1402 AsyncHubResult
1402 1403 A subclass of AsyncResult that retrieves results from the Hub
1403 1404
1404 1405 """
1405 1406 block = self.block if block is None else block
1406 1407 if indices_or_msg_ids is None:
1407 1408 indices_or_msg_ids = -1
1408 1409
1409 1410 single_result = False
1410 1411 if not isinstance(indices_or_msg_ids, (list,tuple)):
1411 1412 indices_or_msg_ids = [indices_or_msg_ids]
1412 1413 single_result = True
1413 1414
1414 1415 theids = []
1415 1416 for id in indices_or_msg_ids:
1416 1417 if isinstance(id, int):
1417 1418 id = self.history[id]
1418 1419 if not isinstance(id, basestring):
1419 1420 raise TypeError("indices must be str or int, not %r"%id)
1420 1421 theids.append(id)
1421 1422
1422 1423 local_ids = filter(lambda msg_id: msg_id in self.outstanding or msg_id in self.results, theids)
1423 1424 remote_ids = filter(lambda msg_id: msg_id not in local_ids, theids)
1424 1425
1425 1426 # given single msg_id initially, get_result shot get the result itself,
1426 1427 # not a length-one list
1427 1428 if single_result:
1428 1429 theids = theids[0]
1429 1430
1430 1431 if remote_ids:
1431 1432 ar = AsyncHubResult(self, msg_ids=theids)
1432 1433 else:
1433 1434 ar = AsyncResult(self, msg_ids=theids)
1434 1435
1435 1436 if block:
1436 1437 ar.wait()
1437 1438
1438 1439 return ar
1439 1440
1440 1441 @spin_first
1441 1442 def resubmit(self, indices_or_msg_ids=None, metadata=None, block=None):
1442 1443 """Resubmit one or more tasks.
1443 1444
1444 1445 in-flight tasks may not be resubmitted.
1445 1446
1446 1447 Parameters
1447 1448 ----------
1448 1449
1449 1450 indices_or_msg_ids : integer history index, str msg_id, or list of either
1450 1451 The indices or msg_ids of indices to be retrieved
1451 1452
1452 1453 block : bool
1453 1454 Whether to wait for the result to be done
1454 1455
1455 1456 Returns
1456 1457 -------
1457 1458
1458 1459 AsyncHubResult
1459 1460 A subclass of AsyncResult that retrieves results from the Hub
1460 1461
1461 1462 """
1462 1463 block = self.block if block is None else block
1463 1464 if indices_or_msg_ids is None:
1464 1465 indices_or_msg_ids = -1
1465 1466
1466 1467 if not isinstance(indices_or_msg_ids, (list,tuple)):
1467 1468 indices_or_msg_ids = [indices_or_msg_ids]
1468 1469
1469 1470 theids = []
1470 1471 for id in indices_or_msg_ids:
1471 1472 if isinstance(id, int):
1472 1473 id = self.history[id]
1473 1474 if not isinstance(id, basestring):
1474 1475 raise TypeError("indices must be str or int, not %r"%id)
1475 1476 theids.append(id)
1476 1477
1477 1478 content = dict(msg_ids = theids)
1478 1479
1479 1480 self.session.send(self._query_socket, 'resubmit_request', content)
1480 1481
1481 1482 zmq.select([self._query_socket], [], [])
1482 1483 idents,msg = self.session.recv(self._query_socket, zmq.NOBLOCK)
1483 1484 if self.debug:
1484 1485 pprint(msg)
1485 1486 content = msg['content']
1486 1487 if content['status'] != 'ok':
1487 1488 raise self._unwrap_exception(content)
1488 1489 mapping = content['resubmitted']
1489 1490 new_ids = [ mapping[msg_id] for msg_id in theids ]
1490 1491
1491 1492 ar = AsyncHubResult(self, msg_ids=new_ids)
1492 1493
1493 1494 if block:
1494 1495 ar.wait()
1495 1496
1496 1497 return ar
1497 1498
1498 1499 @spin_first
1499 1500 def result_status(self, msg_ids, status_only=True):
1500 1501 """Check on the status of the result(s) of the apply request with `msg_ids`.
1501 1502
1502 1503 If status_only is False, then the actual results will be retrieved, else
1503 1504 only the status of the results will be checked.
1504 1505
1505 1506 Parameters
1506 1507 ----------
1507 1508
1508 1509 msg_ids : list of msg_ids
1509 1510 if int:
1510 1511 Passed as index to self.history for convenience.
1511 1512 status_only : bool (default: True)
1512 1513 if False:
1513 1514 Retrieve the actual results of completed tasks.
1514 1515
1515 1516 Returns
1516 1517 -------
1517 1518
1518 1519 results : dict
1519 1520 There will always be the keys 'pending' and 'completed', which will
1520 1521 be lists of msg_ids that are incomplete or complete. If `status_only`
1521 1522 is False, then completed results will be keyed by their `msg_id`.
1522 1523 """
1523 1524 if not isinstance(msg_ids, (list,tuple)):
1524 1525 msg_ids = [msg_ids]
1525 1526
1526 1527 theids = []
1527 1528 for msg_id in msg_ids:
1528 1529 if isinstance(msg_id, int):
1529 1530 msg_id = self.history[msg_id]
1530 1531 if not isinstance(msg_id, basestring):
1531 1532 raise TypeError("msg_ids must be str, not %r"%msg_id)
1532 1533 theids.append(msg_id)
1533 1534
1534 1535 completed = []
1535 1536 local_results = {}
1536 1537
1537 1538 # comment this block out to temporarily disable local shortcut:
1538 1539 for msg_id in theids:
1539 1540 if msg_id in self.results:
1540 1541 completed.append(msg_id)
1541 1542 local_results[msg_id] = self.results[msg_id]
1542 1543 theids.remove(msg_id)
1543 1544
1544 1545 if theids: # some not locally cached
1545 1546 content = dict(msg_ids=theids, status_only=status_only)
1546 1547 msg = self.session.send(self._query_socket, "result_request", content=content)
1547 1548 zmq.select([self._query_socket], [], [])
1548 1549 idents,msg = self.session.recv(self._query_socket, zmq.NOBLOCK)
1549 1550 if self.debug:
1550 1551 pprint(msg)
1551 1552 content = msg['content']
1552 1553 if content['status'] != 'ok':
1553 1554 raise self._unwrap_exception(content)
1554 1555 buffers = msg['buffers']
1555 1556 else:
1556 1557 content = dict(completed=[],pending=[])
1557 1558
1558 1559 content['completed'].extend(completed)
1559 1560
1560 1561 if status_only:
1561 1562 return content
1562 1563
1563 1564 failures = []
1564 1565 # load cached results into result:
1565 1566 content.update(local_results)
1566 1567
1567 1568 # update cache with results:
1568 1569 for msg_id in sorted(theids):
1569 1570 if msg_id in content['completed']:
1570 1571 rec = content[msg_id]
1571 1572 parent = rec['header']
1572 1573 header = rec['result_header']
1573 1574 rcontent = rec['result_content']
1574 1575 iodict = rec['io']
1575 1576 if isinstance(rcontent, str):
1576 1577 rcontent = self.session.unpack(rcontent)
1577 1578
1578 1579 md = self.metadata[msg_id]
1579 1580 md_msg = dict(
1580 1581 content=rcontent,
1581 1582 parent_header=parent,
1582 1583 header=header,
1583 1584 metadata=rec['result_metadata'],
1584 1585 )
1585 1586 md.update(self._extract_metadata(md_msg))
1586 1587 if rec.get('received'):
1587 1588 md['received'] = rec['received']
1588 1589 md.update(iodict)
1589 1590
1590 1591 if rcontent['status'] == 'ok':
1591 1592 if header['msg_type'] == 'apply_reply':
1592 1593 res,buffers = serialize.unserialize_object(buffers)
1593 1594 elif header['msg_type'] == 'execute_reply':
1594 1595 res = ExecuteReply(msg_id, rcontent, md)
1595 1596 else:
1596 1597 raise KeyError("unhandled msg type: %r" % header['msg_type'])
1597 1598 else:
1598 1599 res = self._unwrap_exception(rcontent)
1599 1600 failures.append(res)
1600 1601
1601 1602 self.results[msg_id] = res
1602 1603 content[msg_id] = res
1603 1604
1604 1605 if len(theids) == 1 and failures:
1605 1606 raise failures[0]
1606 1607
1607 1608 error.collect_exceptions(failures, "result_status")
1608 1609 return content
1609 1610
1610 1611 @spin_first
1611 1612 def queue_status(self, targets='all', verbose=False):
1612 1613 """Fetch the status of engine queues.
1613 1614
1614 1615 Parameters
1615 1616 ----------
1616 1617
1617 1618 targets : int/str/list of ints/strs
1618 1619 the engines whose states are to be queried.
1619 1620 default : all
1620 1621 verbose : bool
1621 1622 Whether to return lengths only, or lists of ids for each element
1622 1623 """
1623 1624 if targets == 'all':
1624 1625 # allow 'all' to be evaluated on the engine
1625 1626 engine_ids = None
1626 1627 else:
1627 1628 engine_ids = self._build_targets(targets)[1]
1628 1629 content = dict(targets=engine_ids, verbose=verbose)
1629 1630 self.session.send(self._query_socket, "queue_request", content=content)
1630 1631 idents,msg = self.session.recv(self._query_socket, 0)
1631 1632 if self.debug:
1632 1633 pprint(msg)
1633 1634 content = msg['content']
1634 1635 status = content.pop('status')
1635 1636 if status != 'ok':
1636 1637 raise self._unwrap_exception(content)
1637 1638 content = rekey(content)
1638 1639 if isinstance(targets, int):
1639 1640 return content[targets]
1640 1641 else:
1641 1642 return content
1642 1643
1643 1644 def _build_msgids_from_target(self, targets=None):
1644 1645 """Build a list of msg_ids from the list of engine targets"""
1645 1646 if not targets: # needed as _build_targets otherwise uses all engines
1646 1647 return []
1647 1648 target_ids = self._build_targets(targets)[0]
1648 1649 return filter(lambda md_id: self.metadata[md_id]["engine_uuid"] in target_ids, self.metadata)
1649 1650
1650 1651 def _build_msgids_from_jobs(self, jobs=None):
1651 1652 """Build a list of msg_ids from "jobs" """
1652 1653 if not jobs:
1653 1654 return []
1654 1655 msg_ids = []
1655 1656 if isinstance(jobs, (basestring,AsyncResult)):
1656 1657 jobs = [jobs]
1657 1658 bad_ids = filter(lambda obj: not isinstance(obj, (basestring, AsyncResult)), jobs)
1658 1659 if bad_ids:
1659 1660 raise TypeError("Invalid msg_id type %r, expected str or AsyncResult"%bad_ids[0])
1660 1661 for j in jobs:
1661 1662 if isinstance(j, AsyncResult):
1662 1663 msg_ids.extend(j.msg_ids)
1663 1664 else:
1664 1665 msg_ids.append(j)
1665 1666 return msg_ids
1666 1667
1667 1668 def purge_local_results(self, jobs=[], targets=[]):
1668 1669 """Clears the client caches of results and frees such memory.
1669 1670
1670 1671 Individual results can be purged by msg_id, or the entire
1671 1672 history of specific targets can be purged.
1672 1673
1673 1674 Use `purge_local_results('all')` to scrub everything from the Clients's db.
1674 1675
1675 1676 The client must have no outstanding tasks before purging the caches.
1676 1677 Raises `AssertionError` if there are still outstanding tasks.
1677 1678
1678 1679 After this call all `AsyncResults` are invalid and should be discarded.
1679 1680
1680 1681 If you must "reget" the results, you can still do so by using
1681 1682 `client.get_result(msg_id)` or `client.get_result(asyncresult)`. This will
1682 1683 redownload the results from the hub if they are still available
1683 1684 (i.e `client.purge_hub_results(...)` has not been called.
1684 1685
1685 1686 Parameters
1686 1687 ----------
1687 1688
1688 1689 jobs : str or list of str or AsyncResult objects
1689 1690 the msg_ids whose results should be purged.
1690 1691 targets : int/str/list of ints/strs
1691 1692 The targets, by int_id, whose entire results are to be purged.
1692 1693
1693 1694 default : None
1694 1695 """
1695 1696 assert not self.outstanding, "Can't purge a client with outstanding tasks!"
1696 1697
1697 1698 if not targets and not jobs:
1698 1699 raise ValueError("Must specify at least one of `targets` and `jobs`")
1699 1700
1700 1701 if jobs == 'all':
1701 1702 self.results.clear()
1702 1703 self.metadata.clear()
1703 1704 return
1704 1705 else:
1705 1706 msg_ids = []
1706 1707 msg_ids.extend(self._build_msgids_from_target(targets))
1707 1708 msg_ids.extend(self._build_msgids_from_jobs(jobs))
1708 1709 map(self.results.pop, msg_ids)
1709 1710 map(self.metadata.pop, msg_ids)
1710 1711
1711 1712
1712 1713 @spin_first
1713 1714 def purge_hub_results(self, jobs=[], targets=[]):
1714 1715 """Tell the Hub to forget results.
1715 1716
1716 1717 Individual results can be purged by msg_id, or the entire
1717 1718 history of specific targets can be purged.
1718 1719
1719 1720 Use `purge_results('all')` to scrub everything from the Hub's db.
1720 1721
1721 1722 Parameters
1722 1723 ----------
1723 1724
1724 1725 jobs : str or list of str or AsyncResult objects
1725 1726 the msg_ids whose results should be forgotten.
1726 1727 targets : int/str/list of ints/strs
1727 1728 The targets, by int_id, whose entire history is to be purged.
1728 1729
1729 1730 default : None
1730 1731 """
1731 1732 if not targets and not jobs:
1732 1733 raise ValueError("Must specify at least one of `targets` and `jobs`")
1733 1734 if targets:
1734 1735 targets = self._build_targets(targets)[1]
1735 1736
1736 1737 # construct msg_ids from jobs
1737 1738 if jobs == 'all':
1738 1739 msg_ids = jobs
1739 1740 else:
1740 1741 msg_ids = self._build_msgids_from_jobs(jobs)
1741 1742
1742 1743 content = dict(engine_ids=targets, msg_ids=msg_ids)
1743 1744 self.session.send(self._query_socket, "purge_request", content=content)
1744 1745 idents, msg = self.session.recv(self._query_socket, 0)
1745 1746 if self.debug:
1746 1747 pprint(msg)
1747 1748 content = msg['content']
1748 1749 if content['status'] != 'ok':
1749 1750 raise self._unwrap_exception(content)
1750 1751
1751 1752 def purge_results(self, jobs=[], targets=[]):
1752 1753 """Clears the cached results from both the hub and the local client
1753 1754
1754 1755 Individual results can be purged by msg_id, or the entire
1755 1756 history of specific targets can be purged.
1756 1757
1757 1758 Use `purge_results('all')` to scrub every cached result from both the Hub's and
1758 1759 the Client's db.
1759 1760
1760 1761 Equivalent to calling both `purge_hub_results()` and `purge_client_results()` with
1761 1762 the same arguments.
1762 1763
1763 1764 Parameters
1764 1765 ----------
1765 1766
1766 1767 jobs : str or list of str or AsyncResult objects
1767 1768 the msg_ids whose results should be forgotten.
1768 1769 targets : int/str/list of ints/strs
1769 1770 The targets, by int_id, whose entire history is to be purged.
1770 1771
1771 1772 default : None
1772 1773 """
1773 1774 self.purge_local_results(jobs=jobs, targets=targets)
1774 1775 self.purge_hub_results(jobs=jobs, targets=targets)
1775 1776
1776 1777 def purge_everything(self):
1777 1778 """Clears all content from previous Tasks from both the hub and the local client
1778 1779
1779 1780 In addition to calling `purge_results("all")` it also deletes the history and
1780 1781 other bookkeeping lists.
1781 1782 """
1782 1783 self.purge_results("all")
1783 1784 self.history = []
1784 1785 self.session.digest_history.clear()
1785 1786
1786 1787 @spin_first
1787 1788 def hub_history(self):
1788 1789 """Get the Hub's history
1789 1790
1790 1791 Just like the Client, the Hub has a history, which is a list of msg_ids.
1791 1792 This will contain the history of all clients, and, depending on configuration,
1792 1793 may contain history across multiple cluster sessions.
1793 1794
1794 1795 Any msg_id returned here is a valid argument to `get_result`.
1795 1796
1796 1797 Returns
1797 1798 -------
1798 1799
1799 1800 msg_ids : list of strs
1800 1801 list of all msg_ids, ordered by task submission time.
1801 1802 """
1802 1803
1803 1804 self.session.send(self._query_socket, "history_request", content={})
1804 1805 idents, msg = self.session.recv(self._query_socket, 0)
1805 1806
1806 1807 if self.debug:
1807 1808 pprint(msg)
1808 1809 content = msg['content']
1809 1810 if content['status'] != 'ok':
1810 1811 raise self._unwrap_exception(content)
1811 1812 else:
1812 1813 return content['history']
1813 1814
1814 1815 @spin_first
1815 1816 def db_query(self, query, keys=None):
1816 1817 """Query the Hub's TaskRecord database
1817 1818
1818 1819 This will return a list of task record dicts that match `query`
1819 1820
1820 1821 Parameters
1821 1822 ----------
1822 1823
1823 1824 query : mongodb query dict
1824 1825 The search dict. See mongodb query docs for details.
1825 1826 keys : list of strs [optional]
1826 1827 The subset of keys to be returned. The default is to fetch everything but buffers.
1827 1828 'msg_id' will *always* be included.
1828 1829 """
1829 1830 if isinstance(keys, basestring):
1830 1831 keys = [keys]
1831 1832 content = dict(query=query, keys=keys)
1832 1833 self.session.send(self._query_socket, "db_request", content=content)
1833 1834 idents, msg = self.session.recv(self._query_socket, 0)
1834 1835 if self.debug:
1835 1836 pprint(msg)
1836 1837 content = msg['content']
1837 1838 if content['status'] != 'ok':
1838 1839 raise self._unwrap_exception(content)
1839 1840
1840 1841 records = content['records']
1841 1842
1842 1843 buffer_lens = content['buffer_lens']
1843 1844 result_buffer_lens = content['result_buffer_lens']
1844 1845 buffers = msg['buffers']
1845 1846 has_bufs = buffer_lens is not None
1846 1847 has_rbufs = result_buffer_lens is not None
1847 1848 for i,rec in enumerate(records):
1848 1849 # relink buffers
1849 1850 if has_bufs:
1850 1851 blen = buffer_lens[i]
1851 1852 rec['buffers'], buffers = buffers[:blen],buffers[blen:]
1852 1853 if has_rbufs:
1853 1854 blen = result_buffer_lens[i]
1854 1855 rec['result_buffers'], buffers = buffers[:blen],buffers[blen:]
1855 1856
1856 1857 return records
1857 1858
1858 1859 __all__ = [ 'Client' ]
@@ -1,441 +1,442 b''
1 1 # encoding: utf-8
2 2 """
3 3 =============
4 4 parallelmagic
5 5 =============
6 6
7 7 Magic command interface for interactive parallel work.
8 8
9 9 Usage
10 10 =====
11 11
12 12 ``%autopx``
13 13
14 14 {AUTOPX_DOC}
15 15
16 16 ``%px``
17 17
18 18 {PX_DOC}
19 19
20 20 ``%pxresult``
21 21
22 22 {RESULT_DOC}
23 23
24 24 ``%pxconfig``
25 25
26 26 {CONFIG_DOC}
27 27
28 28 """
29 from __future__ import print_function
29 30
30 31 #-----------------------------------------------------------------------------
31 32 # Copyright (C) 2008 The IPython Development Team
32 33 #
33 34 # Distributed under the terms of the BSD License. The full license is in
34 35 # the file COPYING, distributed as part of this software.
35 36 #-----------------------------------------------------------------------------
36 37
37 38 #-----------------------------------------------------------------------------
38 39 # Imports
39 40 #-----------------------------------------------------------------------------
40 41
41 42 import ast
42 43 import re
43 44
44 45 from IPython.core.error import UsageError
45 46 from IPython.core.magic import Magics
46 47 from IPython.core import magic_arguments
47 48 from IPython.testing.skipdoctest import skip_doctest
48 49
49 50 #-----------------------------------------------------------------------------
50 51 # Definitions of magic functions for use with IPython
51 52 #-----------------------------------------------------------------------------
52 53
53 54
54 55 NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used."
55 56
56 57 def exec_args(f):
57 58 """decorator for adding block/targets args for execution
58 59
59 60 applied to %pxconfig and %%px
60 61 """
61 62 args = [
62 63 magic_arguments.argument('-b', '--block', action="store_const",
63 64 const=True, dest='block',
64 65 help="use blocking (sync) execution",
65 66 ),
66 67 magic_arguments.argument('-a', '--noblock', action="store_const",
67 68 const=False, dest='block',
68 69 help="use non-blocking (async) execution",
69 70 ),
70 71 magic_arguments.argument('-t', '--targets', type=str,
71 72 help="specify the targets on which to execute",
72 73 ),
73 74 magic_arguments.argument('--local', action="store_const",
74 75 const=True, dest="local",
75 76 help="also execute the cell in the local namespace",
76 77 ),
77 78 magic_arguments.argument('--verbose', action="store_const",
78 79 const=True, dest="set_verbose",
79 80 help="print a message at each execution",
80 81 ),
81 82 magic_arguments.argument('--no-verbose', action="store_const",
82 83 const=False, dest="set_verbose",
83 84 help="don't print any messages",
84 85 ),
85 86 ]
86 87 for a in args:
87 88 f = a(f)
88 89 return f
89 90
90 91 def output_args(f):
91 92 """decorator for output-formatting args
92 93
93 94 applied to %pxresult and %%px
94 95 """
95 96 args = [
96 97 magic_arguments.argument('-r', action="store_const", dest='groupby',
97 98 const='order',
98 99 help="collate outputs in order (same as group-outputs=order)"
99 100 ),
100 101 magic_arguments.argument('-e', action="store_const", dest='groupby',
101 102 const='engine',
102 103 help="group outputs by engine (same as group-outputs=engine)"
103 104 ),
104 105 magic_arguments.argument('--group-outputs', dest='groupby', type=str,
105 106 choices=['engine', 'order', 'type'], default='type',
106 107 help="""Group the outputs in a particular way.
107 108
108 109 Choices are:
109 110
110 111 type: group outputs of all engines by type (stdout, stderr, displaypub, etc.).
111 112
112 113 engine: display all output for each engine together.
113 114
114 115 order: like type, but individual displaypub output from each engine is collated.
115 116 For example, if multiple plots are generated by each engine, the first
116 117 figure of each engine will be displayed, then the second of each, etc.
117 118 """
118 119 ),
119 120 magic_arguments.argument('-o', '--out', dest='save_name', type=str,
120 121 help="""store the AsyncResult object for this computation
121 122 in the global namespace under this name.
122 123 """
123 124 ),
124 125 ]
125 126 for a in args:
126 127 f = a(f)
127 128 return f
128 129
129 130 class ParallelMagics(Magics):
130 131 """A set of magics useful when controlling a parallel IPython cluster.
131 132 """
132 133
133 134 # magic-related
134 135 magics = None
135 136 registered = True
136 137
137 138 # suffix for magics
138 139 suffix = ''
139 140 # A flag showing if autopx is activated or not
140 141 _autopx = False
141 142 # the current view used by the magics:
142 143 view = None
143 144 # last result cache for %pxresult
144 145 last_result = None
145 146 # verbose flag
146 147 verbose = False
147 148
148 149 def __init__(self, shell, view, suffix=''):
149 150 self.view = view
150 151 self.suffix = suffix
151 152
152 153 # register magics
153 154 self.magics = dict(cell={},line={})
154 155 line_magics = self.magics['line']
155 156
156 157 px = 'px' + suffix
157 158 if not suffix:
158 159 # keep %result for legacy compatibility
159 160 line_magics['result'] = self.result
160 161
161 162 line_magics['pxresult' + suffix] = self.result
162 163 line_magics[px] = self.px
163 164 line_magics['pxconfig' + suffix] = self.pxconfig
164 165 line_magics['auto' + px] = self.autopx
165 166
166 167 self.magics['cell'][px] = self.cell_px
167 168
168 169 super(ParallelMagics, self).__init__(shell=shell)
169 170
170 171 def _eval_target_str(self, ts):
171 172 if ':' in ts:
172 173 targets = eval("self.view.client.ids[%s]" % ts)
173 174 elif 'all' in ts:
174 175 targets = 'all'
175 176 else:
176 177 targets = eval(ts)
177 178 return targets
178 179
179 180 @magic_arguments.magic_arguments()
180 181 @exec_args
181 182 def pxconfig(self, line):
182 183 """configure default targets/blocking for %px magics"""
183 184 args = magic_arguments.parse_argstring(self.pxconfig, line)
184 185 if args.targets:
185 186 self.view.targets = self._eval_target_str(args.targets)
186 187 if args.block is not None:
187 188 self.view.block = args.block
188 189 if args.set_verbose is not None:
189 190 self.verbose = args.set_verbose
190 191
191 192 @magic_arguments.magic_arguments()
192 193 @output_args
193 194 @skip_doctest
194 195 def result(self, line=''):
195 196 """Print the result of the last asynchronous %px command.
196 197
197 198 This lets you recall the results of %px computations after
198 199 asynchronous submission (block=False).
199 200
200 201 Examples
201 202 --------
202 203 ::
203 204
204 205 In [23]: %px os.getpid()
205 206 Async parallel execution on engine(s): all
206 207
207 208 In [24]: %pxresult
208 209 Out[8:10]: 60920
209 210 Out[9:10]: 60921
210 211 Out[10:10]: 60922
211 212 Out[11:10]: 60923
212 213 """
213 214 args = magic_arguments.parse_argstring(self.result, line)
214 215
215 216 if self.last_result is None:
216 217 raise UsageError(NO_LAST_RESULT)
217 218
218 219 self.last_result.get()
219 220 self.last_result.display_outputs(groupby=args.groupby)
220 221
221 222 @skip_doctest
222 223 def px(self, line=''):
223 224 """Executes the given python command in parallel.
224 225
225 226 Examples
226 227 --------
227 228 ::
228 229
229 230 In [24]: %px a = os.getpid()
230 231 Parallel execution on engine(s): all
231 232
232 233 In [25]: %px print a
233 234 [stdout:0] 1234
234 235 [stdout:1] 1235
235 236 [stdout:2] 1236
236 237 [stdout:3] 1237
237 238 """
238 239 return self.parallel_execute(line)
239 240
240 241 def parallel_execute(self, cell, block=None, groupby='type', save_name=None):
241 242 """implementation used by %px and %%parallel"""
242 243
243 244 # defaults:
244 245 block = self.view.block if block is None else block
245 246
246 247 base = "Parallel" if block else "Async parallel"
247 248
248 249 targets = self.view.targets
249 250 if isinstance(targets, list) and len(targets) > 10:
250 251 str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:]
251 252 else:
252 253 str_targets = str(targets)
253 254 if self.verbose:
254 print base + " execution on engine(s): %s" % str_targets
255 print(base + " execution on engine(s): %s" % str_targets)
255 256
256 257 result = self.view.execute(cell, silent=False, block=False)
257 258 self.last_result = result
258 259
259 260 if save_name:
260 261 self.shell.user_ns[save_name] = result
261 262
262 263 if block:
263 264 result.get()
264 265 result.display_outputs(groupby)
265 266 else:
266 267 # return AsyncResult only on non-blocking submission
267 268 return result
268 269
269 270 @magic_arguments.magic_arguments()
270 271 @exec_args
271 272 @output_args
272 273 @skip_doctest
273 274 def cell_px(self, line='', cell=None):
274 275 """Executes the cell in parallel.
275 276
276 277 Examples
277 278 --------
278 279 ::
279 280
280 281 In [24]: %%px --noblock
281 282 ....: a = os.getpid()
282 283 Async parallel execution on engine(s): all
283 284
284 285 In [25]: %%px
285 286 ....: print a
286 287 [stdout:0] 1234
287 288 [stdout:1] 1235
288 289 [stdout:2] 1236
289 290 [stdout:3] 1237
290 291 """
291 292
292 293 args = magic_arguments.parse_argstring(self.cell_px, line)
293 294
294 295 if args.targets:
295 296 save_targets = self.view.targets
296 297 self.view.targets = self._eval_target_str(args.targets)
297 298 # if running local, don't block until after local has run
298 299 block = False if args.local else args.block
299 300 try:
300 301 ar = self.parallel_execute(cell, block=block,
301 302 groupby=args.groupby,
302 303 save_name=args.save_name,
303 304 )
304 305 finally:
305 306 if args.targets:
306 307 self.view.targets = save_targets
307 308
308 309 # run locally after submitting remote
309 310 block = self.view.block if args.block is None else args.block
310 311 if args.local:
311 312 self.shell.run_cell(cell)
312 313 # now apply blocking behavor to remote execution
313 314 if block:
314 315 ar.get()
315 316 ar.display_outputs(args.groupby)
316 317 if not block:
317 318 return ar
318 319
319 320 @skip_doctest
320 321 def autopx(self, line=''):
321 322 """Toggles auto parallel mode.
322 323
323 324 Once this is called, all commands typed at the command line are send to
324 325 the engines to be executed in parallel. To control which engine are
325 326 used, the ``targets`` attribute of the view before
326 327 entering ``%autopx`` mode.
327 328
328 329
329 330 Then you can do the following::
330 331
331 332 In [25]: %autopx
332 333 %autopx to enabled
333 334
334 335 In [26]: a = 10
335 336 Parallel execution on engine(s): [0,1,2,3]
336 337 In [27]: print a
337 338 Parallel execution on engine(s): [0,1,2,3]
338 339 [stdout:0] 10
339 340 [stdout:1] 10
340 341 [stdout:2] 10
341 342 [stdout:3] 10
342 343
343 344
344 345 In [27]: %autopx
345 346 %autopx disabled
346 347 """
347 348 if self._autopx:
348 349 self._disable_autopx()
349 350 else:
350 351 self._enable_autopx()
351 352
352 353 def _enable_autopx(self):
353 354 """Enable %autopx mode by saving the original run_cell and installing
354 355 pxrun_cell.
355 356 """
356 357 # override run_cell
357 358 self._original_run_cell = self.shell.run_cell
358 359 self.shell.run_cell = self.pxrun_cell
359 360
360 361 self._autopx = True
361 print "%autopx enabled"
362 print("%autopx enabled")
362 363
363 364 def _disable_autopx(self):
364 365 """Disable %autopx by restoring the original InteractiveShell.run_cell.
365 366 """
366 367 if self._autopx:
367 368 self.shell.run_cell = self._original_run_cell
368 369 self._autopx = False
369 print "%autopx disabled"
370 print("%autopx disabled")
370 371
371 372 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
372 373 """drop-in replacement for InteractiveShell.run_cell.
373 374
374 375 This executes code remotely, instead of in the local namespace.
375 376
376 377 See InteractiveShell.run_cell for details.
377 378 """
378 379
379 380 if (not raw_cell) or raw_cell.isspace():
380 381 return
381 382
382 383 ipself = self.shell
383 384
384 385 with ipself.builtin_trap:
385 386 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
386 387
387 388 # Store raw and processed history
388 389 if store_history:
389 390 ipself.history_manager.store_inputs(ipself.execution_count,
390 391 cell, raw_cell)
391 392
392 393 # ipself.logger.log(cell, raw_cell)
393 394
394 395 cell_name = ipself.compile.cache(cell, ipself.execution_count)
395 396
396 397 try:
397 398 ast.parse(cell, filename=cell_name)
398 399 except (OverflowError, SyntaxError, ValueError, TypeError,
399 400 MemoryError):
400 401 # Case 1
401 402 ipself.showsyntaxerror()
402 403 ipself.execution_count += 1
403 404 return None
404 405 except NameError:
405 406 # ignore name errors, because we don't know the remote keys
406 407 pass
407 408
408 409 if store_history:
409 410 # Write output to the database. Does nothing unless
410 411 # history output logging is enabled.
411 412 ipself.history_manager.store_output(ipself.execution_count)
412 413 # Each cell is a *single* input, regardless of how many lines it has
413 414 ipself.execution_count += 1
414 415 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
415 416 self._disable_autopx()
416 417 return False
417 418 else:
418 419 try:
419 420 result = self.view.execute(cell, silent=False, block=False)
420 421 except:
421 422 ipself.showtraceback()
422 423 return True
423 424 else:
424 425 if self.view.block:
425 426 try:
426 427 result.get()
427 428 except:
428 429 self.shell.showtraceback()
429 430 return True
430 431 else:
431 432 with ipself.builtin_trap:
432 433 result.display_outputs()
433 434 return False
434 435
435 436
436 437 __doc__ = __doc__.format(
437 438 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
438 439 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
439 440 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__,
440 441 CONFIG_DOC = ' '*8 + ParallelMagics.pxconfig.__doc__,
441 442 )
@@ -1,1116 +1,1117 b''
1 1 """Views of remote engines.
2 2
3 3 Authors:
4 4
5 5 * Min RK
6 6 """
7 from __future__ import print_function
7 8 #-----------------------------------------------------------------------------
8 9 # Copyright (C) 2010-2011 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
18 19 import imp
19 20 import sys
20 21 import warnings
21 22 from contextlib import contextmanager
22 23 from types import ModuleType
23 24
24 25 import zmq
25 26
26 27 from IPython.testing.skipdoctest import skip_doctest
27 28 from IPython.utils.traitlets import (
28 29 HasTraits, Any, Bool, List, Dict, Set, Instance, CFloat, Integer
29 30 )
30 31 from IPython.external.decorator import decorator
31 32
32 33 from IPython.parallel import util
33 34 from IPython.parallel.controller.dependency import Dependency, dependent
34 35
35 36 from . import map as Map
36 37 from .asyncresult import AsyncResult, AsyncMapResult
37 38 from .remotefunction import ParallelFunction, parallel, remote, getname
38 39
39 40 #-----------------------------------------------------------------------------
40 41 # Decorators
41 42 #-----------------------------------------------------------------------------
42 43
43 44 @decorator
44 45 def save_ids(f, self, *args, **kwargs):
45 46 """Keep our history and outstanding attributes up to date after a method call."""
46 47 n_previous = len(self.client.history)
47 48 try:
48 49 ret = f(self, *args, **kwargs)
49 50 finally:
50 51 nmsgs = len(self.client.history) - n_previous
51 52 msg_ids = self.client.history[-nmsgs:]
52 53 self.history.extend(msg_ids)
53 54 map(self.outstanding.add, msg_ids)
54 55 return ret
55 56
56 57 @decorator
57 58 def sync_results(f, self, *args, **kwargs):
58 59 """sync relevant results from self.client to our results attribute."""
59 60 if self._in_sync_results:
60 61 return f(self, *args, **kwargs)
61 62 self._in_sync_results = True
62 63 try:
63 64 ret = f(self, *args, **kwargs)
64 65 finally:
65 66 self._in_sync_results = False
66 67 self._sync_results()
67 68 return ret
68 69
69 70 @decorator
70 71 def spin_after(f, self, *args, **kwargs):
71 72 """call spin after the method."""
72 73 ret = f(self, *args, **kwargs)
73 74 self.spin()
74 75 return ret
75 76
76 77 #-----------------------------------------------------------------------------
77 78 # Classes
78 79 #-----------------------------------------------------------------------------
79 80
80 81 @skip_doctest
81 82 class View(HasTraits):
82 83 """Base View class for more convenint apply(f,*args,**kwargs) syntax via attributes.
83 84
84 85 Don't use this class, use subclasses.
85 86
86 87 Methods
87 88 -------
88 89
89 90 spin
90 91 flushes incoming results and registration state changes
91 92 control methods spin, and requesting `ids` also ensures up to date
92 93
93 94 wait
94 95 wait on one or more msg_ids
95 96
96 97 execution methods
97 98 apply
98 99 legacy: execute, run
99 100
100 101 data movement
101 102 push, pull, scatter, gather
102 103
103 104 query methods
104 105 get_result, queue_status, purge_results, result_status
105 106
106 107 control methods
107 108 abort, shutdown
108 109
109 110 """
110 111 # flags
111 112 block=Bool(False)
112 113 track=Bool(True)
113 114 targets = Any()
114 115
115 116 history=List()
116 117 outstanding = Set()
117 118 results = Dict()
118 119 client = Instance('IPython.parallel.Client')
119 120
120 121 _socket = Instance('zmq.Socket')
121 122 _flag_names = List(['targets', 'block', 'track'])
122 123 _in_sync_results = Bool(False)
123 124 _targets = Any()
124 125 _idents = Any()
125 126
126 127 def __init__(self, client=None, socket=None, **flags):
127 128 super(View, self).__init__(client=client, _socket=socket)
128 129 self.results = client.results
129 130 self.block = client.block
130 131
131 132 self.set_flags(**flags)
132 133
133 134 assert not self.__class__ is View, "Don't use base View objects, use subclasses"
134 135
135 136 def __repr__(self):
136 137 strtargets = str(self.targets)
137 138 if len(strtargets) > 16:
138 139 strtargets = strtargets[:12]+'...]'
139 140 return "<%s %s>"%(self.__class__.__name__, strtargets)
140 141
141 142 def __len__(self):
142 143 if isinstance(self.targets, list):
143 144 return len(self.targets)
144 145 elif isinstance(self.targets, int):
145 146 return 1
146 147 else:
147 148 return len(self.client)
148 149
149 150 def set_flags(self, **kwargs):
150 151 """set my attribute flags by keyword.
151 152
152 153 Views determine behavior with a few attributes (`block`, `track`, etc.).
153 154 These attributes can be set all at once by name with this method.
154 155
155 156 Parameters
156 157 ----------
157 158
158 159 block : bool
159 160 whether to wait for results
160 161 track : bool
161 162 whether to create a MessageTracker to allow the user to
162 163 safely edit after arrays and buffers during non-copying
163 164 sends.
164 165 """
165 166 for name, value in kwargs.iteritems():
166 167 if name not in self._flag_names:
167 168 raise KeyError("Invalid name: %r"%name)
168 169 else:
169 170 setattr(self, name, value)
170 171
171 172 @contextmanager
172 173 def temp_flags(self, **kwargs):
173 174 """temporarily set flags, for use in `with` statements.
174 175
175 176 See set_flags for permanent setting of flags
176 177
177 178 Examples
178 179 --------
179 180
180 181 >>> view.track=False
181 182 ...
182 183 >>> with view.temp_flags(track=True):
183 184 ... ar = view.apply(dostuff, my_big_array)
184 185 ... ar.tracker.wait() # wait for send to finish
185 186 >>> view.track
186 187 False
187 188
188 189 """
189 190 # preflight: save flags, and set temporaries
190 191 saved_flags = {}
191 192 for f in self._flag_names:
192 193 saved_flags[f] = getattr(self, f)
193 194 self.set_flags(**kwargs)
194 195 # yield to the with-statement block
195 196 try:
196 197 yield
197 198 finally:
198 199 # postflight: restore saved flags
199 200 self.set_flags(**saved_flags)
200 201
201 202
202 203 #----------------------------------------------------------------
203 204 # apply
204 205 #----------------------------------------------------------------
205 206
206 207 def _sync_results(self):
207 208 """to be called by @sync_results decorator
208 209
209 210 after submitting any tasks.
210 211 """
211 212 delta = self.outstanding.difference(self.client.outstanding)
212 213 completed = self.outstanding.intersection(delta)
213 214 self.outstanding = self.outstanding.difference(completed)
214 215
215 216 @sync_results
216 217 @save_ids
217 218 def _really_apply(self, f, args, kwargs, block=None, **options):
218 219 """wrapper for client.send_apply_request"""
219 220 raise NotImplementedError("Implement in subclasses")
220 221
221 222 def apply(self, f, *args, **kwargs):
222 223 """calls f(*args, **kwargs) on remote engines, returning the result.
223 224
224 225 This method sets all apply flags via this View's attributes.
225 226
226 227 if self.block is False:
227 228 returns AsyncResult
228 229 else:
229 230 returns actual result of f(*args, **kwargs)
230 231 """
231 232 return self._really_apply(f, args, kwargs)
232 233
233 234 def apply_async(self, f, *args, **kwargs):
234 235 """calls f(*args, **kwargs) on remote engines in a nonblocking manner.
235 236
236 237 returns AsyncResult
237 238 """
238 239 return self._really_apply(f, args, kwargs, block=False)
239 240
240 241 @spin_after
241 242 def apply_sync(self, f, *args, **kwargs):
242 243 """calls f(*args, **kwargs) on remote engines in a blocking manner,
243 244 returning the result.
244 245
245 246 returns: actual result of f(*args, **kwargs)
246 247 """
247 248 return self._really_apply(f, args, kwargs, block=True)
248 249
249 250 #----------------------------------------------------------------
250 251 # wrappers for client and control methods
251 252 #----------------------------------------------------------------
252 253 @sync_results
253 254 def spin(self):
254 255 """spin the client, and sync"""
255 256 self.client.spin()
256 257
257 258 @sync_results
258 259 def wait(self, jobs=None, timeout=-1):
259 260 """waits on one or more `jobs`, for up to `timeout` seconds.
260 261
261 262 Parameters
262 263 ----------
263 264
264 265 jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects
265 266 ints are indices to self.history
266 267 strs are msg_ids
267 268 default: wait on all outstanding messages
268 269 timeout : float
269 270 a time in seconds, after which to give up.
270 271 default is -1, which means no timeout
271 272
272 273 Returns
273 274 -------
274 275
275 276 True : when all msg_ids are done
276 277 False : timeout reached, some msg_ids still outstanding
277 278 """
278 279 if jobs is None:
279 280 jobs = self.history
280 281 return self.client.wait(jobs, timeout)
281 282
282 283 def abort(self, jobs=None, targets=None, block=None):
283 284 """Abort jobs on my engines.
284 285
285 286 Parameters
286 287 ----------
287 288
288 289 jobs : None, str, list of strs, optional
289 290 if None: abort all jobs.
290 291 else: abort specific msg_id(s).
291 292 """
292 293 block = block if block is not None else self.block
293 294 targets = targets if targets is not None else self.targets
294 295 jobs = jobs if jobs is not None else list(self.outstanding)
295 296
296 297 return self.client.abort(jobs=jobs, targets=targets, block=block)
297 298
298 299 def queue_status(self, targets=None, verbose=False):
299 300 """Fetch the Queue status of my engines"""
300 301 targets = targets if targets is not None else self.targets
301 302 return self.client.queue_status(targets=targets, verbose=verbose)
302 303
303 304 def purge_results(self, jobs=[], targets=[]):
304 305 """Instruct the controller to forget specific results."""
305 306 if targets is None or targets == 'all':
306 307 targets = self.targets
307 308 return self.client.purge_results(jobs=jobs, targets=targets)
308 309
309 310 def shutdown(self, targets=None, restart=False, hub=False, block=None):
310 311 """Terminates one or more engine processes, optionally including the hub.
311 312 """
312 313 block = self.block if block is None else block
313 314 if targets is None or targets == 'all':
314 315 targets = self.targets
315 316 return self.client.shutdown(targets=targets, restart=restart, hub=hub, block=block)
316 317
317 318 @spin_after
318 319 def get_result(self, indices_or_msg_ids=None):
319 320 """return one or more results, specified by history index or msg_id.
320 321
321 322 See client.get_result for details.
322 323
323 324 """
324 325
325 326 if indices_or_msg_ids is None:
326 327 indices_or_msg_ids = -1
327 328 if isinstance(indices_or_msg_ids, int):
328 329 indices_or_msg_ids = self.history[indices_or_msg_ids]
329 330 elif isinstance(indices_or_msg_ids, (list,tuple,set)):
330 331 indices_or_msg_ids = list(indices_or_msg_ids)
331 332 for i,index in enumerate(indices_or_msg_ids):
332 333 if isinstance(index, int):
333 334 indices_or_msg_ids[i] = self.history[index]
334 335 return self.client.get_result(indices_or_msg_ids)
335 336
336 337 #-------------------------------------------------------------------
337 338 # Map
338 339 #-------------------------------------------------------------------
339 340
340 341 @sync_results
341 342 def map(self, f, *sequences, **kwargs):
342 343 """override in subclasses"""
343 344 raise NotImplementedError
344 345
345 346 def map_async(self, f, *sequences, **kwargs):
346 347 """Parallel version of builtin `map`, using this view's engines.
347 348
348 349 This is equivalent to map(...block=False)
349 350
350 351 See `self.map` for details.
351 352 """
352 353 if 'block' in kwargs:
353 354 raise TypeError("map_async doesn't take a `block` keyword argument.")
354 355 kwargs['block'] = False
355 356 return self.map(f,*sequences,**kwargs)
356 357
357 358 def map_sync(self, f, *sequences, **kwargs):
358 359 """Parallel version of builtin `map`, using this view's engines.
359 360
360 361 This is equivalent to map(...block=True)
361 362
362 363 See `self.map` for details.
363 364 """
364 365 if 'block' in kwargs:
365 366 raise TypeError("map_sync doesn't take a `block` keyword argument.")
366 367 kwargs['block'] = True
367 368 return self.map(f,*sequences,**kwargs)
368 369
369 370 def imap(self, f, *sequences, **kwargs):
370 371 """Parallel version of `itertools.imap`.
371 372
372 373 See `self.map` for details.
373 374
374 375 """
375 376
376 377 return iter(self.map_async(f,*sequences, **kwargs))
377 378
378 379 #-------------------------------------------------------------------
379 380 # Decorators
380 381 #-------------------------------------------------------------------
381 382
382 383 def remote(self, block=None, **flags):
383 384 """Decorator for making a RemoteFunction"""
384 385 block = self.block if block is None else block
385 386 return remote(self, block=block, **flags)
386 387
387 388 def parallel(self, dist='b', block=None, **flags):
388 389 """Decorator for making a ParallelFunction"""
389 390 block = self.block if block is None else block
390 391 return parallel(self, dist=dist, block=block, **flags)
391 392
392 393 @skip_doctest
393 394 class DirectView(View):
394 395 """Direct Multiplexer View of one or more engines.
395 396
396 397 These are created via indexed access to a client:
397 398
398 399 >>> dv_1 = client[1]
399 400 >>> dv_all = client[:]
400 401 >>> dv_even = client[::2]
401 402 >>> dv_some = client[1:3]
402 403
403 404 This object provides dictionary access to engine namespaces:
404 405
405 406 # push a=5:
406 407 >>> dv['a'] = 5
407 408 # pull 'foo':
408 409 >>> db['foo']
409 410
410 411 """
411 412
412 413 def __init__(self, client=None, socket=None, targets=None):
413 414 super(DirectView, self).__init__(client=client, socket=socket, targets=targets)
414 415
415 416 @property
416 417 def importer(self):
417 418 """sync_imports(local=True) as a property.
418 419
419 420 See sync_imports for details.
420 421
421 422 """
422 423 return self.sync_imports(True)
423 424
424 425 @contextmanager
425 426 def sync_imports(self, local=True, quiet=False):
426 427 """Context Manager for performing simultaneous local and remote imports.
427 428
428 429 'import x as y' will *not* work. The 'as y' part will simply be ignored.
429 430
430 431 If `local=True`, then the package will also be imported locally.
431 432
432 433 If `quiet=True`, no output will be produced when attempting remote
433 434 imports.
434 435
435 436 Note that remote-only (`local=False`) imports have not been implemented.
436 437
437 438 >>> with view.sync_imports():
438 439 ... from numpy import recarray
439 440 importing recarray from numpy on engine(s)
440 441
441 442 """
442 443 import __builtin__
443 444 local_import = __builtin__.__import__
444 445 modules = set()
445 446 results = []
446 447 @util.interactive
447 448 def remote_import(name, fromlist, level):
448 449 """the function to be passed to apply, that actually performs the import
449 450 on the engine, and loads up the user namespace.
450 451 """
451 452 import sys
452 453 user_ns = globals()
453 454 mod = __import__(name, fromlist=fromlist, level=level)
454 455 if fromlist:
455 456 for key in fromlist:
456 457 user_ns[key] = getattr(mod, key)
457 458 else:
458 459 user_ns[name] = sys.modules[name]
459 460
460 461 def view_import(name, globals={}, locals={}, fromlist=[], level=0):
461 462 """the drop-in replacement for __import__, that optionally imports
462 463 locally as well.
463 464 """
464 465 # don't override nested imports
465 466 save_import = __builtin__.__import__
466 467 __builtin__.__import__ = local_import
467 468
468 469 if imp.lock_held():
469 470 # this is a side-effect import, don't do it remotely, or even
470 471 # ignore the local effects
471 472 return local_import(name, globals, locals, fromlist, level)
472 473
473 474 imp.acquire_lock()
474 475 if local:
475 476 mod = local_import(name, globals, locals, fromlist, level)
476 477 else:
477 478 raise NotImplementedError("remote-only imports not yet implemented")
478 479 imp.release_lock()
479 480
480 481 key = name+':'+','.join(fromlist or [])
481 482 if level <= 0 and key not in modules:
482 483 modules.add(key)
483 484 if not quiet:
484 485 if fromlist:
485 print "importing %s from %s on engine(s)"%(','.join(fromlist), name)
486 print("importing %s from %s on engine(s)"%(','.join(fromlist), name))
486 487 else:
487 print "importing %s on engine(s)"%name
488 print("importing %s on engine(s)"%name)
488 489 results.append(self.apply_async(remote_import, name, fromlist, level))
489 490 # restore override
490 491 __builtin__.__import__ = save_import
491 492
492 493 return mod
493 494
494 495 # override __import__
495 496 __builtin__.__import__ = view_import
496 497 try:
497 498 # enter the block
498 499 yield
499 500 except ImportError:
500 501 if local:
501 502 raise
502 503 else:
503 504 # ignore import errors if not doing local imports
504 505 pass
505 506 finally:
506 507 # always restore __import__
507 508 __builtin__.__import__ = local_import
508 509
509 510 for r in results:
510 511 # raise possible remote ImportErrors here
511 512 r.get()
512 513
513 514
514 515 @sync_results
515 516 @save_ids
516 517 def _really_apply(self, f, args=None, kwargs=None, targets=None, block=None, track=None):
517 518 """calls f(*args, **kwargs) on remote engines, returning the result.
518 519
519 520 This method sets all of `apply`'s flags via this View's attributes.
520 521
521 522 Parameters
522 523 ----------
523 524
524 525 f : callable
525 526
526 527 args : list [default: empty]
527 528
528 529 kwargs : dict [default: empty]
529 530
530 531 targets : target list [default: self.targets]
531 532 where to run
532 533 block : bool [default: self.block]
533 534 whether to block
534 535 track : bool [default: self.track]
535 536 whether to ask zmq to track the message, for safe non-copying sends
536 537
537 538 Returns
538 539 -------
539 540
540 541 if self.block is False:
541 542 returns AsyncResult
542 543 else:
543 544 returns actual result of f(*args, **kwargs) on the engine(s)
544 545 This will be a list of self.targets is also a list (even length 1), or
545 546 the single result if self.targets is an integer engine id
546 547 """
547 548 args = [] if args is None else args
548 549 kwargs = {} if kwargs is None else kwargs
549 550 block = self.block if block is None else block
550 551 track = self.track if track is None else track
551 552 targets = self.targets if targets is None else targets
552 553
553 554 _idents, _targets = self.client._build_targets(targets)
554 555 msg_ids = []
555 556 trackers = []
556 557 for ident in _idents:
557 558 msg = self.client.send_apply_request(self._socket, f, args, kwargs, track=track,
558 559 ident=ident)
559 560 if track:
560 561 trackers.append(msg['tracker'])
561 562 msg_ids.append(msg['header']['msg_id'])
562 563 if isinstance(targets, int):
563 564 msg_ids = msg_ids[0]
564 565 tracker = None if track is False else zmq.MessageTracker(*trackers)
565 566 ar = AsyncResult(self.client, msg_ids, fname=getname(f), targets=_targets, tracker=tracker)
566 567 if block:
567 568 try:
568 569 return ar.get()
569 570 except KeyboardInterrupt:
570 571 pass
571 572 return ar
572 573
573 574
574 575 @sync_results
575 576 def map(self, f, *sequences, **kwargs):
576 577 """view.map(f, *sequences, block=self.block) => list|AsyncMapResult
577 578
578 579 Parallel version of builtin `map`, using this View's `targets`.
579 580
580 581 There will be one task per target, so work will be chunked
581 582 if the sequences are longer than `targets`.
582 583
583 584 Results can be iterated as they are ready, but will become available in chunks.
584 585
585 586 Parameters
586 587 ----------
587 588
588 589 f : callable
589 590 function to be mapped
590 591 *sequences: one or more sequences of matching length
591 592 the sequences to be distributed and passed to `f`
592 593 block : bool
593 594 whether to wait for the result or not [default self.block]
594 595
595 596 Returns
596 597 -------
597 598
598 599 if block=False:
599 600 AsyncMapResult
600 601 An object like AsyncResult, but which reassembles the sequence of results
601 602 into a single list. AsyncMapResults can be iterated through before all
602 603 results are complete.
603 604 else:
604 605 list
605 606 the result of map(f,*sequences)
606 607 """
607 608
608 609 block = kwargs.pop('block', self.block)
609 610 for k in kwargs.keys():
610 611 if k not in ['block', 'track']:
611 612 raise TypeError("invalid keyword arg, %r"%k)
612 613
613 614 assert len(sequences) > 0, "must have some sequences to map onto!"
614 615 pf = ParallelFunction(self, f, block=block, **kwargs)
615 616 return pf.map(*sequences)
616 617
617 618 @sync_results
618 619 @save_ids
619 620 def execute(self, code, silent=True, targets=None, block=None):
620 621 """Executes `code` on `targets` in blocking or nonblocking manner.
621 622
622 623 ``execute`` is always `bound` (affects engine namespace)
623 624
624 625 Parameters
625 626 ----------
626 627
627 628 code : str
628 629 the code string to be executed
629 630 block : bool
630 631 whether or not to wait until done to return
631 632 default: self.block
632 633 """
633 634 block = self.block if block is None else block
634 635 targets = self.targets if targets is None else targets
635 636
636 637 _idents, _targets = self.client._build_targets(targets)
637 638 msg_ids = []
638 639 trackers = []
639 640 for ident in _idents:
640 641 msg = self.client.send_execute_request(self._socket, code, silent=silent, ident=ident)
641 642 msg_ids.append(msg['header']['msg_id'])
642 643 if isinstance(targets, int):
643 644 msg_ids = msg_ids[0]
644 645 ar = AsyncResult(self.client, msg_ids, fname='execute', targets=_targets)
645 646 if block:
646 647 try:
647 648 ar.get()
648 649 except KeyboardInterrupt:
649 650 pass
650 651 return ar
651 652
652 653 def run(self, filename, targets=None, block=None):
653 654 """Execute contents of `filename` on my engine(s).
654 655
655 656 This simply reads the contents of the file and calls `execute`.
656 657
657 658 Parameters
658 659 ----------
659 660
660 661 filename : str
661 662 The path to the file
662 663 targets : int/str/list of ints/strs
663 664 the engines on which to execute
664 665 default : all
665 666 block : bool
666 667 whether or not to wait until done
667 668 default: self.block
668 669
669 670 """
670 671 with open(filename, 'r') as f:
671 672 # add newline in case of trailing indented whitespace
672 673 # which will cause SyntaxError
673 674 code = f.read()+'\n'
674 675 return self.execute(code, block=block, targets=targets)
675 676
676 677 def update(self, ns):
677 678 """update remote namespace with dict `ns`
678 679
679 680 See `push` for details.
680 681 """
681 682 return self.push(ns, block=self.block, track=self.track)
682 683
683 684 def push(self, ns, targets=None, block=None, track=None):
684 685 """update remote namespace with dict `ns`
685 686
686 687 Parameters
687 688 ----------
688 689
689 690 ns : dict
690 691 dict of keys with which to update engine namespace(s)
691 692 block : bool [default : self.block]
692 693 whether to wait to be notified of engine receipt
693 694
694 695 """
695 696
696 697 block = block if block is not None else self.block
697 698 track = track if track is not None else self.track
698 699 targets = targets if targets is not None else self.targets
699 700 # applier = self.apply_sync if block else self.apply_async
700 701 if not isinstance(ns, dict):
701 702 raise TypeError("Must be a dict, not %s"%type(ns))
702 703 return self._really_apply(util._push, kwargs=ns, block=block, track=track, targets=targets)
703 704
704 705 def get(self, key_s):
705 706 """get object(s) by `key_s` from remote namespace
706 707
707 708 see `pull` for details.
708 709 """
709 710 # block = block if block is not None else self.block
710 711 return self.pull(key_s, block=True)
711 712
712 713 def pull(self, names, targets=None, block=None):
713 714 """get object(s) by `name` from remote namespace
714 715
715 716 will return one object if it is a key.
716 717 can also take a list of keys, in which case it will return a list of objects.
717 718 """
718 719 block = block if block is not None else self.block
719 720 targets = targets if targets is not None else self.targets
720 721 applier = self.apply_sync if block else self.apply_async
721 722 if isinstance(names, basestring):
722 723 pass
723 724 elif isinstance(names, (list,tuple,set)):
724 725 for key in names:
725 726 if not isinstance(key, basestring):
726 727 raise TypeError("keys must be str, not type %r"%type(key))
727 728 else:
728 729 raise TypeError("names must be strs, not %r"%names)
729 730 return self._really_apply(util._pull, (names,), block=block, targets=targets)
730 731
731 732 def scatter(self, key, seq, dist='b', flatten=False, targets=None, block=None, track=None):
732 733 """
733 734 Partition a Python sequence and send the partitions to a set of engines.
734 735 """
735 736 block = block if block is not None else self.block
736 737 track = track if track is not None else self.track
737 738 targets = targets if targets is not None else self.targets
738 739
739 740 # construct integer ID list:
740 741 targets = self.client._build_targets(targets)[1]
741 742
742 743 mapObject = Map.dists[dist]()
743 744 nparts = len(targets)
744 745 msg_ids = []
745 746 trackers = []
746 747 for index, engineid in enumerate(targets):
747 748 partition = mapObject.getPartition(seq, index, nparts)
748 749 if flatten and len(partition) == 1:
749 750 ns = {key: partition[0]}
750 751 else:
751 752 ns = {key: partition}
752 753 r = self.push(ns, block=False, track=track, targets=engineid)
753 754 msg_ids.extend(r.msg_ids)
754 755 if track:
755 756 trackers.append(r._tracker)
756 757
757 758 if track:
758 759 tracker = zmq.MessageTracker(*trackers)
759 760 else:
760 761 tracker = None
761 762
762 763 r = AsyncResult(self.client, msg_ids, fname='scatter', targets=targets, tracker=tracker)
763 764 if block:
764 765 r.wait()
765 766 else:
766 767 return r
767 768
768 769 @sync_results
769 770 @save_ids
770 771 def gather(self, key, dist='b', targets=None, block=None):
771 772 """
772 773 Gather a partitioned sequence on a set of engines as a single local seq.
773 774 """
774 775 block = block if block is not None else self.block
775 776 targets = targets if targets is not None else self.targets
776 777 mapObject = Map.dists[dist]()
777 778 msg_ids = []
778 779
779 780 # construct integer ID list:
780 781 targets = self.client._build_targets(targets)[1]
781 782
782 783 for index, engineid in enumerate(targets):
783 784 msg_ids.extend(self.pull(key, block=False, targets=engineid).msg_ids)
784 785
785 786 r = AsyncMapResult(self.client, msg_ids, mapObject, fname='gather')
786 787
787 788 if block:
788 789 try:
789 790 return r.get()
790 791 except KeyboardInterrupt:
791 792 pass
792 793 return r
793 794
794 795 def __getitem__(self, key):
795 796 return self.get(key)
796 797
797 798 def __setitem__(self,key, value):
798 799 self.update({key:value})
799 800
800 801 def clear(self, targets=None, block=None):
801 802 """Clear the remote namespaces on my engines."""
802 803 block = block if block is not None else self.block
803 804 targets = targets if targets is not None else self.targets
804 805 return self.client.clear(targets=targets, block=block)
805 806
806 807 #----------------------------------------
807 808 # activate for %px, %autopx, etc. magics
808 809 #----------------------------------------
809 810
810 811 def activate(self, suffix=''):
811 812 """Activate IPython magics associated with this View
812 813
813 814 Defines the magics `%px, %autopx, %pxresult, %%px, %pxconfig`
814 815
815 816 Parameters
816 817 ----------
817 818
818 819 suffix: str [default: '']
819 820 The suffix, if any, for the magics. This allows you to have
820 821 multiple views associated with parallel magics at the same time.
821 822
822 823 e.g. ``rc[::2].activate(suffix='_even')`` will give you
823 824 the magics ``%px_even``, ``%pxresult_even``, etc. for running magics
824 825 on the even engines.
825 826 """
826 827
827 828 from IPython.parallel.client.magics import ParallelMagics
828 829
829 830 try:
830 831 # This is injected into __builtins__.
831 832 ip = get_ipython()
832 833 except NameError:
833 print "The IPython parallel magics (%px, etc.) only work within IPython."
834 print("The IPython parallel magics (%px, etc.) only work within IPython.")
834 835 return
835 836
836 837 M = ParallelMagics(ip, self, suffix)
837 838 ip.magics_manager.register(M)
838 839
839 840
840 841 @skip_doctest
841 842 class LoadBalancedView(View):
842 843 """An load-balancing View that only executes via the Task scheduler.
843 844
844 845 Load-balanced views can be created with the client's `view` method:
845 846
846 847 >>> v = client.load_balanced_view()
847 848
848 849 or targets can be specified, to restrict the potential destinations:
849 850
850 851 >>> v = client.client.load_balanced_view([1,3])
851 852
852 853 which would restrict loadbalancing to between engines 1 and 3.
853 854
854 855 """
855 856
856 857 follow=Any()
857 858 after=Any()
858 859 timeout=CFloat()
859 860 retries = Integer(0)
860 861
861 862 _task_scheme = Any()
862 863 _flag_names = List(['targets', 'block', 'track', 'follow', 'after', 'timeout', 'retries'])
863 864
864 865 def __init__(self, client=None, socket=None, **flags):
865 866 super(LoadBalancedView, self).__init__(client=client, socket=socket, **flags)
866 867 self._task_scheme=client._task_scheme
867 868
868 869 def _validate_dependency(self, dep):
869 870 """validate a dependency.
870 871
871 872 For use in `set_flags`.
872 873 """
873 874 if dep is None or isinstance(dep, (basestring, AsyncResult, Dependency)):
874 875 return True
875 876 elif isinstance(dep, (list,set, tuple)):
876 877 for d in dep:
877 878 if not isinstance(d, (basestring, AsyncResult)):
878 879 return False
879 880 elif isinstance(dep, dict):
880 881 if set(dep.keys()) != set(Dependency().as_dict().keys()):
881 882 return False
882 883 if not isinstance(dep['msg_ids'], list):
883 884 return False
884 885 for d in dep['msg_ids']:
885 886 if not isinstance(d, basestring):
886 887 return False
887 888 else:
888 889 return False
889 890
890 891 return True
891 892
892 893 def _render_dependency(self, dep):
893 894 """helper for building jsonable dependencies from various input forms."""
894 895 if isinstance(dep, Dependency):
895 896 return dep.as_dict()
896 897 elif isinstance(dep, AsyncResult):
897 898 return dep.msg_ids
898 899 elif dep is None:
899 900 return []
900 901 else:
901 902 # pass to Dependency constructor
902 903 return list(Dependency(dep))
903 904
904 905 def set_flags(self, **kwargs):
905 906 """set my attribute flags by keyword.
906 907
907 908 A View is a wrapper for the Client's apply method, but with attributes
908 909 that specify keyword arguments, those attributes can be set by keyword
909 910 argument with this method.
910 911
911 912 Parameters
912 913 ----------
913 914
914 915 block : bool
915 916 whether to wait for results
916 917 track : bool
917 918 whether to create a MessageTracker to allow the user to
918 919 safely edit after arrays and buffers during non-copying
919 920 sends.
920 921
921 922 after : Dependency or collection of msg_ids
922 923 Only for load-balanced execution (targets=None)
923 924 Specify a list of msg_ids as a time-based dependency.
924 925 This job will only be run *after* the dependencies
925 926 have been met.
926 927
927 928 follow : Dependency or collection of msg_ids
928 929 Only for load-balanced execution (targets=None)
929 930 Specify a list of msg_ids as a location-based dependency.
930 931 This job will only be run on an engine where this dependency
931 932 is met.
932 933
933 934 timeout : float/int or None
934 935 Only for load-balanced execution (targets=None)
935 936 Specify an amount of time (in seconds) for the scheduler to
936 937 wait for dependencies to be met before failing with a
937 938 DependencyTimeout.
938 939
939 940 retries : int
940 941 Number of times a task will be retried on failure.
941 942 """
942 943
943 944 super(LoadBalancedView, self).set_flags(**kwargs)
944 945 for name in ('follow', 'after'):
945 946 if name in kwargs:
946 947 value = kwargs[name]
947 948 if self._validate_dependency(value):
948 949 setattr(self, name, value)
949 950 else:
950 951 raise ValueError("Invalid dependency: %r"%value)
951 952 if 'timeout' in kwargs:
952 953 t = kwargs['timeout']
953 954 if not isinstance(t, (int, long, float, type(None))):
954 955 raise TypeError("Invalid type for timeout: %r"%type(t))
955 956 if t is not None:
956 957 if t < 0:
957 958 raise ValueError("Invalid timeout: %s"%t)
958 959 self.timeout = t
959 960
960 961 @sync_results
961 962 @save_ids
962 963 def _really_apply(self, f, args=None, kwargs=None, block=None, track=None,
963 964 after=None, follow=None, timeout=None,
964 965 targets=None, retries=None):
965 966 """calls f(*args, **kwargs) on a remote engine, returning the result.
966 967
967 968 This method temporarily sets all of `apply`'s flags for a single call.
968 969
969 970 Parameters
970 971 ----------
971 972
972 973 f : callable
973 974
974 975 args : list [default: empty]
975 976
976 977 kwargs : dict [default: empty]
977 978
978 979 block : bool [default: self.block]
979 980 whether to block
980 981 track : bool [default: self.track]
981 982 whether to ask zmq to track the message, for safe non-copying sends
982 983
983 984 !!!!!! TODO: THE REST HERE !!!!
984 985
985 986 Returns
986 987 -------
987 988
988 989 if self.block is False:
989 990 returns AsyncResult
990 991 else:
991 992 returns actual result of f(*args, **kwargs) on the engine(s)
992 993 This will be a list of self.targets is also a list (even length 1), or
993 994 the single result if self.targets is an integer engine id
994 995 """
995 996
996 997 # validate whether we can run
997 998 if self._socket.closed:
998 999 msg = "Task farming is disabled"
999 1000 if self._task_scheme == 'pure':
1000 1001 msg += " because the pure ZMQ scheduler cannot handle"
1001 1002 msg += " disappearing engines."
1002 1003 raise RuntimeError(msg)
1003 1004
1004 1005 if self._task_scheme == 'pure':
1005 1006 # pure zmq scheme doesn't support extra features
1006 1007 msg = "Pure ZMQ scheduler doesn't support the following flags:"
1007 1008 "follow, after, retries, targets, timeout"
1008 1009 if (follow or after or retries or targets or timeout):
1009 1010 # hard fail on Scheduler flags
1010 1011 raise RuntimeError(msg)
1011 1012 if isinstance(f, dependent):
1012 1013 # soft warn on functional dependencies
1013 1014 warnings.warn(msg, RuntimeWarning)
1014 1015
1015 1016 # build args
1016 1017 args = [] if args is None else args
1017 1018 kwargs = {} if kwargs is None else kwargs
1018 1019 block = self.block if block is None else block
1019 1020 track = self.track if track is None else track
1020 1021 after = self.after if after is None else after
1021 1022 retries = self.retries if retries is None else retries
1022 1023 follow = self.follow if follow is None else follow
1023 1024 timeout = self.timeout if timeout is None else timeout
1024 1025 targets = self.targets if targets is None else targets
1025 1026
1026 1027 if not isinstance(retries, int):
1027 1028 raise TypeError('retries must be int, not %r'%type(retries))
1028 1029
1029 1030 if targets is None:
1030 1031 idents = []
1031 1032 else:
1032 1033 idents = self.client._build_targets(targets)[0]
1033 1034 # ensure *not* bytes
1034 1035 idents = [ ident.decode() for ident in idents ]
1035 1036
1036 1037 after = self._render_dependency(after)
1037 1038 follow = self._render_dependency(follow)
1038 1039 metadata = dict(after=after, follow=follow, timeout=timeout, targets=idents, retries=retries)
1039 1040
1040 1041 msg = self.client.send_apply_request(self._socket, f, args, kwargs, track=track,
1041 1042 metadata=metadata)
1042 1043 tracker = None if track is False else msg['tracker']
1043 1044
1044 1045 ar = AsyncResult(self.client, msg['header']['msg_id'], fname=getname(f), targets=None, tracker=tracker)
1045 1046
1046 1047 if block:
1047 1048 try:
1048 1049 return ar.get()
1049 1050 except KeyboardInterrupt:
1050 1051 pass
1051 1052 return ar
1052 1053
1053 1054 @sync_results
1054 1055 @save_ids
1055 1056 def map(self, f, *sequences, **kwargs):
1056 1057 """view.map(f, *sequences, block=self.block, chunksize=1, ordered=True) => list|AsyncMapResult
1057 1058
1058 1059 Parallel version of builtin `map`, load-balanced by this View.
1059 1060
1060 1061 `block`, and `chunksize` can be specified by keyword only.
1061 1062
1062 1063 Each `chunksize` elements will be a separate task, and will be
1063 1064 load-balanced. This lets individual elements be available for iteration
1064 1065 as soon as they arrive.
1065 1066
1066 1067 Parameters
1067 1068 ----------
1068 1069
1069 1070 f : callable
1070 1071 function to be mapped
1071 1072 *sequences: one or more sequences of matching length
1072 1073 the sequences to be distributed and passed to `f`
1073 1074 block : bool [default self.block]
1074 1075 whether to wait for the result or not
1075 1076 track : bool
1076 1077 whether to create a MessageTracker to allow the user to
1077 1078 safely edit after arrays and buffers during non-copying
1078 1079 sends.
1079 1080 chunksize : int [default 1]
1080 1081 how many elements should be in each task.
1081 1082 ordered : bool [default True]
1082 1083 Whether the results should be gathered as they arrive, or enforce
1083 1084 the order of submission.
1084 1085
1085 1086 Only applies when iterating through AsyncMapResult as results arrive.
1086 1087 Has no effect when block=True.
1087 1088
1088 1089 Returns
1089 1090 -------
1090 1091
1091 1092 if block=False:
1092 1093 AsyncMapResult
1093 1094 An object like AsyncResult, but which reassembles the sequence of results
1094 1095 into a single list. AsyncMapResults can be iterated through before all
1095 1096 results are complete.
1096 1097 else:
1097 1098 the result of map(f,*sequences)
1098 1099
1099 1100 """
1100 1101
1101 1102 # default
1102 1103 block = kwargs.get('block', self.block)
1103 1104 chunksize = kwargs.get('chunksize', 1)
1104 1105 ordered = kwargs.get('ordered', True)
1105 1106
1106 1107 keyset = set(kwargs.keys())
1107 1108 extra_keys = keyset.difference_update(set(['block', 'chunksize']))
1108 1109 if extra_keys:
1109 1110 raise TypeError("Invalid kwargs: %s"%list(extra_keys))
1110 1111
1111 1112 assert len(sequences) > 0, "must have some sequences to map onto!"
1112 1113
1113 1114 pf = ParallelFunction(self, f, block=block, chunksize=chunksize, ordered=ordered)
1114 1115 return pf.map(*sequences)
1115 1116
1116 1117 __all__ = ['LoadBalancedView', 'DirectView']
@@ -1,132 +1,133 b''
1 1 """toplevel setup/teardown for parallel tests."""
2 from __future__ import print_function
2 3
3 4 #-------------------------------------------------------------------------------
4 5 # Copyright (C) 2011 The IPython Development Team
5 6 #
6 7 # Distributed under the terms of the BSD License. The full license is in
7 8 # the file COPYING, distributed as part of this software.
8 9 #-------------------------------------------------------------------------------
9 10
10 11 #-------------------------------------------------------------------------------
11 12 # Imports
12 13 #-------------------------------------------------------------------------------
13 14
14 15 import os
15 16 import tempfile
16 17 import time
17 18 from subprocess import Popen, PIPE, STDOUT
18 19
19 20 import nose
20 21
21 22 from IPython.utils.path import get_ipython_dir
22 23 from IPython.parallel import Client
23 24 from IPython.parallel.apps.launcher import (LocalProcessLauncher,
24 25 ipengine_cmd_argv,
25 26 ipcontroller_cmd_argv,
26 27 SIGKILL,
27 28 ProcessStateError,
28 29 )
29 30
30 31 # globals
31 32 launchers = []
32 33 blackhole = open(os.devnull, 'w')
33 34
34 35 # Launcher class
35 36 class TestProcessLauncher(LocalProcessLauncher):
36 37 """subclass LocalProcessLauncher, to prevent extra sockets and threads being created on Windows"""
37 38 def start(self):
38 39 if self.state == 'before':
39 40 self.process = Popen(self.args,
40 41 stdout=PIPE, stderr=STDOUT,
41 42 env=os.environ,
42 43 cwd=self.work_dir
43 44 )
44 45 self.notify_start(self.process.pid)
45 46 self.poll = self.process.poll
46 47 # Store stdout & stderr to show with failing tests.
47 48 # This is defined in IPython.testing.iptest
48 49 nose.ipy_stream_capturer.add_stream(self.process.stdout.fileno())
49 50 nose.ipy_stream_capturer.ensure_started()
50 51 else:
51 52 s = 'The process was already started and has state: %r' % self.state
52 53 raise ProcessStateError(s)
53 54
54 55 # nose setup/teardown
55 56
56 57 def setup():
57 58 cluster_dir = os.path.join(get_ipython_dir(), 'profile_iptest')
58 59 engine_json = os.path.join(cluster_dir, 'security', 'ipcontroller-engine.json')
59 60 client_json = os.path.join(cluster_dir, 'security', 'ipcontroller-client.json')
60 61 for json in (engine_json, client_json):
61 62 if os.path.exists(json):
62 63 os.remove(json)
63 64
64 65 cp = TestProcessLauncher()
65 66 cp.cmd_and_args = ipcontroller_cmd_argv + \
66 67 ['--profile=iptest', '--log-level=20', '--ping=250', '--dictdb']
67 68 cp.start()
68 69 launchers.append(cp)
69 70 tic = time.time()
70 71 while not os.path.exists(engine_json) or not os.path.exists(client_json):
71 72 if cp.poll() is not None:
72 73 raise RuntimeError("The test controller exited with status %s" % cp.poll())
73 74 elif time.time()-tic > 15:
74 75 raise RuntimeError("Timeout waiting for the test controller to start.")
75 76 time.sleep(0.1)
76 77 add_engines(1)
77 78
78 79 def add_engines(n=1, profile='iptest', total=False):
79 80 """add a number of engines to a given profile.
80 81
81 82 If total is True, then already running engines are counted, and only
82 83 the additional engines necessary (if any) are started.
83 84 """
84 85 rc = Client(profile=profile)
85 86 base = len(rc)
86 87
87 88 if total:
88 89 n = max(n - base, 0)
89 90
90 91 eps = []
91 92 for i in range(n):
92 93 ep = TestProcessLauncher()
93 94 ep.cmd_and_args = ipengine_cmd_argv + [
94 95 '--profile=%s' % profile,
95 96 '--log-level=50',
96 97 '--InteractiveShell.colors=nocolor'
97 98 ]
98 99 ep.start()
99 100 launchers.append(ep)
100 101 eps.append(ep)
101 102 tic = time.time()
102 103 while len(rc) < base+n:
103 104 if any([ ep.poll() is not None for ep in eps ]):
104 105 raise RuntimeError("A test engine failed to start.")
105 106 elif time.time()-tic > 15:
106 107 raise RuntimeError("Timeout waiting for engines to connect.")
107 108 time.sleep(.1)
108 109 rc.spin()
109 110 rc.close()
110 111 return eps
111 112
112 113 def teardown():
113 114 time.sleep(1)
114 115 while launchers:
115 116 p = launchers.pop()
116 117 nose.ipy_stream_capturer.remove_stream(p.process.stdout.fileno())
117 118 if p.poll() is None:
118 119 try:
119 120 p.stop()
120 121 except Exception as e:
121 print e
122 print(e)
122 123 pass
123 124 if p.poll() is None:
124 125 time.sleep(.25)
125 126 if p.poll() is None:
126 127 try:
127 print 'cleaning up test process...'
128 print('cleaning up test process...')
128 129 p.signal(SIGKILL)
129 130 except:
130 print "couldn't shutdown process: ", p
131 print("couldn't shutdown process: ", p)
131 132 blackhole.close()
132 133
@@ -1,829 +1,830 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Sphinx directive to support embedded IPython code.
3 3
4 4 This directive allows pasting of entire interactive IPython sessions, prompts
5 5 and all, and their code will actually get re-executed at doc build time, with
6 6 all prompts renumbered sequentially. It also allows you to input code as a pure
7 7 python input by giving the argument python to the directive. The output looks
8 8 like an interactive ipython section.
9 9
10 10 To enable this directive, simply list it in your Sphinx ``conf.py`` file
11 11 (making sure the directory where you placed it is visible to sphinx, as is
12 12 needed for all Sphinx directives).
13 13
14 14 By default this directive assumes that your prompts are unchanged IPython ones,
15 15 but this can be customized. The configurable options that can be placed in
16 16 conf.py are
17 17
18 18 ipython_savefig_dir:
19 19 The directory in which to save the figures. This is relative to the
20 20 Sphinx source directory. The default is `html_static_path`.
21 21 ipython_rgxin:
22 22 The compiled regular expression to denote the start of IPython input
23 23 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
24 24 shouldn't need to change this.
25 25 ipython_rgxout:
26 26 The compiled regular expression to denote the start of IPython output
27 27 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
28 28 shouldn't need to change this.
29 29 ipython_promptin:
30 30 The string to represent the IPython input prompt in the generated ReST.
31 31 The default is 'In [%d]:'. This expects that the line numbers are used
32 32 in the prompt.
33 33 ipython_promptout:
34 34
35 35 The string to represent the IPython prompt in the generated ReST. The
36 36 default is 'Out [%d]:'. This expects that the line numbers are used
37 37 in the prompt.
38 38
39 39 ToDo
40 40 ----
41 41
42 42 - Turn the ad-hoc test() function into a real test suite.
43 43 - Break up ipython-specific functionality from matplotlib stuff into better
44 44 separated code.
45 45
46 46 Authors
47 47 -------
48 48
49 49 - John D Hunter: orignal author.
50 50 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
51 51 - VáclavŠmilauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
52 52 - Skipper Seabold, refactoring, cleanups, pure python addition
53 53 """
54 from __future__ import print_function
54 55
55 56 #-----------------------------------------------------------------------------
56 57 # Imports
57 58 #-----------------------------------------------------------------------------
58 59
59 60 # Stdlib
60 61 import cStringIO
61 62 import os
62 63 import re
63 64 import sys
64 65 import tempfile
65 66 import ast
66 67
67 68 # To keep compatibility with various python versions
68 69 try:
69 70 from hashlib import md5
70 71 except ImportError:
71 72 from md5 import md5
72 73
73 74 # Third-party
74 75 import matplotlib
75 76 import sphinx
76 77 from docutils.parsers.rst import directives
77 78 from docutils import nodes
78 79 from sphinx.util.compat import Directive
79 80
80 81 matplotlib.use('Agg')
81 82
82 83 # Our own
83 84 from IPython import Config, InteractiveShell
84 85 from IPython.core.profiledir import ProfileDir
85 86 from IPython.utils import io
86 87
87 88 #-----------------------------------------------------------------------------
88 89 # Globals
89 90 #-----------------------------------------------------------------------------
90 91 # for tokenizing blocks
91 92 COMMENT, INPUT, OUTPUT = range(3)
92 93
93 94 #-----------------------------------------------------------------------------
94 95 # Functions and class declarations
95 96 #-----------------------------------------------------------------------------
96 97 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
97 98 """
98 99 part is a string of ipython text, comprised of at most one
99 100 input, one ouput, comments, and blank lines. The block parser
100 101 parses the text into a list of::
101 102
102 103 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
103 104
104 105 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
105 106 data is, depending on the type of token::
106 107
107 108 COMMENT : the comment string
108 109
109 110 INPUT: the (DECORATOR, INPUT_LINE, REST) where
110 111 DECORATOR: the input decorator (or None)
111 112 INPUT_LINE: the input as string (possibly multi-line)
112 113 REST : any stdout generated by the input line (not OUTPUT)
113 114
114 115
115 116 OUTPUT: the output string, possibly multi-line
116 117 """
117 118
118 119 block = []
119 120 lines = part.split('\n')
120 121 N = len(lines)
121 122 i = 0
122 123 decorator = None
123 124 while 1:
124 125
125 126 if i==N:
126 127 # nothing left to parse -- the last line
127 128 break
128 129
129 130 line = lines[i]
130 131 i += 1
131 132 line_stripped = line.strip()
132 133 if line_stripped.startswith('#'):
133 134 block.append((COMMENT, line))
134 135 continue
135 136
136 137 if line_stripped.startswith('@'):
137 138 # we're assuming at most one decorator -- may need to
138 139 # rethink
139 140 decorator = line_stripped
140 141 continue
141 142
142 143 # does this look like an input line?
143 144 matchin = rgxin.match(line)
144 145 if matchin:
145 146 lineno, inputline = int(matchin.group(1)), matchin.group(2)
146 147
147 148 # the ....: continuation string
148 149 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
149 150 Nc = len(continuation)
150 151 # input lines can continue on for more than one line, if
151 152 # we have a '\' line continuation char or a function call
152 153 # echo line 'print'. The input line can only be
153 154 # terminated by the end of the block or an output line, so
154 155 # we parse out the rest of the input line if it is
155 156 # multiline as well as any echo text
156 157
157 158 rest = []
158 159 while i<N:
159 160
160 161 # look ahead; if the next line is blank, or a comment, or
161 162 # an output line, we're done
162 163
163 164 nextline = lines[i]
164 165 matchout = rgxout.match(nextline)
165 166 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
166 167 if matchout or nextline.startswith('#'):
167 168 break
168 169 elif nextline.startswith(continuation):
169 170 inputline += '\n' + nextline[Nc:]
170 171 else:
171 172 rest.append(nextline)
172 173 i+= 1
173 174
174 175 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
175 176 continue
176 177
177 178 # if it looks like an output line grab all the text to the end
178 179 # of the block
179 180 matchout = rgxout.match(line)
180 181 if matchout:
181 182 lineno, output = int(matchout.group(1)), matchout.group(2)
182 183 if i<N-1:
183 184 output = '\n'.join([output] + lines[i:])
184 185
185 186 block.append((OUTPUT, output))
186 187 break
187 188
188 189 return block
189 190
190 191 class EmbeddedSphinxShell(object):
191 192 """An embedded IPython instance to run inside Sphinx"""
192 193
193 194 def __init__(self):
194 195
195 196 self.cout = cStringIO.StringIO()
196 197
197 198
198 199 # Create config object for IPython
199 200 config = Config()
200 201 config.Global.display_banner = False
201 202 config.Global.exec_lines = ['import numpy as np',
202 203 'from pylab import *'
203 204 ]
204 205 config.InteractiveShell.autocall = False
205 206 config.InteractiveShell.autoindent = False
206 207 config.InteractiveShell.colors = 'NoColor'
207 208
208 209 # create a profile so instance history isn't saved
209 210 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
210 211 profname = 'auto_profile_sphinx_build'
211 212 pdir = os.path.join(tmp_profile_dir,profname)
212 213 profile = ProfileDir.create_profile_dir(pdir)
213 214
214 215 # Create and initialize ipython, but don't start its mainloop
215 216 IP = InteractiveShell.instance(config=config, profile_dir=profile)
216 217 # io.stdout redirect must be done *after* instantiating InteractiveShell
217 218 io.stdout = self.cout
218 219 io.stderr = self.cout
219 220
220 221 # For debugging, so we can see normal output, use this:
221 222 #from IPython.utils.io import Tee
222 223 #io.stdout = Tee(self.cout, channel='stdout') # dbg
223 224 #io.stderr = Tee(self.cout, channel='stderr') # dbg
224 225
225 226 # Store a few parts of IPython we'll need.
226 227 self.IP = IP
227 228 self.user_ns = self.IP.user_ns
228 229 self.user_global_ns = self.IP.user_global_ns
229 230
230 231 self.input = ''
231 232 self.output = ''
232 233
233 234 self.is_verbatim = False
234 235 self.is_doctest = False
235 236 self.is_suppress = False
236 237
237 238 # on the first call to the savefig decorator, we'll import
238 239 # pyplot as plt so we can make a call to the plt.gcf().savefig
239 240 self._pyplot_imported = False
240 241
241 242 def clear_cout(self):
242 243 self.cout.seek(0)
243 244 self.cout.truncate(0)
244 245
245 246 def process_input_line(self, line, store_history=True):
246 247 """process the input, capturing stdout"""
247 248 #print "input='%s'"%self.input
248 249 stdout = sys.stdout
249 250 splitter = self.IP.input_splitter
250 251 try:
251 252 sys.stdout = self.cout
252 253 splitter.push(line)
253 254 more = splitter.push_accepts_more()
254 255 if not more:
255 256 source_raw = splitter.source_raw_reset()[1]
256 257 self.IP.run_cell(source_raw, store_history=store_history)
257 258 finally:
258 259 sys.stdout = stdout
259 260
260 261 def process_image(self, decorator):
261 262 """
262 263 # build out an image directive like
263 264 # .. image:: somefile.png
264 265 # :width 4in
265 266 #
266 267 # from an input like
267 268 # savefig somefile.png width=4in
268 269 """
269 270 savefig_dir = self.savefig_dir
270 271 source_dir = self.source_dir
271 272 saveargs = decorator.split(' ')
272 273 filename = saveargs[1]
273 274 # insert relative path to image file in source
274 275 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
275 276 source_dir)
276 277
277 278 imagerows = ['.. image:: %s'%outfile]
278 279
279 280 for kwarg in saveargs[2:]:
280 281 arg, val = kwarg.split('=')
281 282 arg = arg.strip()
282 283 val = val.strip()
283 284 imagerows.append(' :%s: %s'%(arg, val))
284 285
285 286 image_file = os.path.basename(outfile) # only return file name
286 287 image_directive = '\n'.join(imagerows)
287 288 return image_file, image_directive
288 289
289 290
290 291 # Callbacks for each type of token
291 292 def process_input(self, data, input_prompt, lineno):
292 293 """Process data block for INPUT token."""
293 294 decorator, input, rest = data
294 295 image_file = None
295 296 image_directive = None
296 297 #print 'INPUT:', data # dbg
297 298 is_verbatim = decorator=='@verbatim' or self.is_verbatim
298 299 is_doctest = decorator=='@doctest' or self.is_doctest
299 300 is_suppress = decorator=='@suppress' or self.is_suppress
300 301 is_savefig = decorator is not None and \
301 302 decorator.startswith('@savefig')
302 303
303 304 input_lines = input.split('\n')
304 305 if len(input_lines) > 1:
305 306 if input_lines[-1] != "":
306 307 input_lines.append('') # make sure there's a blank line
307 308 # so splitter buffer gets reset
308 309
309 310 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
310 311 Nc = len(continuation)
311 312
312 313 if is_savefig:
313 314 image_file, image_directive = self.process_image(decorator)
314 315
315 316 ret = []
316 317 is_semicolon = False
317 318
318 319 for i, line in enumerate(input_lines):
319 320 if line.endswith(';'):
320 321 is_semicolon = True
321 322
322 323 if i==0:
323 324 # process the first input line
324 325 if is_verbatim:
325 326 self.process_input_line('')
326 327 self.IP.execution_count += 1 # increment it anyway
327 328 else:
328 329 # only submit the line in non-verbatim mode
329 330 self.process_input_line(line, store_history=True)
330 331 formatted_line = '%s %s'%(input_prompt, line)
331 332 else:
332 333 # process a continuation line
333 334 if not is_verbatim:
334 335 self.process_input_line(line, store_history=True)
335 336
336 337 formatted_line = '%s %s'%(continuation, line)
337 338
338 339 if not is_suppress:
339 340 ret.append(formatted_line)
340 341
341 342 if not is_suppress and len(rest.strip()) and is_verbatim:
342 343 # the "rest" is the standard output of the
343 344 # input, which needs to be added in
344 345 # verbatim mode
345 346 ret.append(rest)
346 347
347 348 self.cout.seek(0)
348 349 output = self.cout.read()
349 350 if not is_suppress and not is_semicolon:
350 351 ret.append(output)
351 352 elif is_semicolon: # get spacing right
352 353 ret.append('')
353 354
354 355 self.cout.truncate(0)
355 356 return (ret, input_lines, output, is_doctest, image_file,
356 357 image_directive)
357 358 #print 'OUTPUT', output # dbg
358 359
359 360 def process_output(self, data, output_prompt,
360 361 input_lines, output, is_doctest, image_file):
361 362 """Process data block for OUTPUT token."""
362 363 if is_doctest:
363 364 submitted = data.strip()
364 365 found = output
365 366 if found is not None:
366 367 found = found.strip()
367 368
368 369 # XXX - fperez: in 0.11, 'output' never comes with the prompt
369 370 # in it, just the actual output text. So I think all this code
370 371 # can be nuked...
371 372
372 373 # the above comment does not appear to be accurate... (minrk)
373 374
374 375 ind = found.find(output_prompt)
375 376 if ind<0:
376 377 e='output prompt="%s" does not match out line=%s' % \
377 378 (output_prompt, found)
378 379 raise RuntimeError(e)
379 380 found = found[len(output_prompt):].strip()
380 381
381 382 if found!=submitted:
382 383 e = ('doctest failure for input_lines="%s" with '
383 384 'found_output="%s" and submitted output="%s"' %
384 385 (input_lines, found, submitted) )
385 386 raise RuntimeError(e)
386 387 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
387 388
388 389 def process_comment(self, data):
389 390 """Process data fPblock for COMMENT token."""
390 391 if not self.is_suppress:
391 392 return [data]
392 393
393 394 def save_image(self, image_file):
394 395 """
395 396 Saves the image file to disk.
396 397 """
397 398 self.ensure_pyplot()
398 399 command = 'plt.gcf().savefig("%s")'%image_file
399 400 #print 'SAVEFIG', command # dbg
400 401 self.process_input_line('bookmark ipy_thisdir', store_history=False)
401 402 self.process_input_line('cd -b ipy_savedir', store_history=False)
402 403 self.process_input_line(command, store_history=False)
403 404 self.process_input_line('cd -b ipy_thisdir', store_history=False)
404 405 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
405 406 self.clear_cout()
406 407
407 408
408 409 def process_block(self, block):
409 410 """
410 411 process block from the block_parser and return a list of processed lines
411 412 """
412 413 ret = []
413 414 output = None
414 415 input_lines = None
415 416 lineno = self.IP.execution_count
416 417
417 418 input_prompt = self.promptin%lineno
418 419 output_prompt = self.promptout%lineno
419 420 image_file = None
420 421 image_directive = None
421 422
422 423 for token, data in block:
423 424 if token==COMMENT:
424 425 out_data = self.process_comment(data)
425 426 elif token==INPUT:
426 427 (out_data, input_lines, output, is_doctest, image_file,
427 428 image_directive) = \
428 429 self.process_input(data, input_prompt, lineno)
429 430 elif token==OUTPUT:
430 431 out_data = \
431 432 self.process_output(data, output_prompt,
432 433 input_lines, output, is_doctest,
433 434 image_file)
434 435 if out_data:
435 436 ret.extend(out_data)
436 437
437 438 # save the image files
438 439 if image_file is not None:
439 440 self.save_image(image_file)
440 441
441 442 return ret, image_directive
442 443
443 444 def ensure_pyplot(self):
444 445 if self._pyplot_imported:
445 446 return
446 447 self.process_input_line('import matplotlib.pyplot as plt',
447 448 store_history=False)
448 449
449 450 def process_pure_python(self, content):
450 451 """
451 452 content is a list of strings. it is unedited directive conent
452 453
453 454 This runs it line by line in the InteractiveShell, prepends
454 455 prompts as needed capturing stderr and stdout, then returns
455 456 the content as a list as if it were ipython code
456 457 """
457 458 output = []
458 459 savefig = False # keep up with this to clear figure
459 460 multiline = False # to handle line continuation
460 461 multiline_start = None
461 462 fmtin = self.promptin
462 463
463 464 ct = 0
464 465
465 466 for lineno, line in enumerate(content):
466 467
467 468 line_stripped = line.strip()
468 469 if not len(line):
469 470 output.append(line)
470 471 continue
471 472
472 473 # handle decorators
473 474 if line_stripped.startswith('@'):
474 475 output.extend([line])
475 476 if 'savefig' in line:
476 477 savefig = True # and need to clear figure
477 478 continue
478 479
479 480 # handle comments
480 481 if line_stripped.startswith('#'):
481 482 output.extend([line])
482 483 continue
483 484
484 485 # deal with lines checking for multiline
485 486 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
486 487 if not multiline:
487 488 modified = u"%s %s" % (fmtin % ct, line_stripped)
488 489 output.append(modified)
489 490 ct += 1
490 491 try:
491 492 ast.parse(line_stripped)
492 493 output.append(u'')
493 494 except Exception: # on a multiline
494 495 multiline = True
495 496 multiline_start = lineno
496 497 else: # still on a multiline
497 498 modified = u'%s %s' % (continuation, line)
498 499 output.append(modified)
499 500
500 501 # if the next line is indented, it should be part of multiline
501 502 if len(content) > lineno + 1:
502 503 nextline = content[lineno + 1]
503 504 if len(nextline) - len(nextline.lstrip()) > 3:
504 505 continue
505 506 try:
506 507 mod = ast.parse(
507 508 '\n'.join(content[multiline_start:lineno+1]))
508 509 if isinstance(mod.body[0], ast.FunctionDef):
509 510 # check to see if we have the whole function
510 511 for element in mod.body[0].body:
511 512 if isinstance(element, ast.Return):
512 513 multiline = False
513 514 else:
514 515 output.append(u'')
515 516 multiline = False
516 517 except Exception:
517 518 pass
518 519
519 520 if savefig: # clear figure if plotted
520 521 self.ensure_pyplot()
521 522 self.process_input_line('plt.clf()', store_history=False)
522 523 self.clear_cout()
523 524 savefig = False
524 525
525 526 return output
526 527
527 528 class IPythonDirective(Directive):
528 529
529 530 has_content = True
530 531 required_arguments = 0
531 532 optional_arguments = 4 # python, suppress, verbatim, doctest
532 533 final_argumuent_whitespace = True
533 534 option_spec = { 'python': directives.unchanged,
534 535 'suppress' : directives.flag,
535 536 'verbatim' : directives.flag,
536 537 'doctest' : directives.flag,
537 538 }
538 539
539 540 shell = None
540 541
541 542 seen_docs = set()
542 543
543 544 def get_config_options(self):
544 545 # contains sphinx configuration variables
545 546 config = self.state.document.settings.env.config
546 547
547 548 # get config variables to set figure output directory
548 549 confdir = self.state.document.settings.env.app.confdir
549 550 savefig_dir = config.ipython_savefig_dir
550 551 source_dir = os.path.dirname(self.state.document.current_source)
551 552 if savefig_dir is None:
552 553 savefig_dir = config.html_static_path
553 554 if isinstance(savefig_dir, list):
554 555 savefig_dir = savefig_dir[0] # safe to assume only one path?
555 556 savefig_dir = os.path.join(confdir, savefig_dir)
556 557
557 558 # get regex and prompt stuff
558 559 rgxin = config.ipython_rgxin
559 560 rgxout = config.ipython_rgxout
560 561 promptin = config.ipython_promptin
561 562 promptout = config.ipython_promptout
562 563
563 564 return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout
564 565
565 566 def setup(self):
566 567 if self.shell is None:
567 568 self.shell = EmbeddedSphinxShell()
568 569 # reset the execution count if we haven't processed this doc
569 570 #NOTE: this may be borked if there are multiple seen_doc tmp files
570 571 #check time stamp?
571 572
572 573 if not self.state.document.current_source in self.seen_docs:
573 574 self.shell.IP.history_manager.reset()
574 575 self.shell.IP.execution_count = 1
575 576 self.seen_docs.add(self.state.document.current_source)
576 577
577 578
578 579
579 580 # get config values
580 581 (savefig_dir, source_dir, rgxin,
581 582 rgxout, promptin, promptout) = self.get_config_options()
582 583
583 584 # and attach to shell so we don't have to pass them around
584 585 self.shell.rgxin = rgxin
585 586 self.shell.rgxout = rgxout
586 587 self.shell.promptin = promptin
587 588 self.shell.promptout = promptout
588 589 self.shell.savefig_dir = savefig_dir
589 590 self.shell.source_dir = source_dir
590 591
591 592 # setup bookmark for saving figures directory
592 593
593 594 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
594 595 store_history=False)
595 596 self.shell.clear_cout()
596 597
597 598 return rgxin, rgxout, promptin, promptout
598 599
599 600
600 601 def teardown(self):
601 602 # delete last bookmark
602 603 self.shell.process_input_line('bookmark -d ipy_savedir',
603 604 store_history=False)
604 605 self.shell.clear_cout()
605 606
606 607 def run(self):
607 608 debug = False
608 609
609 610 #TODO, any reason block_parser can't be a method of embeddable shell
610 611 # then we wouldn't have to carry these around
611 612 rgxin, rgxout, promptin, promptout = self.setup()
612 613
613 614 options = self.options
614 615 self.shell.is_suppress = 'suppress' in options
615 616 self.shell.is_doctest = 'doctest' in options
616 617 self.shell.is_verbatim = 'verbatim' in options
617 618
618 619
619 620 # handle pure python code
620 621 if 'python' in self.arguments:
621 622 content = self.content
622 623 self.content = self.shell.process_pure_python(content)
623 624
624 625 parts = '\n'.join(self.content).split('\n\n')
625 626
626 627 lines = ['.. code-block:: ipython','']
627 628 figures = []
628 629
629 630 for part in parts:
630 631
631 632 block = block_parser(part, rgxin, rgxout, promptin, promptout)
632 633
633 634 if len(block):
634 635 rows, figure = self.shell.process_block(block)
635 636 for row in rows:
636 637 lines.extend([' %s'%line for line in row.split('\n')])
637 638
638 639 if figure is not None:
639 640 figures.append(figure)
640 641
641 642 #text = '\n'.join(lines)
642 643 #figs = '\n'.join(figures)
643 644
644 645 for figure in figures:
645 646 lines.append('')
646 647 lines.extend(figure.split('\n'))
647 648 lines.append('')
648 649
649 650 #print lines
650 651 if len(lines)>2:
651 652 if debug:
652 print '\n'.join(lines)
653 print('\n'.join(lines))
653 654 else: #NOTE: this raises some errors, what's it for?
654 655 #print 'INSERTING %d lines'%len(lines)
655 656 self.state_machine.insert_input(
656 657 lines, self.state_machine.input_lines.source(0))
657 658
658 659 text = '\n'.join(lines)
659 660 txtnode = nodes.literal_block(text, text)
660 661 txtnode['language'] = 'ipython'
661 662 #imgnode = nodes.image(figs)
662 663
663 664 # cleanup
664 665 self.teardown()
665 666
666 667 return []#, imgnode]
667 668
668 669 # Enable as a proper Sphinx directive
669 670 def setup(app):
670 671 setup.app = app
671 672
672 673 app.add_directive('ipython', IPythonDirective)
673 674 app.add_config_value('ipython_savefig_dir', None, True)
674 675 app.add_config_value('ipython_rgxin',
675 676 re.compile('In \[(\d+)\]:\s?(.*)\s*'), True)
676 677 app.add_config_value('ipython_rgxout',
677 678 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True)
678 679 app.add_config_value('ipython_promptin', 'In [%d]:', True)
679 680 app.add_config_value('ipython_promptout', 'Out[%d]:', True)
680 681
681 682
682 683 # Simple smoke test, needs to be converted to a proper automatic test.
683 684 def test():
684 685
685 686 examples = [
686 687 r"""
687 688 In [9]: pwd
688 689 Out[9]: '/home/jdhunter/py4science/book'
689 690
690 691 In [10]: cd bookdata/
691 692 /home/jdhunter/py4science/book/bookdata
692 693
693 694 In [2]: from pylab import *
694 695
695 696 In [2]: ion()
696 697
697 698 In [3]: im = imread('stinkbug.png')
698 699
699 700 @savefig mystinkbug.png width=4in
700 701 In [4]: imshow(im)
701 702 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
702 703
703 704 """,
704 705 r"""
705 706
706 707 In [1]: x = 'hello world'
707 708
708 709 # string methods can be
709 710 # used to alter the string
710 711 @doctest
711 712 In [2]: x.upper()
712 713 Out[2]: 'HELLO WORLD'
713 714
714 715 @verbatim
715 716 In [3]: x.st<TAB>
716 717 x.startswith x.strip
717 718 """,
718 719 r"""
719 720
720 721 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
721 722 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
722 723
723 724 In [131]: print url.split('&')
724 725 ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
725 726
726 727 In [60]: import urllib
727 728
728 729 """,
729 730 r"""\
730 731
731 732 In [133]: import numpy.random
732 733
733 734 @suppress
734 735 In [134]: numpy.random.seed(2358)
735 736
736 737 @doctest
737 738 In [135]: numpy.random.rand(10,2)
738 739 Out[135]:
739 740 array([[ 0.64524308, 0.59943846],
740 741 [ 0.47102322, 0.8715456 ],
741 742 [ 0.29370834, 0.74776844],
742 743 [ 0.99539577, 0.1313423 ],
743 744 [ 0.16250302, 0.21103583],
744 745 [ 0.81626524, 0.1312433 ],
745 746 [ 0.67338089, 0.72302393],
746 747 [ 0.7566368 , 0.07033696],
747 748 [ 0.22591016, 0.77731835],
748 749 [ 0.0072729 , 0.34273127]])
749 750
750 751 """,
751 752
752 753 r"""
753 754 In [106]: print x
754 755 jdh
755 756
756 757 In [109]: for i in range(10):
757 758 .....: print i
758 759 .....:
759 760 .....:
760 761 0
761 762 1
762 763 2
763 764 3
764 765 4
765 766 5
766 767 6
767 768 7
768 769 8
769 770 9
770 771 """,
771 772
772 773 r"""
773 774
774 775 In [144]: from pylab import *
775 776
776 777 In [145]: ion()
777 778
778 779 # use a semicolon to suppress the output
779 780 @savefig test_hist.png width=4in
780 781 In [151]: hist(np.random.randn(10000), 100);
781 782
782 783
783 784 @savefig test_plot.png width=4in
784 785 In [151]: plot(np.random.randn(10000), 'o');
785 786 """,
786 787
787 788 r"""
788 789 # use a semicolon to suppress the output
789 790 In [151]: plt.clf()
790 791
791 792 @savefig plot_simple.png width=4in
792 793 In [151]: plot([1,2,3])
793 794
794 795 @savefig hist_simple.png width=4in
795 796 In [151]: hist(np.random.randn(10000), 100);
796 797
797 798 """,
798 799 r"""
799 800 # update the current fig
800 801 In [151]: ylabel('number')
801 802
802 803 In [152]: title('normal distribution')
803 804
804 805
805 806 @savefig hist_with_text.png
806 807 In [153]: grid(True)
807 808
808 809 """,
809 810 ]
810 811 # skip local-file depending first example:
811 812 examples = examples[1:]
812 813
813 814 #ipython_directive.DEBUG = True # dbg
814 815 #options = dict(suppress=True) # dbg
815 816 options = dict()
816 817 for example in examples:
817 818 content = example.split('\n')
818 819 IPythonDirective('debug', arguments=None, options=options,
819 820 content=content, lineno=0,
820 821 content_offset=None, block_text=None,
821 822 state=None, state_machine=None,
822 823 )
823 824
824 825 # Run test suite as a script
825 826 if __name__=='__main__':
826 827 if not os.path.isdir('_static'):
827 828 os.mkdir('_static')
828 829 test()
829 print 'All OK? Check figures in _static/'
830 print('All OK? Check figures in _static/')
@@ -1,292 +1,293 b''
1 1 # encoding: utf-8
2 2 """
3 3 An embedded IPython shell.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 * Fernando Perez
9 9
10 10 Notes
11 11 -----
12 12 """
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Copyright (C) 2008-2011 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-----------------------------------------------------------------------------
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Imports
23 23 #-----------------------------------------------------------------------------
24 24
25 25 from __future__ import with_statement
26 from __future__ import print_function
26 27
27 28 import sys
28 29 import warnings
29 30
30 31 from IPython.core import ultratb, compilerop
31 32 from IPython.core.magic import Magics, magics_class, line_magic
32 33 from IPython.core.interactiveshell import DummyMod
33 34 from IPython.terminal.interactiveshell import TerminalInteractiveShell
34 35 from IPython.terminal.ipapp import load_default_config
35 36
36 37 from IPython.utils.traitlets import Bool, CBool, Unicode
37 38 from IPython.utils.io import ask_yes_no
38 39
39 40
40 41 #-----------------------------------------------------------------------------
41 42 # Classes and functions
42 43 #-----------------------------------------------------------------------------
43 44
44 45 # This is an additional magic that is exposed in embedded shells.
45 46 @magics_class
46 47 class EmbeddedMagics(Magics):
47 48
48 49 @line_magic
49 50 def kill_embedded(self, parameter_s=''):
50 51 """%kill_embedded : deactivate for good the current embedded IPython.
51 52
52 53 This function (after asking for confirmation) sets an internal flag so
53 54 that an embedded IPython will never activate again. This is useful to
54 55 permanently disable a shell that is being called inside a loop: once
55 56 you've figured out what you needed from it, you may then kill it and
56 57 the program will then continue to run without the interactive shell
57 58 interfering again.
58 59 """
59 60
60 61 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
61 62 "(y/n)? [y/N] ",'n')
62 63 if kill:
63 64 self.shell.embedded_active = False
64 65 print ("This embedded IPython will not reactivate anymore "
65 66 "once you exit.")
66 67
67 68
68 69 class InteractiveShellEmbed(TerminalInteractiveShell):
69 70
70 71 dummy_mode = Bool(False)
71 72 exit_msg = Unicode('')
72 73 embedded = CBool(True)
73 74 embedded_active = CBool(True)
74 75 # Like the base class display_banner is not configurable, but here it
75 76 # is True by default.
76 77 display_banner = CBool(True)
77 78
78 79 def __init__(self, config=None, ipython_dir=None, user_ns=None,
79 80 user_module=None, custom_exceptions=((),None),
80 81 usage=None, banner1=None, banner2=None,
81 82 display_banner=None, exit_msg=u'', user_global_ns=None):
82 83
83 84 if user_global_ns is not None:
84 85 warnings.warn("user_global_ns has been replaced by user_module. The\
85 86 parameter will be ignored.", DeprecationWarning)
86 87
87 88 super(InteractiveShellEmbed,self).__init__(
88 89 config=config, ipython_dir=ipython_dir, user_ns=user_ns,
89 90 user_module=user_module, custom_exceptions=custom_exceptions,
90 91 usage=usage, banner1=banner1, banner2=banner2,
91 92 display_banner=display_banner
92 93 )
93 94
94 95 self.exit_msg = exit_msg
95 96
96 97 # don't use the ipython crash handler so that user exceptions aren't
97 98 # trapped
98 99 sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
99 100 mode=self.xmode,
100 101 call_pdb=self.pdb)
101 102
102 103 def init_sys_modules(self):
103 104 pass
104 105
105 106 def init_magics(self):
106 107 super(InteractiveShellEmbed, self).init_magics()
107 108 self.register_magics(EmbeddedMagics)
108 109
109 110 def __call__(self, header='', local_ns=None, module=None, dummy=None,
110 111 stack_depth=1, global_ns=None, compile_flags=None):
111 112 """Activate the interactive interpreter.
112 113
113 114 __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
114 115 the interpreter shell with the given local and global namespaces, and
115 116 optionally print a header string at startup.
116 117
117 118 The shell can be globally activated/deactivated using the
118 119 dummy_mode attribute. This allows you to turn off a shell used
119 120 for debugging globally.
120 121
121 122 However, *each* time you call the shell you can override the current
122 123 state of dummy_mode with the optional keyword parameter 'dummy'. For
123 124 example, if you set dummy mode on with IPShell.dummy_mode = True, you
124 125 can still have a specific call work by making it as IPShell(dummy=False).
125 126 """
126 127
127 128 # If the user has turned it off, go away
128 129 if not self.embedded_active:
129 130 return
130 131
131 132 # Normal exits from interactive mode set this flag, so the shell can't
132 133 # re-enter (it checks this variable at the start of interactive mode).
133 134 self.exit_now = False
134 135
135 136 # Allow the dummy parameter to override the global __dummy_mode
136 137 if dummy or (dummy != 0 and self.dummy_mode):
137 138 return
138 139
139 140 if self.has_readline:
140 141 self.set_readline_completer()
141 142
142 143 # self.banner is auto computed
143 144 if header:
144 145 self.old_banner2 = self.banner2
145 146 self.banner2 = self.banner2 + '\n' + header + '\n'
146 147 else:
147 148 self.old_banner2 = ''
148 149
149 150 # Call the embedding code with a stack depth of 1 so it can skip over
150 151 # our call and get the original caller's namespaces.
151 152 self.mainloop(local_ns, module, stack_depth=stack_depth,
152 153 global_ns=global_ns, compile_flags=compile_flags)
153 154
154 155 self.banner2 = self.old_banner2
155 156
156 157 if self.exit_msg is not None:
157 print self.exit_msg
158 print(self.exit_msg)
158 159
159 160 def mainloop(self, local_ns=None, module=None, stack_depth=0,
160 161 display_banner=None, global_ns=None, compile_flags=None):
161 162 """Embeds IPython into a running python program.
162 163
163 164 Input:
164 165
165 166 - header: An optional header message can be specified.
166 167
167 168 - local_ns, module: working local namespace (a dict) and module (a
168 169 module or similar object). If given as None, they are automatically
169 170 taken from the scope where the shell was called, so that
170 171 program variables become visible.
171 172
172 173 - stack_depth: specifies how many levels in the stack to go to
173 174 looking for namespaces (when local_ns or module is None). This
174 175 allows an intermediate caller to make sure that this function gets
175 176 the namespace from the intended level in the stack. By default (0)
176 177 it will get its locals and globals from the immediate caller.
177 178
178 179 - compile_flags: A bit field identifying the __future__ features
179 180 that are enabled, as passed to the builtin `compile` function. If
180 181 given as None, they are automatically taken from the scope where the
181 182 shell was called.
182 183
183 184 Warning: it's possible to use this in a program which is being run by
184 185 IPython itself (via %run), but some funny things will happen (a few
185 186 globals get overwritten). In the future this will be cleaned up, as
186 187 there is no fundamental reason why it can't work perfectly."""
187 188
188 189 if (global_ns is not None) and (module is None):
189 190 warnings.warn("global_ns is deprecated, use module instead.", DeprecationWarning)
190 191 module = DummyMod()
191 192 module.__dict__ = global_ns
192 193
193 194 # Get locals and globals from caller
194 195 if ((local_ns is None or module is None or compile_flags is None)
195 196 and self.default_user_namespaces):
196 197 call_frame = sys._getframe(stack_depth).f_back
197 198
198 199 if local_ns is None:
199 200 local_ns = call_frame.f_locals
200 201 if module is None:
201 202 global_ns = call_frame.f_globals
202 203 module = sys.modules[global_ns['__name__']]
203 204 if compile_flags is None:
204 205 compile_flags = (call_frame.f_code.co_flags &
205 206 compilerop.PyCF_MASK)
206 207
207 208 # Save original namespace and module so we can restore them after
208 209 # embedding; otherwise the shell doesn't shut down correctly.
209 210 orig_user_module = self.user_module
210 211 orig_user_ns = self.user_ns
211 212 orig_compile_flags = self.compile.flags
212 213
213 214 # Update namespaces and fire up interpreter
214 215
215 216 # The global one is easy, we can just throw it in
216 217 if module is not None:
217 218 self.user_module = module
218 219
219 220 # But the user/local one is tricky: ipython needs it to store internal
220 221 # data, but we also need the locals. We'll throw our hidden variables
221 222 # like _ih and get_ipython() into the local namespace, but delete them
222 223 # later.
223 224 if local_ns is not None:
224 225 self.user_ns = local_ns
225 226 self.init_user_ns()
226 227
227 228 # Compiler flags
228 229 if compile_flags is not None:
229 230 self.compile.flags = compile_flags
230 231
231 232 # Patch for global embedding to make sure that things don't overwrite
232 233 # user globals accidentally. Thanks to Richard <rxe@renre-europe.com>
233 234 # FIXME. Test this a bit more carefully (the if.. is new)
234 235 # N.B. This can't now ever be called. Not sure what it was for.
235 236 # And now, since it wasn't called in the previous version, I'm
236 237 # commenting out these lines so they can't be called with my new changes
237 238 # --TK, 2011-12-10
238 239 #if local_ns is None and module is None:
239 240 # self.user_global_ns.update(__main__.__dict__)
240 241
241 242 # make sure the tab-completer has the correct frame information, so it
242 243 # actually completes using the frame's locals/globals
243 244 self.set_completer_frame()
244 245
245 246 with self.builtin_trap, self.display_trap:
246 247 self.interact(display_banner=display_banner)
247 248
248 249 # now, purge out the local namespace of IPython's hidden variables.
249 250 if local_ns is not None:
250 251 for name in self.user_ns_hidden:
251 252 local_ns.pop(name, None)
252 253
253 254 # Restore original namespace so shell can shut down when we exit.
254 255 self.user_module = orig_user_module
255 256 self.user_ns = orig_user_ns
256 257 self.compile.flags = orig_compile_flags
257 258
258 259
259 260 def embed(**kwargs):
260 261 """Call this to embed IPython at the current point in your program.
261 262
262 263 The first invocation of this will create an :class:`InteractiveShellEmbed`
263 264 instance and then call it. Consecutive calls just call the already
264 265 created instance.
265 266
266 267 If you don't want the kernel to initialize the namespace
267 268 from the scope of the surrounding function,
268 269 and/or you want to load full IPython configuration,
269 270 you probably want `IPython.start_ipython()` instead.
270 271
271 272 Here is a simple example::
272 273
273 274 from IPython import embed
274 275 a = 10
275 276 b = 20
276 277 embed('First time')
277 278 c = 30
278 279 d = 40
279 280 embed
280 281
281 282 Full customization can be done by passing a :class:`Config` in as the
282 283 config argument.
283 284 """
284 285 config = kwargs.get('config')
285 286 header = kwargs.pop('header', u'')
286 287 compile_flags = kwargs.pop('compile_flags', None)
287 288 if config is None:
288 289 config = load_default_config()
289 290 config.InteractiveShellEmbed = config.TerminalInteractiveShell
290 291 kwargs['config'] = config
291 292 shell = InteractiveShellEmbed.instance(**kwargs)
292 293 shell(header=header, stack_depth=2, compile_flags=compile_flags)
@@ -1,388 +1,389 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6
7 7 Authors
8 8 -------
9 9
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min Ragan-Kelley
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2011 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 from __future__ import absolute_import
27 from __future__ import print_function
27 28
28 29 import logging
29 30 import os
30 31 import sys
31 32
32 33 from IPython.config.loader import (
33 34 Config, PyFileConfigLoader, ConfigFileNotFound
34 35 )
35 36 from IPython.config.application import boolean_flag, catch_config_error
36 37 from IPython.core import release
37 38 from IPython.core import usage
38 39 from IPython.core.completer import IPCompleter
39 40 from IPython.core.crashhandler import CrashHandler
40 41 from IPython.core.formatters import PlainTextFormatter
41 42 from IPython.core.history import HistoryManager
42 43 from IPython.core.prompts import PromptManager
43 44 from IPython.core.application import (
44 45 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
45 46 )
46 47 from IPython.core.magics import ScriptMagics
47 48 from IPython.core.shellapp import (
48 49 InteractiveShellApp, shell_flags, shell_aliases
49 50 )
50 51 from IPython.extensions.storemagic import StoreMagics
51 52 from IPython.terminal.interactiveshell import TerminalInteractiveShell
52 53 from IPython.utils import warn
53 54 from IPython.utils.path import get_ipython_dir, check_for_old_config
54 55 from IPython.utils.traitlets import (
55 56 Bool, List, Dict,
56 57 )
57 58
58 59 #-----------------------------------------------------------------------------
59 60 # Globals, utilities and helpers
60 61 #-----------------------------------------------------------------------------
61 62
62 63 _examples = """
63 64 ipython --matplotlib # enable matplotlib integration
64 65 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
65 66
66 67 ipython --log-level=DEBUG # set logging to DEBUG
67 68 ipython --profile=foo # start with profile foo
68 69
69 70 ipython qtconsole # start the qtconsole GUI application
70 71 ipython help qtconsole # show the help for the qtconsole subcmd
71 72
72 73 ipython console # start the terminal-based console application
73 74 ipython help console # show the help for the console subcmd
74 75
75 76 ipython notebook # start the IPython notebook
76 77 ipython help notebook # show the help for the notebook subcmd
77 78
78 79 ipython profile create foo # create profile foo w/ default config files
79 80 ipython help profile # show the help for the profile subcmd
80 81
81 82 ipython locate # print the path to the IPython directory
82 83 ipython locate profile foo # print the path to the directory for profile `foo`
83 84
84 85 ipython nbconvert # convert notebooks to/from other formats
85 86 """
86 87
87 88 #-----------------------------------------------------------------------------
88 89 # Crash handler for this application
89 90 #-----------------------------------------------------------------------------
90 91
91 92 class IPAppCrashHandler(CrashHandler):
92 93 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
93 94
94 95 def __init__(self, app):
95 96 contact_name = release.author
96 97 contact_email = release.author_email
97 98 bug_tracker = 'https://github.com/ipython/ipython/issues'
98 99 super(IPAppCrashHandler,self).__init__(
99 100 app, contact_name, contact_email, bug_tracker
100 101 )
101 102
102 103 def make_report(self,traceback):
103 104 """Return a string containing a crash report."""
104 105
105 106 sec_sep = self.section_sep
106 107 # Start with parent report
107 108 report = [super(IPAppCrashHandler, self).make_report(traceback)]
108 109 # Add interactive-specific info we may have
109 110 rpt_add = report.append
110 111 try:
111 112 rpt_add(sec_sep+"History of session input:")
112 113 for line in self.app.shell.user_ns['_ih']:
113 114 rpt_add(line)
114 115 rpt_add('\n*** Last line of input (may not be in above history):\n')
115 116 rpt_add(self.app.shell._last_input_line+'\n')
116 117 except:
117 118 pass
118 119
119 120 return ''.join(report)
120 121
121 122 #-----------------------------------------------------------------------------
122 123 # Aliases and Flags
123 124 #-----------------------------------------------------------------------------
124 125 flags = dict(base_flags)
125 126 flags.update(shell_flags)
126 127 frontend_flags = {}
127 128 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
128 129 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
129 130 'Turn on auto editing of files with syntax errors.',
130 131 'Turn off auto editing of files with syntax errors.'
131 132 )
132 133 addflag('banner', 'TerminalIPythonApp.display_banner',
133 134 "Display a banner upon starting IPython.",
134 135 "Don't display a banner upon starting IPython."
135 136 )
136 137 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
137 138 """Set to confirm when you try to exit IPython with an EOF (Control-D
138 139 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
139 140 you can force a direct exit without any confirmation.""",
140 141 "Don't prompt the user when exiting."
141 142 )
142 143 addflag('term-title', 'TerminalInteractiveShell.term_title',
143 144 "Enable auto setting the terminal title.",
144 145 "Disable auto setting the terminal title."
145 146 )
146 147 classic_config = Config()
147 148 classic_config.InteractiveShell.cache_size = 0
148 149 classic_config.PlainTextFormatter.pprint = False
149 150 classic_config.PromptManager.in_template = '>>> '
150 151 classic_config.PromptManager.in2_template = '... '
151 152 classic_config.PromptManager.out_template = ''
152 153 classic_config.InteractiveShell.separate_in = ''
153 154 classic_config.InteractiveShell.separate_out = ''
154 155 classic_config.InteractiveShell.separate_out2 = ''
155 156 classic_config.InteractiveShell.colors = 'NoColor'
156 157 classic_config.InteractiveShell.xmode = 'Plain'
157 158
158 159 frontend_flags['classic']=(
159 160 classic_config,
160 161 "Gives IPython a similar feel to the classic Python prompt."
161 162 )
162 163 # # log doesn't make so much sense this way anymore
163 164 # paa('--log','-l',
164 165 # action='store_true', dest='InteractiveShell.logstart',
165 166 # help="Start logging to the default log file (./ipython_log.py).")
166 167 #
167 168 # # quick is harder to implement
168 169 frontend_flags['quick']=(
169 170 {'TerminalIPythonApp' : {'quick' : True}},
170 171 "Enable quick startup with no config files."
171 172 )
172 173
173 174 frontend_flags['i'] = (
174 175 {'TerminalIPythonApp' : {'force_interact' : True}},
175 176 """If running code from the command line, become interactive afterwards.
176 177 Note: can also be given simply as '-i.'"""
177 178 )
178 179 flags.update(frontend_flags)
179 180
180 181 aliases = dict(base_aliases)
181 182 aliases.update(shell_aliases)
182 183
183 184 #-----------------------------------------------------------------------------
184 185 # Main classes and functions
185 186 #-----------------------------------------------------------------------------
186 187
187 188
188 189 class LocateIPythonApp(BaseIPythonApplication):
189 190 description = """print the path to the IPython dir"""
190 191 subcommands = Dict(dict(
191 192 profile=('IPython.core.profileapp.ProfileLocate',
192 193 "print the path to an IPython profile directory",
193 194 ),
194 195 ))
195 196 def start(self):
196 197 if self.subapp is not None:
197 198 return self.subapp.start()
198 199 else:
199 print self.ipython_dir
200 print(self.ipython_dir)
200 201
201 202
202 203 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
203 204 name = u'ipython'
204 205 description = usage.cl_usage
205 206 crash_handler_class = IPAppCrashHandler
206 207 examples = _examples
207 208
208 209 flags = Dict(flags)
209 210 aliases = Dict(aliases)
210 211 classes = List()
211 212 def _classes_default(self):
212 213 """This has to be in a method, for TerminalIPythonApp to be available."""
213 214 return [
214 215 InteractiveShellApp, # ShellApp comes before TerminalApp, because
215 216 self.__class__, # it will also affect subclasses (e.g. QtConsole)
216 217 TerminalInteractiveShell,
217 218 PromptManager,
218 219 HistoryManager,
219 220 ProfileDir,
220 221 PlainTextFormatter,
221 222 IPCompleter,
222 223 ScriptMagics,
223 224 StoreMagics,
224 225 ]
225 226
226 227 subcommands = Dict(dict(
227 228 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
228 229 """Launch the IPython Qt Console."""
229 230 ),
230 231 notebook=('IPython.html.notebookapp.NotebookApp',
231 232 """Launch the IPython HTML Notebook Server."""
232 233 ),
233 234 profile = ("IPython.core.profileapp.ProfileApp",
234 235 "Create and manage IPython profiles."
235 236 ),
236 237 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
237 238 "Start a kernel without an attached frontend."
238 239 ),
239 240 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
240 241 """Launch the IPython terminal-based Console."""
241 242 ),
242 243 locate=('IPython.terminal.ipapp.LocateIPythonApp',
243 244 LocateIPythonApp.description
244 245 ),
245 246 history=('IPython.core.historyapp.HistoryApp',
246 247 "Manage the IPython history database."
247 248 ),
248 249 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
249 250 "Convert notebooks to/from other formats."
250 251 ),
251 252 ))
252 253
253 254 # *do* autocreate requested profile, but don't create the config file.
254 255 auto_create=Bool(True)
255 256 # configurables
256 257 ignore_old_config=Bool(False, config=True,
257 258 help="Suppress warning messages about legacy config files"
258 259 )
259 260 quick = Bool(False, config=True,
260 261 help="""Start IPython quickly by skipping the loading of config files."""
261 262 )
262 263 def _quick_changed(self, name, old, new):
263 264 if new:
264 265 self.load_config_file = lambda *a, **kw: None
265 266 self.ignore_old_config=True
266 267
267 268 display_banner = Bool(True, config=True,
268 269 help="Whether to display a banner upon starting IPython."
269 270 )
270 271
271 272 # if there is code of files to run from the cmd line, don't interact
272 273 # unless the --i flag (App.force_interact) is true.
273 274 force_interact = Bool(False, config=True,
274 275 help="""If a command or file is given via the command-line,
275 276 e.g. 'ipython foo.py"""
276 277 )
277 278 def _force_interact_changed(self, name, old, new):
278 279 if new:
279 280 self.interact = True
280 281
281 282 def _file_to_run_changed(self, name, old, new):
282 283 if new:
283 284 self.something_to_run = True
284 285 if new and not self.force_interact:
285 286 self.interact = False
286 287 _code_to_run_changed = _file_to_run_changed
287 288 _module_to_run_changed = _file_to_run_changed
288 289
289 290 # internal, not-configurable
290 291 interact=Bool(True)
291 292 something_to_run=Bool(False)
292 293
293 294 def parse_command_line(self, argv=None):
294 295 """override to allow old '-pylab' flag with deprecation warning"""
295 296
296 297 argv = sys.argv[1:] if argv is None else argv
297 298
298 299 if '-pylab' in argv:
299 300 # deprecated `-pylab` given,
300 301 # warn and transform into current syntax
301 302 argv = argv[:] # copy, don't clobber
302 303 idx = argv.index('-pylab')
303 304 warn.warn("`-pylab` flag has been deprecated.\n"
304 305 " Use `--matplotlib <backend>` and import pylab manually.")
305 306 argv[idx] = '--pylab'
306 307
307 308 return super(TerminalIPythonApp, self).parse_command_line(argv)
308 309
309 310 @catch_config_error
310 311 def initialize(self, argv=None):
311 312 """Do actions after construct, but before starting the app."""
312 313 super(TerminalIPythonApp, self).initialize(argv)
313 314 if self.subapp is not None:
314 315 # don't bother initializing further, starting subapp
315 316 return
316 317 if not self.ignore_old_config:
317 318 check_for_old_config(self.ipython_dir)
318 319 # print self.extra_args
319 320 if self.extra_args and not self.something_to_run:
320 321 self.file_to_run = self.extra_args[0]
321 322 self.init_path()
322 323 # create the shell
323 324 self.init_shell()
324 325 # and draw the banner
325 326 self.init_banner()
326 327 # Now a variety of things that happen after the banner is printed.
327 328 self.init_gui_pylab()
328 329 self.init_extensions()
329 330 self.init_code()
330 331
331 332 def init_shell(self):
332 333 """initialize the InteractiveShell instance"""
333 334 # Create an InteractiveShell instance.
334 335 # shell.display_banner should always be False for the terminal
335 336 # based app, because we call shell.show_banner() by hand below
336 337 # so the banner shows *before* all extension loading stuff.
337 338 self.shell = TerminalInteractiveShell.instance(parent=self,
338 339 display_banner=False, profile_dir=self.profile_dir,
339 340 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
340 341 self.shell.configurables.append(self)
341 342
342 343 def init_banner(self):
343 344 """optionally display the banner"""
344 345 if self.display_banner and self.interact:
345 346 self.shell.show_banner()
346 347 # Make sure there is a space below the banner.
347 if self.log_level <= logging.INFO: print
348 if self.log_level <= logging.INFO: print()
348 349
349 350 def _pylab_changed(self, name, old, new):
350 351 """Replace --pylab='inline' with --pylab='auto'"""
351 352 if new == 'inline':
352 353 warn.warn("'inline' not available as pylab backend, "
353 354 "using 'auto' instead.")
354 355 self.pylab = 'auto'
355 356
356 357 def start(self):
357 358 if self.subapp is not None:
358 359 return self.subapp.start()
359 360 # perform any prexec steps:
360 361 if self.interact:
361 362 self.log.debug("Starting IPython's mainloop...")
362 363 self.shell.mainloop()
363 364 else:
364 365 self.log.debug("IPython not interactive...")
365 366
366 367
367 368 def load_default_config(ipython_dir=None):
368 369 """Load the default config file from the default ipython_dir.
369 370
370 371 This is useful for embedded shells.
371 372 """
372 373 if ipython_dir is None:
373 374 ipython_dir = get_ipython_dir()
374 375 profile_dir = os.path.join(ipython_dir, 'profile_default')
375 376 cl = PyFileConfigLoader("ipython_config.py", profile_dir)
376 377 try:
377 378 config = cl.load_config()
378 379 except ConfigFileNotFound:
379 380 # no config found
380 381 config = Config()
381 382 return config
382 383
383 384
384 385 launch_new_instance = TerminalIPythonApp.launch_instance
385 386
386 387
387 388 if __name__ == '__main__':
388 389 launch_new_instance()
@@ -1,18 +1,19 b''
1 1 #!/usr/bin/env python
2 2 """Nose-based test runner.
3 3 """
4 from __future__ import print_function
4 5
5 6 from nose.core import main
6 7 from nose.plugins.builtin import plugins
7 8 from nose.plugins.doctests import Doctest
8 9
9 10 from . import ipdoctest
10 11 from .ipdoctest import IPDocTestRunner
11 12
12 13 if __name__ == '__main__':
13 print 'WARNING: this code is incomplete!'
14 print
14 print('WARNING: this code is incomplete!')
15 print()
15 16
16 17 pp = [x() for x in plugins] # activate all builtin plugins first
17 18 main(testRunner=IPDocTestRunner(),
18 19 plugins=pp+[ipdoctest.IPythonDoctest(),Doctest()])
@@ -1,19 +1,20 b''
1 1 """Simple script to show reference holding behavior.
2 2
3 3 This is used by a companion test case.
4 4 """
5 from __future__ import print_function
5 6
6 7 import gc
7 8
8 9 class C(object):
9 10 def __del__(self):
10 11 pass
11 12 #print 'deleting object...' # dbg
12 13
13 14 if __name__ == '__main__':
14 15 c = C()
15 16
16 17 c_refs = gc.get_referrers(c)
17 18 ref_ids = map(id,c_refs)
18 19
19 print 'c referrers:',map(type,c_refs)
20 print('c referrers:',map(type,c_refs))
@@ -1,2 +1,3 b''
1 from __future__ import print_function
1 2 x = 1
2 print 'x is:',x
3 print('x is:',x)
@@ -1,168 +1,169 b''
1 1 """Tests for the decorators we've created for IPython.
2 2 """
3 from __future__ import print_function
3 4
4 5 # Module imports
5 6 # Std lib
6 7 import inspect
7 8 import sys
8 9
9 10 # Third party
10 11 import nose.tools as nt
11 12
12 13 # Our own
13 14 from IPython.testing import decorators as dec
14 15 from IPython.testing.skipdoctest import skip_doctest
15 16
16 17 #-----------------------------------------------------------------------------
17 18 # Utilities
18 19
19 20 # Note: copied from OInspect, kept here so the testing stuff doesn't create
20 21 # circular dependencies and is easier to reuse.
21 22 def getargspec(obj):
22 23 """Get the names and default values of a function's arguments.
23 24
24 25 A tuple of four things is returned: (args, varargs, varkw, defaults).
25 26 'args' is a list of the argument names (it may contain nested lists).
26 27 'varargs' and 'varkw' are the names of the * and ** arguments or None.
27 28 'defaults' is an n-tuple of the default values of the last n arguments.
28 29
29 30 Modified version of inspect.getargspec from the Python Standard
30 31 Library."""
31 32
32 33 if inspect.isfunction(obj):
33 34 func_obj = obj
34 35 elif inspect.ismethod(obj):
35 36 func_obj = obj.im_func
36 37 else:
37 38 raise TypeError('arg is not a Python function')
38 39 args, varargs, varkw = inspect.getargs(func_obj.func_code)
39 40 return args, varargs, varkw, func_obj.func_defaults
40 41
41 42 #-----------------------------------------------------------------------------
42 43 # Testing functions
43 44
44 45 @dec.as_unittest
45 46 def trivial():
46 47 """A trivial test"""
47 48 pass
48 49
49 50
50 51 @dec.skip
51 52 def test_deliberately_broken():
52 53 """A deliberately broken test - we want to skip this one."""
53 54 1/0
54 55
55 56 @dec.skip('Testing the skip decorator')
56 57 def test_deliberately_broken2():
57 58 """Another deliberately broken test - we want to skip this one."""
58 59 1/0
59 60
60 61
61 62 # Verify that we can correctly skip the doctest for a function at will, but
62 63 # that the docstring itself is NOT destroyed by the decorator.
63 64 @skip_doctest
64 65 def doctest_bad(x,y=1,**k):
65 66 """A function whose doctest we need to skip.
66 67
67 68 >>> 1+1
68 69 3
69 70 """
70 print 'x:',x
71 print 'y:',y
72 print 'k:',k
71 print('x:',x)
72 print('y:',y)
73 print('k:',k)
73 74
74 75
75 76 def call_doctest_bad():
76 77 """Check that we can still call the decorated functions.
77 78
78 79 >>> doctest_bad(3,y=4)
79 80 x: 3
80 81 y: 4
81 82 k: {}
82 83 """
83 84 pass
84 85
85 86
86 87 def test_skip_dt_decorator():
87 88 """Doctest-skipping decorator should preserve the docstring.
88 89 """
89 90 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
90 91 check = """A function whose doctest we need to skip.
91 92
92 93 >>> 1+1
93 94 3
94 95 """
95 96 # Fetch the docstring from doctest_bad after decoration.
96 97 val = doctest_bad.__doc__
97 98
98 99 nt.assert_equal(check,val,"doctest_bad docstrings don't match")
99 100
100 101
101 102 # Doctest skipping should work for class methods too
102 103 class FooClass(object):
103 104 """FooClass
104 105
105 106 Example:
106 107
107 108 >>> 1+1
108 109 2
109 110 """
110 111
111 112 @skip_doctest
112 113 def __init__(self,x):
113 114 """Make a FooClass.
114 115
115 116 Example:
116 117
117 118 >>> f = FooClass(3)
118 119 junk
119 120 """
120 print 'Making a FooClass.'
121 print('Making a FooClass.')
121 122 self.x = x
122 123
123 124 @skip_doctest
124 125 def bar(self,y):
125 126 """Example:
126 127
127 128 >>> ff = FooClass(3)
128 129 >>> ff.bar(0)
129 130 boom!
130 131 >>> 1/0
131 132 bam!
132 133 """
133 134 return 1/y
134 135
135 136 def baz(self,y):
136 137 """Example:
137 138
138 139 >>> ff2 = FooClass(3)
139 140 Making a FooClass.
140 141 >>> ff2.baz(3)
141 142 True
142 143 """
143 144 return self.x==y
144 145
145 146
146 147 def test_skip_dt_decorator2():
147 148 """Doctest-skipping decorator should preserve function signature.
148 149 """
149 150 # Hardcoded correct answer
150 151 dtargs = (['x', 'y'], None, 'k', (1,))
151 152 # Introspect out the value
152 153 dtargsr = getargspec(doctest_bad)
153 154 assert dtargsr==dtargs, \
154 155 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
155 156
156 157
157 158 @dec.skip_linux
158 159 def test_linux():
159 160 nt.assert_false(sys.platform.startswith('linux'),"This test can't run under linux")
160 161
161 162 @dec.skip_win32
162 163 def test_win32():
163 164 nt.assert_not_equal(sys.platform,'win32',"This test can't run under windows")
164 165
165 166 @dec.skip_osx
166 167 def test_osx():
167 168 nt.assert_not_equal(sys.platform,'darwin',"This test can't run under osx")
168 169
@@ -1,133 +1,134 b''
1 1 # encoding: utf-8
2 2 """
3 3 Tests for testing.tools
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import with_statement
17 from __future__ import print_function
17 18
18 19 import os
19 20 import unittest
20 21
21 22 import nose.tools as nt
22 23
23 24 from IPython.testing import decorators as dec
24 25 from IPython.testing import tools as tt
25 26
26 27 #-----------------------------------------------------------------------------
27 28 # Tests
28 29 #-----------------------------------------------------------------------------
29 30
30 31 @dec.skip_win32
31 32 def test_full_path_posix():
32 33 spath = '/foo/bar.py'
33 34 result = tt.full_path(spath,['a.txt','b.txt'])
34 35 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
35 36 spath = '/foo'
36 37 result = tt.full_path(spath,['a.txt','b.txt'])
37 38 nt.assert_equal(result, ['/a.txt', '/b.txt'])
38 39 result = tt.full_path(spath,'a.txt')
39 40 nt.assert_equal(result, ['/a.txt'])
40 41
41 42
42 43 @dec.skip_if_not_win32
43 44 def test_full_path_win32():
44 45 spath = 'c:\\foo\\bar.py'
45 46 result = tt.full_path(spath,['a.txt','b.txt'])
46 47 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
47 48 spath = 'c:\\foo'
48 49 result = tt.full_path(spath,['a.txt','b.txt'])
49 50 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
50 51 result = tt.full_path(spath,'a.txt')
51 52 nt.assert_equal(result, ['c:\\a.txt'])
52 53
53 54
54 55 def test_parser():
55 56 err = ("FAILED (errors=1)", 1, 0)
56 57 fail = ("FAILED (failures=1)", 0, 1)
57 58 both = ("FAILED (errors=1, failures=1)", 1, 1)
58 59 for txt, nerr, nfail in [err, fail, both]:
59 60 nerr1, nfail1 = tt.parse_test_output(txt)
60 61 nt.assert_equal(nerr, nerr1)
61 62 nt.assert_equal(nfail, nfail1)
62 63
63 64
64 65 def test_temp_pyfile():
65 66 src = 'pass\n'
66 67 fname, fh = tt.temp_pyfile(src)
67 68 assert os.path.isfile(fname)
68 69 fh.close()
69 70 with open(fname) as fh2:
70 71 src2 = fh2.read()
71 72 nt.assert_equal(src2, src)
72 73
73 74 class TestAssertPrints(unittest.TestCase):
74 75 def test_passing(self):
75 76 with tt.AssertPrints("abc"):
76 print "abcd"
77 print "def"
78 print b"ghi"
77 print("abcd")
78 print("def")
79 print(b"ghi")
79 80
80 81 def test_failing(self):
81 82 def func():
82 83 with tt.AssertPrints("abc"):
83 print "acd"
84 print "def"
85 print b"ghi"
84 print("acd")
85 print("def")
86 print(b"ghi")
86 87
87 88 self.assertRaises(AssertionError, func)
88 89
89 90
90 91 class Test_ipexec_validate(unittest.TestCase, tt.TempFileMixin):
91 92 def test_main_path(self):
92 93 """Test with only stdout results.
93 94 """
94 95 self.mktmp("print('A')\n"
95 96 "print('B')\n"
96 97 )
97 98 out = "A\nB"
98 99 tt.ipexec_validate(self.fname, out)
99 100
100 101 def test_main_path2(self):
101 102 """Test with only stdout results, expecting windows line endings.
102 103 """
103 104 self.mktmp("print('A')\n"
104 105 "print('B')\n"
105 106 )
106 107 out = "A\r\nB"
107 108 tt.ipexec_validate(self.fname, out)
108 109
109 110 def test_exception_path(self):
110 111 """Test exception path in exception_validate.
111 112 """
112 113 self.mktmp("from __future__ import print_function\n"
113 114 "import sys\n"
114 115 "print('A')\n"
115 116 "print('B')\n"
116 117 "print('C', file=sys.stderr)\n"
117 118 "print('D', file=sys.stderr)\n"
118 119 )
119 120 out = "A\nB"
120 121 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
121 122
122 123 def test_exception_path2(self):
123 124 """Test exception path in exception_validate, expecting windows line endings.
124 125 """
125 126 self.mktmp("from __future__ import print_function\n"
126 127 "import sys\n"
127 128 "print('A')\n"
128 129 "print('B')\n"
129 130 "print('C', file=sys.stderr)\n"
130 131 "print('D', file=sys.stderr)\n"
131 132 )
132 133 out = "A\r\nB"
133 134 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
@@ -1,438 +1,439 b''
1 1 """Patched version of standard library tokenize, to deal with various bugs.
2 2
3 3 Patches
4 4
5 5 - Relevant parts of Gareth Rees' patch for Python issue #12691 (untokenizing),
6 6 manually applied.
7 7 - Newlines in comments and blank lines should be either NL or NEWLINE, depending
8 8 on whether they are in a multi-line statement. Filed as Python issue #17061.
9 9
10 10 -------------------------------------------------------------------------------
11 11 Tokenization help for Python programs.
12 12
13 13 generate_tokens(readline) is a generator that breaks a stream of
14 14 text into Python tokens. It accepts a readline-like method which is called
15 15 repeatedly to get the next line of input (or "" for EOF). It generates
16 16 5-tuples with these members:
17 17
18 18 the token type (see token.py)
19 19 the token (a string)
20 20 the starting (row, column) indices of the token (a 2-tuple of ints)
21 21 the ending (row, column) indices of the token (a 2-tuple of ints)
22 22 the original line (string)
23 23
24 24 It is designed to match the working of the Python tokenizer exactly, except
25 25 that it produces COMMENT tokens for comments and gives type OP for all
26 26 operators
27 27
28 28 Older entry points
29 29 tokenize_loop(readline, tokeneater)
30 30 tokenize(readline, tokeneater=printtoken)
31 31 are the same, except instead of generating tokens, tokeneater is a callback
32 32 function to which the 5 fields described above are passed as 5 arguments,
33 33 each time a new token is found."""
34 from __future__ import print_function
34 35
35 36 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
36 37 __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, '
37 38 'Skip Montanaro, Raymond Hettinger')
38 39
39 40 import string, re
40 41 from token import *
41 42
42 43 import token
43 44 __all__ = [x for x in dir(token) if not x.startswith("_")]
44 45 __all__ += ["COMMENT", "tokenize", "generate_tokens", "NL", "untokenize"]
45 46 del x
46 47 del token
47 48
48 49 __all__ += ["TokenError"]
49 50
50 51 COMMENT = N_TOKENS
51 52 tok_name[COMMENT] = 'COMMENT'
52 53 NL = N_TOKENS + 1
53 54 tok_name[NL] = 'NL'
54 55 N_TOKENS += 2
55 56
56 57 def group(*choices): return '(' + '|'.join(choices) + ')'
57 58 def any(*choices): return group(*choices) + '*'
58 59 def maybe(*choices): return group(*choices) + '?'
59 60
60 61 Whitespace = r'[ \f\t]*'
61 62 Comment = r'#[^\r\n]*'
62 63 Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment)
63 64 Name = r'[a-zA-Z_]\w*'
64 65
65 66 Hexnumber = r'0[xX][\da-fA-F]+[lL]?'
66 67 Octnumber = r'(0[oO][0-7]+)|(0[0-7]*)[lL]?'
67 68 Binnumber = r'0[bB][01]+[lL]?'
68 69 Decnumber = r'[1-9]\d*[lL]?'
69 70 Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber)
70 71 Exponent = r'[eE][-+]?\d+'
71 72 Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
72 73 Expfloat = r'\d+' + Exponent
73 74 Floatnumber = group(Pointfloat, Expfloat)
74 75 Imagnumber = group(r'\d+[jJ]', Floatnumber + r'[jJ]')
75 76 Number = group(Imagnumber, Floatnumber, Intnumber)
76 77
77 78 # Tail end of ' string.
78 79 Single = r"[^'\\]*(?:\\.[^'\\]*)*'"
79 80 # Tail end of " string.
80 81 Double = r'[^"\\]*(?:\\.[^"\\]*)*"'
81 82 # Tail end of ''' string.
82 83 Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
83 84 # Tail end of """ string.
84 85 Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
85 86 Triple = group("[uUbB]?[rR]?'''", '[uUbB]?[rR]?"""')
86 87 # Single-line ' or " string.
87 88 String = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
88 89 r'[uUbB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
89 90
90 91 # Because of leftmost-then-longest match semantics, be sure to put the
91 92 # longest operators first (e.g., if = came before ==, == would get
92 93 # recognized as two instances of =).
93 94 Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=",
94 95 r"//=?",
95 96 r"[+\-*/%&|^=<>]=?",
96 97 r"~")
97 98
98 99 Bracket = '[][(){}]'
99 100 Special = group(r'\r?\n', r'[:;.,`@]')
100 101 Funny = group(Operator, Bracket, Special)
101 102
102 103 PlainToken = group(Number, Funny, String, Name)
103 104 Token = Ignore + PlainToken
104 105
105 106 # First (or only) line of ' or " string.
106 107 ContStr = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
107 108 group("'", r'\\\r?\n'),
108 109 r'[uUbB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
109 110 group('"', r'\\\r?\n'))
110 111 PseudoExtras = group(r'\\\r?\n', Comment, Triple)
111 112 PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
112 113
113 114 tokenprog, pseudoprog, single3prog, double3prog = map(
114 115 re.compile, (Token, PseudoToken, Single3, Double3))
115 116 endprogs = {"'": re.compile(Single), '"': re.compile(Double),
116 117 "'''": single3prog, '"""': double3prog,
117 118 "r'''": single3prog, 'r"""': double3prog,
118 119 "u'''": single3prog, 'u"""': double3prog,
119 120 "ur'''": single3prog, 'ur"""': double3prog,
120 121 "R'''": single3prog, 'R"""': double3prog,
121 122 "U'''": single3prog, 'U"""': double3prog,
122 123 "uR'''": single3prog, 'uR"""': double3prog,
123 124 "Ur'''": single3prog, 'Ur"""': double3prog,
124 125 "UR'''": single3prog, 'UR"""': double3prog,
125 126 "b'''": single3prog, 'b"""': double3prog,
126 127 "br'''": single3prog, 'br"""': double3prog,
127 128 "B'''": single3prog, 'B"""': double3prog,
128 129 "bR'''": single3prog, 'bR"""': double3prog,
129 130 "Br'''": single3prog, 'Br"""': double3prog,
130 131 "BR'''": single3prog, 'BR"""': double3prog,
131 132 'r': None, 'R': None, 'u': None, 'U': None,
132 133 'b': None, 'B': None}
133 134
134 135 triple_quoted = {}
135 136 for t in ("'''", '"""',
136 137 "r'''", 'r"""', "R'''", 'R"""',
137 138 "u'''", 'u"""', "U'''", 'U"""',
138 139 "ur'''", 'ur"""', "Ur'''", 'Ur"""',
139 140 "uR'''", 'uR"""', "UR'''", 'UR"""',
140 141 "b'''", 'b"""', "B'''", 'B"""',
141 142 "br'''", 'br"""', "Br'''", 'Br"""',
142 143 "bR'''", 'bR"""', "BR'''", 'BR"""'):
143 144 triple_quoted[t] = t
144 145 single_quoted = {}
145 146 for t in ("'", '"',
146 147 "r'", 'r"', "R'", 'R"',
147 148 "u'", 'u"', "U'", 'U"',
148 149 "ur'", 'ur"', "Ur'", 'Ur"',
149 150 "uR'", 'uR"', "UR'", 'UR"',
150 151 "b'", 'b"', "B'", 'B"',
151 152 "br'", 'br"', "Br'", 'Br"',
152 153 "bR'", 'bR"', "BR'", 'BR"' ):
153 154 single_quoted[t] = t
154 155
155 156 tabsize = 8
156 157
157 158 class TokenError(Exception): pass
158 159
159 160 class StopTokenizing(Exception): pass
160 161
161 162 def printtoken(type, token, srow_scol, erow_ecol, line): # for testing
162 163 srow, scol = srow_scol
163 164 erow, ecol = erow_ecol
164 print "%d,%d-%d,%d:\t%s\t%s" % \
165 (srow, scol, erow, ecol, tok_name[type], repr(token))
165 print("%d,%d-%d,%d:\t%s\t%s" % \
166 (srow, scol, erow, ecol, tok_name[type], repr(token)))
166 167
167 168 def tokenize(readline, tokeneater=printtoken):
168 169 """
169 170 The tokenize() function accepts two parameters: one representing the
170 171 input stream, and one providing an output mechanism for tokenize().
171 172
172 173 The first parameter, readline, must be a callable object which provides
173 174 the same interface as the readline() method of built-in file objects.
174 175 Each call to the function should return one line of input as a string.
175 176
176 177 The second parameter, tokeneater, must also be a callable object. It is
177 178 called once for each token, with five arguments, corresponding to the
178 179 tuples generated by generate_tokens().
179 180 """
180 181 try:
181 182 tokenize_loop(readline, tokeneater)
182 183 except StopTokenizing:
183 184 pass
184 185
185 186 # backwards compatible interface
186 187 def tokenize_loop(readline, tokeneater):
187 188 for token_info in generate_tokens(readline):
188 189 tokeneater(*token_info)
189 190
190 191 class Untokenizer:
191 192
192 193 def __init__(self):
193 194 self.tokens = []
194 195 self.prev_row = 1
195 196 self.prev_col = 0
196 197
197 198 def add_whitespace(self, start):
198 199 row, col = start
199 200 assert row >= self.prev_row
200 201 col_offset = col - self.prev_col
201 202 if col_offset > 0:
202 203 self.tokens.append(" " * col_offset)
203 204 elif row > self.prev_row and tok_type not in (NEWLINE, NL, ENDMARKER):
204 205 # Line was backslash-continued
205 206 self.tokens.append(" ")
206 207
207 208 def untokenize(self, tokens):
208 209 iterable = iter(tokens)
209 210 for t in iterable:
210 211 if len(t) == 2:
211 212 self.compat(t, iterable)
212 213 break
213 214 tok_type, token, start, end = t[:4]
214 215 self.add_whitespace(start)
215 216 self.tokens.append(token)
216 217 self.prev_row, self.prev_col = end
217 218 if tok_type in (NEWLINE, NL):
218 219 self.prev_row += 1
219 220 self.prev_col = 0
220 221 return "".join(self.tokens)
221 222
222 223 def compat(self, token, iterable):
223 224 # This import is here to avoid problems when the itertools
224 225 # module is not built yet and tokenize is imported.
225 226 from itertools import chain
226 227 startline = False
227 228 prevstring = False
228 229 indents = []
229 230 toks_append = self.tokens.append
230 231 for tok in chain([token], iterable):
231 232 toknum, tokval = tok[:2]
232 233
233 234 if toknum in (NAME, NUMBER):
234 235 tokval += ' '
235 236
236 237 # Insert a space between two consecutive strings
237 238 if toknum == STRING:
238 239 if prevstring:
239 240 tokval = ' ' + tokval
240 241 prevstring = True
241 242 else:
242 243 prevstring = False
243 244
244 245 if toknum == INDENT:
245 246 indents.append(tokval)
246 247 continue
247 248 elif toknum == DEDENT:
248 249 indents.pop()
249 250 continue
250 251 elif toknum in (NEWLINE, NL):
251 252 startline = True
252 253 elif startline and indents:
253 254 toks_append(indents[-1])
254 255 startline = False
255 256 toks_append(tokval)
256 257
257 258 def untokenize(iterable):
258 259 """Transform tokens back into Python source code.
259 260
260 261 Each element returned by the iterable must be a token sequence
261 262 with at least two elements, a token number and token value. If
262 263 only two tokens are passed, the resulting output is poor.
263 264
264 265 Round-trip invariant for full input:
265 266 Untokenized source will match input source exactly
266 267
267 268 Round-trip invariant for limited intput:
268 269 # Output text will tokenize the back to the input
269 270 t1 = [tok[:2] for tok in generate_tokens(f.readline)]
270 271 newcode = untokenize(t1)
271 272 readline = iter(newcode.splitlines(1)).next
272 273 t2 = [tok[:2] for tok in generate_tokens(readline)]
273 274 assert t1 == t2
274 275 """
275 276 ut = Untokenizer()
276 277 return ut.untokenize(iterable)
277 278
278 279 def generate_tokens(readline):
279 280 """
280 281 The generate_tokens() generator requires one argment, readline, which
281 282 must be a callable object which provides the same interface as the
282 283 readline() method of built-in file objects. Each call to the function
283 284 should return one line of input as a string. Alternately, readline
284 285 can be a callable function terminating with StopIteration:
285 286 readline = open(myfile).next # Example of alternate readline
286 287
287 288 The generator produces 5-tuples with these members: the token type; the
288 289 token string; a 2-tuple (srow, scol) of ints specifying the row and
289 290 column where the token begins in the source; a 2-tuple (erow, ecol) of
290 291 ints specifying the row and column where the token ends in the source;
291 292 and the line on which the token was found. The line passed is the
292 293 logical line; continuation lines are included.
293 294 """
294 295 lnum = parenlev = continued = 0
295 296 namechars, numchars = string.ascii_letters + '_', '0123456789'
296 297 contstr, needcont = '', 0
297 298 contline = None
298 299 indents = [0]
299 300
300 301 while 1: # loop over lines in stream
301 302 try:
302 303 line = readline()
303 304 except StopIteration:
304 305 line = ''
305 306 lnum += 1
306 307 pos, max = 0, len(line)
307 308
308 309 if contstr: # continued string
309 310 if not line:
310 311 raise TokenError, ("EOF in multi-line string", strstart)
311 312 endmatch = endprog.match(line)
312 313 if endmatch:
313 314 pos = end = endmatch.end(0)
314 315 yield (STRING, contstr + line[:end],
315 316 strstart, (lnum, end), contline + line)
316 317 contstr, needcont = '', 0
317 318 contline = None
318 319 elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n':
319 320 yield (ERRORTOKEN, contstr + line,
320 321 strstart, (lnum, len(line)), contline)
321 322 contstr = ''
322 323 contline = None
323 324 continue
324 325 else:
325 326 contstr = contstr + line
326 327 contline = contline + line
327 328 continue
328 329
329 330 elif parenlev == 0 and not continued: # new statement
330 331 if not line: break
331 332 column = 0
332 333 while pos < max: # measure leading whitespace
333 334 if line[pos] == ' ':
334 335 column += 1
335 336 elif line[pos] == '\t':
336 337 column = (column//tabsize + 1)*tabsize
337 338 elif line[pos] == '\f':
338 339 column = 0
339 340 else:
340 341 break
341 342 pos += 1
342 343 if pos == max:
343 344 break
344 345
345 346 if line[pos] in '#\r\n': # skip comments or blank lines
346 347 if line[pos] == '#':
347 348 comment_token = line[pos:].rstrip('\r\n')
348 349 nl_pos = pos + len(comment_token)
349 350 yield (COMMENT, comment_token,
350 351 (lnum, pos), (lnum, pos + len(comment_token)), line)
351 352 yield (NEWLINE, line[nl_pos:],
352 353 (lnum, nl_pos), (lnum, len(line)), line)
353 354 else:
354 355 yield (NEWLINE, line[pos:],
355 356 (lnum, pos), (lnum, len(line)), line)
356 357 continue
357 358
358 359 if column > indents[-1]: # count indents or dedents
359 360 indents.append(column)
360 361 yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line)
361 362 while column < indents[-1]:
362 363 if column not in indents:
363 364 raise IndentationError(
364 365 "unindent does not match any outer indentation level",
365 366 ("<tokenize>", lnum, pos, line))
366 367 indents = indents[:-1]
367 368 yield (DEDENT, '', (lnum, pos), (lnum, pos), line)
368 369
369 370 else: # continued statement
370 371 if not line:
371 372 raise TokenError, ("EOF in multi-line statement", (lnum, 0))
372 373 continued = 0
373 374
374 375 while pos < max:
375 376 pseudomatch = pseudoprog.match(line, pos)
376 377 if pseudomatch: # scan for tokens
377 378 start, end = pseudomatch.span(1)
378 379 spos, epos, pos = (lnum, start), (lnum, end), end
379 380 token, initial = line[start:end], line[start]
380 381
381 382 if initial in numchars or \
382 383 (initial == '.' and token != '.'): # ordinary number
383 384 yield (NUMBER, token, spos, epos, line)
384 385 elif initial in '\r\n':
385 386 yield (NL if parenlev > 0 else NEWLINE,
386 387 token, spos, epos, line)
387 388 elif initial == '#':
388 389 assert not token.endswith("\n")
389 390 yield (COMMENT, token, spos, epos, line)
390 391 elif token in triple_quoted:
391 392 endprog = endprogs[token]
392 393 endmatch = endprog.match(line, pos)
393 394 if endmatch: # all on one line
394 395 pos = endmatch.end(0)
395 396 token = line[start:pos]
396 397 yield (STRING, token, spos, (lnum, pos), line)
397 398 else:
398 399 strstart = (lnum, start) # multiple lines
399 400 contstr = line[start:]
400 401 contline = line
401 402 break
402 403 elif initial in single_quoted or \
403 404 token[:2] in single_quoted or \
404 405 token[:3] in single_quoted:
405 406 if token[-1] == '\n': # continued string
406 407 strstart = (lnum, start)
407 408 endprog = (endprogs[initial] or endprogs[token[1]] or
408 409 endprogs[token[2]])
409 410 contstr, needcont = line[start:], 1
410 411 contline = line
411 412 break
412 413 else: # ordinary string
413 414 yield (STRING, token, spos, epos, line)
414 415 elif initial in namechars: # ordinary name
415 416 yield (NAME, token, spos, epos, line)
416 417 elif initial == '\\': # continued stmt
417 418 continued = 1
418 419 else:
419 420 if initial in '([{':
420 421 parenlev += 1
421 422 elif initial in ')]}':
422 423 parenlev -= 1
423 424 yield (OP, token, spos, epos, line)
424 425 else:
425 426 yield (ERRORTOKEN, line[pos],
426 427 (lnum, pos), (lnum, pos+1), line)
427 428 pos += 1
428 429
429 430 for indent in indents[1:]: # pop remaining indent levels
430 431 yield (DEDENT, '', (lnum, 0), (lnum, 0), '')
431 432 yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '')
432 433
433 434 if __name__ == '__main__': # testing
434 435 import sys
435 436 if len(sys.argv) > 1:
436 437 tokenize(open(sys.argv[1]).readline)
437 438 else:
438 439 tokenize(sys.stdin.readline)
@@ -1,94 +1,95 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for working with stack frames.
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 #-----------------------------------------------------------------------------
14 15 # Imports
15 16 #-----------------------------------------------------------------------------
16 17
17 18 import sys
18 19 from IPython.utils import py3compat
19 20
20 21 #-----------------------------------------------------------------------------
21 22 # Code
22 23 #-----------------------------------------------------------------------------
23 24
24 25 @py3compat.doctest_refactor_print
25 26 def extract_vars(*names,**kw):
26 27 """Extract a set of variables by name from another frame.
27 28
28 29 :Parameters:
29 30 - `*names`: strings
30 31 One or more variable names which will be extracted from the caller's
31 32 frame.
32 33
33 34 :Keywords:
34 35 - `depth`: integer (0)
35 36 How many frames in the stack to walk when looking for your variables.
36 37
37 38
38 39 Examples:
39 40
40 41 In [2]: def func(x):
41 42 ...: y = 1
42 43 ...: print sorted(extract_vars('x','y').items())
43 44 ...:
44 45
45 46 In [3]: func('hello')
46 47 [('x', 'hello'), ('y', 1)]
47 48 """
48 49
49 50 depth = kw.get('depth',0)
50 51
51 52 callerNS = sys._getframe(depth+1).f_locals
52 53 return dict((k,callerNS[k]) for k in names)
53 54
54 55
55 56 def extract_vars_above(*names):
56 57 """Extract a set of variables by name from another frame.
57 58
58 59 Similar to extractVars(), but with a specified depth of 1, so that names
59 60 are exctracted exactly from above the caller.
60 61
61 62 This is simply a convenience function so that the very common case (for us)
62 63 of skipping exactly 1 frame doesn't have to construct a special dict for
63 64 keyword passing."""
64 65
65 66 callerNS = sys._getframe(2).f_locals
66 67 return dict((k,callerNS[k]) for k in names)
67 68
68 69
69 70 def debugx(expr,pre_msg=''):
70 71 """Print the value of an expression from the caller's frame.
71 72
72 73 Takes an expression, evaluates it in the caller's frame and prints both
73 74 the given expression and the resulting value (as well as a debug mark
74 75 indicating the name of the calling function. The input must be of a form
75 76 suitable for eval().
76 77
77 78 An optional message can be passed, which will be prepended to the printed
78 79 expr->value pair."""
79 80
80 81 cf = sys._getframe(1)
81 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
82 eval(expr,cf.f_globals,cf.f_locals))
82 print('[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
83 eval(expr,cf.f_globals,cf.f_locals)))
83 84
84 85
85 86 # deactivate it by uncommenting the following line, which makes it a no-op
86 87 #def debugx(expr,pre_msg=''): pass
87 88
88 89 def extract_module_locals(depth=0):
89 90 """Returns (module, locals) of the funciton `depth` frames away from the caller"""
90 91 f = sys._getframe(depth + 1)
91 92 global_ns = f.f_globals
92 93 module = sys.modules[global_ns['__name__']]
93 94 return (module, f.f_locals)
94 95
@@ -1,366 +1,367 b''
1 1 #!/usr/bin/env python
2 2
3 3 """ PickleShare - a small 'shelve' like datastore with concurrency support
4 4
5 5 Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
6 6 shelve, many processes can access the database simultaneously. Changing a
7 7 value in database is immediately visible to other processes accessing the
8 8 same database.
9 9
10 10 Concurrency is possible because the values are stored in separate files. Hence
11 11 the "database" is a directory where *all* files are governed by PickleShare.
12 12
13 13 Example usage::
14 14
15 15 from pickleshare import *
16 16 db = PickleShareDB('~/testpickleshare')
17 17 db.clear()
18 18 print "Should be empty:",db.items()
19 19 db['hello'] = 15
20 20 db['aku ankka'] = [1,2,313]
21 21 db['paths/are/ok/key'] = [1,(5,46)]
22 22 print db.keys()
23 23 del db['aku ankka']
24 24
25 25 This module is certainly not ZODB, but can be used for low-load
26 26 (non-mission-critical) situations where tiny code size trumps the
27 27 advanced features of a "real" object database.
28 28
29 29 Installation guide: easy_install pickleshare
30 30
31 31 Author: Ville Vainio <vivainio@gmail.com>
32 32 License: MIT open source license.
33 33
34 34 """
35 from __future__ import print_function
35 36
36 37 from IPython.external.path import path as Path
37 38 import os,stat,time
38 39 import collections
39 40 import cPickle as pickle
40 41 import glob
41 42
42 43 def gethashfile(key):
43 44 return ("%02x" % abs(hash(key) % 256))[-2:]
44 45
45 46 _sentinel = object()
46 47
47 48 class PickleShareDB(collections.MutableMapping):
48 49 """ The main 'connection' object for PickleShare database """
49 50 def __init__(self,root):
50 51 """ Return a db object that will manage the specied directory"""
51 52 self.root = Path(root).expanduser().abspath()
52 53 if not self.root.isdir():
53 54 self.root.makedirs()
54 55 # cache has { 'key' : (obj, orig_mod_time) }
55 56 self.cache = {}
56 57
57 58
58 59 def __getitem__(self,key):
59 60 """ db['key'] reading """
60 61 fil = self.root / key
61 62 try:
62 63 mtime = (fil.stat()[stat.ST_MTIME])
63 64 except OSError:
64 65 raise KeyError(key)
65 66
66 67 if fil in self.cache and mtime == self.cache[fil][1]:
67 68 return self.cache[fil][0]
68 69 try:
69 70 # The cached item has expired, need to read
70 71 with fil.open("rb") as f:
71 72 obj = pickle.loads(f.read())
72 73 except:
73 74 raise KeyError(key)
74 75
75 76 self.cache[fil] = (obj,mtime)
76 77 return obj
77 78
78 79 def __setitem__(self,key,value):
79 80 """ db['key'] = 5 """
80 81 fil = self.root / key
81 82 parent = fil.parent
82 83 if parent and not parent.isdir():
83 84 parent.makedirs()
84 85 # We specify protocol 2, so that we can mostly go between Python 2
85 86 # and Python 3. We can upgrade to protocol 3 when Python 2 is obsolete.
86 87 with fil.open('wb') as f:
87 88 pickled = pickle.dump(value, f, protocol=2)
88 89 try:
89 90 self.cache[fil] = (value,fil.mtime)
90 91 except OSError as e:
91 92 if e.errno != 2:
92 93 raise
93 94
94 95 def hset(self, hashroot, key, value):
95 96 """ hashed set """
96 97 hroot = self.root / hashroot
97 98 if not hroot.isdir():
98 99 hroot.makedirs()
99 100 hfile = hroot / gethashfile(key)
100 101 d = self.get(hfile, {})
101 102 d.update( {key : value})
102 103 self[hfile] = d
103 104
104 105
105 106
106 107 def hget(self, hashroot, key, default = _sentinel, fast_only = True):
107 108 """ hashed get """
108 109 hroot = self.root / hashroot
109 110 hfile = hroot / gethashfile(key)
110 111
111 112 d = self.get(hfile, _sentinel )
112 113 #print "got dict",d,"from",hfile
113 114 if d is _sentinel:
114 115 if fast_only:
115 116 if default is _sentinel:
116 117 raise KeyError(key)
117 118
118 119 return default
119 120
120 121 # slow mode ok, works even after hcompress()
121 122 d = self.hdict(hashroot)
122 123
123 124 return d.get(key, default)
124 125
125 126 def hdict(self, hashroot):
126 127 """ Get all data contained in hashed category 'hashroot' as dict """
127 128 hfiles = self.keys(hashroot + "/*")
128 129 hfiles.sort()
129 130 last = len(hfiles) and hfiles[-1] or ''
130 131 if last.endswith('xx'):
131 132 # print "using xx"
132 133 hfiles = [last] + hfiles[:-1]
133 134
134 135 all = {}
135 136
136 137 for f in hfiles:
137 138 # print "using",f
138 139 try:
139 140 all.update(self[f])
140 141 except KeyError:
141 print "Corrupt",f,"deleted - hset is not threadsafe!"
142 print("Corrupt",f,"deleted - hset is not threadsafe!")
142 143 del self[f]
143 144
144 145 self.uncache(f)
145 146
146 147 return all
147 148
148 149 def hcompress(self, hashroot):
149 150 """ Compress category 'hashroot', so hset is fast again
150 151
151 152 hget will fail if fast_only is True for compressed items (that were
152 153 hset before hcompress).
153 154
154 155 """
155 156 hfiles = self.keys(hashroot + "/*")
156 157 all = {}
157 158 for f in hfiles:
158 159 # print "using",f
159 160 all.update(self[f])
160 161 self.uncache(f)
161 162
162 163 self[hashroot + '/xx'] = all
163 164 for f in hfiles:
164 165 p = self.root / f
165 166 if p.basename() == 'xx':
166 167 continue
167 168 p.remove()
168 169
169 170
170 171
171 172 def __delitem__(self,key):
172 173 """ del db["key"] """
173 174 fil = self.root / key
174 175 self.cache.pop(fil,None)
175 176 try:
176 177 fil.remove()
177 178 except OSError:
178 179 # notfound and permission denied are ok - we
179 180 # lost, the other process wins the conflict
180 181 pass
181 182
182 183 def _normalized(self, p):
183 184 """ Make a key suitable for user's eyes """
184 185 return str(self.root.relpathto(p)).replace('\\','/')
185 186
186 187 def keys(self, globpat = None):
187 188 """ All keys in DB, or all keys matching a glob"""
188 189
189 190 if globpat is None:
190 191 files = self.root.walkfiles()
191 192 else:
192 193 files = [Path(p) for p in glob.glob(self.root/globpat)]
193 194 return [self._normalized(p) for p in files if p.isfile()]
194 195
195 196 def __iter__(self):
196 197 return iter(self.keys())
197 198
198 199 def __len__(self):
199 200 return len(self.keys())
200 201
201 202 def uncache(self,*items):
202 203 """ Removes all, or specified items from cache
203 204
204 205 Use this after reading a large amount of large objects
205 206 to free up memory, when you won't be needing the objects
206 207 for a while.
207 208
208 209 """
209 210 if not items:
210 211 self.cache = {}
211 212 for it in items:
212 213 self.cache.pop(it,None)
213 214
214 215 def waitget(self,key, maxwaittime = 60 ):
215 216 """ Wait (poll) for a key to get a value
216 217
217 218 Will wait for `maxwaittime` seconds before raising a KeyError.
218 219 The call exits normally if the `key` field in db gets a value
219 220 within the timeout period.
220 221
221 222 Use this for synchronizing different processes or for ensuring
222 223 that an unfortunately timed "db['key'] = newvalue" operation
223 224 in another process (which causes all 'get' operation to cause a
224 225 KeyError for the duration of pickling) won't screw up your program
225 226 logic.
226 227 """
227 228
228 229 wtimes = [0.2] * 3 + [0.5] * 2 + [1]
229 230 tries = 0
230 231 waited = 0
231 232 while 1:
232 233 try:
233 234 val = self[key]
234 235 return val
235 236 except KeyError:
236 237 pass
237 238
238 239 if waited > maxwaittime:
239 240 raise KeyError(key)
240 241
241 242 time.sleep(wtimes[tries])
242 243 waited+=wtimes[tries]
243 244 if tries < len(wtimes) -1:
244 245 tries+=1
245 246
246 247 def getlink(self,folder):
247 248 """ Get a convenient link for accessing items """
248 249 return PickleShareLink(self, folder)
249 250
250 251 def __repr__(self):
251 252 return "PickleShareDB('%s')" % self.root
252 253
253 254
254 255
255 256 class PickleShareLink:
256 257 """ A shortdand for accessing nested PickleShare data conveniently.
257 258
258 259 Created through PickleShareDB.getlink(), example::
259 260
260 261 lnk = db.getlink('myobjects/test')
261 262 lnk.foo = 2
262 263 lnk.bar = lnk.foo + 5
263 264
264 265 """
265 266 def __init__(self, db, keydir ):
266 267 self.__dict__.update(locals())
267 268
268 269 def __getattr__(self,key):
269 270 return self.__dict__['db'][self.__dict__['keydir']+'/' + key]
270 271 def __setattr__(self,key,val):
271 272 self.db[self.keydir+'/' + key] = val
272 273 def __repr__(self):
273 274 db = self.__dict__['db']
274 275 keys = db.keys( self.__dict__['keydir'] +"/*")
275 276 return "<PickleShareLink '%s': %s>" % (
276 277 self.__dict__['keydir'],
277 278 ";".join([Path(k).basename() for k in keys]))
278 279
279 280
280 281 def test():
281 282 db = PickleShareDB('~/testpickleshare')
282 283 db.clear()
283 print "Should be empty:",db.items()
284 print("Should be empty:",db.items())
284 285 db['hello'] = 15
285 286 db['aku ankka'] = [1,2,313]
286 287 db['paths/nest/ok/keyname'] = [1,(5,46)]
287 288 db.hset('hash', 'aku', 12)
288 289 db.hset('hash', 'ankka', 313)
289 print "12 =",db.hget('hash','aku')
290 print "313 =",db.hget('hash','ankka')
291 print "all hashed",db.hdict('hash')
292 print db.keys()
293 print db.keys('paths/nest/ok/k*')
294 print dict(db) # snapsot of whole db
290 print("12 =",db.hget('hash','aku'))
291 print("313 =",db.hget('hash','ankka'))
292 print("all hashed",db.hdict('hash'))
293 print(db.keys())
294 print(db.keys('paths/nest/ok/k*'))
295 print(dict(db)) # snapsot of whole db
295 296 db.uncache() # frees memory, causes re-reads later
296 297
297 298 # shorthand for accessing deeply nested files
298 299 lnk = db.getlink('myobjects/test')
299 300 lnk.foo = 2
300 301 lnk.bar = lnk.foo + 5
301 print lnk.bar # 7
302 print(lnk.bar) # 7
302 303
303 304 def stress():
304 305 db = PickleShareDB('~/fsdbtest')
305 306 import time,sys
306 307 for i in range(1000):
307 308 for j in range(1000):
308 309 if i % 15 == 0 and i < 200:
309 310 if str(j) in db:
310 311 del db[str(j)]
311 312 continue
312 313
313 314 if j%33 == 0:
314 315 time.sleep(0.02)
315 316
316 317 db[str(j)] = db.get(str(j), []) + [(i,j,"proc %d" % os.getpid())]
317 318 db.hset('hash',j, db.hget('hash',j,15) + 1 )
318 319
319 print i,
320 print(i, end=' ')
320 321 sys.stdout.flush()
321 322 if i % 10 == 0:
322 323 db.uncache()
323 324
324 325 def main():
325 326 import textwrap
326 327 usage = textwrap.dedent("""\
327 328 pickleshare - manage PickleShare databases
328 329
329 330 Usage:
330 331
331 332 pickleshare dump /path/to/db > dump.txt
332 333 pickleshare load /path/to/db < dump.txt
333 334 pickleshare test /path/to/db
334 335 """)
335 336 DB = PickleShareDB
336 337 import sys
337 338 if len(sys.argv) < 2:
338 print usage
339 print(usage)
339 340 return
340 341
341 342 cmd = sys.argv[1]
342 343 args = sys.argv[2:]
343 344 if cmd == 'dump':
344 345 if not args: args= ['.']
345 346 db = DB(args[0])
346 347 import pprint
347 348 pprint.pprint(db.items())
348 349 elif cmd == 'load':
349 350 cont = sys.stdin.read()
350 351 db = DB(args[0])
351 352 data = eval(cont)
352 353 db.clear()
353 354 for k,v in db.items():
354 355 db[k] = v
355 356 elif cmd == 'testwait':
356 357 db = DB(args[0])
357 358 db.clear()
358 print db.waitget('250')
359 print(db.waitget('250'))
359 360 elif cmd == 'test':
360 361 test()
361 362 stress()
362 363
363 364 if __name__== "__main__":
364 365 main()
365 366
366 367
@@ -1,170 +1,171 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.text"""
3 from __future__ import print_function
3 4
4 5 #-----------------------------------------------------------------------------
5 6 # Copyright (C) 2011 The IPython Development Team
6 7 #
7 8 # Distributed under the terms of the BSD License. The full license is in
8 9 # the file COPYING, distributed as part of this software.
9 10 #-----------------------------------------------------------------------------
10 11
11 12 #-----------------------------------------------------------------------------
12 13 # Imports
13 14 #-----------------------------------------------------------------------------
14 15
15 16 import os
16 17 import math
17 18 import random
18 19
19 20 import nose.tools as nt
20 21
21 22 from IPython.utils import text
22 23
23 24 #-----------------------------------------------------------------------------
24 25 # Globals
25 26 #-----------------------------------------------------------------------------
26 27
27 28 def test_columnize():
28 29 """Basic columnize tests."""
29 30 size = 5
30 31 items = [l*size for l in 'abc']
31 32 out = text.columnize(items, displaywidth=80)
32 33 nt.assert_equal(out, 'aaaaa bbbbb ccccc\n')
33 34 out = text.columnize(items, displaywidth=12)
34 35 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb\n')
35 36 out = text.columnize(items, displaywidth=10)
36 37 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\n')
37 38
38 39 def test_columnize_random():
39 40 """Test with random input to hopfully catch edge case """
40 41 for nitems in [random.randint(2,70) for i in range(2,20)]:
41 42 displaywidth = random.randint(20,200)
42 43 rand_len = [random.randint(2,displaywidth) for i in range(nitems)]
43 44 items = ['x'*l for l in rand_len]
44 45 out = text.columnize(items, displaywidth=displaywidth)
45 46 longer_line = max([len(x) for x in out.split('\n')])
46 47 longer_element = max(rand_len)
47 48 if longer_line > displaywidth:
48 print "Columnize displayed something lager than displaywidth : %s " % longer_line
49 print "longer element : %s " % longer_element
50 print "displaywidth : %s " % displaywidth
51 print "number of element : %s " % nitems
52 print "size of each element :\n %s" % rand_len
49 print("Columnize displayed something lager than displaywidth : %s " % longer_line)
50 print("longer element : %s " % longer_element)
51 print("displaywidth : %s " % displaywidth)
52 print("number of element : %s " % nitems)
53 print("size of each element :\n %s" % rand_len)
53 54 assert False
54 55
55 56 def test_columnize_medium():
56 57 """Test with inputs than shouldn't be wider tahn 80 """
57 58 size = 40
58 59 items = [l*size for l in 'abc']
59 60 out = text.columnize(items, displaywidth=80)
60 61 nt.assert_equal(out, '\n'.join(items+['']))
61 62
62 63 def test_columnize_long():
63 64 """Test columnize with inputs longer than the display window"""
64 65 size = 11
65 66 items = [l*size for l in 'abc']
66 67 out = text.columnize(items, displaywidth=size-1)
67 68 nt.assert_equal(out, '\n'.join(items+['']))
68 69
69 70 def eval_formatter_check(f):
70 71 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café")
71 72 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
72 73 nt.assert_equal(s, "12 3 hello")
73 74 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
74 75 nt.assert_equal(s, "12 6 4 3 2 2 1")
75 76 s = f.format('{[n//i for i in range(1,8)]}', **ns)
76 77 nt.assert_equal(s, "[12, 6, 4, 3, 2, 2, 1]")
77 78 s = f.format("{stuff!s}", **ns)
78 79 nt.assert_equal(s, ns['stuff'])
79 80 s = f.format("{stuff!r}", **ns)
80 81 nt.assert_equal(s, repr(ns['stuff']))
81 82
82 83 # Check with unicode:
83 84 s = f.format("{u}", **ns)
84 85 nt.assert_equal(s, ns['u'])
85 86 # This decodes in a platform dependent manner, but it shouldn't error out
86 87 s = f.format("{b}", **ns)
87 88
88 89 nt.assert_raises(NameError, f.format, '{dne}', **ns)
89 90
90 91 def eval_formatter_slicing_check(f):
91 92 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
92 93 s = f.format(" {stuff.split()[:]} ", **ns)
93 94 nt.assert_equal(s, " ['hello', 'there'] ")
94 95 s = f.format(" {stuff.split()[::-1]} ", **ns)
95 96 nt.assert_equal(s, " ['there', 'hello'] ")
96 97 s = f.format("{stuff[::2]}", **ns)
97 98 nt.assert_equal(s, ns['stuff'][::2])
98 99
99 100 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
100 101
101 102 def eval_formatter_no_slicing_check(f):
102 103 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
103 104
104 105 s = f.format('{n:x} {pi**2:+f}', **ns)
105 106 nt.assert_equal(s, "c +9.869604")
106 107
107 108 s = f.format('{stuff[slice(1,4)]}', **ns)
108 109 nt.assert_equal(s, 'ell')
109 110
110 111 nt.assert_raises(SyntaxError, f.format, "{a[:]}")
111 112
112 113 def test_eval_formatter():
113 114 f = text.EvalFormatter()
114 115 eval_formatter_check(f)
115 116 eval_formatter_no_slicing_check(f)
116 117
117 118 def test_full_eval_formatter():
118 119 f = text.FullEvalFormatter()
119 120 eval_formatter_check(f)
120 121 eval_formatter_slicing_check(f)
121 122
122 123 def test_dollar_formatter():
123 124 f = text.DollarFormatter()
124 125 eval_formatter_check(f)
125 126 eval_formatter_slicing_check(f)
126 127
127 128 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
128 129 s = f.format("$n", **ns)
129 130 nt.assert_equal(s, "12")
130 131 s = f.format("$n.real", **ns)
131 132 nt.assert_equal(s, "12")
132 133 s = f.format("$n/{stuff[:5]}", **ns)
133 134 nt.assert_equal(s, "12/hello")
134 135 s = f.format("$n $$HOME", **ns)
135 136 nt.assert_equal(s, "12 $HOME")
136 137 s = f.format("${foo}", foo="HOME")
137 138 nt.assert_equal(s, "$HOME")
138 139
139 140
140 141 def test_long_substr():
141 142 data = ['hi']
142 143 nt.assert_equal(text.long_substr(data), 'hi')
143 144
144 145
145 146 def test_long_substr2():
146 147 data = ['abc', 'abd', 'abf', 'ab']
147 148 nt.assert_equal(text.long_substr(data), 'ab')
148 149
149 150 def test_long_substr_empty():
150 151 data = []
151 152 nt.assert_equal(text.long_substr(data), '')
152 153
153 154 def test_strip_email():
154 155 src = """\
155 156 >> >>> def f(x):
156 157 >> ... return x+1
157 158 >> ...
158 159 >> >>> zz = f(2.5)"""
159 160 cln = """\
160 161 >>> def f(x):
161 162 ... return x+1
162 163 ...
163 164 >>> zz = f(2.5)"""
164 165 nt.assert_equal(text.strip_email_quotes(src), cln)
165 166
166 167
167 168 def test_strip_email2():
168 169 src = '> > > list()'
169 170 cln = 'list()'
170 171 nt.assert_equal(text.strip_email_quotes(src), cln)
General Comments 0
You need to be logged in to leave comments. Login now