##// END OF EJS Templates
remove all trailling spaces
Bernardo B. Marques -
Show More
@@ -109,10 +109,10 b' class Application(SingletonConfigurable):'
109 new = getattr(logging, new)
109 new = getattr(logging, new)
110 self.log_level = new
110 self.log_level = new
111 self.log.setLevel(new)
111 self.log.setLevel(new)
112
112
113 # the alias map for configurables
113 # the alias map for configurables
114 aliases = Dict({'log-level' : 'Application.log_level'})
114 aliases = Dict({'log-level' : 'Application.log_level'})
115
115
116 # flags for loading Configurables or store_const style flags
116 # flags for loading Configurables or store_const style flags
117 # flags are loaded from this dict by '--key' flags
117 # flags are loaded from this dict by '--key' flags
118 # this must be a dict of two-tuples, the first element being the Config/dict
118 # this must be a dict of two-tuples, the first element being the Config/dict
@@ -124,20 +124,20 b' class Application(SingletonConfigurable):'
124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
127
127
128
128
129 # subcommands for launching other applications
129 # subcommands for launching other applications
130 # if this is not empty, this will be a parent Application
130 # if this is not empty, this will be a parent Application
131 # this must be a dict of two-tuples,
131 # this must be a dict of two-tuples,
132 # the first element being the application class/import string
132 # the first element being the application class/import string
133 # and the second being the help string for the subcommand
133 # and the second being the help string for the subcommand
134 subcommands = Dict()
134 subcommands = Dict()
135 # parse_command_line will initialize a subapp, if requested
135 # parse_command_line will initialize a subapp, if requested
136 subapp = Instance('IPython.config.application.Application', allow_none=True)
136 subapp = Instance('IPython.config.application.Application', allow_none=True)
137
137
138 # extra command-line arguments that don't set config values
138 # extra command-line arguments that don't set config values
139 extra_args = List(Unicode)
139 extra_args = List(Unicode)
140
140
141
141
142 def __init__(self, **kwargs):
142 def __init__(self, **kwargs):
143 SingletonConfigurable.__init__(self, **kwargs)
143 SingletonConfigurable.__init__(self, **kwargs)
@@ -145,7 +145,7 b' class Application(SingletonConfigurable):'
145 # options and config files.
145 # options and config files.
146 if self.__class__ not in self.classes:
146 if self.__class__ not in self.classes:
147 self.classes.insert(0, self.__class__)
147 self.classes.insert(0, self.__class__)
148
148
149 self.init_logging()
149 self.init_logging()
150
150
151 def _config_changed(self, name, old, new):
151 def _config_changed(self, name, old, new):
@@ -157,7 +157,7 b' class Application(SingletonConfigurable):'
157 """Start logging for this application.
157 """Start logging for this application.
158
158
159 The default is to log to stdout using a StreaHandler. The log level
159 The default is to log to stdout using a StreaHandler. The log level
160 starts at loggin.WARN, but this can be adjusted by setting the
160 starts at loggin.WARN, but this can be adjusted by setting the
161 ``log_level`` attribute.
161 ``log_level`` attribute.
162 """
162 """
163 self.log = logging.getLogger(self.__class__.__name__)
163 self.log = logging.getLogger(self.__class__.__name__)
@@ -174,36 +174,36 b' class Application(SingletonConfigurable):'
174
174
175 def initialize(self, argv=None):
175 def initialize(self, argv=None):
176 """Do the basic steps to configure me.
176 """Do the basic steps to configure me.
177
177
178 Override in subclasses.
178 Override in subclasses.
179 """
179 """
180 self.parse_command_line(argv)
180 self.parse_command_line(argv)
181
181
182
182
183 def start(self):
183 def start(self):
184 """Start the app mainloop.
184 """Start the app mainloop.
185
185
186 Override in subclasses.
186 Override in subclasses.
187 """
187 """
188 if self.subapp is not None:
188 if self.subapp is not None:
189 return self.subapp.start()
189 return self.subapp.start()
190
190
191 def print_alias_help(self):
191 def print_alias_help(self):
192 """Print the alias part of the help."""
192 """Print the alias part of the help."""
193 if not self.aliases:
193 if not self.aliases:
194 return
194 return
195
195
196 lines = []
196 lines = []
197 classdict = {}
197 classdict = {}
198 for cls in self.classes:
198 for cls in self.classes:
199 # include all parents (up to, but excluding Configurable) in available names
199 # include all parents (up to, but excluding Configurable) in available names
200 for c in cls.mro()[:-3]:
200 for c in cls.mro()[:-3]:
201 classdict[c.__name__] = c
201 classdict[c.__name__] = c
202
202
203 for alias, longname in self.aliases.iteritems():
203 for alias, longname in self.aliases.iteritems():
204 classname, traitname = longname.split('.',1)
204 classname, traitname = longname.split('.',1)
205 cls = classdict[classname]
205 cls = classdict[classname]
206
206
207 trait = cls.class_traits(config=True)[traitname]
207 trait = cls.class_traits(config=True)[traitname]
208 help = cls.class_get_trait_help(trait).splitlines()
208 help = cls.class_get_trait_help(trait).splitlines()
209 # reformat first line
209 # reformat first line
@@ -213,12 +213,12 b' class Application(SingletonConfigurable):'
213 lines.extend(help)
213 lines.extend(help)
214 # lines.append('')
214 # lines.append('')
215 print os.linesep.join(lines)
215 print os.linesep.join(lines)
216
216
217 def print_flag_help(self):
217 def print_flag_help(self):
218 """Print the flag part of the help."""
218 """Print the flag part of the help."""
219 if not self.flags:
219 if not self.flags:
220 return
220 return
221
221
222 lines = []
222 lines = []
223 for m, (cfg,help) in self.flags.iteritems():
223 for m, (cfg,help) in self.flags.iteritems():
224 prefix = '--' if len(m) > 1 else '-'
224 prefix = '--' if len(m) > 1 else '-'
@@ -226,7 +226,7 b' class Application(SingletonConfigurable):'
226 lines.append(indent(dedent(help.strip())))
226 lines.append(indent(dedent(help.strip())))
227 # lines.append('')
227 # lines.append('')
228 print os.linesep.join(lines)
228 print os.linesep.join(lines)
229
229
230 def print_options(self):
230 def print_options(self):
231 if not self.flags and not self.aliases:
231 if not self.flags and not self.aliases:
232 return
232 return
@@ -240,12 +240,12 b' class Application(SingletonConfigurable):'
240 self.print_flag_help()
240 self.print_flag_help()
241 self.print_alias_help()
241 self.print_alias_help()
242 print
242 print
243
243
244 def print_subcommands(self):
244 def print_subcommands(self):
245 """Print the subcommand part of the help."""
245 """Print the subcommand part of the help."""
246 if not self.subcommands:
246 if not self.subcommands:
247 return
247 return
248
248
249 lines = ["Subcommands"]
249 lines = ["Subcommands"]
250 lines.append('-'*len(lines[0]))
250 lines.append('-'*len(lines[0]))
251 lines.append('')
251 lines.append('')
@@ -258,15 +258,15 b' class Application(SingletonConfigurable):'
258 lines.append(indent(dedent(help.strip())))
258 lines.append(indent(dedent(help.strip())))
259 lines.append('')
259 lines.append('')
260 print os.linesep.join(lines)
260 print os.linesep.join(lines)
261
261
262 def print_help(self, classes=False):
262 def print_help(self, classes=False):
263 """Print the help for each Configurable class in self.classes.
263 """Print the help for each Configurable class in self.classes.
264
264
265 If classes=False (the default), only flags and aliases are printed.
265 If classes=False (the default), only flags and aliases are printed.
266 """
266 """
267 self.print_subcommands()
267 self.print_subcommands()
268 self.print_options()
268 self.print_options()
269
269
270 if classes:
270 if classes:
271 if self.classes:
271 if self.classes:
272 print "Class parameters"
272 print "Class parameters"
@@ -275,7 +275,7 b' class Application(SingletonConfigurable):'
275 for p in wrap_paragraphs(self.keyvalue_description):
275 for p in wrap_paragraphs(self.keyvalue_description):
276 print p
276 print p
277 print
277 print
278
278
279 for cls in self.classes:
279 for cls in self.classes:
280 cls.class_print_help()
280 cls.class_print_help()
281 print
281 print
@@ -315,21 +315,21 b' class Application(SingletonConfigurable):'
315 # Save the combined config as self.config, which triggers the traits
315 # Save the combined config as self.config, which triggers the traits
316 # events.
316 # events.
317 self.config = newconfig
317 self.config = newconfig
318
318
319 def initialize_subcommand(self, subc, argv=None):
319 def initialize_subcommand(self, subc, argv=None):
320 """Initialize a subcommand with argv."""
320 """Initialize a subcommand with argv."""
321 subapp,help = self.subcommands.get(subc)
321 subapp,help = self.subcommands.get(subc)
322
322
323 if isinstance(subapp, basestring):
323 if isinstance(subapp, basestring):
324 subapp = import_item(subapp)
324 subapp = import_item(subapp)
325
325
326 # clear existing instances
326 # clear existing instances
327 self.__class__.clear_instance()
327 self.__class__.clear_instance()
328 # instantiate
328 # instantiate
329 self.subapp = subapp.instance()
329 self.subapp = subapp.instance()
330 # and initialize subapp
330 # and initialize subapp
331 self.subapp.initialize(argv)
331 self.subapp.initialize(argv)
332
332
333 def parse_command_line(self, argv=None):
333 def parse_command_line(self, argv=None):
334 """Parse the command line arguments."""
334 """Parse the command line arguments."""
335 argv = sys.argv[1:] if argv is None else argv
335 argv = sys.argv[1:] if argv is None else argv
@@ -340,7 +340,7 b' class Application(SingletonConfigurable):'
340 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
340 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
341 # it's a subcommand, and *not* a flag or class parameter
341 # it's a subcommand, and *not* a flag or class parameter
342 return self.initialize_subcommand(subc, subargv)
342 return self.initialize_subcommand(subc, subargv)
343
343
344 if '-h' in argv or '--help' in argv or '--help-all' in argv:
344 if '-h' in argv or '--help' in argv or '--help-all' in argv:
345 self.print_description()
345 self.print_description()
346 self.print_help('--help-all' in argv)
346 self.print_help('--help-all' in argv)
@@ -350,7 +350,7 b' class Application(SingletonConfigurable):'
350 if '--version' in argv:
350 if '--version' in argv:
351 self.print_version()
351 self.print_version()
352 self.exit(0)
352 self.exit(0)
353
353
354 loader = KVArgParseConfigLoader(argv=argv, aliases=self.aliases,
354 loader = KVArgParseConfigLoader(argv=argv, aliases=self.aliases,
355 flags=self.flags)
355 flags=self.flags)
356 try:
356 try:
@@ -383,7 +383,7 b' class Application(SingletonConfigurable):'
383 else:
383 else:
384 self.log.debug("Loaded config file: %s", loader.full_filename)
384 self.log.debug("Loaded config file: %s", loader.full_filename)
385 self.update_config(config)
385 self.update_config(config)
386
386
387 def generate_config_file(self):
387 def generate_config_file(self):
388 """generate default config file from Configurables"""
388 """generate default config file from Configurables"""
389 lines = ["# Configuration file for %s."%self.name]
389 lines = ["# Configuration file for %s."%self.name]
@@ -404,10 +404,10 b' class Application(SingletonConfigurable):'
404
404
405 def boolean_flag(name, configurable, set_help='', unset_help=''):
405 def boolean_flag(name, configurable, set_help='', unset_help=''):
406 """Helper for building basic --trait, --no-trait flags.
406 """Helper for building basic --trait, --no-trait flags.
407
407
408 Parameters
408 Parameters
409 ----------
409 ----------
410
410
411 name : str
411 name : str
412 The name of the flag.
412 The name of the flag.
413 configurable : str
413 configurable : str
@@ -416,10 +416,10 b" def boolean_flag(name, configurable, set_help='', unset_help=''):"
416 help string for --name flag
416 help string for --name flag
417 unset_help : unicode
417 unset_help : unicode
418 help string for --no-name flag
418 help string for --no-name flag
419
419
420 Returns
420 Returns
421 -------
421 -------
422
422
423 cfg : dict
423 cfg : dict
424 A dict with two keys: 'name', and 'no-name', for setting and unsetting
424 A dict with two keys: 'name', and 'no-name', for setting and unsetting
425 the trait, respectively.
425 the trait, respectively.
@@ -427,9 +427,9 b" def boolean_flag(name, configurable, set_help='', unset_help=''):"
427 # default helpstrings
427 # default helpstrings
428 set_help = set_help or "set %s=True"%configurable
428 set_help = set_help or "set %s=True"%configurable
429 unset_help = unset_help or "set %s=False"%configurable
429 unset_help = unset_help or "set %s=False"%configurable
430
430
431 cls,trait = configurable.split('.')
431 cls,trait = configurable.split('.')
432
432
433 setter = {cls : {trait : True}}
433 setter = {cls : {trait : True}}
434 unsetter = {cls : {trait : False}}
434 unsetter = {cls : {trait : False}}
435 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
435 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
@@ -55,16 +55,16 b' class Configurable(HasTraits):'
55 Parameters
55 Parameters
56 ----------
56 ----------
57 config : Config
57 config : Config
58 If this is empty, default values are used. If config is a
58 If this is empty, default values are used. If config is a
59 :class:`Config` instance, it will be used to configure the
59 :class:`Config` instance, it will be used to configure the
60 instance.
60 instance.
61
61
62 Notes
62 Notes
63 -----
63 -----
64 Subclasses of Configurable must call the :meth:`__init__` method of
64 Subclasses of Configurable must call the :meth:`__init__` method of
65 :class:`Configurable` *before* doing anything else and using
65 :class:`Configurable` *before* doing anything else and using
66 :func:`super`::
66 :func:`super`::
67
67
68 class MyConfigurable(Configurable):
68 class MyConfigurable(Configurable):
69 def __init__(self, config=None):
69 def __init__(self, config=None):
70 super(MyConfigurable, self).__init__(config)
70 super(MyConfigurable, self).__init__(config)
@@ -82,7 +82,7 b' class Configurable(HasTraits):'
82 # making that a class attribute.
82 # making that a class attribute.
83 # self.config = deepcopy(config)
83 # self.config = deepcopy(config)
84 self.config = config
84 self.config = config
85 # This should go second so individual keyword arguments override
85 # This should go second so individual keyword arguments override
86 # the values in config.
86 # the values in config.
87 super(Configurable, self).__init__(**kwargs)
87 super(Configurable, self).__init__(**kwargs)
88 self.created = datetime.datetime.now()
88 self.created = datetime.datetime.now()
@@ -105,11 +105,11 b' class Configurable(HasTraits):'
105 # classes that are Configurable subclasses. This starts with Configurable
105 # classes that are Configurable subclasses. This starts with Configurable
106 # and works down the mro loading the config for each section.
106 # and works down the mro loading the config for each section.
107 section_names = [cls.__name__ for cls in \
107 section_names = [cls.__name__ for cls in \
108 reversed(self.__class__.__mro__) if
108 reversed(self.__class__.__mro__) if
109 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
109 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
110
110
111 for sname in section_names:
111 for sname in section_names:
112 # Don't do a blind getattr as that would cause the config to
112 # Don't do a blind getattr as that would cause the config to
113 # dynamically create the section with name self.__class__.__name__.
113 # dynamically create the section with name self.__class__.__name__.
114 if new._has_section(sname):
114 if new._has_section(sname):
115 my_config = new[sname]
115 my_config = new[sname]
@@ -149,7 +149,7 b' class Configurable(HasTraits):'
149 help = cls.class_get_trait_help(v)
149 help = cls.class_get_trait_help(v)
150 final_help.append(help)
150 final_help.append(help)
151 return '\n'.join(final_help)
151 return '\n'.join(final_help)
152
152
153 @classmethod
153 @classmethod
154 def class_get_trait_help(cls, trait):
154 def class_get_trait_help(cls, trait):
155 """Get the help string for a single trait."""
155 """Get the help string for a single trait."""
@@ -167,7 +167,7 b' class Configurable(HasTraits):'
167 if 'Enum' in trait.__class__.__name__:
167 if 'Enum' in trait.__class__.__name__:
168 # include Enum choices
168 # include Enum choices
169 lines.append(indent('Choices: %r'%(trait.values,)))
169 lines.append(indent('Choices: %r'%(trait.values,)))
170
170
171 help = trait.get_metadata('help')
171 help = trait.get_metadata('help')
172 if help is not None:
172 if help is not None:
173 help = '\n'.join(wrap_paragraphs(help, 76))
173 help = '\n'.join(wrap_paragraphs(help, 76))
@@ -185,9 +185,9 b' class Configurable(HasTraits):'
185 def c(s):
185 def c(s):
186 """return a commented, wrapped block."""
186 """return a commented, wrapped block."""
187 s = '\n\n'.join(wrap_paragraphs(s, 78))
187 s = '\n\n'.join(wrap_paragraphs(s, 78))
188
188
189 return '# ' + s.replace('\n', '\n# ')
189 return '# ' + s.replace('\n', '\n# ')
190
190
191 # section header
191 # section header
192 breaker = '#' + '-'*78
192 breaker = '#' + '-'*78
193 s = "# %s configuration"%cls.__name__
193 s = "# %s configuration"%cls.__name__
@@ -202,7 +202,7 b' class Configurable(HasTraits):'
202 if desc:
202 if desc:
203 lines.append(c(desc))
203 lines.append(c(desc))
204 lines.append('')
204 lines.append('')
205
205
206 parents = []
206 parents = []
207 for parent in cls.mro():
207 for parent in cls.mro():
208 # only include parents that are not base classes
208 # only include parents that are not base classes
@@ -211,20 +211,20 b' class Configurable(HasTraits):'
211 if parent is not cls and issubclass(parent, Configurable) and \
211 if parent is not cls and issubclass(parent, Configurable) and \
212 parent.class_traits(config=True):
212 parent.class_traits(config=True):
213 parents.append(parent)
213 parents.append(parent)
214
214
215 if parents:
215 if parents:
216 pstr = ', '.join([ p.__name__ for p in parents ])
216 pstr = ', '.join([ p.__name__ for p in parents ])
217 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
217 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
218 lines.append('')
218 lines.append('')
219
219
220 for name,trait in cls.class_traits(config=True).iteritems():
220 for name,trait in cls.class_traits(config=True).iteritems():
221 help = trait.get_metadata('help') or ''
221 help = trait.get_metadata('help') or ''
222 lines.append(c(help))
222 lines.append(c(help))
223 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
223 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
224 lines.append('')
224 lines.append('')
225 return '\n'.join(lines)
225 return '\n'.join(lines)
226
226
227
227
228
228
229 class SingletonConfigurable(Configurable):
229 class SingletonConfigurable(Configurable):
230 """A configurable that only allows one instance.
230 """A configurable that only allows one instance.
@@ -235,20 +235,20 b' class SingletonConfigurable(Configurable):'
235 """
235 """
236
236
237 _instance = None
237 _instance = None
238
238
239 @classmethod
239 @classmethod
240 def _walk_mro(cls):
240 def _walk_mro(cls):
241 """Walk the cls.mro() for parent classes that are also singletons
241 """Walk the cls.mro() for parent classes that are also singletons
242
242
243 For use in instance()
243 For use in instance()
244 """
244 """
245
245
246 for subclass in cls.mro():
246 for subclass in cls.mro():
247 if issubclass(cls, subclass) and \
247 if issubclass(cls, subclass) and \
248 issubclass(subclass, SingletonConfigurable) and \
248 issubclass(subclass, SingletonConfigurable) and \
249 subclass != SingletonConfigurable:
249 subclass != SingletonConfigurable:
250 yield subclass
250 yield subclass
251
251
252 @classmethod
252 @classmethod
253 def clear_instance(cls):
253 def clear_instance(cls):
254 """unset _instance for this class and singleton parents.
254 """unset _instance for this class and singleton parents.
@@ -260,7 +260,7 b' class SingletonConfigurable(Configurable):'
260 # only clear instances that are instances
260 # only clear instances that are instances
261 # of the calling class
261 # of the calling class
262 subclass._instance = None
262 subclass._instance = None
263
263
264 @classmethod
264 @classmethod
265 def instance(cls, *args, **kwargs):
265 def instance(cls, *args, **kwargs):
266 """Returns a global instance of this class.
266 """Returns a global instance of this class.
@@ -297,7 +297,7 b' class SingletonConfigurable(Configurable):'
297 # parent classes' _instance attribute.
297 # parent classes' _instance attribute.
298 for subclass in cls._walk_mro():
298 for subclass in cls._walk_mro():
299 subclass._instance = inst
299 subclass._instance = inst
300
300
301 if isinstance(cls._instance, cls):
301 if isinstance(cls._instance, cls):
302 return cls._instance
302 return cls._instance
303 else:
303 else:
@@ -314,15 +314,15 b' class SingletonConfigurable(Configurable):'
314
314
315 class LoggingConfigurable(Configurable):
315 class LoggingConfigurable(Configurable):
316 """A parent class for Configurables that log.
316 """A parent class for Configurables that log.
317
317
318 Subclasses have a log trait, and the default behavior
318 Subclasses have a log trait, and the default behavior
319 is to get the logger from the currently running Application
319 is to get the logger from the currently running Application
320 via Application.instance().log.
320 via Application.instance().log.
321 """
321 """
322
322
323 log = Instance('logging.Logger')
323 log = Instance('logging.Logger')
324 def _log_default(self):
324 def _log_default(self):
325 from IPython.config.application import Application
325 from IPython.config.application import Application
326 return Application.instance().log
326 return Application.instance().log
327
327
328
328
@@ -53,14 +53,14 b' class ArgumentError(ConfigLoaderError):'
53
53
54 class ArgumentParser(argparse.ArgumentParser):
54 class ArgumentParser(argparse.ArgumentParser):
55 """Simple argparse subclass that prints help to stdout by default."""
55 """Simple argparse subclass that prints help to stdout by default."""
56
56
57 def print_help(self, file=None):
57 def print_help(self, file=None):
58 if file is None:
58 if file is None:
59 file = sys.stdout
59 file = sys.stdout
60 return super(ArgumentParser, self).print_help(file)
60 return super(ArgumentParser, self).print_help(file)
61
61
62 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
62 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Config class for holding config information
65 # Config class for holding config information
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
@@ -125,10 +125,10 b' class Config(dict):'
125 # infinite recursion on top of PyPy. Instead, we manually fish the
125 # infinite recursion on top of PyPy. Instead, we manually fish the
126 # bound method.
126 # bound method.
127 is_section_key = self.__class__._is_section_key.__get__(self)
127 is_section_key = self.__class__._is_section_key.__get__(self)
128
128
129 # Because we use this for an exec namespace, we need to delegate
129 # Because we use this for an exec namespace, we need to delegate
130 # the lookup of names in __builtin__ to itself. This means
130 # the lookup of names in __builtin__ to itself. This means
131 # that you can't have section or attribute names that are
131 # that you can't have section or attribute names that are
132 # builtins.
132 # builtins.
133 try:
133 try:
134 return getattr(builtin_mod, key)
134 return getattr(builtin_mod, key)
@@ -182,25 +182,25 b' class Config(dict):'
182
182
183 class ConfigLoader(object):
183 class ConfigLoader(object):
184 """A object for loading configurations from just about anywhere.
184 """A object for loading configurations from just about anywhere.
185
185
186 The resulting configuration is packaged as a :class:`Struct`.
186 The resulting configuration is packaged as a :class:`Struct`.
187
187
188 Notes
188 Notes
189 -----
189 -----
190 A :class:`ConfigLoader` does one thing: load a config from a source
190 A :class:`ConfigLoader` does one thing: load a config from a source
191 (file, command line arguments) and returns the data as a :class:`Struct`.
191 (file, command line arguments) and returns the data as a :class:`Struct`.
192 There are lots of things that :class:`ConfigLoader` does not do. It does
192 There are lots of things that :class:`ConfigLoader` does not do. It does
193 not implement complex logic for finding config files. It does not handle
193 not implement complex logic for finding config files. It does not handle
194 default values or merge multiple configs. These things need to be
194 default values or merge multiple configs. These things need to be
195 handled elsewhere.
195 handled elsewhere.
196 """
196 """
197
197
198 def __init__(self):
198 def __init__(self):
199 """A base class for config loaders.
199 """A base class for config loaders.
200
200
201 Examples
201 Examples
202 --------
202 --------
203
203
204 >>> cl = ConfigLoader()
204 >>> cl = ConfigLoader()
205 >>> config = cl.load_config()
205 >>> config = cl.load_config()
206 >>> config
206 >>> config
@@ -213,7 +213,7 b' class ConfigLoader(object):'
213
213
214 def load_config(self):
214 def load_config(self):
215 """Load a config from somewhere, return a :class:`Config` instance.
215 """Load a config from somewhere, return a :class:`Config` instance.
216
216
217 Usually, this will cause self.config to be set and then returned.
217 Usually, this will cause self.config to be set and then returned.
218 However, in most cases, :meth:`ConfigLoader.clear` should be called
218 However, in most cases, :meth:`ConfigLoader.clear` should be called
219 to erase any previous state.
219 to erase any previous state.
@@ -233,7 +233,7 b' class FileConfigLoader(ConfigLoader):'
233
233
234 class PyFileConfigLoader(FileConfigLoader):
234 class PyFileConfigLoader(FileConfigLoader):
235 """A config loader for pure python files.
235 """A config loader for pure python files.
236
236
237 This calls execfile on a plain python file and looks for attributes
237 This calls execfile on a plain python file and looks for attributes
238 that are all caps. These attribute are added to the config Struct.
238 that are all caps. These attribute are added to the config Struct.
239 """
239 """
@@ -276,10 +276,10 b' class PyFileConfigLoader(FileConfigLoader):'
276 # and self.config. The sub-config is loaded with the same path
276 # and self.config. The sub-config is loaded with the same path
277 # as the parent, but it uses an empty config which is then merged
277 # as the parent, but it uses an empty config which is then merged
278 # with the parents.
278 # with the parents.
279
279
280 # If a profile is specified, the config file will be loaded
280 # If a profile is specified, the config file will be loaded
281 # from that profile
281 # from that profile
282
282
283 def load_subconfig(fname, profile=None):
283 def load_subconfig(fname, profile=None):
284 # import here to prevent circular imports
284 # import here to prevent circular imports
285 from IPython.core.profiledir import ProfileDir, ProfileDirError
285 from IPython.core.profiledir import ProfileDir, ProfileDirError
@@ -303,7 +303,7 b' class PyFileConfigLoader(FileConfigLoader):'
303 pass
303 pass
304 else:
304 else:
305 self.config._merge(sub_config)
305 self.config._merge(sub_config)
306
306
307 # Again, this needs to be a closure and should be used in config
307 # Again, this needs to be a closure and should be used in config
308 # files to get the config being loaded.
308 # files to get the config being loaded.
309 def get_config():
309 def get_config():
@@ -340,7 +340,7 b' class CommandLineConfigLoader(ConfigLoader):'
340 # it succeeds. If it still fails, we let it raise.
340 # it succeeds. If it still fails, we let it raise.
341 exec_str = u'self.config.' + lhs + '=' + repr(rhs)
341 exec_str = u'self.config.' + lhs + '=' + repr(rhs)
342 exec exec_str in locals(), globals()
342 exec exec_str in locals(), globals()
343
343
344 def _load_flag(self, cfg):
344 def _load_flag(self, cfg):
345 """update self.config from a flag, which can be a dict or Config"""
345 """update self.config from a flag, which can be a dict or Config"""
346 if isinstance(cfg, (dict, Config)):
346 if isinstance(cfg, (dict, Config)):
@@ -373,7 +373,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
373 """A config loader that loads key value pairs from the command line.
373 """A config loader that loads key value pairs from the command line.
374
374
375 This allows command line options to be gives in the following form::
375 This allows command line options to be gives in the following form::
376
376
377 ipython --profile="foo" --InteractiveShell.autocall=False
377 ipython --profile="foo" --InteractiveShell.autocall=False
378 """
378 """
379
379
@@ -414,13 +414,13 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
414 self.argv = argv
414 self.argv = argv
415 self.aliases = aliases or {}
415 self.aliases = aliases or {}
416 self.flags = flags or {}
416 self.flags = flags or {}
417
417
418
418
419 def clear(self):
419 def clear(self):
420 super(KeyValueConfigLoader, self).clear()
420 super(KeyValueConfigLoader, self).clear()
421 self.extra_args = []
421 self.extra_args = []
422
422
423
423
424 def _decode_argv(self, argv, enc=None):
424 def _decode_argv(self, argv, enc=None):
425 """decode argv if bytes, using stin.encoding, falling back on default enc"""
425 """decode argv if bytes, using stin.encoding, falling back on default enc"""
426 uargv = []
426 uargv = []
@@ -432,16 +432,16 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
432 arg = arg.decode(enc)
432 arg = arg.decode(enc)
433 uargv.append(arg)
433 uargv.append(arg)
434 return uargv
434 return uargv
435
435
436
436
437 def load_config(self, argv=None, aliases=None, flags=None):
437 def load_config(self, argv=None, aliases=None, flags=None):
438 """Parse the configuration and generate the Config object.
438 """Parse the configuration and generate the Config object.
439
439
440 After loading, any arguments that are not key-value or
440 After loading, any arguments that are not key-value or
441 flags will be stored in self.extra_args - a list of
441 flags will be stored in self.extra_args - a list of
442 unparsed command-line arguments. This is used for
442 unparsed command-line arguments. This is used for
443 arguments such as input files or subcommands.
443 arguments such as input files or subcommands.
444
444
445 Parameters
445 Parameters
446 ----------
446 ----------
447 argv : list, optional
447 argv : list, optional
@@ -454,7 +454,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
454 Of the form: `{'alias' : 'Configurable.trait'}`
454 Of the form: `{'alias' : 'Configurable.trait'}`
455 flags : dict
455 flags : dict
456 A dict of flags, keyed by str name. Values can be Config objects
456 A dict of flags, keyed by str name. Values can be Config objects
457 or dicts. When the flag is triggered, The config is loaded as
457 or dicts. When the flag is triggered, The config is loaded as
458 `self.config.update(cfg)`.
458 `self.config.update(cfg)`.
459 """
459 """
460 from IPython.config.configurable import Configurable
460 from IPython.config.configurable import Configurable
@@ -466,20 +466,20 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
466 aliases = self.aliases
466 aliases = self.aliases
467 if flags is None:
467 if flags is None:
468 flags = self.flags
468 flags = self.flags
469
469
470 # ensure argv is a list of unicode strings:
470 # ensure argv is a list of unicode strings:
471 uargv = self._decode_argv(argv)
471 uargv = self._decode_argv(argv)
472 for idx,raw in enumerate(uargv):
472 for idx,raw in enumerate(uargv):
473 # strip leading '-'
473 # strip leading '-'
474 item = raw.lstrip('-')
474 item = raw.lstrip('-')
475
475
476 if raw == '--':
476 if raw == '--':
477 # don't parse arguments after '--'
477 # don't parse arguments after '--'
478 # this is useful for relaying arguments to scripts, e.g.
478 # this is useful for relaying arguments to scripts, e.g.
479 # ipython -i foo.py --pylab=qt -- args after '--' go-to-foo.py
479 # ipython -i foo.py --pylab=qt -- args after '--' go-to-foo.py
480 self.extra_args.extend(uargv[idx+1:])
480 self.extra_args.extend(uargv[idx+1:])
481 break
481 break
482
482
483 if kv_pattern.match(raw):
483 if kv_pattern.match(raw):
484 lhs,rhs = item.split('=',1)
484 lhs,rhs = item.split('=',1)
485 # Substitute longnames for aliases.
485 # Substitute longnames for aliases.
@@ -489,7 +489,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
489 # probably a mistyped alias, but not technically illegal
489 # probably a mistyped alias, but not technically illegal
490 warn.warn("Unrecognized alias: '%s', it will probably have no effect."%lhs)
490 warn.warn("Unrecognized alias: '%s', it will probably have no effect."%lhs)
491 self._exec_config_str(lhs, rhs)
491 self._exec_config_str(lhs, rhs)
492
492
493 elif flag_pattern.match(raw):
493 elif flag_pattern.match(raw):
494 if item in flags:
494 if item in flags:
495 cfg,help = flags[item]
495 cfg,help = flags[item]
@@ -503,7 +503,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
503 else:
503 else:
504 raise ArgumentError("Invalid argument: '%s'"%raw)
504 raise ArgumentError("Invalid argument: '%s'"%raw)
505 else:
505 else:
506 # keep all args that aren't valid in a list,
506 # keep all args that aren't valid in a list,
507 # in case our parent knows what to do with them.
507 # in case our parent knows what to do with them.
508 self.extra_args.append(item)
508 self.extra_args.append(item)
509 return self.config
509 return self.config
@@ -541,7 +541,7 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
541 self.argv = argv
541 self.argv = argv
542 self.aliases = aliases or {}
542 self.aliases = aliases or {}
543 self.flags = flags or {}
543 self.flags = flags or {}
544
544
545 self.parser_args = parser_args
545 self.parser_args = parser_args
546 self.version = parser_kw.pop("version", None)
546 self.version = parser_kw.pop("version", None)
547 kwargs = dict(argument_default=argparse.SUPPRESS)
547 kwargs = dict(argument_default=argparse.SUPPRESS)
@@ -597,10 +597,10 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
597
597
598 class KVArgParseConfigLoader(ArgParseConfigLoader):
598 class KVArgParseConfigLoader(ArgParseConfigLoader):
599 """A config loader that loads aliases and flags with argparse,
599 """A config loader that loads aliases and flags with argparse,
600 but will use KVLoader for the rest. This allows better parsing
600 but will use KVLoader for the rest. This allows better parsing
601 of common args, such as `ipython -c 'print 5'`, but still gets
601 of common args, such as `ipython -c 'print 5'`, but still gets
602 arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
602 arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
603
603
604 def _convert_to_config(self):
604 def _convert_to_config(self):
605 """self.parsed_data->self.config"""
605 """self.parsed_data->self.config"""
606 for k, v in vars(self.parsed_data).iteritems():
606 for k, v in vars(self.parsed_data).iteritems():
@@ -626,14 +626,14 b' class KVArgParseConfigLoader(ArgParseConfigLoader):'
626 paa('--'+key, type=unicode, dest=value, nargs=nargs)
626 paa('--'+key, type=unicode, dest=value, nargs=nargs)
627 for key, (value, help) in flags.iteritems():
627 for key, (value, help) in flags.iteritems():
628 if key in self.aliases:
628 if key in self.aliases:
629 #
629 #
630 self.alias_flags[self.aliases[key]] = value
630 self.alias_flags[self.aliases[key]] = value
631 continue
631 continue
632 if len(key) is 1:
632 if len(key) is 1:
633 paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
633 paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
634 else:
634 else:
635 paa('--'+key, action='append_const', dest='_flags', const=value)
635 paa('--'+key, action='append_const', dest='_flags', const=value)
636
636
637 def _convert_to_config(self):
637 def _convert_to_config(self):
638 """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
638 """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
639 # remove subconfigs list from namespace before transforming the Namespace
639 # remove subconfigs list from namespace before transforming the Namespace
@@ -642,7 +642,7 b' class KVArgParseConfigLoader(ArgParseConfigLoader):'
642 del self.parsed_data._flags
642 del self.parsed_data._flags
643 else:
643 else:
644 subcs = []
644 subcs = []
645
645
646 for k, v in vars(self.parsed_data).iteritems():
646 for k, v in vars(self.parsed_data).iteritems():
647 if v is None:
647 if v is None:
648 # it was a flag that shares the name of an alias
648 # it was a flag that shares the name of an alias
@@ -650,10 +650,10 b' class KVArgParseConfigLoader(ArgParseConfigLoader):'
650 else:
650 else:
651 # eval the KV assignment
651 # eval the KV assignment
652 self._exec_config_str(k, v)
652 self._exec_config_str(k, v)
653
653
654 for subc in subcs:
654 for subc in subcs:
655 self._load_flag(subc)
655 self._load_flag(subc)
656
656
657 if self.extra_args:
657 if self.extra_args:
658 sub_parser = KeyValueConfigLoader()
658 sub_parser = KeyValueConfigLoader()
659 sub_parser.load_config(self.extra_args)
659 sub_parser.load_config(self.extra_args)
@@ -49,7 +49,7 b' def default_aliases():'
49 # their case. For example, things like 'less' or 'clear' that manipulate
49 # their case. For example, things like 'less' or 'clear' that manipulate
50 # the terminal should NOT be declared here, as they will only work if the
50 # the terminal should NOT be declared here, as they will only work if the
51 # kernel is running inside a true terminal, and not over the network.
51 # kernel is running inside a true terminal, and not over the network.
52
52
53 if os.name == 'posix':
53 if os.name == 'posix':
54 default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
54 default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
55 ('mv', 'mv -i'), ('rm', 'rm -i'), ('cp', 'cp -i'),
55 ('mv', 'mv -i'), ('rm', 'rm -i'), ('cp', 'cp -i'),
@@ -94,7 +94,7 b' def default_aliases():'
94 ]
94 ]
95 else:
95 else:
96 default_aliases = []
96 default_aliases = []
97
97
98 return default_aliases
98 return default_aliases
99
99
100
100
@@ -213,33 +213,33 b' class AliasManager(Configurable):'
213 return cmd
213 return cmd
214
214
215 def expand_alias(self, line):
215 def expand_alias(self, line):
216 """ Expand an alias in the command line
216 """ Expand an alias in the command line
217
217
218 Returns the provided command line, possibly with the first word
218 Returns the provided command line, possibly with the first word
219 (command) translated according to alias expansion rules.
219 (command) translated according to alias expansion rules.
220
220
221 [ipython]|16> _ip.expand_aliases("np myfile.txt")
221 [ipython]|16> _ip.expand_aliases("np myfile.txt")
222 <16> 'q:/opt/np/notepad++.exe myfile.txt'
222 <16> 'q:/opt/np/notepad++.exe myfile.txt'
223 """
223 """
224
224
225 pre,_,fn,rest = split_user_input(line)
225 pre,_,fn,rest = split_user_input(line)
226 res = pre + self.expand_aliases(fn, rest)
226 res = pre + self.expand_aliases(fn, rest)
227 return res
227 return res
228
228
229 def expand_aliases(self, fn, rest):
229 def expand_aliases(self, fn, rest):
230 """Expand multiple levels of aliases:
230 """Expand multiple levels of aliases:
231
231
232 if:
232 if:
233
233
234 alias foo bar /tmp
234 alias foo bar /tmp
235 alias baz foo
235 alias baz foo
236
236
237 then:
237 then:
238
238
239 baz huhhahhei -> bar /tmp huhhahhei
239 baz huhhahhei -> bar /tmp huhhahhei
240 """
240 """
241 line = fn + " " + rest
241 line = fn + " " + rest
242
242
243 done = set()
243 done = set()
244 while 1:
244 while 1:
245 pre,_,fn,rest = split_user_input(line, shell_line_split)
245 pre,_,fn,rest = split_user_input(line, shell_line_split)
@@ -259,5 +259,5 b' class AliasManager(Configurable):'
259 line=l2
259 line=l2
260 else:
260 else:
261 break
261 break
262
262
263 return line
263 return line
@@ -5,7 +5,7 b' An application for IPython.'
5 All top-level applications should use the classes in this module for
5 All top-level applications should use the classes in this module for
6 handling configuration and creating componenets.
6 handling configuration and creating componenets.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the configurable objects, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10
10
11 Authors:
11 Authors:
@@ -78,15 +78,15 b' class BaseIPythonApplication(Application):'
78 name = Unicode(u'ipython')
78 name = Unicode(u'ipython')
79 description = Unicode(u'IPython: an enhanced interactive Python shell.')
79 description = Unicode(u'IPython: an enhanced interactive Python shell.')
80 version = Unicode(release.version)
80 version = Unicode(release.version)
81
81
82 aliases = Dict(base_aliases)
82 aliases = Dict(base_aliases)
83 flags = Dict(base_flags)
83 flags = Dict(base_flags)
84 classes = List([ProfileDir])
84 classes = List([ProfileDir])
85
85
86 # Track whether the config_file has changed,
86 # Track whether the config_file has changed,
87 # because some logic happens only if we aren't using the default.
87 # because some logic happens only if we aren't using the default.
88 config_file_specified = Bool(False)
88 config_file_specified = Bool(False)
89
89
90 config_file_name = Unicode(u'ipython_config.py')
90 config_file_name = Unicode(u'ipython_config.py')
91 def _config_file_name_default(self):
91 def _config_file_name_default(self):
92 return self.name.replace('-','_') + u'_config.py'
92 return self.name.replace('-','_') + u'_config.py'
@@ -108,13 +108,13 b' class BaseIPythonApplication(Application):'
108 )
108 )
109 def _profile_default(self):
109 def _profile_default(self):
110 return "python3" if py3compat.PY3 else "default"
110 return "python3" if py3compat.PY3 else "default"
111
111
112 def _profile_changed(self, name, old, new):
112 def _profile_changed(self, name, old, new):
113 self.builtin_profile_dir = os.path.join(
113 self.builtin_profile_dir = os.path.join(
114 get_ipython_package_dir(), u'config', u'profile', new
114 get_ipython_package_dir(), u'config', u'profile', new
115 )
115 )
116
116
117 ipython_dir = Unicode(get_ipython_dir(), config=True,
117 ipython_dir = Unicode(get_ipython_dir(), config=True,
118 help="""
118 help="""
119 The name of the IPython directory. This directory is used for logging
119 The name of the IPython directory. This directory is used for logging
120 configuration (through profiles), history storage, etc. The default
120 configuration (through profiles), history storage, etc. The default
@@ -122,16 +122,16 b' class BaseIPythonApplication(Application):'
122 the environment variable IPYTHON_DIR.
122 the environment variable IPYTHON_DIR.
123 """
123 """
124 )
124 )
125
125
126 overwrite = Bool(False, config=True,
126 overwrite = Bool(False, config=True,
127 help="""Whether to overwrite existing config files when copying""")
127 help="""Whether to overwrite existing config files when copying""")
128 auto_create = Bool(False, config=True,
128 auto_create = Bool(False, config=True,
129 help="""Whether to create profile dir if it doesn't exist""")
129 help="""Whether to create profile dir if it doesn't exist""")
130
130
131 config_files = List(Unicode)
131 config_files = List(Unicode)
132 def _config_files_default(self):
132 def _config_files_default(self):
133 return [u'ipython_config.py']
133 return [u'ipython_config.py']
134
134
135 copy_config_files = Bool(False, config=True,
135 copy_config_files = Bool(False, config=True,
136 help="""Whether to install the default config files into the profile dir.
136 help="""Whether to install the default config files into the profile dir.
137 If a new profile is being created, and IPython contains config files for that
137 If a new profile is being created, and IPython contains config files for that
@@ -147,7 +147,7 b' class BaseIPythonApplication(Application):'
147 # ensure even default IPYTHON_DIR exists
147 # ensure even default IPYTHON_DIR exists
148 if not os.path.exists(self.ipython_dir):
148 if not os.path.exists(self.ipython_dir):
149 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
149 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
150
150
151 #-------------------------------------------------------------------------
151 #-------------------------------------------------------------------------
152 # Various stages of Application creation
152 # Various stages of Application creation
153 #-------------------------------------------------------------------------
153 #-------------------------------------------------------------------------
@@ -183,7 +183,7 b' class BaseIPythonApplication(Application):'
183 try:
183 try:
184 Application.load_config_file(
184 Application.load_config_file(
185 self,
185 self,
186 base_config,
186 base_config,
187 path=self.config_file_paths
187 path=self.config_file_paths
188 )
188 )
189 except IOError:
189 except IOError:
@@ -198,7 +198,7 b' class BaseIPythonApplication(Application):'
198 try:
198 try:
199 Application.load_config_file(
199 Application.load_config_file(
200 self,
200 self,
201 self.config_file_name,
201 self.config_file_name,
202 path=self.config_file_paths
202 path=self.config_file_paths
203 )
203 )
204 except IOError:
204 except IOError:
@@ -258,17 +258,17 b' class BaseIPythonApplication(Application):'
258 self.exit(1)
258 self.exit(1)
259 else:
259 else:
260 self.log.info("Using existing profile dir: %r"%location)
260 self.log.info("Using existing profile dir: %r"%location)
261
261
262 self.profile_dir = p
262 self.profile_dir = p
263 self.config_file_paths.append(p.location)
263 self.config_file_paths.append(p.location)
264
264
265 def init_config_files(self):
265 def init_config_files(self):
266 """[optionally] copy default config files into profile dir."""
266 """[optionally] copy default config files into profile dir."""
267 # copy config files
267 # copy config files
268 path = self.builtin_profile_dir
268 path = self.builtin_profile_dir
269 if self.copy_config_files:
269 if self.copy_config_files:
270 src = self.profile
270 src = self.profile
271
271
272 cfg = self.config_file_name
272 cfg = self.config_file_name
273 if path and os.path.exists(os.path.join(path, cfg)):
273 if path and os.path.exists(os.path.join(path, cfg)):
274 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
274 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
@@ -289,8 +289,8 b' class BaseIPythonApplication(Application):'
289 self.log.warn("Staging bundled %s from %s into %r"%(
289 self.log.warn("Staging bundled %s from %s into %r"%(
290 cfg, self.profile, self.profile_dir.location)
290 cfg, self.profile, self.profile_dir.location)
291 )
291 )
292
292
293
293
294 def stage_default_config_file(self):
294 def stage_default_config_file(self):
295 """auto generate default config file, and stage it into the profile."""
295 """auto generate default config file, and stage it into the profile."""
296 s = self.generate_config_file()
296 s = self.generate_config_file()
@@ -299,8 +299,8 b' class BaseIPythonApplication(Application):'
299 self.log.warn("Generating default config file: %r"%(fname))
299 self.log.warn("Generating default config file: %r"%(fname))
300 with open(fname, 'w') as f:
300 with open(fname, 'w') as f:
301 f.write(s)
301 f.write(s)
302
302
303
303
304 def initialize(self, argv=None):
304 def initialize(self, argv=None):
305 # don't hook up crash handler before parsing command-line
305 # don't hook up crash handler before parsing command-line
306 self.parse_command_line(argv)
306 self.parse_command_line(argv)
@@ -126,7 +126,7 b' def has_open_quotes(s):'
126
126
127 def protect_filename(s):
127 def protect_filename(s):
128 """Escape a string to protect certain characters."""
128 """Escape a string to protect certain characters."""
129
129
130 return "".join([(ch in PROTECTABLES and '\\' + ch or ch)
130 return "".join([(ch in PROTECTABLES and '\\' + ch or ch)
131 for ch in s])
131 for ch in s])
132
132
@@ -156,7 +156,7 b' def expand_user(path):'
156 path : str
156 path : str
157 String to be expanded. If no ~ is present, the output is the same as the
157 String to be expanded. If no ~ is present, the output is the same as the
158 input.
158 input.
159
159
160 Returns
160 Returns
161 -------
161 -------
162 newpath : str
162 newpath : str
@@ -170,7 +170,7 b' def expand_user(path):'
170 tilde_expand = False
170 tilde_expand = False
171 tilde_val = ''
171 tilde_val = ''
172 newpath = path
172 newpath = path
173
173
174 if path.startswith('~'):
174 if path.startswith('~'):
175 tilde_expand = True
175 tilde_expand = True
176 rest = path[1:]
176 rest = path[1:]
@@ -229,7 +229,7 b' class CompletionSplitter(object):'
229 automatically builds the necessary """
229 automatically builds the necessary """
230
230
231 # Private interface
231 # Private interface
232
232
233 # A string of delimiter characters. The default value makes sense for
233 # A string of delimiter characters. The default value makes sense for
234 # IPython's most typical usage patterns.
234 # IPython's most typical usage patterns.
235 _delims = DELIMS
235 _delims = DELIMS
@@ -265,15 +265,15 b' class CompletionSplitter(object):'
265
265
266
266
267 class Completer(Configurable):
267 class Completer(Configurable):
268
268
269 greedy = CBool(False, config=True,
269 greedy = CBool(False, config=True,
270 help="""Activate greedy completion
270 help="""Activate greedy completion
271
271
272 This will enable completion on elements of lists, results of function calls, etc.,
272 This will enable completion on elements of lists, results of function calls, etc.,
273 but can be unsafe because the code is actually evaluated on TAB.
273 but can be unsafe because the code is actually evaluated on TAB.
274 """
274 """
275 )
275 )
276
276
277 def __init__(self, namespace=None, global_namespace=None, config=None):
277 def __init__(self, namespace=None, global_namespace=None, config=None):
278 """Create a new completer for the command line.
278 """Create a new completer for the command line.
279
279
@@ -307,7 +307,7 b' class Completer(Configurable):'
307 self.global_namespace = {}
307 self.global_namespace = {}
308 else:
308 else:
309 self.global_namespace = global_namespace
309 self.global_namespace = global_namespace
310
310
311 super(Completer, self).__init__(config=config)
311 super(Completer, self).__init__(config=config)
312
312
313 def complete(self, text, state):
313 def complete(self, text, state):
@@ -319,7 +319,7 b' class Completer(Configurable):'
319 """
319 """
320 if self.use_main_ns:
320 if self.use_main_ns:
321 self.namespace = __main__.__dict__
321 self.namespace = __main__.__dict__
322
322
323 if state == 0:
323 if state == 0:
324 if "." in text:
324 if "." in text:
325 self.matches = self.attr_matches(text)
325 self.matches = self.attr_matches(text)
@@ -369,7 +369,7 b' class Completer(Configurable):'
369 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
369 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
370
370
371 if m:
371 if m:
372 expr, attr = m.group(1, 3)
372 expr, attr = m.group(1, 3)
373 elif self.greedy:
373 elif self.greedy:
374 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
374 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
375 if not m2:
375 if not m2:
@@ -377,7 +377,7 b' class Completer(Configurable):'
377 expr, attr = m2.group(1,2)
377 expr, attr = m2.group(1,2)
378 else:
378 else:
379 return []
379 return []
380
380
381 try:
381 try:
382 obj = eval(expr, self.namespace)
382 obj = eval(expr, self.namespace)
383 except:
383 except:
@@ -387,7 +387,7 b' class Completer(Configurable):'
387 return []
387 return []
388
388
389 words = dir2(obj)
389 words = dir2(obj)
390
390
391 try:
391 try:
392 words = generics.complete_object(obj, words)
392 words = generics.complete_object(obj, words)
393 except TryNext:
393 except TryNext:
@@ -407,10 +407,10 b' class IPCompleter(Completer):'
407 self.splitter.set_delims(GREEDY_DELIMS)
407 self.splitter.set_delims(GREEDY_DELIMS)
408 else:
408 else:
409 self.splitter.set_delims(DELIMS)
409 self.splitter.set_delims(DELIMS)
410
410
411 if self.readline:
411 if self.readline:
412 self.readline.set_completer_delims(self.splitter.get_delims())
412 self.readline.set_completer_delims(self.splitter.get_delims())
413
413
414 def __init__(self, shell=None, namespace=None, global_namespace=None,
414 def __init__(self, shell=None, namespace=None, global_namespace=None,
415 omit__names=True, alias_table=None, use_readline=True,
415 omit__names=True, alias_table=None, use_readline=True,
416 config=None):
416 config=None):
@@ -448,7 +448,7 b' class IPCompleter(Completer):'
448
448
449 # Readline configuration, only used by the rlcompleter method.
449 # Readline configuration, only used by the rlcompleter method.
450 if use_readline:
450 if use_readline:
451 # We store the right version of readline so that later code
451 # We store the right version of readline so that later code
452 import IPython.utils.rlineimpl as readline
452 import IPython.utils.rlineimpl as readline
453 self.readline = readline
453 self.readline = readline
454 else:
454 else:
@@ -475,7 +475,7 b' class IPCompleter(Completer):'
475 # buffers, to avoid completion problems.
475 # buffers, to avoid completion problems.
476 term = os.environ.get('TERM','xterm')
476 term = os.environ.get('TERM','xterm')
477 self.dumb_terminal = term in ['dumb','emacs']
477 self.dumb_terminal = term in ['dumb','emacs']
478
478
479 # Special handling of backslashes needed in win32 platforms
479 # Special handling of backslashes needed in win32 platforms
480 if sys.platform == "win32":
480 if sys.platform == "win32":
481 self.clean_glob = self._clean_glob_win32
481 self.clean_glob = self._clean_glob_win32
@@ -489,7 +489,7 b' class IPCompleter(Completer):'
489 self.alias_matches,
489 self.alias_matches,
490 self.python_func_kw_matches,
490 self.python_func_kw_matches,
491 ]
491 ]
492
492
493 def all_completions(self, text):
493 def all_completions(self, text):
494 """
494 """
495 Wrapper around the complete method for the benefit of emacs
495 Wrapper around the complete method for the benefit of emacs
@@ -502,7 +502,7 b' class IPCompleter(Completer):'
502
502
503 def _clean_glob_win32(self,text):
503 def _clean_glob_win32(self,text):
504 return [f.replace("\\","/")
504 return [f.replace("\\","/")
505 for f in self.glob("%s*" % text)]
505 for f in self.glob("%s*" % text)]
506
506
507 def file_matches(self, text):
507 def file_matches(self, text):
508 """Match filenames, expanding ~USER type strings.
508 """Match filenames, expanding ~USER type strings.
@@ -569,7 +569,7 b' class IPCompleter(Completer):'
569 # beginning of filename so that we don't double-write the part
569 # beginning of filename so that we don't double-write the part
570 # of the filename we have so far
570 # of the filename we have so far
571 len_lsplit = len(lsplit)
571 len_lsplit = len(lsplit)
572 matches = [text_prefix + text0 +
572 matches = [text_prefix + text0 +
573 protect_filename(f[len_lsplit:]) for f in m0]
573 protect_filename(f[len_lsplit:]) for f in m0]
574 else:
574 else:
575 if open_quotes:
575 if open_quotes:
@@ -578,7 +578,7 b' class IPCompleter(Completer):'
578 # would cause bugs when the filesystem call is made).
578 # would cause bugs when the filesystem call is made).
579 matches = m0
579 matches = m0
580 else:
580 else:
581 matches = [text_prefix +
581 matches = [text_prefix +
582 protect_filename(f) for f in m0]
582 protect_filename(f) for f in m0]
583
583
584 #io.rprint('mm', matches) # dbg
584 #io.rprint('mm', matches) # dbg
@@ -595,7 +595,7 b' class IPCompleter(Completer):'
595 return [ pre+m for m in magics if m.startswith(baretext)]
595 return [ pre+m for m in magics if m.startswith(baretext)]
596
596
597 def alias_matches(self, text):
597 def alias_matches(self, text):
598 """Match internal system aliases"""
598 """Match internal system aliases"""
599 #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg
599 #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg
600
600
601 # if we are not in the first 'item', alias matching
601 # if we are not in the first 'item', alias matching
@@ -715,10 +715,10 b' class IPCompleter(Completer):'
715
715
716 def dispatch_custom_completer(self, text):
716 def dispatch_custom_completer(self, text):
717 #io.rprint("Custom! '%s' %s" % (text, self.custom_completers)) # dbg
717 #io.rprint("Custom! '%s' %s" % (text, self.custom_completers)) # dbg
718 line = self.line_buffer
718 line = self.line_buffer
719 if not line.strip():
719 if not line.strip():
720 return None
720 return None
721
721
722 # Create a little structure to pass all the relevant information about
722 # Create a little structure to pass all the relevant information about
723 # the current completion to any custom completer.
723 # the current completion to any custom completer.
724 event = Bunch()
724 event = Bunch()
@@ -727,16 +727,16 b' class IPCompleter(Completer):'
727 cmd = line.split(None,1)[0]
727 cmd = line.split(None,1)[0]
728 event.command = cmd
728 event.command = cmd
729 event.text_until_cursor = self.text_until_cursor
729 event.text_until_cursor = self.text_until_cursor
730
730
731 #print "\ncustom:{%s]\n" % event # dbg
731 #print "\ncustom:{%s]\n" % event # dbg
732
732
733 # for foo etc, try also to find completer for %foo
733 # for foo etc, try also to find completer for %foo
734 if not cmd.startswith(self.magic_escape):
734 if not cmd.startswith(self.magic_escape):
735 try_magic = self.custom_completers.s_matches(
735 try_magic = self.custom_completers.s_matches(
736 self.magic_escape + cmd)
736 self.magic_escape + cmd)
737 else:
737 else:
738 try_magic = []
738 try_magic = []
739
739
740 for c in itertools.chain(self.custom_completers.s_matches(cmd),
740 for c in itertools.chain(self.custom_completers.s_matches(cmd),
741 try_magic,
741 try_magic,
742 self.custom_completers.flat_matches(self.text_until_cursor)):
742 self.custom_completers.flat_matches(self.text_until_cursor)):
@@ -753,9 +753,9 b' class IPCompleter(Completer):'
753 return [r for r in res if r.lower().startswith(text_low)]
753 return [r for r in res if r.lower().startswith(text_low)]
754 except TryNext:
754 except TryNext:
755 pass
755 pass
756
756
757 return None
757 return None
758
758
759 def complete(self, text=None, line_buffer=None, cursor_pos=None):
759 def complete(self, text=None, line_buffer=None, cursor_pos=None):
760 """Find completions for the given text and line context.
760 """Find completions for the given text and line context.
761
761
@@ -785,7 +785,7 b' class IPCompleter(Completer):'
785 -------
785 -------
786 text : str
786 text : str
787 Text that was actually used in the completion.
787 Text that was actually used in the completion.
788
788
789 matches : list
789 matches : list
790 A list of completion matches.
790 A list of completion matches.
791 """
791 """
@@ -803,7 +803,7 b' class IPCompleter(Completer):'
803 # If no line buffer is given, assume the input text is all there was
803 # If no line buffer is given, assume the input text is all there was
804 if line_buffer is None:
804 if line_buffer is None:
805 line_buffer = text
805 line_buffer = text
806
806
807 self.line_buffer = line_buffer
807 self.line_buffer = line_buffer
808 self.text_until_cursor = self.line_buffer[:cursor_pos]
808 self.text_until_cursor = self.line_buffer[:cursor_pos]
809 #io.rprint('\nCOMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg
809 #io.rprint('\nCOMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg
@@ -893,7 +893,7 b' class IPCompleter(Completer):'
893 import traceback; traceback.print_exc()
893 import traceback; traceback.print_exc()
894 else:
894 else:
895 # The normal production version is here
895 # The normal production version is here
896
896
897 # This method computes the self.matches array
897 # This method computes the self.matches array
898 self.complete(text, line_buffer, cursor_pos)
898 self.complete(text, line_buffer, cursor_pos)
899
899
@@ -78,7 +78,7 b' def shlex_split(x):'
78 if len(endofline) >= 1:
78 if len(endofline) >= 1:
79 comps.append(''.join(endofline))
79 comps.append(''.join(endofline))
80 return comps
80 return comps
81
81
82 except ValueError:
82 except ValueError:
83 endofline = [x[-1:]]+endofline
83 endofline = [x[-1:]]+endofline
84 x = x[:-1]
84 x = x[:-1]
@@ -108,7 +108,7 b' def module_list(path):'
108 isfile = os.path.isfile
108 isfile = os.path.isfile
109 pjoin = os.path.join
109 pjoin = os.path.join
110 basename = os.path.basename
110 basename = os.path.basename
111
111
112 # Now find actual path matches for packages or modules
112 # Now find actual path matches for packages or modules
113 folder_list = [p for p in folder_list
113 folder_list = [p for p in folder_list
114 if isfile(pjoin(path, p,'__init__.py'))
114 if isfile(pjoin(path, p,'__init__.py'))
@@ -125,12 +125,12 b' def get_root_modules():'
125
125
126 if 'rootmodules' in ip.db:
126 if 'rootmodules' in ip.db:
127 return ip.db['rootmodules']
127 return ip.db['rootmodules']
128
128
129 t = time()
129 t = time()
130 store = False
130 store = False
131 modules = list(sys.builtin_module_names)
131 modules = list(sys.builtin_module_names)
132 for path in sys.path:
132 for path in sys.path:
133 modules += module_list(path)
133 modules += module_list(path)
134 if time() - t >= TIMEOUT_STORAGE and not store:
134 if time() - t >= TIMEOUT_STORAGE and not store:
135 store = True
135 store = True
136 print("\nCaching the list of root modules, please wait!")
136 print("\nCaching the list of root modules, please wait!")
@@ -141,7 +141,7 b' def get_root_modules():'
141 print("This is taking too long, we give up.\n")
141 print("This is taking too long, we give up.\n")
142 ip.db['rootmodules'] = []
142 ip.db['rootmodules'] = []
143 return []
143 return []
144
144
145 modules = set(modules)
145 modules = set(modules)
146 if '__init__' in modules:
146 if '__init__' in modules:
147 modules.remove('__init__')
147 modules.remove('__init__')
@@ -173,7 +173,7 b' def try_import(mod, only_modules=False):'
173 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
173 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
174 completions.extend( [attr for attr in dir(m) if
174 completions.extend( [attr for attr in dir(m) if
175 is_importable(m, attr, only_modules)])
175 is_importable(m, attr, only_modules)])
176
176
177 completions.extend(getattr(m, '__all__', []))
177 completions.extend(getattr(m, '__all__', []))
178 if m_is_init:
178 if m_is_init:
179 completions.extend(module_list(os.path.dirname(m.__file__)))
179 completions.extend(module_list(os.path.dirname(m.__file__)))
@@ -192,22 +192,22 b' def quick_completer(cmd, completions):'
192
192
193 Takes either a list of completions, or all completions in string (that will
193 Takes either a list of completions, or all completions in string (that will
194 be split on whitespace).
194 be split on whitespace).
195
195
196 Example::
196 Example::
197
197
198 [d:\ipython]|1> import ipy_completers
198 [d:\ipython]|1> import ipy_completers
199 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
199 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
200 [d:\ipython]|3> foo b<TAB>
200 [d:\ipython]|3> foo b<TAB>
201 bar baz
201 bar baz
202 [d:\ipython]|3> foo ba
202 [d:\ipython]|3> foo ba
203 """
203 """
204
204
205 if isinstance(completions, basestring):
205 if isinstance(completions, basestring):
206 completions = completions.split()
206 completions = completions.split()
207
207
208 def do_complete(self, event):
208 def do_complete(self, event):
209 return completions
209 return completions
210
210
211 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
211 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
212
212
213
213
@@ -226,7 +226,7 b' def module_completion(line):'
226 # from whatever <tab> -> 'import '
226 # from whatever <tab> -> 'import '
227 if nwords == 3 and words[0] == 'from':
227 if nwords == 3 and words[0] == 'from':
228 return ['import ']
228 return ['import ']
229
229
230 # 'from xy<tab>' or 'import xy<tab>'
230 # 'from xy<tab>' or 'import xy<tab>'
231 if nwords < 3 and (words[0] in ['import','from']) :
231 if nwords < 3 and (words[0] in ['import','from']) :
232 if nwords == 1:
232 if nwords == 1:
@@ -236,7 +236,7 b' def module_completion(line):'
236 return get_root_modules()
236 return get_root_modules()
237 completion_list = try_import('.'.join(mod[:-1]), True)
237 completion_list = try_import('.'.join(mod[:-1]), True)
238 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
238 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
239
239
240 # 'from xyz import abc<tab>'
240 # 'from xyz import abc<tab>'
241 if nwords >= 3 and words[0] == 'from':
241 if nwords >= 3 and words[0] == 'from':
242 mod = words[1]
242 mod = words[1]
@@ -275,7 +275,7 b' def magic_run_completer(self, event):'
275 lglob = glob.glob
275 lglob = glob.glob
276 isdir = os.path.isdir
276 isdir = os.path.isdir
277 relpath, tilde_expand, tilde_val = expand_user(relpath)
277 relpath, tilde_expand, tilde_val = expand_user(relpath)
278
278
279 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
279 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
280
280
281 # Find if the user has already typed the first filename, after which we
281 # Find if the user has already typed the first filename, after which we
@@ -305,7 +305,7 b' def cd_completer(self, event):'
305 return bkms.keys()
305 return bkms.keys()
306 else:
306 else:
307 return []
307 return []
308
308
309 if event.symbol == '-':
309 if event.symbol == '-':
310 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
310 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
311 # jump in directory history by number
311 # jump in directory history by number
@@ -329,7 +329,7 b' def cd_completer(self, event):'
329 # we don't want to deal with any of that, complex code
329 # we don't want to deal with any of that, complex code
330 # for this is elsewhere
330 # for this is elsewhere
331 raise TryNext
331 raise TryNext
332
332
333 found.append(d)
333 found.append(d)
334
334
335 if not found:
335 if not found:
@@ -341,7 +341,7 b' def cd_completer(self, event):'
341 bkmatches = [s for s in bks if s.startswith(event.symbol)]
341 bkmatches = [s for s in bks if s.startswith(event.symbol)]
342 if bkmatches:
342 if bkmatches:
343 return bkmatches
343 return bkmatches
344
344
345 raise TryNext
345 raise TryNext
346
346
347 return [compress_user(p, tilde_expand, tilde_val) for p in found]
347 return [compress_user(p, tilde_expand, tilde_val) for p in found]
@@ -67,14 +67,14 b' class CrashHandler(object):'
67 message_template = _default_message_template
67 message_template = _default_message_template
68 section_sep = '\n\n'+'*'*75+'\n\n'
68 section_sep = '\n\n'+'*'*75+'\n\n'
69
69
70 def __init__(self, app, contact_name=None, contact_email=None,
70 def __init__(self, app, contact_name=None, contact_email=None,
71 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
71 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
72 """Create a new crash handler
72 """Create a new crash handler
73
73
74 Parameters
74 Parameters
75 ----------
75 ----------
76 app : Application
76 app : Application
77 A running :class:`Application` instance, which will be queried at
77 A running :class:`Application` instance, which will be queried at
78 crash time for internal information.
78 crash time for internal information.
79
79
80 contact_name : str
80 contact_name : str
@@ -106,7 +106,7 b' class CrashHandler(object):'
106 contact_email = contact_email,
106 contact_email = contact_email,
107 bug_tracker = bug_tracker,
107 bug_tracker = bug_tracker,
108 crash_report_fname = self.crash_report_fname)
108 crash_report_fname = self.crash_report_fname)
109
109
110
110
111 def __call__(self, etype, evalue, etb):
111 def __call__(self, etype, evalue, etb):
112 """Handle an exception, call for compatible with sys.excepthook"""
112 """Handle an exception, call for compatible with sys.excepthook"""
@@ -160,13 +160,13 b' class CrashHandler(object):'
160
160
161 def make_report(self,traceback):
161 def make_report(self,traceback):
162 """Return a string containing a crash report."""
162 """Return a string containing a crash report."""
163
163
164 sec_sep = self.section_sep
164 sec_sep = self.section_sep
165
165
166 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
166 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
167 rpt_add = report.append
167 rpt_add = report.append
168 rpt_add(sys_info())
168 rpt_add(sys_info())
169
169
170 try:
170 try:
171 config = pformat(self.app.config)
171 config = pformat(self.app.config)
172 rpt_add(sec_sep)
172 rpt_add(sec_sep)
@@ -39,7 +39,7 b' has_pydb = False'
39 prompt = 'ipdb> '
39 prompt = 'ipdb> '
40 #We have to check this directly from sys.argv, config struct not yet available
40 #We have to check this directly from sys.argv, config struct not yet available
41 if '-pydb' in sys.argv:
41 if '-pydb' in sys.argv:
42 try:
42 try:
43 import pydb
43 import pydb
44 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
44 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
45 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
45 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
@@ -130,7 +130,7 b' class Tracer(object):'
130
130
131 This is similar to the pdb.set_trace() function from the std lib, but
131 This is similar to the pdb.set_trace() function from the std lib, but
132 using IPython's enhanced debugger."""
132 using IPython's enhanced debugger."""
133
133
134 self.debugger.set_trace(sys._getframe().f_back)
134 self.debugger.set_trace(sys._getframe().f_back)
135
135
136
136
@@ -173,9 +173,9 b' class Pdb(OldPdb):'
173 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
173 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
174 else:
174 else:
175 OldPdb.__init__(self,completekey,stdin,stdout)
175 OldPdb.__init__(self,completekey,stdin,stdout)
176
176
177 self.prompt = prompt # The default prompt is '(Pdb)'
177 self.prompt = prompt # The default prompt is '(Pdb)'
178
178
179 # IPython changes...
179 # IPython changes...
180 self.is_pydb = has_pydb
180 self.is_pydb = has_pydb
181
181
@@ -207,7 +207,7 b' class Pdb(OldPdb):'
207 # module and add a few attributes needed for debugging
207 # module and add a few attributes needed for debugging
208 self.color_scheme_table = exception_colors()
208 self.color_scheme_table = exception_colors()
209
209
210 # shorthands
210 # shorthands
211 C = coloransi.TermColors
211 C = coloransi.TermColors
212 cst = self.color_scheme_table
212 cst = self.color_scheme_table
213
213
@@ -250,11 +250,11 b' class Pdb(OldPdb):'
250 self.shell.set_completer_frame(self.curframe)
250 self.shell.set_completer_frame(self.curframe)
251
251
252 def new_do_quit(self, arg):
252 def new_do_quit(self, arg):
253
253
254 if hasattr(self, 'old_all_completions'):
254 if hasattr(self, 'old_all_completions'):
255 self.shell.Completer.all_completions=self.old_all_completions
255 self.shell.Completer.all_completions=self.old_all_completions
256
256
257
257
258 return OldPdb.do_quit(self, arg)
258 return OldPdb.do_quit(self, arg)
259
259
260 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
260 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
@@ -288,9 +288,9 b' class Pdb(OldPdb):'
288
288
289 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
289 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
290 import linecache, repr
290 import linecache, repr
291
291
292 ret = []
292 ret = []
293
293
294 Colors = self.color_scheme_table.active_colors
294 Colors = self.color_scheme_table.active_colors
295 ColorsNormal = Colors.Normal
295 ColorsNormal = Colors.Normal
296 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
296 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
@@ -298,9 +298,9 b' class Pdb(OldPdb):'
298 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
298 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
299 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
299 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
300 ColorsNormal)
300 ColorsNormal)
301
301
302 frame, lineno = frame_lineno
302 frame, lineno = frame_lineno
303
303
304 return_value = ''
304 return_value = ''
305 if '__return__' in frame.f_locals:
305 if '__return__' in frame.f_locals:
306 rv = frame.f_locals['__return__']
306 rv = frame.f_locals['__return__']
@@ -311,14 +311,14 b' class Pdb(OldPdb):'
311 #s = filename + '(' + `lineno` + ')'
311 #s = filename + '(' + `lineno` + ')'
312 filename = self.canonic(frame.f_code.co_filename)
312 filename = self.canonic(frame.f_code.co_filename)
313 link = tpl_link % filename
313 link = tpl_link % filename
314
314
315 if frame.f_code.co_name:
315 if frame.f_code.co_name:
316 func = frame.f_code.co_name
316 func = frame.f_code.co_name
317 else:
317 else:
318 func = "<lambda>"
318 func = "<lambda>"
319
319
320 call = ''
320 call = ''
321 if func != '?':
321 if func != '?':
322 if '__args__' in frame.f_locals:
322 if '__args__' in frame.f_locals:
323 args = repr.repr(frame.f_locals['__args__'])
323 args = repr.repr(frame.f_locals['__args__'])
324 else:
324 else:
@@ -332,13 +332,13 b' class Pdb(OldPdb):'
332 else:
332 else:
333 ret.append(' ')
333 ret.append(' ')
334 ret.append('%s(%s)%s\n' % (link,lineno,call))
334 ret.append('%s(%s)%s\n' % (link,lineno,call))
335
335
336 start = lineno - 1 - context//2
336 start = lineno - 1 - context//2
337 lines = linecache.getlines(filename)
337 lines = linecache.getlines(filename)
338 start = max(start, 0)
338 start = max(start, 0)
339 start = min(start, len(lines) - context)
339 start = min(start, len(lines) - context)
340 lines = lines[start : start + context]
340 lines = lines[start : start + context]
341
341
342 for i,line in enumerate(lines):
342 for i,line in enumerate(lines):
343 show_arrow = (start + 1 + i == lineno)
343 show_arrow = (start + 1 + i == lineno)
344 linetpl = (frame is self.curframe or show_arrow) \
344 linetpl = (frame is self.curframe or show_arrow) \
@@ -362,7 +362,7 b' class Pdb(OldPdb):'
362 if lineno in self.get_file_breaks(filename):
362 if lineno in self.get_file_breaks(filename):
363 bps = self.get_breaks(filename, lineno)
363 bps = self.get_breaks(filename, lineno)
364 bp = bps[-1]
364 bp = bps[-1]
365
365
366 if bp:
366 if bp:
367 Colors = self.color_scheme_table.active_colors
367 Colors = self.color_scheme_table.active_colors
368 bp_mark = str(bp.number)
368 bp_mark = str(bp.number)
@@ -387,7 +387,7 b' class Pdb(OldPdb):'
387 else:
387 else:
388 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
388 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
389 line = tpl_line % (bp_mark_color + bp_mark, num, line)
389 line = tpl_line % (bp_mark_color + bp_mark, num, line)
390
390
391 return line
391 return line
392
392
393 def list_command_pydb(self, arg):
393 def list_command_pydb(self, arg):
@@ -395,7 +395,7 b' class Pdb(OldPdb):'
395 filename, first, last = OldPdb.parse_list_cmd(self, arg)
395 filename, first, last = OldPdb.parse_list_cmd(self, arg)
396 if filename is not None:
396 if filename is not None:
397 self.print_list_lines(filename, first, last)
397 self.print_list_lines(filename, first, last)
398
398
399 def print_list_lines(self, filename, first, last):
399 def print_list_lines(self, filename, first, last):
400 """The printing (as opposed to the parsing part of a 'list'
400 """The printing (as opposed to the parsing part of a 'list'
401 command."""
401 command."""
@@ -496,7 +496,7 b' class Pdb(OldPdb):'
496 #
496 #
497 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
497 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
498 #######################################################################
498 #######################################################################
499
499
500 if not line:
500 if not line:
501 print >>self.stdout, 'End of file'
501 print >>self.stdout, 'End of file'
502 return 0
502 return 0
@@ -49,7 +49,7 b' def display(*objs, **kwargs):'
49 """
49 """
50 include = kwargs.get('include')
50 include = kwargs.get('include')
51 exclude = kwargs.get('exclude')
51 exclude = kwargs.get('exclude')
52
52
53 from IPython.core.interactiveshell import InteractiveShell
53 from IPython.core.interactiveshell import InteractiveShell
54 inst = InteractiveShell.instance()
54 inst = InteractiveShell.instance()
55 format = inst.display_formatter.format
55 format = inst.display_formatter.format
@@ -237,7 +237,7 b' class DisplayObject(object):'
237 in the frontend. The MIME type of the data should match the
237 in the frontend. The MIME type of the data should match the
238 subclasses used, so the Png subclass should be used for 'image/png'
238 subclasses used, so the Png subclass should be used for 'image/png'
239 data. If the data is a URL, the data will first be downloaded
239 data. If the data is a URL, the data will first be downloaded
240 and then displayed. If
240 and then displayed. If
241
241
242 Parameters
242 Parameters
243 ----------
243 ----------
@@ -123,7 +123,7 b' class DisplayHook(Configurable):'
123 return no_cache_def
123 return no_cache_def
124 else:
124 else:
125 return p_str
125 return p_str
126
126
127 def set_colors(self, colors):
127 def set_colors(self, colors):
128 """Set the active color scheme and configure colors for the three
128 """Set the active color scheme and configure colors for the three
129 prompt subsystems."""
129 prompt subsystems."""
@@ -135,7 +135,7 b' class DisplayHook(Configurable):'
135 prompts.prompt_specials = prompts.prompt_specials_nocolor
135 prompts.prompt_specials = prompts.prompt_specials_nocolor
136 else:
136 else:
137 prompts.prompt_specials = prompts.prompt_specials_color
137 prompts.prompt_specials = prompts.prompt_specials_color
138
138
139 self.color_table.set_active_scheme(colors)
139 self.color_table.set_active_scheme(colors)
140 self.prompt1.set_colors()
140 self.prompt1.set_colors()
141 self.prompt2.set_colors()
141 self.prompt2.set_colors()
@@ -227,7 +227,7 b' class DisplayHook(Configurable):'
227 format_dict : dict
227 format_dict : dict
228 The format dict for the object passed to `sys.displayhook`.
228 The format dict for the object passed to `sys.displayhook`.
229 """
229 """
230 # We want to print because we want to always make sure we have a
230 # We want to print because we want to always make sure we have a
231 # newline, even if all the prompt separators are ''. This is the
231 # newline, even if all the prompt separators are ''. This is the
232 # standard IPython behavior.
232 # standard IPython behavior.
233 result_repr = format_dict['text/plain']
233 result_repr = format_dict['text/plain']
@@ -291,7 +291,7 b' class DisplayHook(Configurable):'
291
291
292 def __call__(self, result=None):
292 def __call__(self, result=None):
293 """Printing with history cache management.
293 """Printing with history cache management.
294
294
295 This is invoked everytime the interpreter needs to print, and is
295 This is invoked everytime the interpreter needs to print, and is
296 activated by setting the variable sys.displayhook to it.
296 activated by setting the variable sys.displayhook to it.
297 """
297 """
@@ -310,17 +310,17 b' class DisplayHook(Configurable):'
310 raise ValueError,"You shouldn't have reached the cache flush "\
310 raise ValueError,"You shouldn't have reached the cache flush "\
311 "if full caching is not enabled!"
311 "if full caching is not enabled!"
312 # delete auto-generated vars from global namespace
312 # delete auto-generated vars from global namespace
313
313
314 for n in range(1,self.prompt_count + 1):
314 for n in range(1,self.prompt_count + 1):
315 key = '_'+`n`
315 key = '_'+`n`
316 try:
316 try:
317 del self.shell.user_ns[key]
317 del self.shell.user_ns[key]
318 except: pass
318 except: pass
319 self.shell.user_ns['_oh'].clear()
319 self.shell.user_ns['_oh'].clear()
320
320
321 # Release our own references to objects:
321 # Release our own references to objects:
322 self._, self.__, self.___ = '', '', ''
322 self._, self.__, self.___ = '', '', ''
323
323
324 if '_' not in __builtin__.__dict__:
324 if '_' not in __builtin__.__dict__:
325 self.shell.user_ns.update({'_':None,'__':None, '___':None})
325 self.shell.user_ns.update({'_':None,'__':None, '___':None})
326 import gc
326 import gc
@@ -87,7 +87,7 b' class DisplayPublisher(Configurable):'
87 A string that give the function or method that created the data,
87 A string that give the function or method that created the data,
88 such as 'IPython.core.page'.
88 such as 'IPython.core.page'.
89 data : dict
89 data : dict
90 A dictionary having keys that are valid MIME types (like
90 A dictionary having keys that are valid MIME types (like
91 'text/plain' or 'image/svg+xml') and values that are the data for
91 'text/plain' or 'image/svg+xml') and values that are the data for
92 that MIME type. The data itself must be a JSON'able data
92 that MIME type. The data itself must be a JSON'able data
93 structure. Minimally all data should have the 'text/plain' data,
93 structure. Minimally all data should have the 'text/plain' data,
@@ -128,7 +128,7 b' def publish_display_data(source, data, metadata=None):'
128 A string that give the function or method that created the data,
128 A string that give the function or method that created the data,
129 such as 'IPython.core.page'.
129 such as 'IPython.core.page'.
130 data : dict
130 data : dict
131 A dictionary having keys that are valid MIME types (like
131 A dictionary having keys that are valid MIME types (like
132 'text/plain' or 'image/svg+xml') and values that are the data for
132 'text/plain' or 'image/svg+xml') and values that are the data for
133 that MIME type. The data itself must be a JSON'able data
133 that MIME type. The data itself must be a JSON'able data
134 structure. Minimally all data should have the 'text/plain' data,
134 structure. Minimally all data should have the 'text/plain' data,
@@ -32,7 +32,7 b' class IPythonCoreError(Exception):'
32
32
33 class TryNext(IPythonCoreError):
33 class TryNext(IPythonCoreError):
34 """Try next hook exception.
34 """Try next hook exception.
35
35
36 Raise this in your hook function to indicate that the next hook handler
36 Raise this in your hook function to indicate that the next hook handler
37 should be used to handle the operation. If you pass arguments to the
37 should be used to handle the operation. If you pass arguments to the
38 constructor those arguments will be used by the next hook instead of the
38 constructor those arguments will be used by the next hook instead of the
@@ -45,7 +45,7 b' class TryNext(IPythonCoreError):'
45
45
46 class UsageError(IPythonCoreError):
46 class UsageError(IPythonCoreError):
47 """Error in magic function arguments, etc.
47 """Error in magic function arguments, etc.
48
48
49 Something that probably won't warrant a full traceback, but should
49 Something that probably won't warrant a full traceback, but should
50 nevertheless interrupt a macro / batch file.
50 nevertheless interrupt a macro / batch file.
51 """
51 """
@@ -27,16 +27,16 b' def exception_colors():'
27 >>> print ec.active_colors
27 >>> print ec.active_colors
28 None
28 None
29
29
30 Now we activate a color scheme:
30 Now we activate a color scheme:
31 >>> ec.set_active_scheme('NoColor')
31 >>> ec.set_active_scheme('NoColor')
32 >>> ec.active_scheme_name
32 >>> ec.active_scheme_name
33 'NoColor'
33 'NoColor'
34 >>> ec.active_colors.keys()
34 >>> ec.active_colors.keys()
35 ['em', 'filenameEm', 'excName', 'valEm', 'nameEm', 'line', 'topline',
35 ['em', 'filenameEm', 'excName', 'valEm', 'nameEm', 'line', 'topline',
36 'name', 'caret', 'val', 'vName', 'Normal', 'filename', 'linenoEm',
36 'name', 'caret', 'val', 'vName', 'Normal', 'filename', 'linenoEm',
37 'lineno', 'normalEm']
37 'lineno', 'normalEm']
38 """
38 """
39
39
40 ex_colors = ColorSchemeTable()
40 ex_colors = ColorSchemeTable()
41
41
42 # Populate it with color schemes
42 # Populate it with color schemes
@@ -36,13 +36,13 b' class ExtensionManager(Configurable):'
36 def load_ipython_extension(ipython):
36 def load_ipython_extension(ipython):
37 # Do things with ipython
37 # Do things with ipython
38
38
39 This function is called after your extension is imported and the
39 This function is called after your extension is imported and the
40 currently active :class:`InteractiveShell` instance is passed as
40 currently active :class:`InteractiveShell` instance is passed as
41 the only argument. You can do anything you want with IPython at
41 the only argument. You can do anything you want with IPython at
42 that point, including defining new magic and aliases, adding new
42 that point, including defining new magic and aliases, adding new
43 components, etc.
43 components, etc.
44
44
45 The :func:`load_ipython_extension` will be called again is you
45 The :func:`load_ipython_extension` will be called again is you
46 load or reload the extension again. It is up to the extension
46 load or reload the extension again. It is up to the extension
47 author to add code to manage that.
47 author to add code to manage that.
48
48
@@ -361,26 +361,26 b' class PlainTextFormatter(BaseFormatter):'
361
361
362 # The newline character.
362 # The newline character.
363 newline = Unicode('\n', config=True)
363 newline = Unicode('\n', config=True)
364
364
365 # format-string for pprinting floats
365 # format-string for pprinting floats
366 float_format = Unicode('%r')
366 float_format = Unicode('%r')
367 # setter for float precision, either int or direct format-string
367 # setter for float precision, either int or direct format-string
368 float_precision = CUnicode('', config=True)
368 float_precision = CUnicode('', config=True)
369
369
370 def _float_precision_changed(self, name, old, new):
370 def _float_precision_changed(self, name, old, new):
371 """float_precision changed, set float_format accordingly.
371 """float_precision changed, set float_format accordingly.
372
372
373 float_precision can be set by int or str.
373 float_precision can be set by int or str.
374 This will set float_format, after interpreting input.
374 This will set float_format, after interpreting input.
375 If numpy has been imported, numpy print precision will also be set.
375 If numpy has been imported, numpy print precision will also be set.
376
376
377 integer `n` sets format to '%.nf', otherwise, format set directly.
377 integer `n` sets format to '%.nf', otherwise, format set directly.
378
378
379 An empty string returns to defaults (repr for float, 8 for numpy).
379 An empty string returns to defaults (repr for float, 8 for numpy).
380
380
381 This parameter can be set via the '%precision' magic.
381 This parameter can be set via the '%precision' magic.
382 """
382 """
383
383
384 if '%' in new:
384 if '%' in new:
385 # got explicit format string
385 # got explicit format string
386 fmt = new
386 fmt = new
@@ -397,7 +397,7 b' class PlainTextFormatter(BaseFormatter):'
397 raise ValueError("Precision must be int or format string, not %r"%new)
397 raise ValueError("Precision must be int or format string, not %r"%new)
398 except AssertionError:
398 except AssertionError:
399 raise ValueError("int precision must be non-negative, not %r"%i)
399 raise ValueError("int precision must be non-negative, not %r"%i)
400
400
401 fmt = '%%.%if'%i
401 fmt = '%%.%if'%i
402 if 'numpy' in sys.modules:
402 if 'numpy' in sys.modules:
403 # set numpy precision if it has been imported
403 # set numpy precision if it has been imported
@@ -458,7 +458,7 b' class HTMLFormatter(BaseFormatter):'
458 this.
458 this.
459
459
460 The return value of this formatter should be a valid HTML snippet that
460 The return value of this formatter should be a valid HTML snippet that
461 could be injected into an existing DOM. It should *not* include the
461 could be injected into an existing DOM. It should *not* include the
462 ```<html>`` or ```<body>`` tags.
462 ```<html>`` or ```<body>`` tags.
463 """
463 """
464 format_type = Unicode('text/html')
464 format_type = Unicode('text/html')
@@ -550,7 +550,7 b' class JavascriptFormatter(BaseFormatter):'
550 """A Javascript formatter.
550 """A Javascript formatter.
551
551
552 To define the callables that compute the Javascript representation of
552 To define the callables that compute the Javascript representation of
553 your objects, define a :meth:`_repr_javascript_` method or use the
553 your objects, define a :meth:`_repr_javascript_` method or use the
554 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
554 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
555 that handle this.
555 that handle this.
556
556
@@ -67,19 +67,19 b' class HistoryManager(Configurable):'
67 # Should we log output to the database? (default no)
67 # Should we log output to the database? (default no)
68 db_log_output = Bool(False, config=True)
68 db_log_output = Bool(False, config=True)
69 # Write to database every x commands (higher values save disk access & power)
69 # Write to database every x commands (higher values save disk access & power)
70 # Values of 1 or less effectively disable caching.
70 # Values of 1 or less effectively disable caching.
71 db_cache_size = Int(0, config=True)
71 db_cache_size = Int(0, config=True)
72 # The input and output caches
72 # The input and output caches
73 db_input_cache = List()
73 db_input_cache = List()
74 db_output_cache = List()
74 db_output_cache = List()
75
75
76 # History saving in separate thread
76 # History saving in separate thread
77 save_thread = Instance('IPython.core.history.HistorySavingThread')
77 save_thread = Instance('IPython.core.history.HistorySavingThread')
78 try: # Event is a function returning an instance of _Event...
78 try: # Event is a function returning an instance of _Event...
79 save_flag = Instance(threading._Event)
79 save_flag = Instance(threading._Event)
80 except AttributeError: # ...until Python 3.3, when it's a class.
80 except AttributeError: # ...until Python 3.3, when it's a class.
81 save_flag = Instance(threading.Event)
81 save_flag = Instance(threading.Event)
82
82
83 # Private interface
83 # Private interface
84 # Variables used to store the three last inputs from the user. On each new
84 # Variables used to store the three last inputs from the user. On each new
85 # history update, we populate the user's namespace with these, shifted as
85 # history update, we populate the user's namespace with these, shifted as
@@ -119,7 +119,7 b' class HistoryManager(Configurable):'
119 else:
119 else:
120 # The hist_file is probably :memory: or something else.
120 # The hist_file is probably :memory: or something else.
121 raise
121 raise
122
122
123 self.save_flag = threading.Event()
123 self.save_flag = threading.Event()
124 self.db_input_cache_lock = threading.Lock()
124 self.db_input_cache_lock = threading.Lock()
125 self.db_output_cache_lock = threading.Lock()
125 self.db_output_cache_lock = threading.Lock()
@@ -128,7 +128,7 b' class HistoryManager(Configurable):'
128
128
129 self.new_session()
129 self.new_session()
130
130
131
131
132 def init_db(self):
132 def init_db(self):
133 """Connect to the database, and create tables if necessary."""
133 """Connect to the database, and create tables if necessary."""
134 # use detect_types so that timestamps return datetime objects
134 # use detect_types so that timestamps return datetime objects
@@ -136,7 +136,7 b' class HistoryManager(Configurable):'
136 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
136 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
137 primary key autoincrement, start timestamp,
137 primary key autoincrement, start timestamp,
138 end timestamp, num_cmds integer, remark text)""")
138 end timestamp, num_cmds integer, remark text)""")
139 self.db.execute("""CREATE TABLE IF NOT EXISTS history
139 self.db.execute("""CREATE TABLE IF NOT EXISTS history
140 (session integer, line integer, source text, source_raw text,
140 (session integer, line integer, source text, source_raw text,
141 PRIMARY KEY (session, line))""")
141 PRIMARY KEY (session, line))""")
142 # Output history is optional, but ensure the table's there so it can be
142 # Output history is optional, but ensure the table's there so it can be
@@ -145,17 +145,17 b' class HistoryManager(Configurable):'
145 (session integer, line integer, output text,
145 (session integer, line integer, output text,
146 PRIMARY KEY (session, line))""")
146 PRIMARY KEY (session, line))""")
147 self.db.commit()
147 self.db.commit()
148
148
149 def new_session(self, conn=None):
149 def new_session(self, conn=None):
150 """Get a new session number."""
150 """Get a new session number."""
151 if conn is None:
151 if conn is None:
152 conn = self.db
152 conn = self.db
153
153
154 with conn:
154 with conn:
155 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
155 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
156 NULL, "") """, (datetime.datetime.now(),))
156 NULL, "") """, (datetime.datetime.now(),))
157 self.session_number = cur.lastrowid
157 self.session_number = cur.lastrowid
158
158
159 def end_session(self):
159 def end_session(self):
160 """Close the database session, filling in the end time and line count."""
160 """Close the database session, filling in the end time and line count."""
161 self.writeout_cache()
161 self.writeout_cache()
@@ -164,33 +164,33 b' class HistoryManager(Configurable):'
164 session==?""", (datetime.datetime.now(),
164 session==?""", (datetime.datetime.now(),
165 len(self.input_hist_parsed)-1, self.session_number))
165 len(self.input_hist_parsed)-1, self.session_number))
166 self.session_number = 0
166 self.session_number = 0
167
167
168 def name_session(self, name):
168 def name_session(self, name):
169 """Give the current session a name in the history database."""
169 """Give the current session a name in the history database."""
170 with self.db:
170 with self.db:
171 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
171 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
172 (name, self.session_number))
172 (name, self.session_number))
173
173
174 def reset(self, new_session=True):
174 def reset(self, new_session=True):
175 """Clear the session history, releasing all object references, and
175 """Clear the session history, releasing all object references, and
176 optionally open a new session."""
176 optionally open a new session."""
177 self.output_hist.clear()
177 self.output_hist.clear()
178 # The directory history can't be completely empty
178 # The directory history can't be completely empty
179 self.dir_hist[:] = [os.getcwdu()]
179 self.dir_hist[:] = [os.getcwdu()]
180
180
181 if new_session:
181 if new_session:
182 if self.session_number:
182 if self.session_number:
183 self.end_session()
183 self.end_session()
184 self.input_hist_parsed[:] = [""]
184 self.input_hist_parsed[:] = [""]
185 self.input_hist_raw[:] = [""]
185 self.input_hist_raw[:] = [""]
186 self.new_session()
186 self.new_session()
187
187
188 ## -------------------------------
188 ## -------------------------------
189 ## Methods for retrieving history:
189 ## Methods for retrieving history:
190 ## -------------------------------
190 ## -------------------------------
191 def _run_sql(self, sql, params, raw=True, output=False):
191 def _run_sql(self, sql, params, raw=True, output=False):
192 """Prepares and runs an SQL query for the history database.
192 """Prepares and runs an SQL query for the history database.
193
193
194 Parameters
194 Parameters
195 ----------
195 ----------
196 sql : str
196 sql : str
@@ -199,7 +199,7 b' class HistoryManager(Configurable):'
199 Parameters passed to the SQL query (to replace "?")
199 Parameters passed to the SQL query (to replace "?")
200 raw, output : bool
200 raw, output : bool
201 See :meth:`get_range`
201 See :meth:`get_range`
202
202
203 Returns
203 Returns
204 -------
204 -------
205 Tuples as :meth:`get_range`
205 Tuples as :meth:`get_range`
@@ -214,38 +214,38 b' class HistoryManager(Configurable):'
214 if output: # Regroup into 3-tuples, and parse JSON
214 if output: # Regroup into 3-tuples, and parse JSON
215 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
215 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
216 return cur
216 return cur
217
217
218
218
219 def get_session_info(self, session=0):
219 def get_session_info(self, session=0):
220 """get info about a session
220 """get info about a session
221
221
222 Parameters
222 Parameters
223 ----------
223 ----------
224
224
225 session : int
225 session : int
226 Session number to retrieve. The current session is 0, and negative
226 Session number to retrieve. The current session is 0, and negative
227 numbers count back from current session, so -1 is previous session.
227 numbers count back from current session, so -1 is previous session.
228
228
229 Returns
229 Returns
230 -------
230 -------
231
231
232 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
232 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
233
233
234 Sessions that are running or did not exit cleanly will have `end=None`
234 Sessions that are running or did not exit cleanly will have `end=None`
235 and `num_cmds=None`.
235 and `num_cmds=None`.
236
236
237 """
237 """
238
238
239 if session <= 0:
239 if session <= 0:
240 session += self.session_number
240 session += self.session_number
241
241
242 query = "SELECT * from sessions where session == ?"
242 query = "SELECT * from sessions where session == ?"
243 return self.db.execute(query, (session,)).fetchone()
243 return self.db.execute(query, (session,)).fetchone()
244
244
245
245
246 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
246 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
247 """Get the last n lines from the history database.
247 """Get the last n lines from the history database.
248
248
249 Parameters
249 Parameters
250 ----------
250 ----------
251 n : int
251 n : int
@@ -256,7 +256,7 b' class HistoryManager(Configurable):'
256 If False (default), n+1 lines are fetched, and the latest one
256 If False (default), n+1 lines are fetched, and the latest one
257 is discarded. This is intended to be used where the function
257 is discarded. This is intended to be used where the function
258 is called by a user command, which it should not return.
258 is called by a user command, which it should not return.
259
259
260 Returns
260 Returns
261 -------
261 -------
262 Tuples as :meth:`get_range`
262 Tuples as :meth:`get_range`
@@ -269,12 +269,12 b' class HistoryManager(Configurable):'
269 if not include_latest:
269 if not include_latest:
270 return reversed(list(cur)[1:])
270 return reversed(list(cur)[1:])
271 return reversed(list(cur))
271 return reversed(list(cur))
272
272
273 def search(self, pattern="*", raw=True, search_raw=True,
273 def search(self, pattern="*", raw=True, search_raw=True,
274 output=False):
274 output=False):
275 """Search the database using unix glob-style matching (wildcards
275 """Search the database using unix glob-style matching (wildcards
276 * and ?).
276 * and ?).
277
277
278 Parameters
278 Parameters
279 ----------
279 ----------
280 pattern : str
280 pattern : str
@@ -283,7 +283,7 b' class HistoryManager(Configurable):'
283 If True, search the raw input, otherwise, the parsed input
283 If True, search the raw input, otherwise, the parsed input
284 raw, output : bool
284 raw, output : bool
285 See :meth:`get_range`
285 See :meth:`get_range`
286
286
287 Returns
287 Returns
288 -------
288 -------
289 Tuples as :meth:`get_range`
289 Tuples as :meth:`get_range`
@@ -294,12 +294,12 b' class HistoryManager(Configurable):'
294 self.writeout_cache()
294 self.writeout_cache()
295 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
295 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
296 raw=raw, output=output)
296 raw=raw, output=output)
297
297
298 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
298 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
299 """Get input and output history from the current session. Called by
299 """Get input and output history from the current session. Called by
300 get_range, and takes similar parameters."""
300 get_range, and takes similar parameters."""
301 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
301 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
302
302
303 n = len(input_hist)
303 n = len(input_hist)
304 if start < 0:
304 if start < 0:
305 start += n
305 start += n
@@ -307,17 +307,17 b' class HistoryManager(Configurable):'
307 stop = n
307 stop = n
308 elif stop < 0:
308 elif stop < 0:
309 stop += n
309 stop += n
310
310
311 for i in range(start, stop):
311 for i in range(start, stop):
312 if output:
312 if output:
313 line = (input_hist[i], self.output_hist_reprs.get(i))
313 line = (input_hist[i], self.output_hist_reprs.get(i))
314 else:
314 else:
315 line = input_hist[i]
315 line = input_hist[i]
316 yield (0, i, line)
316 yield (0, i, line)
317
317
318 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
318 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
319 """Retrieve input by session.
319 """Retrieve input by session.
320
320
321 Parameters
321 Parameters
322 ----------
322 ----------
323 session : int
323 session : int
@@ -335,7 +335,7 b' class HistoryManager(Configurable):'
335 objects for the current session, or text reprs from previous
335 objects for the current session, or text reprs from previous
336 sessions if db_log_output was enabled at the time. Where no output
336 sessions if db_log_output was enabled at the time. Where no output
337 is found, None is used.
337 is found, None is used.
338
338
339 Returns
339 Returns
340 -------
340 -------
341 An iterator over the desired lines. Each line is a 3-tuple, either
341 An iterator over the desired lines. Each line is a 3-tuple, either
@@ -346,21 +346,21 b' class HistoryManager(Configurable):'
346 return self._get_range_session(start, stop, raw, output)
346 return self._get_range_session(start, stop, raw, output)
347 if session < 0:
347 if session < 0:
348 session += self.session_number
348 session += self.session_number
349
349
350 if stop:
350 if stop:
351 lineclause = "line >= ? AND line < ?"
351 lineclause = "line >= ? AND line < ?"
352 params = (session, start, stop)
352 params = (session, start, stop)
353 else:
353 else:
354 lineclause = "line>=?"
354 lineclause = "line>=?"
355 params = (session, start)
355 params = (session, start)
356
356
357 return self._run_sql("WHERE session==? AND %s""" % lineclause,
357 return self._run_sql("WHERE session==? AND %s""" % lineclause,
358 params, raw=raw, output=output)
358 params, raw=raw, output=output)
359
359
360 def get_range_by_str(self, rangestr, raw=True, output=False):
360 def get_range_by_str(self, rangestr, raw=True, output=False):
361 """Get lines of history from a string of ranges, as used by magic
361 """Get lines of history from a string of ranges, as used by magic
362 commands %hist, %save, %macro, etc.
362 commands %hist, %save, %macro, etc.
363
363
364 Parameters
364 Parameters
365 ----------
365 ----------
366 rangestr : str
366 rangestr : str
@@ -368,7 +368,7 b' class HistoryManager(Configurable):'
368 :func:`magic_history` for full details.
368 :func:`magic_history` for full details.
369 raw, output : bool
369 raw, output : bool
370 As :meth:`get_range`
370 As :meth:`get_range`
371
371
372 Returns
372 Returns
373 -------
373 -------
374 Tuples as :meth:`get_range`
374 Tuples as :meth:`get_range`
@@ -376,19 +376,19 b' class HistoryManager(Configurable):'
376 for sess, s, e in extract_hist_ranges(rangestr):
376 for sess, s, e in extract_hist_ranges(rangestr):
377 for line in self.get_range(sess, s, e, raw=raw, output=output):
377 for line in self.get_range(sess, s, e, raw=raw, output=output):
378 yield line
378 yield line
379
379
380 ## ----------------------------
380 ## ----------------------------
381 ## Methods for storing history:
381 ## Methods for storing history:
382 ## ----------------------------
382 ## ----------------------------
383 def store_inputs(self, line_num, source, source_raw=None):
383 def store_inputs(self, line_num, source, source_raw=None):
384 """Store source and raw input in history and create input cache
384 """Store source and raw input in history and create input cache
385 variables _i*.
385 variables _i*.
386
386
387 Parameters
387 Parameters
388 ----------
388 ----------
389 line_num : int
389 line_num : int
390 The prompt number of this input.
390 The prompt number of this input.
391
391
392 source : str
392 source : str
393 Python input.
393 Python input.
394
394
@@ -400,14 +400,14 b' class HistoryManager(Configurable):'
400 source_raw = source
400 source_raw = source
401 source = source.rstrip('\n')
401 source = source.rstrip('\n')
402 source_raw = source_raw.rstrip('\n')
402 source_raw = source_raw.rstrip('\n')
403
403
404 # do not store exit/quit commands
404 # do not store exit/quit commands
405 if self._exit_re.match(source_raw.strip()):
405 if self._exit_re.match(source_raw.strip()):
406 return
406 return
407
407
408 self.input_hist_parsed.append(source)
408 self.input_hist_parsed.append(source)
409 self.input_hist_raw.append(source_raw)
409 self.input_hist_raw.append(source_raw)
410
410
411 with self.db_input_cache_lock:
411 with self.db_input_cache_lock:
412 self.db_input_cache.append((line_num, source, source_raw))
412 self.db_input_cache.append((line_num, source, source_raw))
413 # Trigger to flush cache and write to DB.
413 # Trigger to flush cache and write to DB.
@@ -427,12 +427,12 b' class HistoryManager(Configurable):'
427 '_iii': self._iii,
427 '_iii': self._iii,
428 new_i : self._i00 }
428 new_i : self._i00 }
429 self.shell.user_ns.update(to_main)
429 self.shell.user_ns.update(to_main)
430
430
431 def store_output(self, line_num):
431 def store_output(self, line_num):
432 """If database output logging is enabled, this saves all the
432 """If database output logging is enabled, this saves all the
433 outputs from the indicated prompt number to the database. It's
433 outputs from the indicated prompt number to the database. It's
434 called by run_cell after code has been executed.
434 called by run_cell after code has been executed.
435
435
436 Parameters
436 Parameters
437 ----------
437 ----------
438 line_num : int
438 line_num : int
@@ -441,29 +441,29 b' class HistoryManager(Configurable):'
441 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
441 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
442 return
442 return
443 output = self.output_hist_reprs[line_num]
443 output = self.output_hist_reprs[line_num]
444
444
445 with self.db_output_cache_lock:
445 with self.db_output_cache_lock:
446 self.db_output_cache.append((line_num, output))
446 self.db_output_cache.append((line_num, output))
447 if self.db_cache_size <= 1:
447 if self.db_cache_size <= 1:
448 self.save_flag.set()
448 self.save_flag.set()
449
449
450 def _writeout_input_cache(self, conn):
450 def _writeout_input_cache(self, conn):
451 with conn:
451 with conn:
452 for line in self.db_input_cache:
452 for line in self.db_input_cache:
453 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
453 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
454 (self.session_number,)+line)
454 (self.session_number,)+line)
455
455
456 def _writeout_output_cache(self, conn):
456 def _writeout_output_cache(self, conn):
457 with conn:
457 with conn:
458 for line in self.db_output_cache:
458 for line in self.db_output_cache:
459 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
459 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
460 (self.session_number,)+line)
460 (self.session_number,)+line)
461
461
462 def writeout_cache(self, conn=None):
462 def writeout_cache(self, conn=None):
463 """Write any entries in the cache to the database."""
463 """Write any entries in the cache to the database."""
464 if conn is None:
464 if conn is None:
465 conn = self.db
465 conn = self.db
466
466
467 with self.db_input_cache_lock:
467 with self.db_input_cache_lock:
468 try:
468 try:
469 self._writeout_input_cache(conn)
469 self._writeout_input_cache(conn)
@@ -492,7 +492,7 b' class HistoryManager(Configurable):'
492 class HistorySavingThread(threading.Thread):
492 class HistorySavingThread(threading.Thread):
493 """This thread takes care of writing history to the database, so that
493 """This thread takes care of writing history to the database, so that
494 the UI isn't held up while that happens.
494 the UI isn't held up while that happens.
495
495
496 It waits for the HistoryManager's save_flag to be set, then writes out
496 It waits for the HistoryManager's save_flag to be set, then writes out
497 the history cache. The main thread is responsible for setting the flag when
497 the history cache. The main thread is responsible for setting the flag when
498 the cache size reaches a defined threshold."""
498 the cache size reaches a defined threshold."""
@@ -502,7 +502,7 b' class HistorySavingThread(threading.Thread):'
502 super(HistorySavingThread, self).__init__()
502 super(HistorySavingThread, self).__init__()
503 self.history_manager = history_manager
503 self.history_manager = history_manager
504 atexit.register(self.stop)
504 atexit.register(self.stop)
505
505
506 def run(self):
506 def run(self):
507 # We need a separate db connection per thread:
507 # We need a separate db connection per thread:
508 try:
508 try:
@@ -516,10 +516,10 b' class HistorySavingThread(threading.Thread):'
516 except Exception as e:
516 except Exception as e:
517 print(("The history saving thread hit an unexpected error (%s)."
517 print(("The history saving thread hit an unexpected error (%s)."
518 "History will not be written to the database.") % repr(e))
518 "History will not be written to the database.") % repr(e))
519
519
520 def stop(self):
520 def stop(self):
521 """This can be called from the main thread to safely stop this thread.
521 """This can be called from the main thread to safely stop this thread.
522
522
523 Note that it does not attempt to write out remaining history before
523 Note that it does not attempt to write out remaining history before
524 exiting. That should be done by calling the HistoryManager's
524 exiting. That should be done by calling the HistoryManager's
525 end_session method."""
525 end_session method."""
@@ -527,7 +527,7 b' class HistorySavingThread(threading.Thread):'
527 self.history_manager.save_flag.set()
527 self.history_manager.save_flag.set()
528 self.join()
528 self.join()
529
529
530
530
531 # To match, e.g. ~5/8-~2/3
531 # To match, e.g. ~5/8-~2/3
532 range_re = re.compile(r"""
532 range_re = re.compile(r"""
533 ((?P<startsess>~?\d+)/)?
533 ((?P<startsess>~?\d+)/)?
@@ -539,7 +539,7 b' $""", re.VERBOSE)'
539
539
540 def extract_hist_ranges(ranges_str):
540 def extract_hist_ranges(ranges_str):
541 """Turn a string of history ranges into 3-tuples of (session, start, stop).
541 """Turn a string of history ranges into 3-tuples of (session, start, stop).
542
542
543 Examples
543 Examples
544 --------
544 --------
545 list(extract_input_ranges("~8/5-~7/4 2"))
545 list(extract_input_ranges("~8/5-~7/4 2"))
@@ -578,7 +578,7 b' def _format_lineno(session, line):'
578 @skip_doctest
578 @skip_doctest
579 def magic_history(self, parameter_s = ''):
579 def magic_history(self, parameter_s = ''):
580 """Print input history (_i<n> variables), with most recent last.
580 """Print input history (_i<n> variables), with most recent last.
581
581
582 %history -> print at most 40 inputs (some may be multi-line)\\
582 %history -> print at most 40 inputs (some may be multi-line)\\
583 %history n -> print at most n inputs\\
583 %history n -> print at most n inputs\\
584 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
584 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
@@ -594,7 +594,7 b" def magic_history(self, parameter_s = ''):"
594 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
594 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
595 of 6 sessions ago.
595 of 6 sessions ago.
596 Multiple ranges can be entered, separated by spaces
596 Multiple ranges can be entered, separated by spaces
597
597
598 The same syntax is used by %macro, %save, %edit, %rerun
598 The same syntax is used by %macro, %save, %edit, %rerun
599
599
600 Options:
600 Options:
@@ -609,29 +609,29 b" def magic_history(self, parameter_s = ''):"
609 doctest-ready output.
609 doctest-ready output.
610
610
611 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
611 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
612
612
613 -t: print the 'translated' history, as IPython understands it. IPython
613 -t: print the 'translated' history, as IPython understands it. IPython
614 filters your input and converts it all into valid Python source before
614 filters your input and converts it all into valid Python source before
615 executing it (things like magics or aliases are turned into function
615 executing it (things like magics or aliases are turned into function
616 calls, for example). With this option, you'll see the native history
616 calls, for example). With this option, you'll see the native history
617 instead of the user-entered version: '%cd /' will be seen as
617 instead of the user-entered version: '%cd /' will be seen as
618 'get_ipython().magic("%cd /")' instead of '%cd /'.
618 'get_ipython().magic("%cd /")' instead of '%cd /'.
619
619
620 -g: treat the arg as a pattern to grep for in (full) history.
620 -g: treat the arg as a pattern to grep for in (full) history.
621 This includes the saved history (almost all commands ever written).
621 This includes the saved history (almost all commands ever written).
622 Use '%hist -g' to show full saved history (may be very long).
622 Use '%hist -g' to show full saved history (may be very long).
623
623
624 -l: get the last n lines from all sessions. Specify n as a single arg, or
624 -l: get the last n lines from all sessions. Specify n as a single arg, or
625 the default is the last 10 lines.
625 the default is the last 10 lines.
626
626
627 -f FILENAME: instead of printing the output to the screen, redirect it to
627 -f FILENAME: instead of printing the output to the screen, redirect it to
628 the given file. The file is always overwritten, though IPython asks for
628 the given file. The file is always overwritten, though IPython asks for
629 confirmation first if it already exists.
629 confirmation first if it already exists.
630
630
631 Examples
631 Examples
632 --------
632 --------
633 ::
633 ::
634
634
635 In [6]: %hist -n 4 6
635 In [6]: %hist -n 4 6
636 4:a = 12
636 4:a = 12
637 5:print a**2
637 5:print a**2
@@ -642,10 +642,10 b" def magic_history(self, parameter_s = ''):"
642 print('This feature is only available if numbered prompts are in use.')
642 print('This feature is only available if numbered prompts are in use.')
643 return
643 return
644 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
644 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
645
645
646 # For brevity
646 # For brevity
647 history_manager = self.shell.history_manager
647 history_manager = self.shell.history_manager
648
648
649 def _format_lineno(session, line):
649 def _format_lineno(session, line):
650 """Helper function to format line numbers properly."""
650 """Helper function to format line numbers properly."""
651 if session in (0, history_manager.session_number):
651 if session in (0, history_manager.session_number):
@@ -661,22 +661,22 b" def magic_history(self, parameter_s = ''):"
661 close_at_end = False
661 close_at_end = False
662 else:
662 else:
663 if os.path.exists(outfname):
663 if os.path.exists(outfname):
664 if not io.ask_yes_no("File %r exists. Overwrite?" % outfname):
664 if not io.ask_yes_no("File %r exists. Overwrite?" % outfname):
665 print('Aborting.')
665 print('Aborting.')
666 return
666 return
667
667
668 outfile = open(outfname,'w')
668 outfile = open(outfname,'w')
669 close_at_end = True
669 close_at_end = True
670
670
671 print_nums = 'n' in opts
671 print_nums = 'n' in opts
672 get_output = 'o' in opts
672 get_output = 'o' in opts
673 pyprompts = 'p' in opts
673 pyprompts = 'p' in opts
674 # Raw history is the default
674 # Raw history is the default
675 raw = not('t' in opts)
675 raw = not('t' in opts)
676
676
677 default_length = 40
677 default_length = 40
678 pattern = None
678 pattern = None
679
679
680 if 'g' in opts: # Glob search
680 if 'g' in opts: # Glob search
681 pattern = "*" + args + "*" if args else "*"
681 pattern = "*" + args + "*" if args else "*"
682 hist = history_manager.search(pattern, raw=raw, output=get_output)
682 hist = history_manager.search(pattern, raw=raw, output=get_output)
@@ -692,11 +692,11 b" def magic_history(self, parameter_s = ''):"
692 hist = history_manager.get_range_by_str(args, raw, get_output)
692 hist = history_manager.get_range_by_str(args, raw, get_output)
693 else: # Just get history for the current session
693 else: # Just get history for the current session
694 hist = history_manager.get_range(raw=raw, output=get_output)
694 hist = history_manager.get_range(raw=raw, output=get_output)
695
695
696 # We could be displaying the entire history, so let's not try to pull it
696 # We could be displaying the entire history, so let's not try to pull it
697 # into a list in memory. Anything that needs more space will just misalign.
697 # into a list in memory. Anything that needs more space will just misalign.
698 width = 4
698 width = 4
699
699
700 for session, lineno, inline in hist:
700 for session, lineno, inline in hist:
701 # Print user history with tabs expanded to 4 spaces. The GUI clients
701 # Print user history with tabs expanded to 4 spaces. The GUI clients
702 # use hard tabs for easier usability in auto-indented code, but we want
702 # use hard tabs for easier usability in auto-indented code, but we want
@@ -704,7 +704,7 b" def magic_history(self, parameter_s = ''):"
704 if get_output:
704 if get_output:
705 inline, output = inline
705 inline, output = inline
706 inline = inline.expandtabs(4).rstrip()
706 inline = inline.expandtabs(4).rstrip()
707
707
708 multiline = "\n" in inline
708 multiline = "\n" in inline
709 line_sep = '\n' if multiline else ' '
709 line_sep = '\n' if multiline else ' '
710 if print_nums:
710 if print_nums:
@@ -727,29 +727,29 b' def magic_rep(self, arg):'
727 %rep are equivalent.
727 %rep are equivalent.
728
728
729 - %recall (no arguments):
729 - %recall (no arguments):
730
730
731 Place a string version of last computation result (stored in the special '_'
731 Place a string version of last computation result (stored in the special '_'
732 variable) to the next input prompt. Allows you to create elaborate command
732 variable) to the next input prompt. Allows you to create elaborate command
733 lines without using copy-paste::
733 lines without using copy-paste::
734
734
735 In[1]: l = ["hei", "vaan"]
735 In[1]: l = ["hei", "vaan"]
736 In[2]: "".join(l)
736 In[2]: "".join(l)
737 Out[2]: heivaan
737 Out[2]: heivaan
738 In[3]: %rep
738 In[3]: %rep
739 In[4]: heivaan_ <== cursor blinking
739 In[4]: heivaan_ <== cursor blinking
740
740
741 %recall 45
741 %recall 45
742
742
743 Place history line 45 on the next input prompt. Use %hist to find
743 Place history line 45 on the next input prompt. Use %hist to find
744 out the number.
744 out the number.
745
745
746 %recall 1-4
746 %recall 1-4
747
747
748 Combine the specified lines into one cell, and place it on the next
748 Combine the specified lines into one cell, and place it on the next
749 input prompt. See %history for the slice syntax.
749 input prompt. See %history for the slice syntax.
750
750
751 %recall foo+bar
751 %recall foo+bar
752
752
753 If foo+bar can be evaluated in the user namespace, the result is
753 If foo+bar can be evaluated in the user namespace, the result is
754 placed at the next input prompt. Otherwise, the history is searched
754 placed at the next input prompt. Otherwise, the history is searched
755 for lines which contain that substring, and the most recent one is
755 for lines which contain that substring, and the most recent one is
@@ -777,18 +777,18 b' def magic_rep(self, arg):'
777 else:
777 else:
778 self.set_next_input(cmd.rstrip())
778 self.set_next_input(cmd.rstrip())
779 print("Couldn't evaluate or find in history:", arg)
779 print("Couldn't evaluate or find in history:", arg)
780
780
781 def magic_rerun(self, parameter_s=''):
781 def magic_rerun(self, parameter_s=''):
782 """Re-run previous input
782 """Re-run previous input
783
783
784 By default, you can specify ranges of input history to be repeated
784 By default, you can specify ranges of input history to be repeated
785 (as with %history). With no arguments, it will repeat the last line.
785 (as with %history). With no arguments, it will repeat the last line.
786
786
787 Options:
787 Options:
788
788
789 -l <n> : Repeat the last n lines of input, not including the
789 -l <n> : Repeat the last n lines of input, not including the
790 current command.
790 current command.
791
791
792 -g foo : Repeat the most recent line which contains foo
792 -g foo : Repeat the most recent line which contains foo
793 """
793 """
794 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
794 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
@@ -820,7 +820,7 b" def magic_rerun(self, parameter_s=''):"
820
820
821
821
822 def init_ipython(ip):
822 def init_ipython(ip):
823 ip.define_magic("rep", magic_rep)
823 ip.define_magic("rep", magic_rep)
824 ip.define_magic("recall", magic_rep)
824 ip.define_magic("recall", magic_rep)
825 ip.define_magic("rerun", magic_rerun)
825 ip.define_magic("rerun", magic_rerun)
826 ip.define_magic("hist",magic_history) # Alternative name
826 ip.define_magic("hist",magic_history) # Alternative name
@@ -64,24 +64,24 b' def editor(self,filename, linenum=None):'
64 # IPython configures a default editor at startup by reading $EDITOR from
64 # IPython configures a default editor at startup by reading $EDITOR from
65 # the environment, and falling back on vi (unix) or notepad (win32).
65 # the environment, and falling back on vi (unix) or notepad (win32).
66 editor = self.editor
66 editor = self.editor
67
67
68 # marker for at which line to open the file (for existing objects)
68 # marker for at which line to open the file (for existing objects)
69 if linenum is None or editor=='notepad':
69 if linenum is None or editor=='notepad':
70 linemark = ''
70 linemark = ''
71 else:
71 else:
72 linemark = '+%d' % int(linenum)
72 linemark = '+%d' % int(linenum)
73
73
74 # Enclose in quotes if necessary and legal
74 # Enclose in quotes if necessary and legal
75 if ' ' in editor and os.path.isfile(editor) and editor[0] != '"':
75 if ' ' in editor and os.path.isfile(editor) and editor[0] != '"':
76 editor = '"%s"' % editor
76 editor = '"%s"' % editor
77
77
78 # Call the actual editor
78 # Call the actual editor
79 if os.system('%s %s %s' % (editor,linemark,filename)) != 0:
79 if os.system('%s %s %s' % (editor,linemark,filename)) != 0:
80 raise TryNext()
80 raise TryNext()
81
81
82 import tempfile
82 import tempfile
83 def fix_error_editor(self,filename,linenum,column,msg):
83 def fix_error_editor(self,filename,linenum,column,msg):
84 """Open the editor at the given filename, linenumber, column and
84 """Open the editor at the given filename, linenumber, column and
85 show an error message. This is used for correcting syntax errors.
85 show an error message. This is used for correcting syntax errors.
86 The current implementation only has special support for the VIM editor,
86 The current implementation only has special support for the VIM editor,
87 and falls back on the 'editor' hook if VIM is not used.
87 and falls back on the 'editor' hook if VIM is not used.
@@ -110,25 +110,25 b' def synchronize_with_editor(self, filename, linenum, column):'
110
110
111 class CommandChainDispatcher:
111 class CommandChainDispatcher:
112 """ Dispatch calls to a chain of commands until some func can handle it
112 """ Dispatch calls to a chain of commands until some func can handle it
113
113
114 Usage: instantiate, execute "add" to add commands (with optional
114 Usage: instantiate, execute "add" to add commands (with optional
115 priority), execute normally via f() calling mechanism.
115 priority), execute normally via f() calling mechanism.
116
116
117 """
117 """
118 def __init__(self,commands=None):
118 def __init__(self,commands=None):
119 if commands is None:
119 if commands is None:
120 self.chain = []
120 self.chain = []
121 else:
121 else:
122 self.chain = commands
122 self.chain = commands
123
123
124
124
125 def __call__(self,*args, **kw):
125 def __call__(self,*args, **kw):
126 """ Command chain is called just like normal func.
126 """ Command chain is called just like normal func.
127
127
128 This will call all funcs in chain with the same args as were given to this
128 This will call all funcs in chain with the same args as were given to this
129 function, and return the result of first func that didn't raise
129 function, and return the result of first func that didn't raise
130 TryNext """
130 TryNext """
131
131
132 for prio,cmd in self.chain:
132 for prio,cmd in self.chain:
133 #print "prio",prio,"cmd",cmd #dbg
133 #print "prio",prio,"cmd",cmd #dbg
134 try:
134 try:
@@ -139,32 +139,32 b' class CommandChainDispatcher:'
139 kw = exc.kwargs
139 kw = exc.kwargs
140 # if no function will accept it, raise TryNext up to the caller
140 # if no function will accept it, raise TryNext up to the caller
141 raise TryNext
141 raise TryNext
142
142
143 def __str__(self):
143 def __str__(self):
144 return str(self.chain)
144 return str(self.chain)
145
145
146 def add(self, func, priority=0):
146 def add(self, func, priority=0):
147 """ Add a func to the cmd chain with given priority """
147 """ Add a func to the cmd chain with given priority """
148 bisect.insort(self.chain,(priority,func))
148 bisect.insort(self.chain,(priority,func))
149
149
150 def __iter__(self):
150 def __iter__(self):
151 """ Return all objects in chain.
151 """ Return all objects in chain.
152
152
153 Handy if the objects are not callable.
153 Handy if the objects are not callable.
154 """
154 """
155 return iter(self.chain)
155 return iter(self.chain)
156
156
157
157
158 def input_prefilter(self,line):
158 def input_prefilter(self,line):
159 """ Default input prefilter
159 """ Default input prefilter
160
160
161 This returns the line as unchanged, so that the interpreter
161 This returns the line as unchanged, so that the interpreter
162 knows that nothing was done and proceeds with "classic" prefiltering
162 knows that nothing was done and proceeds with "classic" prefiltering
163 (%magics, !shell commands etc.).
163 (%magics, !shell commands etc.).
164
164
165 Note that leading whitespace is not passed to this hook. Prefilter
165 Note that leading whitespace is not passed to this hook. Prefilter
166 can't alter indentation.
166 can't alter indentation.
167
167
168 """
168 """
169 #print "attempt to rewrite",line #dbg
169 #print "attempt to rewrite",line #dbg
170 return line
170 return line
@@ -172,17 +172,17 b' def input_prefilter(self,line):'
172
172
173 def shutdown_hook(self):
173 def shutdown_hook(self):
174 """ default shutdown hook
174 """ default shutdown hook
175
175
176 Typically, shotdown hooks should raise TryNext so all shutdown ops are done
176 Typically, shotdown hooks should raise TryNext so all shutdown ops are done
177 """
177 """
178
178
179 #print "default shutdown hook ok" # dbg
179 #print "default shutdown hook ok" # dbg
180 return
180 return
181
181
182
182
183 def late_startup_hook(self):
183 def late_startup_hook(self):
184 """ Executed after ipython has been constructed and configured
184 """ Executed after ipython has been constructed and configured
185
185
186 """
186 """
187 #print "default startup hook ok" # dbg
187 #print "default startup hook ok" # dbg
188
188
@@ -202,11 +202,11 b' def show_in_pager(self,s):'
202
202
203 def pre_prompt_hook(self):
203 def pre_prompt_hook(self):
204 """ Run before displaying the next prompt
204 """ Run before displaying the next prompt
205
205
206 Use this e.g. to display output from asynchronous operations (in order
206 Use this e.g. to display output from asynchronous operations (in order
207 to not mess up text entry)
207 to not mess up text entry)
208 """
208 """
209
209
210 return None
210 return None
211
211
212
212
@@ -219,7 +219,7 b' def clipboard_get(self):'
219 """ Get text from the clipboard.
219 """ Get text from the clipboard.
220 """
220 """
221 from IPython.lib.clipboard import (
221 from IPython.lib.clipboard import (
222 osx_clipboard_get, tkinter_clipboard_get,
222 osx_clipboard_get, tkinter_clipboard_get,
223 win32_clipboard_get
223 win32_clipboard_get
224 )
224 )
225 if sys.platform == 'win32':
225 if sys.platform == 'win32':
@@ -145,7 +145,7 b' class ReadlineNoRecord(object):'
145 def __init__(self, shell):
145 def __init__(self, shell):
146 self.shell = shell
146 self.shell = shell
147 self._nested_level = 0
147 self._nested_level = 0
148
148
149 def __enter__(self):
149 def __enter__(self):
150 if self._nested_level == 0:
150 if self._nested_level == 0:
151 try:
151 try:
@@ -154,7 +154,7 b' class ReadlineNoRecord(object):'
154 except (AttributeError, IndexError): # Can fail with pyreadline
154 except (AttributeError, IndexError): # Can fail with pyreadline
155 self.orig_length, self.readline_tail = 999999, []
155 self.orig_length, self.readline_tail = 999999, []
156 self._nested_level += 1
156 self._nested_level += 1
157
157
158 def __exit__(self, type, value, traceback):
158 def __exit__(self, type, value, traceback):
159 self._nested_level -= 1
159 self._nested_level -= 1
160 if self._nested_level == 0:
160 if self._nested_level == 0:
@@ -164,7 +164,7 b' class ReadlineNoRecord(object):'
164 if e > 0:
164 if e > 0:
165 for _ in range(e):
165 for _ in range(e):
166 self.shell.readline.remove_history_item(self.orig_length)
166 self.shell.readline.remove_history_item(self.orig_length)
167
167
168 # If it still doesn't match, just reload readline history.
168 # If it still doesn't match, just reload readline history.
169 if self.current_length() != self.orig_length \
169 if self.current_length() != self.orig_length \
170 or self.get_readline_tail() != self.readline_tail:
170 or self.get_readline_tail() != self.readline_tail:
@@ -173,10 +173,10 b' class ReadlineNoRecord(object):'
173 pass
173 pass
174 # Returning False will cause exceptions to propagate
174 # Returning False will cause exceptions to propagate
175 return False
175 return False
176
176
177 def current_length(self):
177 def current_length(self):
178 return self.shell.readline.get_current_history_length()
178 return self.shell.readline.get_current_history_length()
179
179
180 def get_readline_tail(self, n=10):
180 def get_readline_tail(self, n=10):
181 """Get the last n items in readline history."""
181 """Get the last n items in readline history."""
182 end = self.shell.readline.get_current_history_length() + 1
182 end = self.shell.readline.get_current_history_length() + 1
@@ -243,7 +243,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
243 get confused with color codes, this capability can be turned off.
243 get confused with color codes, this capability can be turned off.
244 """
244 """
245 )
245 )
246 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
246 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
247 default_value=get_default_colors(), config=True,
247 default_value=get_default_colors(), config=True,
248 help="Set the color scheme (NoColor, Linux, or LightBG)."
248 help="Set the color scheme (NoColor, Linux, or LightBG)."
249 )
249 )
@@ -315,7 +315,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
315 quiet = CBool(False, config=True)
315 quiet = CBool(False, config=True)
316
316
317 history_length = Int(10000, config=True)
317 history_length = Int(10000, config=True)
318
318
319 # The readline stuff will eventually be moved to the terminal subclass
319 # The readline stuff will eventually be moved to the terminal subclass
320 # but for now, we can't do that as readline is welded in everywhere.
320 # but for now, we can't do that as readline is welded in everywhere.
321 readline_use = CBool(True, config=True)
321 readline_use = CBool(True, config=True)
@@ -345,7 +345,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
345 separate_out = SeparateUnicode('', config=True)
345 separate_out = SeparateUnicode('', config=True)
346 separate_out2 = SeparateUnicode('', config=True)
346 separate_out2 = SeparateUnicode('', config=True)
347 wildcards_case_sensitive = CBool(True, config=True)
347 wildcards_case_sensitive = CBool(True, config=True)
348 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
348 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
349 default_value='Context', config=True)
349 default_value='Context', config=True)
350
350
351 # Subcomponents of InteractiveShell
351 # Subcomponents of InteractiveShell
@@ -393,7 +393,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
393 # is what we want to do.
393 # is what we want to do.
394 self.save_sys_module_state()
394 self.save_sys_module_state()
395 self.init_sys_modules()
395 self.init_sys_modules()
396
396
397 # While we're trying to have each part of the code directly access what
397 # While we're trying to have each part of the code directly access what
398 # it needs without keeping redundant references to objects, we have too
398 # it needs without keeping redundant references to objects, we have too
399 # much legacy code that expects ip.db to exist.
399 # much legacy code that expects ip.db to exist.
@@ -583,7 +583,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
583 def init_io(self):
583 def init_io(self):
584 # This will just use sys.stdout and sys.stderr. If you want to
584 # This will just use sys.stdout and sys.stderr. If you want to
585 # override sys.stdout and sys.stderr themselves, you need to do that
585 # override sys.stdout and sys.stderr themselves, you need to do that
586 # *before* instantiating this class, because io holds onto
586 # *before* instantiating this class, because io holds onto
587 # references to the underlying streams.
587 # references to the underlying streams.
588 if sys.platform == 'win32' and self.has_readline:
588 if sys.platform == 'win32' and self.has_readline:
589 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
589 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
@@ -593,7 +593,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
593
593
594 def init_prompts(self):
594 def init_prompts(self):
595 # TODO: This is a pass for now because the prompts are managed inside
595 # TODO: This is a pass for now because the prompts are managed inside
596 # the DisplayHook. Once there is a separate prompt manager, this
596 # the DisplayHook. Once there is a separate prompt manager, this
597 # will initialize that object and all prompt related information.
597 # will initialize that object and all prompt related information.
598 pass
598 pass
599
599
@@ -682,13 +682,13 b' class InteractiveShell(SingletonConfigurable, Magic):'
682 """set_hook(name,hook) -> sets an internal IPython hook.
682 """set_hook(name,hook) -> sets an internal IPython hook.
683
683
684 IPython exposes some of its internal API as user-modifiable hooks. By
684 IPython exposes some of its internal API as user-modifiable hooks. By
685 adding your function to one of these hooks, you can modify IPython's
685 adding your function to one of these hooks, you can modify IPython's
686 behavior to call at runtime your own routines."""
686 behavior to call at runtime your own routines."""
687
687
688 # At some point in the future, this should validate the hook before it
688 # At some point in the future, this should validate the hook before it
689 # accepts it. Probably at least check that the hook takes the number
689 # accepts it. Probably at least check that the hook takes the number
690 # of args it's supposed to.
690 # of args it's supposed to.
691
691
692 f = types.MethodType(hook,self)
692 f = types.MethodType(hook,self)
693
693
694 # check if the hook is for strdispatcher first
694 # check if the hook is for strdispatcher first
@@ -702,14 +702,14 b' class InteractiveShell(SingletonConfigurable, Magic):'
702 sdp.add_re(re.compile(re_key), f, priority )
702 sdp.add_re(re.compile(re_key), f, priority )
703 self.strdispatchers[name] = sdp
703 self.strdispatchers[name] = sdp
704 return
704 return
705
705
706 dp = getattr(self.hooks, name, None)
706 dp = getattr(self.hooks, name, None)
707 if name not in IPython.core.hooks.__all__:
707 if name not in IPython.core.hooks.__all__:
708 print "Warning! Hook '%s' is not one of %s" % \
708 print "Warning! Hook '%s' is not one of %s" % \
709 (name, IPython.core.hooks.__all__ )
709 (name, IPython.core.hooks.__all__ )
710 if not dp:
710 if not dp:
711 dp = IPython.core.hooks.CommandChainDispatcher()
711 dp = IPython.core.hooks.CommandChainDispatcher()
712
712
713 try:
713 try:
714 dp.add(f,priority)
714 dp.add(f,priority)
715 except AttributeError:
715 except AttributeError:
@@ -757,7 +757,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
757 must therefore make a *copy* of the given namespace, to allow the
757 must therefore make a *copy* of the given namespace, to allow the
758 original module's __dict__ to be cleared and reused.
758 original module's __dict__ to be cleared and reused.
759
759
760
760
761 Parameters
761 Parameters
762 ----------
762 ----------
763 ns : a namespace (a dict, typically)
763 ns : a namespace (a dict, typically)
@@ -849,7 +849,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
849 else:
849 else:
850 # fallback to our internal debugger
850 # fallback to our internal debugger
851 pm = lambda : self.InteractiveTB.debugger(force=True)
851 pm = lambda : self.InteractiveTB.debugger(force=True)
852
852
853 with self.readline_no_record:
853 with self.readline_no_record:
854 pm()
854 pm()
855
855
@@ -923,7 +923,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
923 # the script will fail, because the function's closure had references
923 # the script will fail, because the function's closure had references
924 # to the original objects, which are now all None. So we must protect
924 # to the original objects, which are now all None. So we must protect
925 # these modules from deletion by keeping a cache.
925 # these modules from deletion by keeping a cache.
926 #
926 #
927 # To avoid keeping stale modules around (we only need the one from the
927 # To avoid keeping stale modules around (we only need the one from the
928 # last run), we use a dict keyed with the full path to the script, so
928 # last run), we use a dict keyed with the full path to the script, so
929 # only the last version of the module is held in the cache. Note,
929 # only the last version of the module is held in the cache. Note,
@@ -931,7 +931,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
931 # __dict__). Because if we try to cache the actual modules, old ones
931 # __dict__). Because if we try to cache the actual modules, old ones
932 # (uncached) could be destroyed while still holding references (such as
932 # (uncached) could be destroyed while still holding references (such as
933 # those held by GUI objects that tend to be long-lived)>
933 # those held by GUI objects that tend to be long-lived)>
934 #
934 #
935 # The %reset command will flush this cache. See the cache_main_mod()
935 # The %reset command will flush this cache. See the cache_main_mod()
936 # and clear_main_mod_cache() methods for details on use.
936 # and clear_main_mod_cache() methods for details on use.
937
937
@@ -1067,11 +1067,11 b' class InteractiveShell(SingletonConfigurable, Magic):'
1067 # module, and can even mutate at runtime, depending on the context
1067 # module, and can even mutate at runtime, depending on the context
1068 # (Python makes no guarantees on it). In contrast, __builtin__ is
1068 # (Python makes no guarantees on it). In contrast, __builtin__ is
1069 # always a module object, though it must be explicitly imported.
1069 # always a module object, though it must be explicitly imported.
1070
1070
1071 # For more details:
1071 # For more details:
1072 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1072 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1073 ns = dict(__builtin__ = builtin_mod)
1073 ns = dict(__builtin__ = builtin_mod)
1074
1074
1075 # Put 'help' in the user namespace
1075 # Put 'help' in the user namespace
1076 try:
1076 try:
1077 from site import _Helper
1077 from site import _Helper
@@ -1093,7 +1093,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1093
1093
1094 # Store myself as the public api!!!
1094 # Store myself as the public api!!!
1095 ns['get_ipython'] = self.get_ipython
1095 ns['get_ipython'] = self.get_ipython
1096
1096
1097 ns['exit'] = self.exiter
1097 ns['exit'] = self.exiter
1098 ns['quit'] = self.exiter
1098 ns['quit'] = self.exiter
1099
1099
@@ -1104,14 +1104,14 b' class InteractiveShell(SingletonConfigurable, Magic):'
1104 # Anything put into ns now would show up in %who. Think twice before
1104 # Anything put into ns now would show up in %who. Think twice before
1105 # putting anything here, as we really want %who to show the user their
1105 # putting anything here, as we really want %who to show the user their
1106 # stuff, not our variables.
1106 # stuff, not our variables.
1107
1107
1108 # Finally, update the real user's namespace
1108 # Finally, update the real user's namespace
1109 self.user_ns.update(ns)
1109 self.user_ns.update(ns)
1110
1110
1111 def reset(self, new_session=True):
1111 def reset(self, new_session=True):
1112 """Clear all internal namespaces, and attempt to release references to
1112 """Clear all internal namespaces, and attempt to release references to
1113 user objects.
1113 user objects.
1114
1114
1115 If new_session is True, a new history session will be opened.
1115 If new_session is True, a new history session will be opened.
1116 """
1116 """
1117 # Clear histories
1117 # Clear histories
@@ -1119,11 +1119,11 b' class InteractiveShell(SingletonConfigurable, Magic):'
1119 # Reset counter used to index all histories
1119 # Reset counter used to index all histories
1120 if new_session:
1120 if new_session:
1121 self.execution_count = 1
1121 self.execution_count = 1
1122
1122
1123 # Flush cached output items
1123 # Flush cached output items
1124 if self.displayhook.do_full_cache:
1124 if self.displayhook.do_full_cache:
1125 self.displayhook.flush()
1125 self.displayhook.flush()
1126
1126
1127 # Restore the user namespaces to minimal usability
1127 # Restore the user namespaces to minimal usability
1128 for ns in self.ns_refs_table:
1128 for ns in self.ns_refs_table:
1129 ns.clear()
1129 ns.clear()
@@ -1137,25 +1137,25 b' class InteractiveShell(SingletonConfigurable, Magic):'
1137 drop_keys.discard('__builtins__')
1137 drop_keys.discard('__builtins__')
1138 for k in drop_keys:
1138 for k in drop_keys:
1139 del ns[k]
1139 del ns[k]
1140
1140
1141 # Restore the user namespaces to minimal usability
1141 # Restore the user namespaces to minimal usability
1142 self.init_user_ns()
1142 self.init_user_ns()
1143
1143
1144 # Restore the default and user aliases
1144 # Restore the default and user aliases
1145 self.alias_manager.clear_aliases()
1145 self.alias_manager.clear_aliases()
1146 self.alias_manager.init_aliases()
1146 self.alias_manager.init_aliases()
1147
1147
1148 # Flush the private list of module references kept for script
1148 # Flush the private list of module references kept for script
1149 # execution protection
1149 # execution protection
1150 self.clear_main_mod_cache()
1150 self.clear_main_mod_cache()
1151
1151
1152 # Clear out the namespace from the last %run
1152 # Clear out the namespace from the last %run
1153 self.new_main_mod()
1153 self.new_main_mod()
1154
1154
1155 def del_var(self, varname, by_name=False):
1155 def del_var(self, varname, by_name=False):
1156 """Delete a variable from the various namespaces, so that, as
1156 """Delete a variable from the various namespaces, so that, as
1157 far as possible, we're not keeping any hidden references to it.
1157 far as possible, we're not keeping any hidden references to it.
1158
1158
1159 Parameters
1159 Parameters
1160 ----------
1160 ----------
1161 varname : str
1161 varname : str
@@ -1170,7 +1170,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1170 ns_refs = self.ns_refs_table + [self.user_ns,
1170 ns_refs = self.ns_refs_table + [self.user_ns,
1171 self.user_global_ns, self._user_main_module.__dict__] +\
1171 self.user_global_ns, self._user_main_module.__dict__] +\
1172 self._main_ns_cache.values()
1172 self._main_ns_cache.values()
1173
1173
1174 if by_name: # Delete by name
1174 if by_name: # Delete by name
1175 for ns in ns_refs:
1175 for ns in ns_refs:
1176 try:
1176 try:
@@ -1188,12 +1188,12 b' class InteractiveShell(SingletonConfigurable, Magic):'
1188 to_delete = [n for n, o in ns.iteritems() if o is obj]
1188 to_delete = [n for n, o in ns.iteritems() if o is obj]
1189 for name in to_delete:
1189 for name in to_delete:
1190 del ns[name]
1190 del ns[name]
1191
1191
1192 # displayhook keeps extra references, but not in a dictionary
1192 # displayhook keeps extra references, but not in a dictionary
1193 for name in ('_', '__', '___'):
1193 for name in ('_', '__', '___'):
1194 if getattr(self.displayhook, name) is obj:
1194 if getattr(self.displayhook, name) is obj:
1195 setattr(self.displayhook, name, None)
1195 setattr(self.displayhook, name, None)
1196
1196
1197 def reset_selective(self, regex=None):
1197 def reset_selective(self, regex=None):
1198 """Clear selective variables from internal namespaces based on a
1198 """Clear selective variables from internal namespaces based on a
1199 specified regular expression.
1199 specified regular expression.
@@ -1214,8 +1214,8 b' class InteractiveShell(SingletonConfigurable, Magic):'
1214 for ns in self.ns_refs_table:
1214 for ns in self.ns_refs_table:
1215 for var in ns:
1215 for var in ns:
1216 if m.search(var):
1216 if m.search(var):
1217 del ns[var]
1217 del ns[var]
1218
1218
1219 def push(self, variables, interactive=True):
1219 def push(self, variables, interactive=True):
1220 """Inject a group of variables into the IPython user namespace.
1220 """Inject a group of variables into the IPython user namespace.
1221
1221
@@ -1252,7 +1252,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1252 (name,cf.f_code.co_name))
1252 (name,cf.f_code.co_name))
1253 else:
1253 else:
1254 raise ValueError('variables must be a dict/str/list/tuple')
1254 raise ValueError('variables must be a dict/str/list/tuple')
1255
1255
1256 # Propagate variables to user namespace
1256 # Propagate variables to user namespace
1257 self.user_ns.update(vdict)
1257 self.user_ns.update(vdict)
1258
1258
@@ -1361,11 +1361,11 b' class InteractiveShell(SingletonConfigurable, Magic):'
1361 root = '.'.join(path[:-1])
1361 root = '.'.join(path[:-1])
1362 if info.parent is not None:
1362 if info.parent is not None:
1363 try:
1363 try:
1364 target = getattr(info.parent, '__class__')
1364 target = getattr(info.parent, '__class__')
1365 # The object belongs to a class instance.
1365 # The object belongs to a class instance.
1366 try:
1366 try:
1367 target = getattr(target, path[-1])
1367 target = getattr(target, path[-1])
1368 # The class defines the object.
1368 # The class defines the object.
1369 if isinstance(target, property):
1369 if isinstance(target, property):
1370 oname = root + '.__class__.' + path[-1]
1370 oname = root + '.__class__.' + path[-1]
1371 info = Struct(self._ofind(oname))
1371 info = Struct(self._ofind(oname))
@@ -1380,7 +1380,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1380 """Find an object and return a struct with info about it."""
1380 """Find an object and return a struct with info about it."""
1381 inf = Struct(self._ofind(oname, namespaces))
1381 inf = Struct(self._ofind(oname, namespaces))
1382 return Struct(self._ofind_property(oname, inf))
1382 return Struct(self._ofind_property(oname, inf))
1383
1383
1384 def _inspect(self, meth, oname, namespaces=None, **kw):
1384 def _inspect(self, meth, oname, namespaces=None, **kw):
1385 """Generic interface to the inspector system.
1385 """Generic interface to the inspector system.
1386
1386
@@ -1422,7 +1422,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1422 def init_traceback_handlers(self, custom_exceptions):
1422 def init_traceback_handlers(self, custom_exceptions):
1423 # Syntax error handler.
1423 # Syntax error handler.
1424 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1424 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1425
1425
1426 # The interactive one is initialized with an offset, meaning we always
1426 # The interactive one is initialized with an offset, meaning we always
1427 # want to remove the topmost item in the traceback, which is our own
1427 # want to remove the topmost item in the traceback, which is our own
1428 # internal code. Valid modes: ['Plain','Context','Verbose']
1428 # internal code. Valid modes: ['Plain','Context','Verbose']
@@ -1526,7 +1526,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1526 care of calling it if needed, so unless you are explicitly catching a
1526 care of calling it if needed, so unless you are explicitly catching a
1527 SyntaxError exception, don't try to analyze the stack manually and
1527 SyntaxError exception, don't try to analyze the stack manually and
1528 simply call this method."""
1528 simply call this method."""
1529
1529
1530 try:
1530 try:
1531 if exc_tuple is None:
1531 if exc_tuple is None:
1532 etype, value, tb = sys.exc_info()
1532 etype, value, tb = sys.exc_info()
@@ -1540,7 +1540,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1540 else:
1540 else:
1541 self.write_err('No traceback available to show.\n')
1541 self.write_err('No traceback available to show.\n')
1542 return
1542 return
1543
1543
1544 if etype is SyntaxError:
1544 if etype is SyntaxError:
1545 # Though this won't be called by syntax errors in the input
1545 # Though this won't be called by syntax errors in the input
1546 # line, there may be SyntaxError cases with imported code.
1546 # line, there may be SyntaxError cases with imported code.
@@ -1570,14 +1570,14 b' class InteractiveShell(SingletonConfigurable, Magic):'
1570 else:
1570 else:
1571 stb = self.InteractiveTB.structured_traceback(etype,
1571 stb = self.InteractiveTB.structured_traceback(etype,
1572 value, tb, tb_offset=tb_offset)
1572 value, tb, tb_offset=tb_offset)
1573
1573
1574 if self.call_pdb:
1574 if self.call_pdb:
1575 # drop into debugger
1575 # drop into debugger
1576 self.debugger(force=True)
1576 self.debugger(force=True)
1577
1577
1578 # Actually show the traceback
1578 # Actually show the traceback
1579 self._showtraceback(etype, value, stb)
1579 self._showtraceback(etype, value, stb)
1580
1580
1581 except KeyboardInterrupt:
1581 except KeyboardInterrupt:
1582 self.write_err("\nKeyboardInterrupt\n")
1582 self.write_err("\nKeyboardInterrupt\n")
1583
1583
@@ -1604,7 +1604,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1604 sys.last_type = etype
1604 sys.last_type = etype
1605 sys.last_value = value
1605 sys.last_value = value
1606 sys.last_traceback = last_traceback
1606 sys.last_traceback = last_traceback
1607
1607
1608 if filename and etype is SyntaxError:
1608 if filename and etype is SyntaxError:
1609 # Work hard to stuff the correct filename in the exception
1609 # Work hard to stuff the correct filename in the exception
1610 try:
1610 try:
@@ -1622,13 +1622,13 b' class InteractiveShell(SingletonConfigurable, Magic):'
1622 value = msg, (filename, lineno, offset, line)
1622 value = msg, (filename, lineno, offset, line)
1623 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1623 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1624 self._showtraceback(etype, value, stb)
1624 self._showtraceback(etype, value, stb)
1625
1625
1626 # This is overridden in TerminalInteractiveShell to show a message about
1626 # This is overridden in TerminalInteractiveShell to show a message about
1627 # the %paste magic.
1627 # the %paste magic.
1628 def showindentationerror(self):
1628 def showindentationerror(self):
1629 """Called by run_cell when there's an IndentationError in code entered
1629 """Called by run_cell when there's an IndentationError in code entered
1630 at the prompt.
1630 at the prompt.
1631
1631
1632 This is overridden in TerminalInteractiveShell to show a message about
1632 This is overridden in TerminalInteractiveShell to show a message about
1633 the %paste magic."""
1633 the %paste magic."""
1634 self.showsyntaxerror()
1634 self.showsyntaxerror()
@@ -1660,7 +1660,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1660 self.has_readline = True
1660 self.has_readline = True
1661 self.readline = readline
1661 self.readline = readline
1662 sys.modules['readline'] = readline
1662 sys.modules['readline'] = readline
1663
1663
1664 # Platform-specific configuration
1664 # Platform-specific configuration
1665 if os.name == 'nt':
1665 if os.name == 'nt':
1666 # FIXME - check with Frederick to see if we can harmonize
1666 # FIXME - check with Frederick to see if we can harmonize
@@ -1686,7 +1686,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1686 except:
1686 except:
1687 warn('Problems reading readline initialization file <%s>'
1687 warn('Problems reading readline initialization file <%s>'
1688 % inputrc_name)
1688 % inputrc_name)
1689
1689
1690 # Configure readline according to user's prefs
1690 # Configure readline according to user's prefs
1691 # This is only done if GNU readline is being used. If libedit
1691 # This is only done if GNU readline is being used. If libedit
1692 # is being used (as on Leopard) the readline config is
1692 # is being used (as on Leopard) the readline config is
@@ -1707,13 +1707,13 b' class InteractiveShell(SingletonConfigurable, Magic):'
1707 readline.set_completer_delims(delims)
1707 readline.set_completer_delims(delims)
1708 # otherwise we end up with a monster history after a while:
1708 # otherwise we end up with a monster history after a while:
1709 readline.set_history_length(self.history_length)
1709 readline.set_history_length(self.history_length)
1710
1710
1711 self.refill_readline_hist()
1711 self.refill_readline_hist()
1712 self.readline_no_record = ReadlineNoRecord(self)
1712 self.readline_no_record = ReadlineNoRecord(self)
1713
1713
1714 # Configure auto-indent for all platforms
1714 # Configure auto-indent for all platforms
1715 self.set_autoindent(self.autoindent)
1715 self.set_autoindent(self.autoindent)
1716
1716
1717 def refill_readline_hist(self):
1717 def refill_readline_hist(self):
1718 # Load the last 1000 lines from history
1718 # Load the last 1000 lines from history
1719 self.readline.clear_history()
1719 self.readline.clear_history()
@@ -1727,13 +1727,13 b' class InteractiveShell(SingletonConfigurable, Magic):'
1727
1727
1728 def set_next_input(self, s):
1728 def set_next_input(self, s):
1729 """ Sets the 'default' input string for the next command line.
1729 """ Sets the 'default' input string for the next command line.
1730
1730
1731 Requires readline.
1731 Requires readline.
1732
1732
1733 Example:
1733 Example:
1734
1734
1735 [D:\ipython]|1> _ip.set_next_input("Hello Word")
1735 [D:\ipython]|1> _ip.set_next_input("Hello Word")
1736 [D:\ipython]|2> Hello Word_ # cursor is here
1736 [D:\ipython]|2> Hello Word_ # cursor is here
1737 """
1737 """
1738 if isinstance(s, unicode):
1738 if isinstance(s, unicode):
1739 s = s.encode(self.stdin_encoding, 'replace')
1739 s = s.encode(self.stdin_encoding, 'replace')
@@ -1770,7 +1770,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1770 from IPython.core.completer import IPCompleter
1770 from IPython.core.completer import IPCompleter
1771 from IPython.core.completerlib import (module_completer,
1771 from IPython.core.completerlib import (module_completer,
1772 magic_run_completer, cd_completer)
1772 magic_run_completer, cd_completer)
1773
1773
1774 self.Completer = IPCompleter(shell=self,
1774 self.Completer = IPCompleter(shell=self,
1775 namespace=self.user_ns,
1775 namespace=self.user_ns,
1776 global_namespace=self.user_global_ns,
1776 global_namespace=self.user_global_ns,
@@ -1779,7 +1779,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1779 use_readline=self.has_readline,
1779 use_readline=self.has_readline,
1780 config=self.config,
1780 config=self.config,
1781 )
1781 )
1782
1782
1783 # Add custom completers to the basic ones built into IPCompleter
1783 # Add custom completers to the basic ones built into IPCompleter
1784 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1784 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1785 self.strdispatchers['complete_command'] = sdisp
1785 self.strdispatchers['complete_command'] = sdisp
@@ -1823,7 +1823,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1823
1823
1824 The optional arguments allow the completion to take more context into
1824 The optional arguments allow the completion to take more context into
1825 account, and are part of the low-level completion API.
1825 account, and are part of the low-level completion API.
1826
1826
1827 This is a wrapper around the completion mechanism, similar to what
1827 This is a wrapper around the completion mechanism, similar to what
1828 readline does at the command line when the TAB key is hit. By
1828 readline does at the command line when the TAB key is hit. By
1829 exposing it as a method, it can be used by other non-readline
1829 exposing it as a method, it can be used by other non-readline
@@ -1897,11 +1897,11 b' class InteractiveShell(SingletonConfigurable, Magic):'
1897 # We do this first so that magic functions can override it.
1897 # We do this first so that magic functions can override it.
1898 if next_input:
1898 if next_input:
1899 self.set_next_input(next_input)
1899 self.set_next_input(next_input)
1900
1900
1901 args = arg_s.split(' ',1)
1901 args = arg_s.split(' ',1)
1902 magic_name = args[0]
1902 magic_name = args[0]
1903 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
1903 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
1904
1904
1905 try:
1905 try:
1906 magic_args = args[1]
1906 magic_args = args[1]
1907 except IndexError:
1907 except IndexError:
@@ -1921,14 +1921,14 b' class InteractiveShell(SingletonConfigurable, Magic):'
1921 return result
1921 return result
1922
1922
1923 def define_magic(self, magicname, func):
1923 def define_magic(self, magicname, func):
1924 """Expose own function as magic function for ipython
1924 """Expose own function as magic function for ipython
1925
1925
1926 def foo_impl(self,parameter_s=''):
1926 def foo_impl(self,parameter_s=''):
1927 'My very own magic!. (Use docstrings, IPython reads them).'
1927 'My very own magic!. (Use docstrings, IPython reads them).'
1928 print 'Magic function. Passed parameter is between < >:'
1928 print 'Magic function. Passed parameter is between < >:'
1929 print '<%s>' % parameter_s
1929 print '<%s>' % parameter_s
1930 print 'The self object is:',self
1930 print 'The self object is:',self
1931
1931
1932 self.define_magic('foo',foo_impl)
1932 self.define_magic('foo',foo_impl)
1933 """
1933 """
1934 im = types.MethodType(func,self)
1934 im = types.MethodType(func,self)
@@ -1948,10 +1948,10 b' class InteractiveShell(SingletonConfigurable, Magic):'
1948 name : str
1948 name : str
1949 The name of the macro.
1949 The name of the macro.
1950 themacro : str or Macro
1950 themacro : str or Macro
1951 The action to do upon invoking the macro. If a string, a new
1951 The action to do upon invoking the macro. If a string, a new
1952 Macro object is created by passing the string to it.
1952 Macro object is created by passing the string to it.
1953 """
1953 """
1954
1954
1955 from IPython.core import macro
1955 from IPython.core import macro
1956
1956
1957 if isinstance(themacro, basestring):
1957 if isinstance(themacro, basestring):
@@ -1981,15 +1981,15 b' class InteractiveShell(SingletonConfigurable, Magic):'
1981 # os.system() or use ip.system=ip.system_raw
1981 # os.system() or use ip.system=ip.system_raw
1982 # if they really want a background process.
1982 # if they really want a background process.
1983 raise OSError("Background processes not supported.")
1983 raise OSError("Background processes not supported.")
1984
1984
1985 # we explicitly do NOT return the subprocess status code, because
1985 # we explicitly do NOT return the subprocess status code, because
1986 # a non-None value would trigger :func:`sys.displayhook` calls.
1986 # a non-None value would trigger :func:`sys.displayhook` calls.
1987 # Instead, we store the exit_code in user_ns.
1987 # Instead, we store the exit_code in user_ns.
1988 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=2))
1988 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=2))
1989
1989
1990 def system_raw(self, cmd):
1990 def system_raw(self, cmd):
1991 """Call the given cmd in a subprocess using os.system
1991 """Call the given cmd in a subprocess using os.system
1992
1992
1993 Parameters
1993 Parameters
1994 ----------
1994 ----------
1995 cmd : str
1995 cmd : str
@@ -1999,7 +1999,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
1999 # a non-None value would trigger :func:`sys.displayhook` calls.
1999 # a non-None value would trigger :func:`sys.displayhook` calls.
2000 # Instead, we store the exit_code in user_ns.
2000 # Instead, we store the exit_code in user_ns.
2001 self.user_ns['_exit_code'] = os.system(self.var_expand(cmd, depth=2))
2001 self.user_ns['_exit_code'] = os.system(self.var_expand(cmd, depth=2))
2002
2002
2003 # use piped system by default, because it is better behaved
2003 # use piped system by default, because it is better behaved
2004 system = system_piped
2004 system = system_piped
2005
2005
@@ -2012,7 +2012,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2012 Command to execute (can not end in '&', as background processes are
2012 Command to execute (can not end in '&', as background processes are
2013 not supported.
2013 not supported.
2014 split : bool, optional
2014 split : bool, optional
2015
2015
2016 If True, split the output into an IPython SList. Otherwise, an
2016 If True, split the output into an IPython SList. Otherwise, an
2017 IPython LSString is returned. These are objects similar to normal
2017 IPython LSString is returned. These are objects similar to normal
2018 lists and strings, with a few convenience attributes for easier
2018 lists and strings, with a few convenience attributes for easier
@@ -2076,7 +2076,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2076 into::
2076 into::
2077
2077
2078 ------> f(x)
2078 ------> f(x)
2079
2079
2080 after the user's input prompt. This helps the user understand that the
2080 after the user's input prompt. This helps the user understand that the
2081 input line was transformed automatically by IPython.
2081 input line was transformed automatically by IPython.
2082 """
2082 """
@@ -2089,7 +2089,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2089 print >> io.stdout, rw
2089 print >> io.stdout, rw
2090 except UnicodeEncodeError:
2090 except UnicodeEncodeError:
2091 print "------> " + cmd
2091 print "------> " + cmd
2092
2092
2093 #-------------------------------------------------------------------------
2093 #-------------------------------------------------------------------------
2094 # Things related to extracting values/expressions from kernel and user_ns
2094 # Things related to extracting values/expressions from kernel and user_ns
2095 #-------------------------------------------------------------------------
2095 #-------------------------------------------------------------------------
@@ -2119,7 +2119,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2119 value = self._simple_error()
2119 value = self._simple_error()
2120 out[varname] = value
2120 out[varname] = value
2121 return out
2121 return out
2122
2122
2123 def user_expressions(self, expressions):
2123 def user_expressions(self, expressions):
2124 """Evaluate a dict of expressions in the user's namespace.
2124 """Evaluate a dict of expressions in the user's namespace.
2125
2125
@@ -2129,7 +2129,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2129 A dict with string keys and string values. The expression values
2129 A dict with string keys and string values. The expression values
2130 should be valid Python expressions, each of which will be evaluated
2130 should be valid Python expressions, each of which will be evaluated
2131 in the user namespace.
2131 in the user namespace.
2132
2132
2133 Returns
2133 Returns
2134 -------
2134 -------
2135 A dict, keyed like the input expressions dict, with the repr() of each
2135 A dict, keyed like the input expressions dict, with the repr() of each
@@ -2251,10 +2251,10 b' class InteractiveShell(SingletonConfigurable, Magic):'
2251 except:
2251 except:
2252 self.showtraceback()
2252 self.showtraceback()
2253 warn('Unknown failure executing file: <%s>' % fname)
2253 warn('Unknown failure executing file: <%s>' % fname)
2254
2254
2255 def run_cell(self, raw_cell, store_history=True):
2255 def run_cell(self, raw_cell, store_history=True):
2256 """Run a complete IPython cell.
2256 """Run a complete IPython cell.
2257
2257
2258 Parameters
2258 Parameters
2259 ----------
2259 ----------
2260 raw_cell : str
2260 raw_cell : str
@@ -2266,11 +2266,11 b' class InteractiveShell(SingletonConfigurable, Magic):'
2266 """
2266 """
2267 if (not raw_cell) or raw_cell.isspace():
2267 if (not raw_cell) or raw_cell.isspace():
2268 return
2268 return
2269
2269
2270 for line in raw_cell.splitlines():
2270 for line in raw_cell.splitlines():
2271 self.input_splitter.push(line)
2271 self.input_splitter.push(line)
2272 cell = self.input_splitter.source_reset()
2272 cell = self.input_splitter.source_reset()
2273
2273
2274 with self.builtin_trap:
2274 with self.builtin_trap:
2275 prefilter_failed = False
2275 prefilter_failed = False
2276 if len(cell.splitlines()) == 1:
2276 if len(cell.splitlines()) == 1:
@@ -2285,18 +2285,18 b' class InteractiveShell(SingletonConfigurable, Magic):'
2285 # don't allow prefilter errors to crash IPython
2285 # don't allow prefilter errors to crash IPython
2286 self.showtraceback()
2286 self.showtraceback()
2287 prefilter_failed = True
2287 prefilter_failed = True
2288
2288
2289 # Store raw and processed history
2289 # Store raw and processed history
2290 if store_history:
2290 if store_history:
2291 self.history_manager.store_inputs(self.execution_count,
2291 self.history_manager.store_inputs(self.execution_count,
2292 cell, raw_cell)
2292 cell, raw_cell)
2293
2293
2294 self.logger.log(cell, raw_cell)
2294 self.logger.log(cell, raw_cell)
2295
2295
2296 if not prefilter_failed:
2296 if not prefilter_failed:
2297 # don't run if prefilter failed
2297 # don't run if prefilter failed
2298 cell_name = self.compile.cache(cell, self.execution_count)
2298 cell_name = self.compile.cache(cell, self.execution_count)
2299
2299
2300 with self.display_trap:
2300 with self.display_trap:
2301 try:
2301 try:
2302 code_ast = self.compile.ast_parse(cell, filename=cell_name)
2302 code_ast = self.compile.ast_parse(cell, filename=cell_name)
@@ -2309,10 +2309,10 b' class InteractiveShell(SingletonConfigurable, Magic):'
2309 self.showsyntaxerror()
2309 self.showsyntaxerror()
2310 self.execution_count += 1
2310 self.execution_count += 1
2311 return None
2311 return None
2312
2312
2313 self.run_ast_nodes(code_ast.body, cell_name,
2313 self.run_ast_nodes(code_ast.body, cell_name,
2314 interactivity="last_expr")
2314 interactivity="last_expr")
2315
2315
2316 # Execute any registered post-execution functions.
2316 # Execute any registered post-execution functions.
2317 for func, status in self._post_execute.iteritems():
2317 for func, status in self._post_execute.iteritems():
2318 if not status:
2318 if not status:
@@ -2323,18 +2323,18 b' class InteractiveShell(SingletonConfigurable, Magic):'
2323 self.showtraceback()
2323 self.showtraceback()
2324 # Deactivate failing function
2324 # Deactivate failing function
2325 self._post_execute[func] = False
2325 self._post_execute[func] = False
2326
2326
2327 if store_history:
2327 if store_history:
2328 # Write output to the database. Does nothing unless
2328 # Write output to the database. Does nothing unless
2329 # history output logging is enabled.
2329 # history output logging is enabled.
2330 self.history_manager.store_output(self.execution_count)
2330 self.history_manager.store_output(self.execution_count)
2331 # Each cell is a *single* input, regardless of how many lines it has
2331 # Each cell is a *single* input, regardless of how many lines it has
2332 self.execution_count += 1
2332 self.execution_count += 1
2333
2333
2334 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
2334 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
2335 """Run a sequence of AST nodes. The execution mode depends on the
2335 """Run a sequence of AST nodes. The execution mode depends on the
2336 interactivity parameter.
2336 interactivity parameter.
2337
2337
2338 Parameters
2338 Parameters
2339 ----------
2339 ----------
2340 nodelist : list
2340 nodelist : list
@@ -2351,13 +2351,13 b' class InteractiveShell(SingletonConfigurable, Magic):'
2351 """
2351 """
2352 if not nodelist:
2352 if not nodelist:
2353 return
2353 return
2354
2354
2355 if interactivity == 'last_expr':
2355 if interactivity == 'last_expr':
2356 if isinstance(nodelist[-1], ast.Expr):
2356 if isinstance(nodelist[-1], ast.Expr):
2357 interactivity = "last"
2357 interactivity = "last"
2358 else:
2358 else:
2359 interactivity = "none"
2359 interactivity = "none"
2360
2360
2361 if interactivity == 'none':
2361 if interactivity == 'none':
2362 to_run_exec, to_run_interactive = nodelist, []
2362 to_run_exec, to_run_interactive = nodelist, []
2363 elif interactivity == 'last':
2363 elif interactivity == 'last':
@@ -2366,7 +2366,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2366 to_run_exec, to_run_interactive = [], nodelist
2366 to_run_exec, to_run_interactive = [], nodelist
2367 else:
2367 else:
2368 raise ValueError("Interactivity was %r" % interactivity)
2368 raise ValueError("Interactivity was %r" % interactivity)
2369
2369
2370 exec_count = self.execution_count
2370 exec_count = self.execution_count
2371
2371
2372 try:
2372 try:
@@ -2394,7 +2394,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2394 self.showtraceback()
2394 self.showtraceback()
2395
2395
2396 return False
2396 return False
2397
2397
2398 def run_code(self, code_obj):
2398 def run_code(self, code_obj):
2399 """Execute a code object.
2399 """Execute a code object.
2400
2400
@@ -2444,7 +2444,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2444 print
2444 print
2445
2445
2446 return outflag
2446 return outflag
2447
2447
2448 # For backwards compatibility
2448 # For backwards compatibility
2449 runcode = run_code
2449 runcode = run_code
2450
2450
@@ -2487,7 +2487,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2487
2487
2488 filename = tempfile.mktemp('.py', prefix)
2488 filename = tempfile.mktemp('.py', prefix)
2489 self.tempfiles.append(filename)
2489 self.tempfiles.append(filename)
2490
2490
2491 if data:
2491 if data:
2492 tmp_file = open(filename,'w')
2492 tmp_file = open(filename,'w')
2493 tmp_file.write(data)
2493 tmp_file.write(data)
@@ -2508,16 +2508,16 b' class InteractiveShell(SingletonConfigurable, Magic):'
2508 if self.quiet:
2508 if self.quiet:
2509 return True
2509 return True
2510 return ask_yes_no(prompt,default)
2510 return ask_yes_no(prompt,default)
2511
2511
2512 def show_usage(self):
2512 def show_usage(self):
2513 """Show a usage message"""
2513 """Show a usage message"""
2514 page.page(IPython.core.usage.interactive_usage)
2514 page.page(IPython.core.usage.interactive_usage)
2515
2515
2516 def find_user_code(self, target, raw=True):
2516 def find_user_code(self, target, raw=True):
2517 """Get a code string from history, file, or a string or macro.
2517 """Get a code string from history, file, or a string or macro.
2518
2518
2519 This is mainly used by magic functions.
2519 This is mainly used by magic functions.
2520
2520
2521 Parameters
2521 Parameters
2522 ----------
2522 ----------
2523 target : str
2523 target : str
@@ -2527,21 +2527,21 b' class InteractiveShell(SingletonConfigurable, Magic):'
2527 raw : bool
2527 raw : bool
2528 If true (default), retrieve raw history. Has no effect on the other
2528 If true (default), retrieve raw history. Has no effect on the other
2529 retrieval mechanisms.
2529 retrieval mechanisms.
2530
2530
2531 Returns
2531 Returns
2532 -------
2532 -------
2533 A string of code.
2533 A string of code.
2534
2534
2535 ValueError is raised if nothing is found, and TypeError if it evaluates
2535 ValueError is raised if nothing is found, and TypeError if it evaluates
2536 to an object of another type. In each case, .args[0] is a printable
2536 to an object of another type. In each case, .args[0] is a printable
2537 message.
2537 message.
2538 """
2538 """
2539 code = self.extract_input_lines(target, raw=raw) # Grab history
2539 code = self.extract_input_lines(target, raw=raw) # Grab history
2540 if code:
2540 if code:
2541 return code
2541 return code
2542 if os.path.isfile(target): # Read file
2542 if os.path.isfile(target): # Read file
2543 return open(target, "r").read()
2543 return open(target, "r").read()
2544
2544
2545 try: # User namespace
2545 try: # User namespace
2546 codeobj = eval(target, self.user_ns)
2546 codeobj = eval(target, self.user_ns)
2547 except Exception:
2547 except Exception:
@@ -2551,7 +2551,7 b' class InteractiveShell(SingletonConfigurable, Magic):'
2551 return codeobj
2551 return codeobj
2552 elif isinstance(codeobj, Macro):
2552 elif isinstance(codeobj, Macro):
2553 return codeobj.value
2553 return codeobj.value
2554
2554
2555 raise TypeError("%s is neither a string nor a macro." % target,
2555 raise TypeError("%s is neither a string nor a macro." % target,
2556 codeobj)
2556 codeobj)
2557
2557
@@ -2567,20 +2567,20 b' class InteractiveShell(SingletonConfigurable, Magic):'
2567 For things that may depend on startup flags or platform specifics (such
2567 For things that may depend on startup flags or platform specifics (such
2568 as having readline or not), register a separate atexit function in the
2568 as having readline or not), register a separate atexit function in the
2569 code that has the appropriate information, rather than trying to
2569 code that has the appropriate information, rather than trying to
2570 clutter
2570 clutter
2571 """
2571 """
2572 # Close the history session (this stores the end time and line count)
2572 # Close the history session (this stores the end time and line count)
2573 # this must be *before* the tempfile cleanup, in case of temporary
2573 # this must be *before* the tempfile cleanup, in case of temporary
2574 # history db
2574 # history db
2575 self.history_manager.end_session()
2575 self.history_manager.end_session()
2576
2576
2577 # Cleanup all tempfiles left around
2577 # Cleanup all tempfiles left around
2578 for tfile in self.tempfiles:
2578 for tfile in self.tempfiles:
2579 try:
2579 try:
2580 os.unlink(tfile)
2580 os.unlink(tfile)
2581 except OSError:
2581 except OSError:
2582 pass
2582 pass
2583
2583
2584 # Clear all user namespaces to release all references cleanly.
2584 # Clear all user namespaces to release all references cleanly.
2585 self.reset(new_session=False)
2585 self.reset(new_session=False)
2586
2586
@@ -58,7 +58,7 b' class Logger(object):'
58 return self._logmode
58 return self._logmode
59
59
60 logmode = property(_get_mode,_set_mode)
60 logmode = property(_get_mode,_set_mode)
61
61
62 def logstart(self,logfname=None,loghead=None,logmode=None,
62 def logstart(self,logfname=None,loghead=None,logmode=None,
63 log_output=False,timestamp=False,log_raw_input=False):
63 log_output=False,timestamp=False,log_raw_input=False):
64 """Generate a new log-file with a default header.
64 """Generate a new log-file with a default header.
@@ -68,7 +68,7 b' class Logger(object):'
68 if self.logfile is not None:
68 if self.logfile is not None:
69 raise RuntimeError('Log file is already active: %s' %
69 raise RuntimeError('Log file is already active: %s' %
70 self.logfname)
70 self.logfname)
71
71
72 # The parameters can override constructor defaults
72 # The parameters can override constructor defaults
73 if logfname is not None: self.logfname = logfname
73 if logfname is not None: self.logfname = logfname
74 if loghead is not None: self.loghead = loghead
74 if loghead is not None: self.loghead = loghead
@@ -78,7 +78,7 b' class Logger(object):'
78 self.timestamp = timestamp
78 self.timestamp = timestamp
79 self.log_output = log_output
79 self.log_output = log_output
80 self.log_raw_input = log_raw_input
80 self.log_raw_input = log_raw_input
81
81
82 # init depending on the log mode requested
82 # init depending on the log mode requested
83 isfile = os.path.isfile
83 isfile = os.path.isfile
84 logmode = self.logmode
84 logmode = self.logmode
@@ -102,12 +102,12 b' class Logger(object):'
102
102
103 elif logmode == 'over':
103 elif logmode == 'over':
104 if isfile(self.logfname):
104 if isfile(self.logfname):
105 os.remove(self.logfname)
105 os.remove(self.logfname)
106 self.logfile = open(self.logfname,'w')
106 self.logfile = open(self.logfname,'w')
107
107
108 elif logmode == 'rotate':
108 elif logmode == 'rotate':
109 if isfile(self.logfname):
109 if isfile(self.logfname):
110 if isfile(self.logfname+'.001~'):
110 if isfile(self.logfname+'.001~'):
111 old = glob.glob(self.logfname+'.*~')
111 old = glob.glob(self.logfname+'.*~')
112 old.sort()
112 old.sort()
113 old.reverse()
113 old.reverse()
@@ -117,7 +117,7 b' class Logger(object):'
117 os.rename(f, root+'.'+`num`.zfill(3)+'~')
117 os.rename(f, root+'.'+`num`.zfill(3)+'~')
118 os.rename(self.logfname, self.logfname+'.001~')
118 os.rename(self.logfname, self.logfname+'.001~')
119 self.logfile = open(self.logfname,'w')
119 self.logfile = open(self.logfname,'w')
120
120
121 if logmode != 'append':
121 if logmode != 'append':
122 self.logfile.write(self.loghead)
122 self.logfile.write(self.loghead)
123
123
@@ -130,7 +130,7 b' class Logger(object):'
130 if val not in [False,True,0,1]:
130 if val not in [False,True,0,1]:
131 raise ValueError, \
131 raise ValueError, \
132 'Call switch_log ONLY with a boolean argument, not with:',val
132 'Call switch_log ONLY with a boolean argument, not with:',val
133
133
134 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
134 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
135
135
136 if self.logfile is None:
136 if self.logfile is None:
@@ -140,7 +140,7 b" Logging hasn't been started yet (use logstart for that)."
140 %logon/%logoff are for temporarily starting and stopping logging for a logfile
140 %logon/%logoff are for temporarily starting and stopping logging for a logfile
141 which already exists. But you must first start the logging process with
141 which already exists. But you must first start the logging process with
142 %logstart (optionally giving a logfile name)."""
142 %logstart (optionally giving a logfile name)."""
143
143
144 else:
144 else:
145 if self.log_active == val:
145 if self.log_active == val:
146 print 'Logging is already',label[val]
146 print 'Logging is already',label[val]
@@ -205,7 +205,7 b' which already exists. But you must first start the logging process with'
205 In order to start logging again, a new logstart() call needs to be
205 In order to start logging again, a new logstart() call needs to be
206 made, possibly (though not necessarily) with a new filename, mode and
206 made, possibly (though not necessarily) with a new filename, mode and
207 other options."""
207 other options."""
208
208
209 if self.logfile is not None:
209 if self.logfile is not None:
210 self.logfile.close()
210 self.logfile.close()
211 self.logfile = None
211 self.logfile = None
@@ -84,13 +84,13 b' def compress_dhist(dh):'
84 newhead.append(h)
84 newhead.append(h)
85 done.add(h)
85 done.add(h)
86
86
87 return newhead + tail
87 return newhead + tail
88
88
89 def needs_local_scope(func):
89 def needs_local_scope(func):
90 """Decorator to mark magic functions which need to local scope to run."""
90 """Decorator to mark magic functions which need to local scope to run."""
91 func.needs_local_scope = True
91 func.needs_local_scope = True
92 return func
92 return func
93
93
94 # Used for exception handling in magic_edit
94 # Used for exception handling in magic_edit
95 class MacroToEdit(ValueError): pass
95 class MacroToEdit(ValueError): pass
96
96
@@ -101,7 +101,7 b' class MacroToEdit(ValueError): pass'
101 # on construction of the main InteractiveShell object. Something odd is going
101 # on construction of the main InteractiveShell object. Something odd is going
102 # on with super() calls, Configurable and the MRO... For now leave it as-is, but
102 # on with super() calls, Configurable and the MRO... For now leave it as-is, but
103 # eventually this needs to be clarified.
103 # eventually this needs to be clarified.
104 # BG: This is because InteractiveShell inherits from this, but is itself a
104 # BG: This is because InteractiveShell inherits from this, but is itself a
105 # Configurable. This messes up the MRO in some way. The fix is that we need to
105 # Configurable. This messes up the MRO in some way. The fix is that we need to
106 # make Magic a configurable that InteractiveShell does not subclass.
106 # make Magic a configurable that InteractiveShell does not subclass.
107
107
@@ -124,7 +124,7 b' class Magic:'
124 # some utility functions
124 # some utility functions
125
125
126 def __init__(self,shell):
126 def __init__(self,shell):
127
127
128 self.options_table = {}
128 self.options_table = {}
129 if profile is None:
129 if profile is None:
130 self.magic_prun = self.profile_missing_notice
130 self.magic_prun = self.profile_missing_notice
@@ -153,7 +153,7 b' python-profiler package from non-free.""")'
153 ['magic_ls','magic_cd',...]"""
153 ['magic_ls','magic_cd',...]"""
154
154
155 # FIXME. This needs a cleanup, in the way the magics list is built.
155 # FIXME. This needs a cleanup, in the way the magics list is built.
156
156
157 # magics in class definition
157 # magics in class definition
158 class_magic = lambda fn: fn.startswith('magic_') and \
158 class_magic = lambda fn: fn.startswith('magic_') and \
159 callable(Magic.__dict__[fn])
159 callable(Magic.__dict__[fn])
@@ -171,13 +171,13 b' python-profiler package from non-free.""")'
171 out.append(fn.replace('magic_','',1))
171 out.append(fn.replace('magic_','',1))
172 out.sort()
172 out.sort()
173 return out
173 return out
174
174
175 def extract_input_lines(self, range_str, raw=False):
175 def extract_input_lines(self, range_str, raw=False):
176 """Return as a string a set of input history slices.
176 """Return as a string a set of input history slices.
177
177
178 Inputs:
178 Inputs:
179
179
180 - range_str: the set of slices is given as a string, like
180 - range_str: the set of slices is given as a string, like
181 "~5/6-~4/2 4:8 9", since this function is for use by magic functions
181 "~5/6-~4/2 4:8 9", since this function is for use by magic functions
182 which get their arguments as strings. The number before the / is the
182 which get their arguments as strings. The number before the / is the
183 session number: ~n goes n back from the current session.
183 session number: ~n goes n back from the current session.
@@ -195,7 +195,7 b' python-profiler package from non-free.""")'
195 lines = self.shell.history_manager.\
195 lines = self.shell.history_manager.\
196 get_range_by_str(range_str, raw=raw)
196 get_range_by_str(range_str, raw=raw)
197 return "\n".join(x for _, _, x in lines)
197 return "\n".join(x for _, _, x in lines)
198
198
199 def arg_err(self,func):
199 def arg_err(self,func):
200 """Print docstring if incorrect arguments were passed"""
200 """Print docstring if incorrect arguments were passed"""
201 print 'Error in arguments:'
201 print 'Error in arguments:'
@@ -209,7 +209,7 b' python-profiler package from non-free.""")'
209 # Magic command names as headers:
209 # Magic command names as headers:
210 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
210 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
211 re.MULTILINE)
211 re.MULTILINE)
212 # Magic commands
212 # Magic commands
213 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
213 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
214 re.MULTILINE)
214 re.MULTILINE)
215 # Paragraph continue
215 # Paragraph continue
@@ -249,11 +249,11 b' python-profiler package from non-free.""")'
249 -posix (True): whether to split the input line in POSIX mode or not,
249 -posix (True): whether to split the input line in POSIX mode or not,
250 as per the conventions outlined in the shlex module from the
250 as per the conventions outlined in the shlex module from the
251 standard library."""
251 standard library."""
252
252
253 # inject default options at the beginning of the input line
253 # inject default options at the beginning of the input line
254 caller = sys._getframe(1).f_code.co_name.replace('magic_','')
254 caller = sys._getframe(1).f_code.co_name.replace('magic_','')
255 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
255 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
256
256
257 mode = kw.get('mode','string')
257 mode = kw.get('mode','string')
258 if mode not in ['string','list']:
258 if mode not in ['string','list']:
259 raise ValueError,'incorrect mode given: %s' % mode
259 raise ValueError,'incorrect mode given: %s' % mode
@@ -272,7 +272,7 b' python-profiler package from non-free.""")'
272 try:
272 try:
273 opts,args = getopt(argv,opt_str,*long_opts)
273 opts,args = getopt(argv,opt_str,*long_opts)
274 except GetoptError,e:
274 except GetoptError,e:
275 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
275 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
276 " ".join(long_opts)))
276 " ".join(long_opts)))
277 for o,a in opts:
277 for o,a in opts:
278 if o.startswith('--'):
278 if o.startswith('--'):
@@ -295,7 +295,7 b' python-profiler package from non-free.""")'
295 args = ' '.join(args)
295 args = ' '.join(args)
296
296
297 return opts,args
297 return opts,args
298
298
299 #......................................................................
299 #......................................................................
300 # And now the actual magic functions
300 # And now the actual magic functions
301
301
@@ -307,11 +307,11 b' python-profiler package from non-free.""")'
307 (' '+mesc).join(self.lsmagic())
307 (' '+mesc).join(self.lsmagic())
308 print '\n' + Magic.auto_status[self.shell.automagic]
308 print '\n' + Magic.auto_status[self.shell.automagic]
309 return None
309 return None
310
310
311 def magic_magic(self, parameter_s = ''):
311 def magic_magic(self, parameter_s = ''):
312 """Print information about the magic function system.
312 """Print information about the magic function system.
313
313
314 Supported formats: -latex, -brief, -rest
314 Supported formats: -latex, -brief, -rest
315 """
315 """
316
316
317 mode = ''
317 mode = ''
@@ -338,30 +338,30 b' python-profiler package from non-free.""")'
338 break
338 break
339 if mode == 'brief':
339 if mode == 'brief':
340 # only first line
340 # only first line
341 if fn.__doc__:
341 if fn.__doc__:
342 fndoc = fn.__doc__.split('\n',1)[0]
342 fndoc = fn.__doc__.split('\n',1)[0]
343 else:
343 else:
344 fndoc = 'No documentation'
344 fndoc = 'No documentation'
345 else:
345 else:
346 if fn.__doc__:
346 if fn.__doc__:
347 fndoc = fn.__doc__.rstrip()
347 fndoc = fn.__doc__.rstrip()
348 else:
348 else:
349 fndoc = 'No documentation'
349 fndoc = 'No documentation'
350
350
351
351
352 if mode == 'rest':
352 if mode == 'rest':
353 rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC,
353 rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC,
354 fname,fndoc))
354 fname,fndoc))
355
355
356 else:
356 else:
357 magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC,
357 magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC,
358 fname,fndoc))
358 fname,fndoc))
359
359
360 magic_docs = ''.join(magic_docs)
360 magic_docs = ''.join(magic_docs)
361
361
362 if mode == 'rest':
362 if mode == 'rest':
363 return "".join(rest_docs)
363 return "".join(rest_docs)
364
364
365 if mode == 'latex':
365 if mode == 'latex':
366 print self.format_latex(magic_docs)
366 print self.format_latex(magic_docs)
367 return
367 return
@@ -369,7 +369,7 b' python-profiler package from non-free.""")'
369 magic_docs = format_screen(magic_docs)
369 magic_docs = format_screen(magic_docs)
370 if mode == 'brief':
370 if mode == 'brief':
371 return magic_docs
371 return magic_docs
372
372
373 outmsg = """
373 outmsg = """
374 IPython's 'magic' functions
374 IPython's 'magic' functions
375 ===========================
375 ===========================
@@ -495,11 +495,11 b' Currently the magic system has the following functions:\\n"""'
495
495
496 def magic_page(self, parameter_s=''):
496 def magic_page(self, parameter_s=''):
497 """Pretty print the object and display it through a pager.
497 """Pretty print the object and display it through a pager.
498
498
499 %page [options] OBJECT
499 %page [options] OBJECT
500
500
501 If no object is given, use _ (last output).
501 If no object is given, use _ (last output).
502
502
503 Options:
503 Options:
504
504
505 -r: page str(object), don't pretty-print it."""
505 -r: page str(object), don't pretty-print it."""
@@ -524,12 +524,12 b' Currently the magic system has the following functions:\\n"""'
524
524
525 def magic_pinfo(self, parameter_s='', namespaces=None):
525 def magic_pinfo(self, parameter_s='', namespaces=None):
526 """Provide detailed information about an object.
526 """Provide detailed information about an object.
527
527
528 '%pinfo object' is just a synonym for object? or ?object."""
528 '%pinfo object' is just a synonym for object? or ?object."""
529
529
530 #print 'pinfo par: <%s>' % parameter_s # dbg
530 #print 'pinfo par: <%s>' % parameter_s # dbg
531
531
532
532
533 # detail_level: 0 -> obj? , 1 -> obj??
533 # detail_level: 0 -> obj? , 1 -> obj??
534 detail_level = 0
534 detail_level = 0
535 # We need to detect if we got called as 'pinfo pinfo foo', which can
535 # We need to detect if we got called as 'pinfo pinfo foo', which can
@@ -546,7 +546,7 b' Currently the magic system has the following functions:\\n"""'
546
546
547 def magic_pinfo2(self, parameter_s='', namespaces=None):
547 def magic_pinfo2(self, parameter_s='', namespaces=None):
548 """Provide extra detailed information about an object.
548 """Provide extra detailed information about an object.
549
549
550 '%pinfo2 object' is just a synonym for object?? or ??object."""
550 '%pinfo2 object' is just a synonym for object?? or ??object."""
551 self.shell._inspect('pinfo', parameter_s, detail_level=1,
551 self.shell._inspect('pinfo', parameter_s, detail_level=1,
552 namespaces=namespaces)
552 namespaces=namespaces)
@@ -556,11 +556,11 b' Currently the magic system has the following functions:\\n"""'
556 """Print the definition header for any callable object.
556 """Print the definition header for any callable object.
557
557
558 If the object is a class, print the constructor information.
558 If the object is a class, print the constructor information.
559
559
560 Examples
560 Examples
561 --------
561 --------
562 ::
562 ::
563
563
564 In [3]: %pdef urllib.urlopen
564 In [3]: %pdef urllib.urlopen
565 urllib.urlopen(url, data=None, proxies=None)
565 urllib.urlopen(url, data=None, proxies=None)
566 """
566 """
@@ -615,7 +615,7 b' Currently the magic system has the following functions:\\n"""'
615 ?-i a* function
615 ?-i a* function
616
616
617 Arguments:
617 Arguments:
618
618
619 PATTERN
619 PATTERN
620
620
621 where PATTERN is a string containing * as a wildcard similar to its
621 where PATTERN is a string containing * as a wildcard similar to its
@@ -642,8 +642,8 b' Currently the magic system has the following functions:\\n"""'
642
642
643 -i/-c: make the pattern case insensitive/sensitive. If neither of
643 -i/-c: make the pattern case insensitive/sensitive. If neither of
644 these options are given, the default is read from your configuration
644 these options are given, the default is read from your configuration
645 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
645 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
646 If this option is not specified in your configuration file, IPython's
646 If this option is not specified in your configuration file, IPython's
647 internal default is to do a case sensitive search.
647 internal default is to do a case sensitive search.
648
648
649 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
649 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
@@ -659,9 +659,9 b' Currently the magic system has the following functions:\\n"""'
659 and it contains module-level globals. You can add namespaces to the
659 and it contains module-level globals. You can add namespaces to the
660 search with -s or exclude them with -e (these options can be given
660 search with -s or exclude them with -e (these options can be given
661 more than once).
661 more than once).
662
662
663 Examples:
663 Examples:
664
664
665 %psearch a* -> objects beginning with an a
665 %psearch a* -> objects beginning with an a
666 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
666 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
667 %psearch a* function -> all functions beginning with an a
667 %psearch a* function -> all functions beginning with an a
@@ -670,11 +670,11 b' Currently the magic system has the following functions:\\n"""'
670 %psearch r*.* string -> all strings in modules beginning with r
670 %psearch r*.* string -> all strings in modules beginning with r
671
671
672 Case sensitve search:
672 Case sensitve search:
673
673
674 %psearch -c a* list all object beginning with lower case a
674 %psearch -c a* list all object beginning with lower case a
675
675
676 Show objects beginning with a single _:
676 Show objects beginning with a single _:
677
677
678 %psearch -a _* list objects beginning with a single underscore"""
678 %psearch -a _* list objects beginning with a single underscore"""
679 try:
679 try:
680 parameter_s.encode('ascii')
680 parameter_s.encode('ascii')
@@ -703,14 +703,14 b' Currently the magic system has the following functions:\\n"""'
703 def_search.extend(opt('s',[]))
703 def_search.extend(opt('s',[]))
704 ns_exclude = ns_exclude=opt('e',[])
704 ns_exclude = ns_exclude=opt('e',[])
705 ns_search = [nm for nm in def_search if nm not in ns_exclude]
705 ns_search = [nm for nm in def_search if nm not in ns_exclude]
706
706
707 # Call the actual search
707 # Call the actual search
708 try:
708 try:
709 psearch(args,shell.ns_table,ns_search,
709 psearch(args,shell.ns_table,ns_search,
710 show_all=opt('a'),ignore_case=ignore_case)
710 show_all=opt('a'),ignore_case=ignore_case)
711 except:
711 except:
712 shell.showtraceback()
712 shell.showtraceback()
713
713
714 @skip_doctest
714 @skip_doctest
715 def magic_who_ls(self, parameter_s=''):
715 def magic_who_ls(self, parameter_s=''):
716 """Return a sorted list of all interactive variables.
716 """Return a sorted list of all interactive variables.
@@ -751,7 +751,7 b' Currently the magic system has the following functions:\\n"""'
751
751
752 out.sort()
752 out.sort()
753 return out
753 return out
754
754
755 @skip_doctest
755 @skip_doctest
756 def magic_who(self, parameter_s=''):
756 def magic_who(self, parameter_s=''):
757 """Print all interactive variables, with some minimal formatting.
757 """Print all interactive variables, with some minimal formatting.
@@ -820,7 +820,7 b' Currently the magic system has the following functions:\\n"""'
820 The same type filtering of %who can be applied here.
820 The same type filtering of %who can be applied here.
821
821
822 For all variables, the type is printed. Additionally it prints:
822 For all variables, the type is printed. Additionally it prints:
823
823
824 - For {},[],(): their length.
824 - For {},[],(): their length.
825
825
826 - For numpy arrays, a summary with shape, number of
826 - For numpy arrays, a summary with shape, number of
@@ -844,7 +844,7 b' Currently the magic system has the following functions:\\n"""'
844 alpha int 123
844 alpha int 123
845 beta str test
845 beta str test
846 """
846 """
847
847
848 varnames = self.magic_who_ls(parameter_s)
848 varnames = self.magic_who_ls(parameter_s)
849 if not varnames:
849 if not varnames:
850 if parameter_s:
850 if parameter_s:
@@ -875,13 +875,13 b' Currently the magic system has the following functions:\\n"""'
875 # Find all variable names and types so we can figure out column sizes
875 # Find all variable names and types so we can figure out column sizes
876 def get_vars(i):
876 def get_vars(i):
877 return self.shell.user_ns[i]
877 return self.shell.user_ns[i]
878
878
879 # some types are well known and can be shorter
879 # some types are well known and can be shorter
880 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
880 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
881 def type_name(v):
881 def type_name(v):
882 tn = type(v).__name__
882 tn = type(v).__name__
883 return abbrevs.get(tn,tn)
883 return abbrevs.get(tn,tn)
884
884
885 varlist = map(get_vars,varnames)
885 varlist = map(get_vars,varnames)
886
886
887 typelist = []
887 typelist = []
@@ -927,7 +927,7 b' Currently the magic system has the following functions:\\n"""'
927 vsize = Numeric.size(var)
927 vsize = Numeric.size(var)
928 vbytes = vsize*var.itemsize()
928 vbytes = vsize*var.itemsize()
929 vdtype = var.typecode()
929 vdtype = var.typecode()
930
930
931 if vbytes < 100000:
931 if vbytes < 100000:
932 print aformat % (vshape,vsize,vdtype,vbytes)
932 print aformat % (vshape,vsize,vdtype,vbytes)
933 else:
933 else:
@@ -947,19 +947,19 b' Currently the magic system has the following functions:\\n"""'
947 print vstr
947 print vstr
948 else:
948 else:
949 print vstr[:25] + "<...>" + vstr[-25:]
949 print vstr[:25] + "<...>" + vstr[-25:]
950
950
951 def magic_reset(self, parameter_s=''):
951 def magic_reset(self, parameter_s=''):
952 """Resets the namespace by removing all names defined by the user.
952 """Resets the namespace by removing all names defined by the user.
953
953
954 Parameters
954 Parameters
955 ----------
955 ----------
956 -f : force reset without asking for confirmation.
956 -f : force reset without asking for confirmation.
957
957
958 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
958 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
959 References to objects may be kept. By default (without this option),
959 References to objects may be kept. By default (without this option),
960 we do a 'hard' reset, giving you a new session and removing all
960 we do a 'hard' reset, giving you a new session and removing all
961 references to objects from the current session.
961 references to objects from the current session.
962
962
963 Examples
963 Examples
964 --------
964 --------
965 In [6]: a = 1
965 In [6]: a = 1
@@ -984,16 +984,16 b' Currently the magic system has the following functions:\\n"""'
984 if not ans:
984 if not ans:
985 print 'Nothing done.'
985 print 'Nothing done.'
986 return
986 return
987
987
988 if 's' in opts: # Soft reset
988 if 's' in opts: # Soft reset
989 user_ns = self.shell.user_ns
989 user_ns = self.shell.user_ns
990 for i in self.magic_who_ls():
990 for i in self.magic_who_ls():
991 del(user_ns[i])
991 del(user_ns[i])
992
992
993 else: # Hard reset
993 else: # Hard reset
994 self.shell.reset(new_session = False)
994 self.shell.reset(new_session = False)
995
995
996
996
997
997
998 def magic_reset_selective(self, parameter_s=''):
998 def magic_reset_selective(self, parameter_s=''):
999 """Resets the namespace by removing names defined by the user.
999 """Resets the namespace by removing names defined by the user.
@@ -1003,7 +1003,7 b' Currently the magic system has the following functions:\\n"""'
1003 %reset_selective [-f] regex
1003 %reset_selective [-f] regex
1004
1004
1005 No action is taken if regex is not included
1005 No action is taken if regex is not included
1006
1006
1007 Options
1007 Options
1008 -f : force reset without asking for confirmation.
1008 -f : force reset without asking for confirmation.
1009
1009
@@ -1013,7 +1013,7 b' Currently the magic system has the following functions:\\n"""'
1013 We first fully reset the namespace so your output looks identical to
1013 We first fully reset the namespace so your output looks identical to
1014 this example for pedagogical reasons; in practice you do not need a
1014 this example for pedagogical reasons; in practice you do not need a
1015 full reset.
1015 full reset.
1016
1016
1017 In [1]: %reset -f
1017 In [1]: %reset -f
1018
1018
1019 Now, with a clean namespace we can make a few variables and use
1019 Now, with a clean namespace we can make a few variables and use
@@ -1044,9 +1044,9 b' Currently the magic system has the following functions:\\n"""'
1044 In [11]: who_ls
1044 In [11]: who_ls
1045 Out[11]: ['a']
1045 Out[11]: ['a']
1046 """
1046 """
1047
1047
1048 opts, regex = self.parse_options(parameter_s,'f')
1048 opts, regex = self.parse_options(parameter_s,'f')
1049
1049
1050 if opts.has_key('f'):
1050 if opts.has_key('f'):
1051 ans = True
1051 ans = True
1052 else:
1052 else:
@@ -1065,16 +1065,16 b' Currently the magic system has the following functions:\\n"""'
1065 except TypeError:
1065 except TypeError:
1066 raise TypeError('regex must be a string or compiled pattern')
1066 raise TypeError('regex must be a string or compiled pattern')
1067 for i in self.magic_who_ls():
1067 for i in self.magic_who_ls():
1068 if m.search(i):
1068 if m.search(i):
1069 del(user_ns[i])
1069 del(user_ns[i])
1070
1070
1071 def magic_xdel(self, parameter_s=''):
1071 def magic_xdel(self, parameter_s=''):
1072 """Delete a variable, trying to clear it from anywhere that
1072 """Delete a variable, trying to clear it from anywhere that
1073 IPython's machinery has references to it. By default, this uses
1073 IPython's machinery has references to it. By default, this uses
1074 the identity of the named object in the user namespace to remove
1074 the identity of the named object in the user namespace to remove
1075 references held under other names. The object is also removed
1075 references held under other names. The object is also removed
1076 from the output history.
1076 from the output history.
1077
1077
1078 Options
1078 Options
1079 -n : Delete the specified name from all namespaces, without
1079 -n : Delete the specified name from all namespaces, without
1080 checking their identity.
1080 checking their identity.
@@ -1084,7 +1084,7 b' Currently the magic system has the following functions:\\n"""'
1084 self.shell.del_var(varname, ('n' in opts))
1084 self.shell.del_var(varname, ('n' in opts))
1085 except (NameError, ValueError) as e:
1085 except (NameError, ValueError) as e:
1086 print type(e).__name__ +": "+ str(e)
1086 print type(e).__name__ +": "+ str(e)
1087
1087
1088 def magic_logstart(self,parameter_s=''):
1088 def magic_logstart(self,parameter_s=''):
1089 """Start logging anywhere in a session.
1089 """Start logging anywhere in a session.
1090
1090
@@ -1125,7 +1125,7 b' Currently the magic system has the following functions:\\n"""'
1125
1125
1126 -t: put timestamps before each input line logged (these are put in
1126 -t: put timestamps before each input line logged (these are put in
1127 comments)."""
1127 comments)."""
1128
1128
1129 opts,par = self.parse_options(parameter_s,'ort')
1129 opts,par = self.parse_options(parameter_s,'ort')
1130 log_output = 'o' in opts
1130 log_output = 'o' in opts
1131 log_raw_input = 'r' in opts
1131 log_raw_input = 'r' in opts
@@ -1172,7 +1172,7 b' Currently the magic system has the following functions:\\n"""'
1172 input_hist = self.shell.history_manager.input_hist_raw
1172 input_hist = self.shell.history_manager.input_hist_raw
1173 else:
1173 else:
1174 input_hist = self.shell.history_manager.input_hist_parsed
1174 input_hist = self.shell.history_manager.input_hist_parsed
1175
1175
1176 if log_output:
1176 if log_output:
1177 log_write = logger.log_write
1177 log_write = logger.log_write
1178 output_hist = self.shell.history_manager.output_hist
1178 output_hist = self.shell.history_manager.output_hist
@@ -1186,7 +1186,7 b' Currently the magic system has the following functions:\\n"""'
1186 if timestamp:
1186 if timestamp:
1187 # re-enable timestamping
1187 # re-enable timestamping
1188 logger.timestamp = True
1188 logger.timestamp = True
1189
1189
1190 print ('Activating auto-logging. '
1190 print ('Activating auto-logging. '
1191 'Current session state plus future input saved.')
1191 'Current session state plus future input saved.')
1192 logger.logstate()
1192 logger.logstate()
@@ -1204,7 +1204,7 b' Currently the magic system has the following functions:\\n"""'
1204
1204
1205 You must have previously started logging."""
1205 You must have previously started logging."""
1206 self.shell.logger.switch_log(0)
1206 self.shell.logger.switch_log(0)
1207
1207
1208 def magic_logon(self,parameter_s=''):
1208 def magic_logon(self,parameter_s=''):
1209 """Restart logging.
1209 """Restart logging.
1210
1210
@@ -1212,14 +1212,14 b' Currently the magic system has the following functions:\\n"""'
1212 stopped with %logoff. For starting logging for the first time, you
1212 stopped with %logoff. For starting logging for the first time, you
1213 must use the %logstart function, which allows you to specify an
1213 must use the %logstart function, which allows you to specify an
1214 optional log filename."""
1214 optional log filename."""
1215
1215
1216 self.shell.logger.switch_log(1)
1216 self.shell.logger.switch_log(1)
1217
1217
1218 def magic_logstate(self,parameter_s=''):
1218 def magic_logstate(self,parameter_s=''):
1219 """Print the status of the logging system."""
1219 """Print the status of the logging system."""
1220
1220
1221 self.shell.logger.logstate()
1221 self.shell.logger.logstate()
1222
1222
1223 def magic_pdb(self, parameter_s=''):
1223 def magic_pdb(self, parameter_s=''):
1224 """Control the automatic calling of the pdb interactive debugger.
1224 """Control the automatic calling of the pdb interactive debugger.
1225
1225
@@ -1314,7 +1314,7 b' Currently the magic system has the following functions:\\n"""'
1314 When more than one key is provided, additional keys are used as
1314 When more than one key is provided, additional keys are used as
1315 secondary criteria when the there is equality in all keys selected
1315 secondary criteria when the there is equality in all keys selected
1316 before them.
1316 before them.
1317
1317
1318 Abbreviations can be used for any key names, as long as the
1318 Abbreviations can be used for any key names, as long as the
1319 abbreviation is unambiguous. The following are the keys currently
1319 abbreviation is unambiguous. The following are the keys currently
1320 defined:
1320 defined:
@@ -1353,16 +1353,16 b' Currently the magic system has the following functions:\\n"""'
1353 If you want to run complete programs under the profiler's control, use
1353 If you want to run complete programs under the profiler's control, use
1354 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
1354 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
1355 contains profiler specific options as described here.
1355 contains profiler specific options as described here.
1356
1356
1357 You can read the complete documentation for the profile module with::
1357 You can read the complete documentation for the profile module with::
1358
1358
1359 In [1]: import profile; profile.help()
1359 In [1]: import profile; profile.help()
1360 """
1360 """
1361
1361
1362 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
1362 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
1363 # protect user quote marks
1363 # protect user quote marks
1364 parameter_s = parameter_s.replace('"',r'\"').replace("'",r"\'")
1364 parameter_s = parameter_s.replace('"',r'\"').replace("'",r"\'")
1365
1365
1366 if user_mode: # regular user call
1366 if user_mode: # regular user call
1367 opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:',
1367 opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:',
1368 list_all=1)
1368 list_all=1)
@@ -1382,7 +1382,7 b' Currently the magic system has the following functions:\\n"""'
1382 namespace = locals()
1382 namespace = locals()
1383
1383
1384 opts.merge(opts_def)
1384 opts.merge(opts_def)
1385
1385
1386 prof = profile.Profile()
1386 prof = profile.Profile()
1387 try:
1387 try:
1388 prof = prof.runctx(arg_str,namespace,namespace)
1388 prof = prof.runctx(arg_str,namespace,namespace)
@@ -1403,7 +1403,7 b' Currently the magic system has the following functions:\\n"""'
1403 lims.append(float(lim))
1403 lims.append(float(lim))
1404 except ValueError:
1404 except ValueError:
1405 lims.append(lim)
1405 lims.append(lim)
1406
1406
1407 # Trap output.
1407 # Trap output.
1408 stdout_trap = StringIO()
1408 stdout_trap = StringIO()
1409
1409
@@ -1420,7 +1420,7 b' Currently the magic system has the following functions:\\n"""'
1420 stats.print_stats(*lims)
1420 stats.print_stats(*lims)
1421 finally:
1421 finally:
1422 sys.stdout = sys_stdout
1422 sys.stdout = sys_stdout
1423
1423
1424 output = stdout_trap.getvalue()
1424 output = stdout_trap.getvalue()
1425 output = output.rstrip()
1425 output = output.rstrip()
1426
1426
@@ -1454,7 +1454,7 b' Currently the magic system has the following functions:\\n"""'
1454
1454
1455 Usage:\\
1455 Usage:\\
1456 %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options]] file [args]
1456 %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options]] file [args]
1457
1457
1458 Parameters after the filename are passed as command-line arguments to
1458 Parameters after the filename are passed as command-line arguments to
1459 the program (put in sys.argv). Then, control returns to IPython's
1459 the program (put in sys.argv). Then, control returns to IPython's
1460 prompt.
1460 prompt.
@@ -1475,7 +1475,7 b' Currently the magic system has the following functions:\\n"""'
1475 interactive work, while giving each program a 'clean sheet' to run in.
1475 interactive work, while giving each program a 'clean sheet' to run in.
1476
1476
1477 Options:
1477 Options:
1478
1478
1479 -n: __name__ is NOT set to '__main__', but to the running file's name
1479 -n: __name__ is NOT set to '__main__', but to the running file's name
1480 without extension (as python does under import). This allows running
1480 without extension (as python does under import). This allows running
1481 scripts and reloading the definitions in them without calling code
1481 scripts and reloading the definitions in them without calling code
@@ -1520,7 +1520,7 b' Currently the magic system has the following functions:\\n"""'
1520 -d: run your program under the control of pdb, the Python debugger.
1520 -d: run your program under the control of pdb, the Python debugger.
1521 This allows you to execute your program step by step, watch variables,
1521 This allows you to execute your program step by step, watch variables,
1522 etc. Internally, what IPython does is similar to calling:
1522 etc. Internally, what IPython does is similar to calling:
1523
1523
1524 pdb.run('execfile("YOURFILENAME")')
1524 pdb.run('execfile("YOURFILENAME")')
1525
1525
1526 with a breakpoint set on line 1 of your file. You can change the line
1526 with a breakpoint set on line 1 of your file. You can change the line
@@ -1580,10 +1580,10 b' Currently the magic system has the following functions:\\n"""'
1580 if filename.lower().endswith('.ipy'):
1580 if filename.lower().endswith('.ipy'):
1581 self.shell.safe_execfile_ipy(filename)
1581 self.shell.safe_execfile_ipy(filename)
1582 return
1582 return
1583
1583
1584 # Control the response to exit() calls made by the script being run
1584 # Control the response to exit() calls made by the script being run
1585 exit_ignore = opts.has_key('e')
1585 exit_ignore = opts.has_key('e')
1586
1586
1587 # Make sure that the running script gets a proper sys.argv as if it
1587 # Make sure that the running script gets a proper sys.argv as if it
1588 # were run from a system shell.
1588 # were run from a system shell.
1589 save_argv = sys.argv # save it for later restoring
1589 save_argv = sys.argv # save it for later restoring
@@ -1626,7 +1626,7 b' Currently the magic system has the following functions:\\n"""'
1626 # This needs to be undone at the end to prevent holding references to
1626 # This needs to be undone at the end to prevent holding references to
1627 # every single object ever created.
1627 # every single object ever created.
1628 sys.modules[main_mod_name] = main_mod
1628 sys.modules[main_mod_name] = main_mod
1629
1629
1630 try:
1630 try:
1631 stats = None
1631 stats = None
1632 with self.readline_no_record:
1632 with self.readline_no_record:
@@ -1663,7 +1663,7 b' Currently the magic system has the following functions:\\n"""'
1663 print "%s prompt to start your script." % deb.prompt
1663 print "%s prompt to start your script." % deb.prompt
1664 try:
1664 try:
1665 deb.run('execfile("%s")' % filename,prog_ns)
1665 deb.run('execfile("%s")' % filename,prog_ns)
1666
1666
1667 except:
1667 except:
1668 etype, value, tb = sys.exc_info()
1668 etype, value, tb = sys.exc_info()
1669 # Skip three frames in the traceback: the %run one,
1669 # Skip three frames in the traceback: the %run one,
@@ -1739,7 +1739,7 b' Currently the magic system has the following functions:\\n"""'
1739 # we can do is to at least restore __builtins__ for the user on
1739 # we can do is to at least restore __builtins__ for the user on
1740 # exit.
1740 # exit.
1741 self.shell.user_ns['__builtins__'] = builtin_mod
1741 self.shell.user_ns['__builtins__'] = builtin_mod
1742
1742
1743 # Ensure key global structures are restored
1743 # Ensure key global structures are restored
1744 sys.argv = save_argv
1744 sys.argv = save_argv
1745 if restore_main:
1745 if restore_main:
@@ -1749,7 +1749,7 b' Currently the magic system has the following functions:\\n"""'
1749 # added. Otherwise it will trap references to objects
1749 # added. Otherwise it will trap references to objects
1750 # contained therein.
1750 # contained therein.
1751 del sys.modules[main_mod_name]
1751 del sys.modules[main_mod_name]
1752
1752
1753 return stats
1753 return stats
1754
1754
1755 @skip_doctest
1755 @skip_doctest
@@ -1764,14 +1764,14 b' Currently the magic system has the following functions:\\n"""'
1764
1764
1765 Options:
1765 Options:
1766 -n<N>: execute the given statement <N> times in a loop. If this value
1766 -n<N>: execute the given statement <N> times in a loop. If this value
1767 is not given, a fitting value is chosen.
1767 is not given, a fitting value is chosen.
1768
1768
1769 -r<R>: repeat the loop iteration <R> times and take the best result.
1769 -r<R>: repeat the loop iteration <R> times and take the best result.
1770 Default: 3
1770 Default: 3
1771
1771
1772 -t: use time.time to measure the time, which is the default on Unix.
1772 -t: use time.time to measure the time, which is the default on Unix.
1773 This function measures wall time.
1773 This function measures wall time.
1774
1774
1775 -c: use time.clock to measure the time, which is the default on
1775 -c: use time.clock to measure the time, which is the default on
1776 Windows and measures wall time. On Unix, resource.getrusage is used
1776 Windows and measures wall time. On Unix, resource.getrusage is used
1777 instead and returns the CPU user time.
1777 instead and returns the CPU user time.
@@ -1779,7 +1779,7 b' Currently the magic system has the following functions:\\n"""'
1779 -p<P>: use a precision of <P> digits to display the timing result.
1779 -p<P>: use a precision of <P> digits to display the timing result.
1780 Default: 3
1780 Default: 3
1781
1781
1782
1782
1783 Examples:
1783 Examples:
1784
1784
1785 In [1]: %timeit pass
1785 In [1]: %timeit pass
@@ -1797,7 +1797,7 b' Currently the magic system has the following functions:\\n"""'
1797
1797
1798 In [6]: %timeit -n1 time.sleep(2)
1798 In [6]: %timeit -n1 time.sleep(2)
1799 1 loops, best of 3: 2 s per loop
1799 1 loops, best of 3: 2 s per loop
1800
1800
1801
1801
1802 The times reported by %timeit will be slightly higher than those
1802 The times reported by %timeit will be slightly higher than those
1803 reported by the timeit.py script when variables are accessed. This is
1803 reported by the timeit.py script when variables are accessed. This is
@@ -1828,10 +1828,10 b' Currently the magic system has the following functions:\\n"""'
1828 # succeeds
1828 # succeeds
1829 #
1829 #
1830 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
1830 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
1831
1831
1832 #units = [u"s", u"ms",u'\xb5',"ns"]
1832 #units = [u"s", u"ms",u'\xb5',"ns"]
1833 units = [u"s", u"ms",u'us',"ns"]
1833 units = [u"s", u"ms",u'us',"ns"]
1834
1834
1835 scaling = [1, 1e3, 1e6, 1e9]
1835 scaling = [1, 1e3, 1e6, 1e9]
1836
1836
1837 opts, stmt = self.parse_options(parameter_s,'n:r:tcp:',
1837 opts, stmt = self.parse_options(parameter_s,'n:r:tcp:',
@@ -1857,15 +1857,15 b' Currently the magic system has the following functions:\\n"""'
1857 # Track compilation time so it can be reported if too long
1857 # Track compilation time so it can be reported if too long
1858 # Minimum time above which compilation time will be reported
1858 # Minimum time above which compilation time will be reported
1859 tc_min = 0.1
1859 tc_min = 0.1
1860
1860
1861 t0 = clock()
1861 t0 = clock()
1862 code = compile(src, "<magic-timeit>", "exec")
1862 code = compile(src, "<magic-timeit>", "exec")
1863 tc = clock()-t0
1863 tc = clock()-t0
1864
1864
1865 ns = {}
1865 ns = {}
1866 exec code in self.shell.user_ns, ns
1866 exec code in self.shell.user_ns, ns
1867 timer.inner = ns["inner"]
1867 timer.inner = ns["inner"]
1868
1868
1869 if number == 0:
1869 if number == 0:
1870 # determine number so that 0.2 <= total time < 2.0
1870 # determine number so that 0.2 <= total time < 2.0
1871 number = 1
1871 number = 1
@@ -1873,7 +1873,7 b' Currently the magic system has the following functions:\\n"""'
1873 if timer.timeit(number) >= 0.2:
1873 if timer.timeit(number) >= 0.2:
1874 break
1874 break
1875 number *= 10
1875 number *= 10
1876
1876
1877 best = min(timer.repeat(repeat, number)) / number
1877 best = min(timer.repeat(repeat, number)) / number
1878
1878
1879 if best > 0.0 and best < 1000.0:
1879 if best > 0.0 and best < 1000.0:
@@ -1901,7 +1901,7 b' Currently the magic system has the following functions:\\n"""'
1901 This function provides very basic timing functionality. In Python
1901 This function provides very basic timing functionality. In Python
1902 2.3, the timeit module offers more control and sophistication, so this
1902 2.3, the timeit module offers more control and sophistication, so this
1903 could be rewritten to use it (patches welcome).
1903 could be rewritten to use it (patches welcome).
1904
1904
1905 Some examples:
1905 Some examples:
1906
1906
1907 In [1]: time 2**128
1907 In [1]: time 2**128
@@ -1936,14 +1936,14 b' Currently the magic system has the following functions:\\n"""'
1936 Wall time: 0.00 s
1936 Wall time: 0.00 s
1937 Compiler : 0.78 s
1937 Compiler : 0.78 s
1938 """
1938 """
1939
1939
1940 # fail immediately if the given expression can't be compiled
1940 # fail immediately if the given expression can't be compiled
1941
1941
1942 expr = self.shell.prefilter(parameter_s,False)
1942 expr = self.shell.prefilter(parameter_s,False)
1943
1943
1944 # Minimum time above which compilation time will be reported
1944 # Minimum time above which compilation time will be reported
1945 tc_min = 0.1
1945 tc_min = 0.1
1946
1946
1947 try:
1947 try:
1948 mode = 'eval'
1948 mode = 'eval'
1949 t0 = clock()
1949 t0 = clock()
@@ -1992,7 +1992,7 b' Currently the magic system has the following functions:\\n"""'
1992 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
1992 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
1993
1993
1994 Options:
1994 Options:
1995
1995
1996 -r: use 'raw' input. By default, the 'processed' history is used,
1996 -r: use 'raw' input. By default, the 'processed' history is used,
1997 so that magics are loaded in their transformed version to valid
1997 so that magics are loaded in their transformed version to valid
1998 Python. If this option is given, the raw input as typed as the
1998 Python. If this option is given, the raw input as typed as the
@@ -2011,7 +2011,7 b' Currently the magic system has the following functions:\\n"""'
2011 notation, where N:M means numbers N through M-1.
2011 notation, where N:M means numbers N through M-1.
2012
2012
2013 For example, if your history contains (%hist prints it):
2013 For example, if your history contains (%hist prints it):
2014
2014
2015 44: x=1
2015 44: x=1
2016 45: y=3
2016 45: y=3
2017 46: z=x+y
2017 46: z=x+y
@@ -2036,9 +2036,9 b' Currently the magic system has the following functions:\\n"""'
2036 code instead of printing them when you type their name.
2036 code instead of printing them when you type their name.
2037
2037
2038 You can view a macro's contents by explicitly printing it with:
2038 You can view a macro's contents by explicitly printing it with:
2039
2039
2040 'print macro_name'.
2040 'print macro_name'.
2041
2041
2042 """
2042 """
2043 opts,args = self.parse_options(parameter_s,'r',mode='list')
2043 opts,args = self.parse_options(parameter_s,'r',mode='list')
2044 if not args: # List existing macros
2044 if not args: # List existing macros
@@ -2048,7 +2048,7 b' Currently the magic system has the following functions:\\n"""'
2048 raise UsageError(
2048 raise UsageError(
2049 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
2049 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
2050 name, codefrom = args[0], " ".join(args[1:])
2050 name, codefrom = args[0], " ".join(args[1:])
2051
2051
2052 #print 'rng',ranges # dbg
2052 #print 'rng',ranges # dbg
2053 try:
2053 try:
2054 lines = self.shell.find_user_code(codefrom, 'r' in opts)
2054 lines = self.shell.find_user_code(codefrom, 'r' in opts)
@@ -2068,13 +2068,13 b' Currently the magic system has the following functions:\\n"""'
2068 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
2068 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
2069
2069
2070 Options:
2070 Options:
2071
2071
2072 -r: use 'raw' input. By default, the 'processed' history is used,
2072 -r: use 'raw' input. By default, the 'processed' history is used,
2073 so that magics are loaded in their transformed version to valid
2073 so that magics are loaded in their transformed version to valid
2074 Python. If this option is given, the raw input as typed as the
2074 Python. If this option is given, the raw input as typed as the
2075 command line is used instead.
2075 command line is used instead.
2076
2076
2077 This function uses the same syntax as %history for input ranges,
2077 This function uses the same syntax as %history for input ranges,
2078 then saves the lines to the filename you specify.
2078 then saves the lines to the filename you specify.
2079
2079
2080 It adds a '.py' extension to the file if you don't do so yourself, and
2080 It adds a '.py' extension to the file if you don't do so yourself, and
@@ -2099,7 +2099,7 b' Currently the magic system has the following functions:\\n"""'
2099 f.write(py3compat.cast_unicode(cmds))
2099 f.write(py3compat.cast_unicode(cmds))
2100 print 'The following commands were written to file `%s`:' % fname
2100 print 'The following commands were written to file `%s`:' % fname
2101 print cmds
2101 print cmds
2102
2102
2103 def magic_pastebin(self, parameter_s = ''):
2103 def magic_pastebin(self, parameter_s = ''):
2104 """Upload code to the 'Lodge it' paste bin, returning the URL."""
2104 """Upload code to the 'Lodge it' paste bin, returning the URL."""
2105 try:
2105 try:
@@ -2110,7 +2110,7 b' Currently the magic system has the following functions:\\n"""'
2110 pbserver = ServerProxy('http://paste.pocoo.org/xmlrpc/')
2110 pbserver = ServerProxy('http://paste.pocoo.org/xmlrpc/')
2111 id = pbserver.pastes.newPaste("python", code)
2111 id = pbserver.pastes.newPaste("python", code)
2112 return "http://paste.pocoo.org/show/" + id
2112 return "http://paste.pocoo.org/show/" + id
2113
2113
2114 def magic_loadpy(self, arg_s):
2114 def magic_loadpy(self, arg_s):
2115 """Load a .py python script into the GUI console.
2115 """Load a .py python script into the GUI console.
2116
2116
@@ -2130,10 +2130,10 b' Currently the magic system has the following functions:\\n"""'
2130 with open(arg_s) as f:
2130 with open(arg_s) as f:
2131 content = f.read()
2131 content = f.read()
2132 self.set_next_input(content)
2132 self.set_next_input(content)
2133
2133
2134 def _find_edit_target(self, args, opts, last_call):
2134 def _find_edit_target(self, args, opts, last_call):
2135 """Utility method used by magic_edit to find what to edit."""
2135 """Utility method used by magic_edit to find what to edit."""
2136
2136
2137 def make_filename(arg):
2137 def make_filename(arg):
2138 "Make a filename from the given args"
2138 "Make a filename from the given args"
2139 arg = unquote_filename(arg)
2139 arg = unquote_filename(arg)
@@ -2141,20 +2141,20 b' Currently the magic system has the following functions:\\n"""'
2141 filename = get_py_filename(arg)
2141 filename = get_py_filename(arg)
2142 except IOError:
2142 except IOError:
2143 # If it ends with .py but doesn't already exist, assume we want
2143 # If it ends with .py but doesn't already exist, assume we want
2144 # a new file.
2144 # a new file.
2145 if arg.endswith('.py'):
2145 if arg.endswith('.py'):
2146 filename = arg
2146 filename = arg
2147 else:
2147 else:
2148 filename = None
2148 filename = None
2149 return filename
2149 return filename
2150
2150
2151 # Set a few locals from the options for convenience:
2151 # Set a few locals from the options for convenience:
2152 opts_prev = 'p' in opts
2152 opts_prev = 'p' in opts
2153 opts_raw = 'r' in opts
2153 opts_raw = 'r' in opts
2154
2154
2155 # custom exceptions
2155 # custom exceptions
2156 class DataIsObject(Exception): pass
2156 class DataIsObject(Exception): pass
2157
2157
2158 # Default line number value
2158 # Default line number value
2159 lineno = opts.get('n',None)
2159 lineno = opts.get('n',None)
2160
2160
@@ -2162,7 +2162,7 b' Currently the magic system has the following functions:\\n"""'
2162 args = '_%s' % last_call[0]
2162 args = '_%s' % last_call[0]
2163 if not self.shell.user_ns.has_key(args):
2163 if not self.shell.user_ns.has_key(args):
2164 args = last_call[1]
2164 args = last_call[1]
2165
2165
2166 # use last_call to remember the state of the previous call, but don't
2166 # use last_call to remember the state of the previous call, but don't
2167 # let it be clobbered by successive '-p' calls.
2167 # let it be clobbered by successive '-p' calls.
2168 try:
2168 try:
@@ -2177,7 +2177,7 b' Currently the magic system has the following functions:\\n"""'
2177 use_temp = True
2177 use_temp = True
2178
2178
2179 data = ''
2179 data = ''
2180
2180
2181 # First, see if the arguments should be a filename.
2181 # First, see if the arguments should be a filename.
2182 filename = make_filename(args)
2182 filename = make_filename(args)
2183 if filename:
2183 if filename:
@@ -2203,16 +2203,16 b' Currently the magic system has the following functions:\\n"""'
2203 "or as a filename." % args)
2203 "or as a filename." % args)
2204 return
2204 return
2205 use_temp = False
2205 use_temp = False
2206
2206
2207 except DataIsObject:
2207 except DataIsObject:
2208 # macros have a special edit function
2208 # macros have a special edit function
2209 if isinstance(data, Macro):
2209 if isinstance(data, Macro):
2210 raise MacroToEdit(data)
2210 raise MacroToEdit(data)
2211
2211
2212 # For objects, try to edit the file where they are defined
2212 # For objects, try to edit the file where they are defined
2213 try:
2213 try:
2214 filename = inspect.getabsfile(data)
2214 filename = inspect.getabsfile(data)
2215 if 'fakemodule' in filename.lower() and inspect.isclass(data):
2215 if 'fakemodule' in filename.lower() and inspect.isclass(data):
2216 # class created by %edit? Try to find source
2216 # class created by %edit? Try to find source
2217 # by looking for method definitions instead, the
2217 # by looking for method definitions instead, the
2218 # __module__ in those classes is FakeModule.
2218 # __module__ in those classes is FakeModule.
@@ -2223,9 +2223,9 b' Currently the magic system has the following functions:\\n"""'
2223 filename = inspect.getabsfile(attr)
2223 filename = inspect.getabsfile(attr)
2224 if filename and 'fakemodule' not in filename.lower():
2224 if filename and 'fakemodule' not in filename.lower():
2225 # change the attribute to be the edit target instead
2225 # change the attribute to be the edit target instead
2226 data = attr
2226 data = attr
2227 break
2227 break
2228
2228
2229 datafile = 1
2229 datafile = 1
2230 except TypeError:
2230 except TypeError:
2231 filename = make_filename(args)
2231 filename = make_filename(args)
@@ -2249,7 +2249,7 b' Currently the magic system has the following functions:\\n"""'
2249 if use_temp:
2249 if use_temp:
2250 filename = self.shell.mktempfile(data)
2250 filename = self.shell.mktempfile(data)
2251 print 'IPython will make a temporary file named:',filename
2251 print 'IPython will make a temporary file named:',filename
2252
2252
2253 return filename, lineno, use_temp
2253 return filename, lineno, use_temp
2254
2254
2255 def _edit_macro(self,mname,macro):
2255 def _edit_macro(self,mname,macro):
@@ -2266,7 +2266,7 b' Currently the magic system has the following functions:\\n"""'
2266 def magic_ed(self,parameter_s=''):
2266 def magic_ed(self,parameter_s=''):
2267 """Alias to %edit."""
2267 """Alias to %edit."""
2268 return self.magic_edit(parameter_s)
2268 return self.magic_edit(parameter_s)
2269
2269
2270 @skip_doctest
2270 @skip_doctest
2271 def magic_edit(self,parameter_s='',last_call=['','']):
2271 def magic_edit(self,parameter_s='',last_call=['','']):
2272 """Bring up an editor and execute the resulting code.
2272 """Bring up an editor and execute the resulting code.
@@ -2280,15 +2280,15 b' Currently the magic system has the following functions:\\n"""'
2280 notepad under Windows. See the end of this docstring for how to change
2280 notepad under Windows. See the end of this docstring for how to change
2281 the editor hook.
2281 the editor hook.
2282
2282
2283 You can also set the value of this editor via the
2283 You can also set the value of this editor via the
2284 ``TerminalInteractiveShell.editor`` option in your configuration file.
2284 ``TerminalInteractiveShell.editor`` option in your configuration file.
2285 This is useful if you wish to use a different editor from your typical
2285 This is useful if you wish to use a different editor from your typical
2286 default with IPython (and for Windows users who typically don't set
2286 default with IPython (and for Windows users who typically don't set
2287 environment variables).
2287 environment variables).
2288
2288
2289 This command allows you to conveniently edit multi-line code right in
2289 This command allows you to conveniently edit multi-line code right in
2290 your IPython session.
2290 your IPython session.
2291
2291
2292 If called without arguments, %edit opens up an empty editor with a
2292 If called without arguments, %edit opens up an empty editor with a
2293 temporary file and will execute the contents of this file when you
2293 temporary file and will execute the contents of this file when you
2294 close it (don't forget to save it!).
2294 close it (don't forget to save it!).
@@ -2301,7 +2301,7 b' Currently the magic system has the following functions:\\n"""'
2301 you can configure this by providing your own modified hook if your
2301 you can configure this by providing your own modified hook if your
2302 favorite editor supports line-number specifications with a different
2302 favorite editor supports line-number specifications with a different
2303 syntax.
2303 syntax.
2304
2304
2305 -p: this will call the editor with the same data as the previous time
2305 -p: this will call the editor with the same data as the previous time
2306 it was used, regardless of how long ago (in your current session) it
2306 it was used, regardless of how long ago (in your current session) it
2307 was.
2307 was.
@@ -2312,7 +2312,7 b' Currently the magic system has the following functions:\\n"""'
2312 this option is given, the raw input as typed as the command line is
2312 this option is given, the raw input as typed as the command line is
2313 used instead. When you exit the editor, it will be executed by
2313 used instead. When you exit the editor, it will be executed by
2314 IPython's own processor.
2314 IPython's own processor.
2315
2315
2316 -x: do not execute the edited code immediately upon exit. This is
2316 -x: do not execute the edited code immediately upon exit. This is
2317 mainly useful if you are editing programs which need to be called with
2317 mainly useful if you are editing programs which need to be called with
2318 command line arguments, which you can then do using %run.
2318 command line arguments, which you can then do using %run.
@@ -2321,7 +2321,7 b' Currently the magic system has the following functions:\\n"""'
2321 Arguments:
2321 Arguments:
2322
2322
2323 If arguments are given, the following possibilites exist:
2323 If arguments are given, the following possibilites exist:
2324
2324
2325 - If the argument is a filename, IPython will load that into the
2325 - If the argument is a filename, IPython will load that into the
2326 editor. It will execute its contents with execfile() when you exit,
2326 editor. It will execute its contents with execfile() when you exit,
2327 loading any code in the file into your interactive namespace.
2327 loading any code in the file into your interactive namespace.
@@ -2364,18 +2364,18 b' Currently the magic system has the following functions:\\n"""'
2364 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
2364 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
2365
2365
2366 We can then call the function foo():
2366 We can then call the function foo():
2367
2367
2368 In [2]: foo()
2368 In [2]: foo()
2369 foo() was defined in an editing session
2369 foo() was defined in an editing session
2370
2370
2371 Now we edit foo. IPython automatically loads the editor with the
2371 Now we edit foo. IPython automatically loads the editor with the
2372 (temporary) file where foo() was previously defined:
2372 (temporary) file where foo() was previously defined:
2373
2373
2374 In [3]: ed foo
2374 In [3]: ed foo
2375 Editing... done. Executing edited code...
2375 Editing... done. Executing edited code...
2376
2376
2377 And if we call foo() again we get the modified version:
2377 And if we call foo() again we get the modified version:
2378
2378
2379 In [4]: foo()
2379 In [4]: foo()
2380 foo() has now been changed!
2380 foo() has now been changed!
2381
2381
@@ -2411,7 +2411,7 b' Currently the magic system has the following functions:\\n"""'
2411 general instructions on how to set a new hook for use once you've
2411 general instructions on how to set a new hook for use once you've
2412 defined it."""
2412 defined it."""
2413 opts,args = self.parse_options(parameter_s,'prxn:')
2413 opts,args = self.parse_options(parameter_s,'prxn:')
2414
2414
2415 try:
2415 try:
2416 filename, lineno, is_temp = self._find_edit_target(args, opts, last_call)
2416 filename, lineno, is_temp = self._find_edit_target(args, opts, last_call)
2417 except MacroToEdit as e:
2417 except MacroToEdit as e:
@@ -2429,12 +2429,12 b' Currently the magic system has the following functions:\\n"""'
2429 except TryNext:
2429 except TryNext:
2430 warn('Could not open editor')
2430 warn('Could not open editor')
2431 return
2431 return
2432
2432
2433 # XXX TODO: should this be generalized for all string vars?
2433 # XXX TODO: should this be generalized for all string vars?
2434 # For now, this is special-cased to blocks created by cpaste
2434 # For now, this is special-cased to blocks created by cpaste
2435 if args.strip() == 'pasted_block':
2435 if args.strip() == 'pasted_block':
2436 self.shell.user_ns['pasted_block'] = file_read(filename)
2436 self.shell.user_ns['pasted_block'] = file_read(filename)
2437
2437
2438 if 'x' in opts: # -x prevents actual execution
2438 if 'x' in opts: # -x prevents actual execution
2439 print
2439 print
2440 else:
2440 else:
@@ -2445,7 +2445,7 b' Currently the magic system has the following functions:\\n"""'
2445 else:
2445 else:
2446 self.shell.safe_execfile(filename,self.shell.user_ns,
2446 self.shell.safe_execfile(filename,self.shell.user_ns,
2447 self.shell.user_ns)
2447 self.shell.user_ns)
2448
2448
2449 if is_temp:
2449 if is_temp:
2450 try:
2450 try:
2451 return open(filename).read()
2451 return open(filename).read()
@@ -2481,19 +2481,19 b' Currently the magic system has the following functions:\\n"""'
2481 Currently implemented schemes: NoColor, Linux, LightBG.
2481 Currently implemented schemes: NoColor, Linux, LightBG.
2482
2482
2483 Color scheme names are not case-sensitive.
2483 Color scheme names are not case-sensitive.
2484
2484
2485 Examples
2485 Examples
2486 --------
2486 --------
2487 To get a plain black and white terminal::
2487 To get a plain black and white terminal::
2488
2488
2489 %colors nocolor
2489 %colors nocolor
2490 """
2490 """
2491
2491
2492 def color_switch_err(name):
2492 def color_switch_err(name):
2493 warn('Error changing %s color schemes.\n%s' %
2493 warn('Error changing %s color schemes.\n%s' %
2494 (name,sys.exc_info()[1]))
2494 (name,sys.exc_info()[1]))
2495
2495
2496
2496
2497 new_scheme = parameter_s.strip()
2497 new_scheme = parameter_s.strip()
2498 if not new_scheme:
2498 if not new_scheme:
2499 raise UsageError(
2499 raise UsageError(
@@ -2517,11 +2517,11 b' http://starship.python.net/crew/theller/ctypes'
2517 Defaulting color scheme to 'NoColor'"""
2517 Defaulting color scheme to 'NoColor'"""
2518 new_scheme = 'NoColor'
2518 new_scheme = 'NoColor'
2519 warn(msg)
2519 warn(msg)
2520
2520
2521 # readline option is 0
2521 # readline option is 0
2522 if not shell.colors_force and not shell.has_readline:
2522 if not shell.colors_force and not shell.has_readline:
2523 new_scheme = 'NoColor'
2523 new_scheme = 'NoColor'
2524
2524
2525 # Set prompt colors
2525 # Set prompt colors
2526 try:
2526 try:
2527 shell.displayhook.set_colors(new_scheme)
2527 shell.displayhook.set_colors(new_scheme)
@@ -2545,7 +2545,7 b' Defaulting color scheme to \'NoColor\'"""'
2545 color_switch_err('object inspector')
2545 color_switch_err('object inspector')
2546 else:
2546 else:
2547 shell.inspector.set_active_scheme('NoColor')
2547 shell.inspector.set_active_scheme('NoColor')
2548
2548
2549 def magic_pprint(self, parameter_s=''):
2549 def magic_pprint(self, parameter_s=''):
2550 """Toggle pretty printing on/off."""
2550 """Toggle pretty printing on/off."""
2551 ptformatter = self.shell.display_formatter.formatters['text/plain']
2551 ptformatter = self.shell.display_formatter.formatters['text/plain']
@@ -2578,7 +2578,7 b' Defaulting color scheme to \'NoColor\'"""'
2578
2578
2579 You can also define aliases with parameters using %s specifiers (one
2579 You can also define aliases with parameters using %s specifiers (one
2580 per parameter):
2580 per parameter):
2581
2581
2582 In [1]: alias parts echo first %s second %s
2582 In [1]: alias parts echo first %s second %s
2583 In [2]: %parts A B
2583 In [2]: %parts A B
2584 first A second B
2584 first A second B
@@ -2589,7 +2589,7 b' Defaulting color scheme to \'NoColor\'"""'
2589 Note that %l and %s are mutually exclusive. You can only use one or
2589 Note that %l and %s are mutually exclusive. You can only use one or
2590 the other in your aliases.
2590 the other in your aliases.
2591
2591
2592 Aliases expand Python variables just like system calls using ! or !!
2592 Aliases expand Python variables just like system calls using ! or !!
2593 do: all expressions prefixed with '$' get expanded. For details of
2593 do: all expressions prefixed with '$' get expanded. For details of
2594 the semantic rules, see PEP-215:
2594 the semantic rules, see PEP-215:
2595 http://www.python.org/peps/pep-0215.html. This is the library used by
2595 http://www.python.org/peps/pep-0215.html. This is the library used by
@@ -2619,7 +2619,7 b' Defaulting color scheme to \'NoColor\'"""'
2619 print "Total number of aliases:", len(aliases)
2619 print "Total number of aliases:", len(aliases)
2620 sys.stdout.flush()
2620 sys.stdout.flush()
2621 return aliases
2621 return aliases
2622
2622
2623 # Now try to define a new one
2623 # Now try to define a new one
2624 try:
2624 try:
2625 alias,cmd = par.split(None, 1)
2625 alias,cmd = par.split(None, 1)
@@ -2649,7 +2649,7 b' Defaulting color scheme to \'NoColor\'"""'
2649 Under Windows, it checks executability as a match agains a
2649 Under Windows, it checks executability as a match agains a
2650 '|'-separated string of extensions, stored in the IPython config
2650 '|'-separated string of extensions, stored in the IPython config
2651 variable win_exec_ext. This defaults to 'exe|com|bat'.
2651 variable win_exec_ext. This defaults to 'exe|com|bat'.
2652
2652
2653 This function also resets the root module cache of module completer,
2653 This function also resets the root module cache of module completer,
2654 used on slow filesystems.
2654 used on slow filesystems.
2655 """
2655 """
@@ -2657,8 +2657,8 b' Defaulting color scheme to \'NoColor\'"""'
2657
2657
2658 # for the benefit of module completer in ipy_completers.py
2658 # for the benefit of module completer in ipy_completers.py
2659 del self.db['rootmodules']
2659 del self.db['rootmodules']
2660
2660
2661 path = [os.path.abspath(os.path.expanduser(p)) for p in
2661 path = [os.path.abspath(os.path.expanduser(p)) for p in
2662 os.environ.get('PATH','').split(os.pathsep)]
2662 os.environ.get('PATH','').split(os.pathsep)]
2663 path = filter(os.path.isdir,path)
2663 path = filter(os.path.isdir,path)
2664
2664
@@ -2717,20 +2717,20 b' Defaulting color scheme to \'NoColor\'"""'
2717 db['syscmdlist'] = syscmdlist
2717 db['syscmdlist'] = syscmdlist
2718 finally:
2718 finally:
2719 os.chdir(savedir)
2719 os.chdir(savedir)
2720
2720
2721 @skip_doctest
2721 @skip_doctest
2722 def magic_pwd(self, parameter_s = ''):
2722 def magic_pwd(self, parameter_s = ''):
2723 """Return the current working directory path.
2723 """Return the current working directory path.
2724
2724
2725 Examples
2725 Examples
2726 --------
2726 --------
2727 ::
2727 ::
2728
2728
2729 In [9]: pwd
2729 In [9]: pwd
2730 Out[9]: '/home/tsuser/sprint/ipython'
2730 Out[9]: '/home/tsuser/sprint/ipython'
2731 """
2731 """
2732 return os.getcwdu()
2732 return os.getcwdu()
2733
2733
2734 @skip_doctest
2734 @skip_doctest
2735 def magic_cd(self, parameter_s=''):
2735 def magic_cd(self, parameter_s=''):
2736 """Change the current working directory.
2736 """Change the current working directory.
@@ -2749,25 +2749,25 b' Defaulting color scheme to \'NoColor\'"""'
2749 cd -<n>: changes to the n-th directory in the directory history.
2749 cd -<n>: changes to the n-th directory in the directory history.
2750
2750
2751 cd --foo: change to directory that matches 'foo' in history
2751 cd --foo: change to directory that matches 'foo' in history
2752
2752
2753 cd -b <bookmark_name>: jump to a bookmark set by %bookmark
2753 cd -b <bookmark_name>: jump to a bookmark set by %bookmark
2754 (note: cd <bookmark_name> is enough if there is no
2754 (note: cd <bookmark_name> is enough if there is no
2755 directory <bookmark_name>, but a bookmark with the name exists.)
2755 directory <bookmark_name>, but a bookmark with the name exists.)
2756 'cd -b <tab>' allows you to tab-complete bookmark names.
2756 'cd -b <tab>' allows you to tab-complete bookmark names.
2757
2757
2758 Options:
2758 Options:
2759
2759
2760 -q: quiet. Do not print the working directory after the cd command is
2760 -q: quiet. Do not print the working directory after the cd command is
2761 executed. By default IPython's cd command does print this directory,
2761 executed. By default IPython's cd command does print this directory,
2762 since the default prompts do not display path information.
2762 since the default prompts do not display path information.
2763
2763
2764 Note that !cd doesn't work for this purpose because the shell where
2764 Note that !cd doesn't work for this purpose because the shell where
2765 !command runs is immediately discarded after executing 'command'.
2765 !command runs is immediately discarded after executing 'command'.
2766
2766
2767 Examples
2767 Examples
2768 --------
2768 --------
2769 ::
2769 ::
2770
2770
2771 In [10]: cd parent/child
2771 In [10]: cd parent/child
2772 /home/tsuser/parent/child
2772 /home/tsuser/parent/child
2773 """
2773 """
@@ -2797,25 +2797,25 b' Defaulting color scheme to \'NoColor\'"""'
2797 if pat in os.path.basename(ent) and os.path.isdir(ent):
2797 if pat in os.path.basename(ent) and os.path.isdir(ent):
2798 ps = ent
2798 ps = ent
2799 break
2799 break
2800
2800
2801 if fallback is None and pat in ent and os.path.isdir(ent):
2801 if fallback is None and pat in ent and os.path.isdir(ent):
2802 fallback = ent
2802 fallback = ent
2803
2803
2804 # if we have no last part match, pick the first full path match
2804 # if we have no last part match, pick the first full path match
2805 if ps is None:
2805 if ps is None:
2806 ps = fallback
2806 ps = fallback
2807
2807
2808 if ps is None:
2808 if ps is None:
2809 print "No matching entry in directory history"
2809 print "No matching entry in directory history"
2810 return
2810 return
2811 else:
2811 else:
2812 opts = {}
2812 opts = {}
2813
2813
2814
2814
2815 else:
2815 else:
2816 #turn all non-space-escaping backslashes to slashes,
2816 #turn all non-space-escaping backslashes to slashes,
2817 # for c:\windows\directory\names\
2817 # for c:\windows\directory\names\
2818 parameter_s = re.sub(r'\\(?! )','/', parameter_s)
2818 parameter_s = re.sub(r'\\(?! )','/', parameter_s)
2819 opts,ps = self.parse_options(parameter_s,'qb',mode='string')
2819 opts,ps = self.parse_options(parameter_s,'qb',mode='string')
2820 # jump to previous
2820 # jump to previous
2821 if ps == '-':
2821 if ps == '-':
@@ -2827,7 +2827,7 b' Defaulting color scheme to \'NoColor\'"""'
2827 else:
2827 else:
2828 if not os.path.isdir(ps) or opts.has_key('b'):
2828 if not os.path.isdir(ps) or opts.has_key('b'):
2829 bkms = self.db.get('bookmarks', {})
2829 bkms = self.db.get('bookmarks', {})
2830
2830
2831 if bkms.has_key(ps):
2831 if bkms.has_key(ps):
2832 target = bkms[ps]
2832 target = bkms[ps]
2833 print '(bookmark:%s) -> %s' % (ps,target)
2833 print '(bookmark:%s) -> %s' % (ps,target)
@@ -2853,14 +2853,14 b' Defaulting color scheme to \'NoColor\'"""'
2853 if oldcwd != cwd:
2853 if oldcwd != cwd:
2854 dhist.append(cwd)
2854 dhist.append(cwd)
2855 self.db['dhist'] = compress_dhist(dhist)[-100:]
2855 self.db['dhist'] = compress_dhist(dhist)[-100:]
2856
2856
2857 else:
2857 else:
2858 os.chdir(self.shell.home_dir)
2858 os.chdir(self.shell.home_dir)
2859 if hasattr(self.shell, 'term_title') and self.shell.term_title:
2859 if hasattr(self.shell, 'term_title') and self.shell.term_title:
2860 set_term_title('IPython: ' + '~')
2860 set_term_title('IPython: ' + '~')
2861 cwd = os.getcwdu()
2861 cwd = os.getcwdu()
2862 dhist = self.shell.user_ns['_dh']
2862 dhist = self.shell.user_ns['_dh']
2863
2863
2864 if oldcwd != cwd:
2864 if oldcwd != cwd:
2865 dhist.append(cwd)
2865 dhist.append(cwd)
2866 self.db['dhist'] = compress_dhist(dhist)[-100:]
2866 self.db['dhist'] = compress_dhist(dhist)[-100:]
@@ -2870,16 +2870,16 b' Defaulting color scheme to \'NoColor\'"""'
2870
2870
2871 def magic_env(self, parameter_s=''):
2871 def magic_env(self, parameter_s=''):
2872 """List environment variables."""
2872 """List environment variables."""
2873
2873
2874 return os.environ.data
2874 return os.environ.data
2875
2875
2876 def magic_pushd(self, parameter_s=''):
2876 def magic_pushd(self, parameter_s=''):
2877 """Place the current dir on stack and change directory.
2877 """Place the current dir on stack and change directory.
2878
2878
2879 Usage:\\
2879 Usage:\\
2880 %pushd ['dirname']
2880 %pushd ['dirname']
2881 """
2881 """
2882
2882
2883 dir_s = self.shell.dir_stack
2883 dir_s = self.shell.dir_stack
2884 tgt = os.path.expanduser(unquote_filename(parameter_s))
2884 tgt = os.path.expanduser(unquote_filename(parameter_s))
2885 cwd = os.getcwdu().replace(self.home_dir,'~')
2885 cwd = os.getcwdu().replace(self.home_dir,'~')
@@ -2912,10 +2912,10 b' Defaulting color scheme to \'NoColor\'"""'
2912 This history is automatically maintained by the %cd command, and
2912 This history is automatically maintained by the %cd command, and
2913 always available as the global list variable _dh. You can use %cd -<n>
2913 always available as the global list variable _dh. You can use %cd -<n>
2914 to go to directory number <n>.
2914 to go to directory number <n>.
2915
2915
2916 Note that most of time, you should view directory history by entering
2916 Note that most of time, you should view directory history by entering
2917 cd -<TAB>.
2917 cd -<TAB>.
2918
2918
2919 """
2919 """
2920
2920
2921 dh = self.shell.user_ns['_dh']
2921 dh = self.shell.user_ns['_dh']
@@ -2943,13 +2943,13 b' Defaulting color scheme to \'NoColor\'"""'
2943 """Shell capture - execute a shell command and capture its output.
2943 """Shell capture - execute a shell command and capture its output.
2944
2944
2945 DEPRECATED. Suboptimal, retained for backwards compatibility.
2945 DEPRECATED. Suboptimal, retained for backwards compatibility.
2946
2946
2947 You should use the form 'var = !command' instead. Example:
2947 You should use the form 'var = !command' instead. Example:
2948
2948
2949 "%sc -l myfiles = ls ~" should now be written as
2949 "%sc -l myfiles = ls ~" should now be written as
2950
2950
2951 "myfiles = !ls ~"
2951 "myfiles = !ls ~"
2952
2952
2953 myfiles.s, myfiles.l and myfiles.n still apply as documented
2953 myfiles.s, myfiles.l and myfiles.n still apply as documented
2954 below.
2954 below.
2955
2955
@@ -2963,7 +2963,7 b' Defaulting color scheme to \'NoColor\'"""'
2963
2963
2964 The '=' sign in the syntax is mandatory, and the variable name you
2964 The '=' sign in the syntax is mandatory, and the variable name you
2965 supply must follow Python's standard conventions for valid names.
2965 supply must follow Python's standard conventions for valid names.
2966
2966
2967 (A special format without variable name exists for internal use)
2967 (A special format without variable name exists for internal use)
2968
2968
2969 Options:
2969 Options:
@@ -2983,7 +2983,7 b' Defaulting color scheme to \'NoColor\'"""'
2983 For example:
2983 For example:
2984
2984
2985 # all-random
2985 # all-random
2986
2986
2987 # Capture into variable a
2987 # Capture into variable a
2988 In [1]: sc a=ls *py
2988 In [1]: sc a=ls *py
2989
2989
@@ -3074,7 +3074,7 b' Defaulting color scheme to \'NoColor\'"""'
3074 !!ls
3074 !!ls
3075 is a shorthand equivalent to:
3075 is a shorthand equivalent to:
3076 %sx ls
3076 %sx ls
3077
3077
3078 2) %sx differs from %sc in that %sx automatically splits into a list,
3078 2) %sx differs from %sc in that %sx automatically splits into a list,
3079 like '%sc -l'. The reason for this is to make it as easy as possible
3079 like '%sc -l'. The reason for this is to make it as easy as possible
3080 to process line-oriented shell output via further python commands.
3080 to process line-oriented shell output via further python commands.
@@ -3093,7 +3093,7 b' Defaulting color scheme to \'NoColor\'"""'
3093 if parameter_s:
3093 if parameter_s:
3094 return self.shell.getoutput(parameter_s)
3094 return self.shell.getoutput(parameter_s)
3095
3095
3096
3096
3097 def magic_bookmark(self, parameter_s=''):
3097 def magic_bookmark(self, parameter_s=''):
3098 """Manage IPython's bookmark system.
3098 """Manage IPython's bookmark system.
3099
3099
@@ -3116,7 +3116,7 b' Defaulting color scheme to \'NoColor\'"""'
3116 raise UsageError("%bookmark: too many arguments")
3116 raise UsageError("%bookmark: too many arguments")
3117
3117
3118 bkms = self.db.get('bookmarks',{})
3118 bkms = self.db.get('bookmarks',{})
3119
3119
3120 if opts.has_key('d'):
3120 if opts.has_key('d'):
3121 try:
3121 try:
3122 todel = args[0]
3122 todel = args[0]
@@ -3157,7 +3157,7 b' Defaulting color scheme to \'NoColor\'"""'
3157
3157
3158 This magic is similar to the cat utility, but it will assume the file
3158 This magic is similar to the cat utility, but it will assume the file
3159 to be Python source and will show it with syntax highlighting. """
3159 to be Python source and will show it with syntax highlighting. """
3160
3160
3161 try:
3161 try:
3162 filename = get_py_filename(parameter_s)
3162 filename = get_py_filename(parameter_s)
3163 cont = file_read(filename)
3163 cont = file_read(filename)
@@ -3205,13 +3205,13 b' Defaulting color scheme to \'NoColor\'"""'
3205 ]
3205 ]
3206
3206
3207 strip_from_start = map(re.compile,strip_re)
3207 strip_from_start = map(re.compile,strip_re)
3208
3208
3209 lines = []
3209 lines = []
3210 for l in raw_lines:
3210 for l in raw_lines:
3211 for pat in strip_from_start:
3211 for pat in strip_from_start:
3212 l = pat.sub('',l)
3212 l = pat.sub('',l)
3213 lines.append(l)
3213 lines.append(l)
3214
3214
3215 block = "\n".join(lines) + '\n'
3215 block = "\n".join(lines) + '\n'
3216 #print "block:\n",block
3216 #print "block:\n",block
3217 return block
3217 return block
@@ -3231,7 +3231,7 b' Defaulting color scheme to \'NoColor\'"""'
3231 """ Show a quick reference sheet """
3231 """ Show a quick reference sheet """
3232 import IPython.core.usage
3232 import IPython.core.usage
3233 qr = IPython.core.usage.quick_reference + self.magic_magic('-brief')
3233 qr = IPython.core.usage.quick_reference + self.magic_magic('-brief')
3234
3234
3235 page.page(qr)
3235 page.page(qr)
3236
3236
3237 def magic_doctest_mode(self,parameter_s=''):
3237 def magic_doctest_mode(self,parameter_s=''):
@@ -3298,7 +3298,7 b' Defaulting color scheme to \'NoColor\'"""'
3298
3298
3299 ptformatter.pprint = False
3299 ptformatter.pprint = False
3300 disp_formatter.plain_text_only = True
3300 disp_formatter.plain_text_only = True
3301
3301
3302 shell.magic_xmode('Plain')
3302 shell.magic_xmode('Plain')
3303 else:
3303 else:
3304 # turn off
3304 # turn off
@@ -3385,7 +3385,7 b' Defaulting color scheme to \'NoColor\'"""'
3385 name = src.replace('profile_', '')
3385 name = src.replace('profile_', '')
3386 print " %s"%name
3386 print " %s"%name
3387 pd = ProfileDir.create_profile_dir_by_name(ipython_dir, name)
3387 pd = ProfileDir.create_profile_dir_by_name(ipython_dir, name)
3388 pd.copy_config_file('ipython_config.py', path=src,
3388 pd.copy_config_file('ipython_config.py', path=src,
3389 overwrite=overwrite)
3389 overwrite=overwrite)
3390
3390
3391 @skip_doctest
3391 @skip_doctest
@@ -3452,16 +3452,16 b' Defaulting color scheme to \'NoColor\'"""'
3452 Backend in use: Qt4Agg
3452 Backend in use: Qt4Agg
3453 For more information, type 'help(pylab)'.
3453 For more information, type 'help(pylab)'.
3454 """
3454 """
3455
3455
3456 if Application.initialized():
3456 if Application.initialized():
3457 app = Application.instance()
3457 app = Application.instance()
3458 try:
3458 try:
3459 import_all_status = app.pylab_import_all
3459 import_all_status = app.pylab_import_all
3460 except AttributeError:
3460 except AttributeError:
3461 import_all_status = True
3461 import_all_status = True
3462 else:
3462 else:
3463 import_all_status = True
3463 import_all_status = True
3464
3464
3465 self.shell.enable_pylab(s,import_all=import_all_status)
3465 self.shell.enable_pylab(s,import_all=import_all_status)
3466
3466
3467 def magic_tb(self, s):
3467 def magic_tb(self, s):
@@ -3469,50 +3469,50 b' Defaulting color scheme to \'NoColor\'"""'
3469
3469
3470 See %xmode for changing exception reporting modes."""
3470 See %xmode for changing exception reporting modes."""
3471 self.shell.showtraceback()
3471 self.shell.showtraceback()
3472
3472
3473 @skip_doctest
3473 @skip_doctest
3474 def magic_precision(self, s=''):
3474 def magic_precision(self, s=''):
3475 """Set floating point precision for pretty printing.
3475 """Set floating point precision for pretty printing.
3476
3476
3477 Can set either integer precision or a format string.
3477 Can set either integer precision or a format string.
3478
3478
3479 If numpy has been imported and precision is an int,
3479 If numpy has been imported and precision is an int,
3480 numpy display precision will also be set, via ``numpy.set_printoptions``.
3480 numpy display precision will also be set, via ``numpy.set_printoptions``.
3481
3481
3482 If no argument is given, defaults will be restored.
3482 If no argument is given, defaults will be restored.
3483
3483
3484 Examples
3484 Examples
3485 --------
3485 --------
3486 ::
3486 ::
3487
3487
3488 In [1]: from math import pi
3488 In [1]: from math import pi
3489
3489
3490 In [2]: %precision 3
3490 In [2]: %precision 3
3491 Out[2]: u'%.3f'
3491 Out[2]: u'%.3f'
3492
3492
3493 In [3]: pi
3493 In [3]: pi
3494 Out[3]: 3.142
3494 Out[3]: 3.142
3495
3495
3496 In [4]: %precision %i
3496 In [4]: %precision %i
3497 Out[4]: u'%i'
3497 Out[4]: u'%i'
3498
3498
3499 In [5]: pi
3499 In [5]: pi
3500 Out[5]: 3
3500 Out[5]: 3
3501
3501
3502 In [6]: %precision %e
3502 In [6]: %precision %e
3503 Out[6]: u'%e'
3503 Out[6]: u'%e'
3504
3504
3505 In [7]: pi**10
3505 In [7]: pi**10
3506 Out[7]: 9.364805e+04
3506 Out[7]: 9.364805e+04
3507
3507
3508 In [8]: %precision
3508 In [8]: %precision
3509 Out[8]: u'%r'
3509 Out[8]: u'%r'
3510
3510
3511 In [9]: pi**10
3511 In [9]: pi**10
3512 Out[9]: 93648.047476082982
3512 Out[9]: 93648.047476082982
3513
3513
3514 """
3514 """
3515
3515
3516 ptformatter = self.shell.display_formatter.formatters['text/plain']
3516 ptformatter = self.shell.display_formatter.formatters['text/plain']
3517 ptformatter.float_precision = s
3517 ptformatter.float_precision = s
3518 return ptformatter.float_format
3518 return ptformatter.float_format
@@ -3583,6 +3583,6 b' Defaulting color scheme to \'NoColor\'"""'
3583 nb = current.reads(s, u'xml')
3583 nb = current.reads(s, u'xml')
3584 with open(new_fname, 'w') as f:
3584 with open(new_fname, 'w') as f:
3585 current.write(nb, f, new_format)
3585 current.write(nb, f, new_format)
3586
3586
3587
3587
3588 # end Magic
3588 # end Magic
@@ -197,7 +197,7 b' def call_tip(oinfo, format_call=True):'
197 When format_call is True, the whole call information is formattted as a
197 When format_call is True, the whole call information is formattted as a
198 single string. Otherwise, the object's name and its argspec dict are
198 single string. Otherwise, the object's name and its argspec dict are
199 returned. If no call information is available, None is returned.
199 returned. If no call information is available, None is returned.
200
200
201 docstring : str or None
201 docstring : str or None
202 The most relevant docstring for calling purposes is returned, if
202 The most relevant docstring for calling purposes is returned, if
203 available. The priority is: call docstring for callable instances, then
203 available. The priority is: call docstring for callable instances, then
@@ -219,7 +219,7 b' def call_tip(oinfo, format_call=True):'
219 else:
219 else:
220 if has_self:
220 if has_self:
221 argspec['args'] = argspec['args'][1:]
221 argspec['args'] = argspec['args'][1:]
222
222
223 call_line = oinfo['name']+format_argspec(argspec)
223 call_line = oinfo['name']+format_argspec(argspec)
224
224
225 # Now get docstring.
225 # Now get docstring.
@@ -249,14 +249,14 b' class Inspector:'
249
249
250 If any exception is generated, None is returned instead and the
250 If any exception is generated, None is returned instead and the
251 exception is suppressed."""
251 exception is suppressed."""
252
252
253 try:
253 try:
254 # We need a plain string here, NOT unicode!
254 # We need a plain string here, NOT unicode!
255 hdef = oname + inspect.formatargspec(*getargspec(obj))
255 hdef = oname + inspect.formatargspec(*getargspec(obj))
256 return py3compat.unicode_to_str(hdef, 'ascii')
256 return py3compat.unicode_to_str(hdef, 'ascii')
257 except:
257 except:
258 return None
258 return None
259
259
260 def __head(self,h):
260 def __head(self,h):
261 """Return a header string with proper colors."""
261 """Return a header string with proper colors."""
262 return '%s%s%s' % (self.color_table.active_colors.header,h,
262 return '%s%s%s' % (self.color_table.active_colors.header,h,
@@ -265,7 +265,7 b' class Inspector:'
265 def set_active_scheme(self,scheme):
265 def set_active_scheme(self,scheme):
266 self.color_table.set_active_scheme(scheme)
266 self.color_table.set_active_scheme(scheme)
267 self.parser.color_table.set_active_scheme(scheme)
267 self.parser.color_table.set_active_scheme(scheme)
268
268
269 def noinfo(self,msg,oname):
269 def noinfo(self,msg,oname):
270 """Generic message when no information is found."""
270 """Generic message when no information is found."""
271 print 'No %s found' % msg,
271 print 'No %s found' % msg,
@@ -273,7 +273,7 b' class Inspector:'
273 print 'for %s' % oname
273 print 'for %s' % oname
274 else:
274 else:
275 print
275 print
276
276
277 def pdef(self,obj,oname=''):
277 def pdef(self,obj,oname=''):
278 """Print the definition header for any callable object.
278 """Print the definition header for any callable object.
279
279
@@ -303,30 +303,30 b' class Inspector:'
303 Optional:
303 Optional:
304 -formatter: a function to run the docstring through for specially
304 -formatter: a function to run the docstring through for specially
305 formatted docstrings.
305 formatted docstrings.
306
306
307 Examples
307 Examples
308 --------
308 --------
309
309
310 In [1]: class NoInit:
310 In [1]: class NoInit:
311 ...: pass
311 ...: pass
312
312
313 In [2]: class NoDoc:
313 In [2]: class NoDoc:
314 ...: def __init__(self):
314 ...: def __init__(self):
315 ...: pass
315 ...: pass
316
316
317 In [3]: %pdoc NoDoc
317 In [3]: %pdoc NoDoc
318 No documentation found for NoDoc
318 No documentation found for NoDoc
319
319
320 In [4]: %pdoc NoInit
320 In [4]: %pdoc NoInit
321 No documentation found for NoInit
321 No documentation found for NoInit
322
322
323 In [5]: obj = NoInit()
323 In [5]: obj = NoInit()
324
324
325 In [6]: %pdoc obj
325 In [6]: %pdoc obj
326 No documentation found for obj
326 No documentation found for obj
327
327
328 In [5]: obj2 = NoDoc()
328 In [5]: obj2 = NoDoc()
329
329
330 In [6]: %pdoc obj2
330 In [6]: %pdoc obj2
331 No documentation found for obj2
331 No documentation found for obj2
332 """
332 """
@@ -355,14 +355,14 b' class Inspector:'
355 self.noinfo('documentation',oname)
355 self.noinfo('documentation',oname)
356 else:
356 else:
357 page.page('\n'.join(lines))
357 page.page('\n'.join(lines))
358
358
359 def psource(self,obj,oname=''):
359 def psource(self,obj,oname=''):
360 """Print the source code for an object."""
360 """Print the source code for an object."""
361
361
362 # Flush the source cache because inspect can return out-of-date source
362 # Flush the source cache because inspect can return out-of-date source
363 linecache.checkcache()
363 linecache.checkcache()
364 try:
364 try:
365 src = getsource(obj)
365 src = getsource(obj)
366 except:
366 except:
367 self.noinfo('source',oname)
367 self.noinfo('source',oname)
368 else:
368 else:
@@ -385,7 +385,7 b' class Inspector:'
385 return
385 return
386
386
387 # We only reach this point if object was successfully queried
387 # We only reach this point if object was successfully queried
388
388
389 # run contents of file through pager starting at line
389 # run contents of file through pager starting at line
390 # where the object is defined
390 # where the object is defined
391 ofile = inspect.getabsfile(obj)
391 ofile = inspect.getabsfile(obj)
@@ -399,10 +399,10 b' class Inspector:'
399 # getsourcelines returns lineno with 1-offset and page() uses
399 # getsourcelines returns lineno with 1-offset and page() uses
400 # 0-offset, so we must adjust.
400 # 0-offset, so we must adjust.
401 page.page(self.format(open(ofile).read()),lineno-1)
401 page.page(self.format(open(ofile).read()),lineno-1)
402
402
403 def _format_fields(self, fields, title_width=12):
403 def _format_fields(self, fields, title_width=12):
404 """Formats a list of fields for display.
404 """Formats a list of fields for display.
405
405
406 Parameters
406 Parameters
407 ----------
407 ----------
408 fields : list
408 fields : list
@@ -428,17 +428,17 b' class Inspector:'
428 ("Length", "length"),
428 ("Length", "length"),
429 ("File", "file"),
429 ("File", "file"),
430 ("Definition", "definition")]
430 ("Definition", "definition")]
431
431
432 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
432 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
433 ("Constructor Docstring","init_docstring"),
433 ("Constructor Docstring","init_docstring"),
434 ("Call def", "call_def"),
434 ("Call def", "call_def"),
435 ("Call docstring", "call_docstring")]
435 ("Call docstring", "call_docstring")]
436
436
437 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
437 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
438 """Show detailed information about an object.
438 """Show detailed information about an object.
439
439
440 Optional arguments:
440 Optional arguments:
441
441
442 - oname: name of the variable pointing to the object.
442 - oname: name of the variable pointing to the object.
443
443
444 - formatter: special formatter for docstrings (see pdoc)
444 - formatter: special formatter for docstrings (see pdoc)
@@ -455,14 +455,14 b' class Inspector:'
455 field = info[key]
455 field = info[key]
456 if field is not None:
456 if field is not None:
457 displayfields.append((title, field.rstrip()))
457 displayfields.append((title, field.rstrip()))
458
458
459 # Source or docstring, depending on detail level and whether
459 # Source or docstring, depending on detail level and whether
460 # source found.
460 # source found.
461 if detail_level > 0 and info['source'] is not None:
461 if detail_level > 0 and info['source'] is not None:
462 displayfields.append(("Source", self.format(py3compat.unicode_to_str(info['source']))))
462 displayfields.append(("Source", self.format(py3compat.unicode_to_str(info['source']))))
463 elif info['docstring'] is not None:
463 elif info['docstring'] is not None:
464 displayfields.append(("Docstring", info["docstring"]))
464 displayfields.append(("Docstring", info["docstring"]))
465
465
466 # Constructor info for classes
466 # Constructor info for classes
467 if info['isclass']:
467 if info['isclass']:
468 if info['init_definition'] or info['init_docstring']:
468 if info['init_definition'] or info['init_docstring']:
@@ -473,14 +473,14 b' class Inspector:'
473 if info['init_docstring'] is not None:
473 if info['init_docstring'] is not None:
474 displayfields.append((" Docstring",
474 displayfields.append((" Docstring",
475 indent(info['init_docstring'])))
475 indent(info['init_docstring'])))
476
476
477 # Info for objects:
477 # Info for objects:
478 else:
478 else:
479 for title, key in self.pinfo_fields_obj:
479 for title, key in self.pinfo_fields_obj:
480 field = info[key]
480 field = info[key]
481 if field is not None:
481 if field is not None:
482 displayfields.append((title, field.rstrip()))
482 displayfields.append((title, field.rstrip()))
483
483
484 # Finally send to printer/pager:
484 # Finally send to printer/pager:
485 if displayfields:
485 if displayfields:
486 page.page(self._format_fields(displayfields))
486 page.page(self._format_fields(displayfields))
@@ -489,7 +489,7 b' class Inspector:'
489 """Compute a dict with detailed information about an object.
489 """Compute a dict with detailed information about an object.
490
490
491 Optional arguments:
491 Optional arguments:
492
492
493 - oname: name of the variable pointing to the object.
493 - oname: name of the variable pointing to the object.
494
494
495 - formatter: special formatter for docstrings (see pdoc)
495 - formatter: special formatter for docstrings (see pdoc)
@@ -532,7 +532,7 b' class Inspector:'
532
532
533 # store output in a dict, we initialize it here and fill it as we go
533 # store output in a dict, we initialize it here and fill it as we go
534 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
534 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
535
535
536 string_max = 200 # max size of strings to show (snipped if longer)
536 string_max = 200 # max size of strings to show (snipped if longer)
537 shalf = int((string_max -5)/2)
537 shalf = int((string_max -5)/2)
538
538
@@ -599,7 +599,7 b' class Inspector:'
599 # avoid repetitions). If source fails, we add them back, see below.
599 # avoid repetitions). If source fails, we add them back, see below.
600 if ds and detail_level == 0:
600 if ds and detail_level == 0:
601 out['docstring'] = ds
601 out['docstring'] = ds
602
602
603 # Original source code for any callable
603 # Original source code for any callable
604 if detail_level:
604 if detail_level:
605 # Flush the source cache because inspect can return out-of-date
605 # Flush the source cache because inspect can return out-of-date
@@ -616,10 +616,10 b' class Inspector:'
616 out['source'] = source.rstrip()
616 out['source'] = source.rstrip()
617 except Exception:
617 except Exception:
618 pass
618 pass
619
619
620 if ds and source is None:
620 if ds and source is None:
621 out['docstring'] = ds
621 out['docstring'] = ds
622
622
623
623
624 # Constructor docstring for classes
624 # Constructor docstring for classes
625 if inspect.isclass(obj):
625 if inspect.isclass(obj):
@@ -692,7 +692,7 b' class Inspector:'
692 # Compute the object's argspec as a callable. The key is to decide
692 # Compute the object's argspec as a callable. The key is to decide
693 # whether to pull it from the object itself, from its __init__ or
693 # whether to pull it from the object itself, from its __init__ or
694 # from its __call__ method.
694 # from its __call__ method.
695
695
696 if inspect.isclass(obj):
696 if inspect.isclass(obj):
697 # Old-style classes need not have an __init__
697 # Old-style classes need not have an __init__
698 callable_obj = getattr(obj, "__init__", None)
698 callable_obj = getattr(obj, "__init__", None)
@@ -727,7 +727,7 b' class Inspector:'
727 - ns_table: dict of name->namespaces for search.
727 - ns_table: dict of name->namespaces for search.
728
728
729 Optional arguments:
729 Optional arguments:
730
730
731 - ns_search: list of namespace names to include in search.
731 - ns_search: list of namespace names to include in search.
732
732
733 - ignore_case(False): make the search case-insensitive.
733 - ignore_case(False): make the search case-insensitive.
@@ -736,7 +736,7 b' class Inspector:'
736 underscores.
736 underscores.
737 """
737 """
738 #print 'ps pattern:<%r>' % pattern # dbg
738 #print 'ps pattern:<%r>' % pattern # dbg
739
739
740 # defaults
740 # defaults
741 type_pattern = 'all'
741 type_pattern = 'all'
742 filter = ''
742 filter = ''
@@ -100,7 +100,7 b' class PrefilterManager(Configurable):'
100 """Main prefilter component.
100 """Main prefilter component.
101
101
102 The IPython prefilter is run on all user input before it is run. The
102 The IPython prefilter is run on all user input before it is run. The
103 prefilter consumes lines of input and produces transformed lines of
103 prefilter consumes lines of input and produces transformed lines of
104 input.
104 input.
105
105
106 The iplementation consists of two phases:
106 The iplementation consists of two phases:
@@ -119,12 +119,12 b' class PrefilterManager(Configurable):'
119
119
120 After all the transformers have been run, the line is fed to the checkers,
120 After all the transformers have been run, the line is fed to the checkers,
121 which are instances of :class:`PrefilterChecker`. The line is passed to
121 which are instances of :class:`PrefilterChecker`. The line is passed to
122 the :meth:`check` method, which either returns `None` or a
122 the :meth:`check` method, which either returns `None` or a
123 :class:`PrefilterHandler` instance. If `None` is returned, the other
123 :class:`PrefilterHandler` instance. If `None` is returned, the other
124 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
124 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
125 the line is passed to the :meth:`handle` method of the returned
125 the line is passed to the :meth:`handle` method of the returned
126 handler and no further checkers are tried.
126 handler and no further checkers are tried.
127
127
128 Both transformers and checkers have a `priority` attribute, that determines
128 Both transformers and checkers have a `priority` attribute, that determines
129 the order in which they are called. Smaller priorities are tried first.
129 the order in which they are called. Smaller priorities are tried first.
130
130
@@ -317,7 +317,7 b' class PrefilterManager(Configurable):'
317
317
318 # Now we compute line_info for the checkers and handlers
318 # Now we compute line_info for the checkers and handlers
319 line_info = LineInfo(line, continue_prompt)
319 line_info = LineInfo(line, continue_prompt)
320
320
321 # the input history needs to track even empty lines
321 # the input history needs to track even empty lines
322 stripped = line.strip()
322 stripped = line.strip()
323
323
@@ -358,7 +358,7 b' class PrefilterManager(Configurable):'
358 for lnum, line in enumerate(llines) ])
358 for lnum, line in enumerate(llines) ])
359 else:
359 else:
360 out = self.prefilter_line(llines[0], continue_prompt)
360 out = self.prefilter_line(llines[0], continue_prompt)
361
361
362 return out
362 return out
363
363
364 #-----------------------------------------------------------------------------
364 #-----------------------------------------------------------------------------
@@ -522,9 +522,9 b' class ShellEscapeChecker(PrefilterChecker):'
522
522
523
523
524 class MacroChecker(PrefilterChecker):
524 class MacroChecker(PrefilterChecker):
525
525
526 priority = Int(250, config=True)
526 priority = Int(250, config=True)
527
527
528 def check(self, line_info):
528 def check(self, line_info):
529 obj = self.shell.user_ns.get(line_info.ifun)
529 obj = self.shell.user_ns.get(line_info.ifun)
530 if isinstance(obj, Macro):
530 if isinstance(obj, Macro):
@@ -555,7 +555,7 b' class MultiLineMagicChecker(PrefilterChecker):'
555 "Allow ! and !! in multi-line statements if multi_line_specials is on"
555 "Allow ! and !! in multi-line statements if multi_line_specials is on"
556 # Note that this one of the only places we check the first character of
556 # Note that this one of the only places we check the first character of
557 # ifun and *not* the pre_char. Also note that the below test matches
557 # ifun and *not* the pre_char. Also note that the below test matches
558 # both ! and !!.
558 # both ! and !!.
559 if line_info.continue_prompt \
559 if line_info.continue_prompt \
560 and self.prefilter_manager.multi_line_specials:
560 and self.prefilter_manager.multi_line_specials:
561 if line_info.esc == ESC_MAGIC:
561 if line_info.esc == ESC_MAGIC:
@@ -591,7 +591,7 b' class AssignmentChecker(PrefilterChecker):'
591 def check(self, line_info):
591 def check(self, line_info):
592 """Check to see if user is assigning to a var for the first time, in
592 """Check to see if user is assigning to a var for the first time, in
593 which case we want to avoid any sort of automagic / autocall games.
593 which case we want to avoid any sort of automagic / autocall games.
594
594
595 This allows users to assign to either alias or magic names true python
595 This allows users to assign to either alias or magic names true python
596 variables (the magic/alias systems always take second seat to true
596 variables (the magic/alias systems always take second seat to true
597 python code). E.g. ls='hi', or ls,that=1,2"""
597 python code). E.g. ls='hi', or ls,that=1,2"""
@@ -669,7 +669,7 b' class AutocallChecker(PrefilterChecker):'
669 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
669 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
670 if not oinfo['found']:
670 if not oinfo['found']:
671 return None
671 return None
672
672
673 if callable(oinfo['obj']) \
673 if callable(oinfo['obj']) \
674 and (not re_exclude_auto.match(line_info.the_rest)) \
674 and (not re_exclude_auto.match(line_info.the_rest)) \
675 and re_fun_name.match(line_info.ifun):
675 and re_fun_name.match(line_info.ifun):
@@ -735,7 +735,7 b' class AliasHandler(PrefilterHandler):'
735 # aliases won't work in indented sections.
735 # aliases won't work in indented sections.
736 line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace,
736 line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace,
737 make_quoted_expr(transformed))
737 make_quoted_expr(transformed))
738
738
739 return line_out
739 return line_out
740
740
741
741
@@ -769,7 +769,7 b' class ShellEscapeHandler(PrefilterHandler):'
769
769
770 class MacroHandler(PrefilterHandler):
770 class MacroHandler(PrefilterHandler):
771 handler_name = Unicode("macro")
771 handler_name = Unicode("macro")
772
772
773 def handle(self, line_info):
773 def handle(self, line_info):
774 obj = self.shell.user_ns.get(line_info.ifun)
774 obj = self.shell.user_ns.get(line_info.ifun)
775 pre_space = line_info.pre_whitespace
775 pre_space = line_info.pre_whitespace
@@ -813,7 +813,7 b' class AutoHandler(PrefilterHandler):'
813
813
814 force_auto = isinstance(obj, IPyAutocall)
814 force_auto = isinstance(obj, IPyAutocall)
815 auto_rewrite = getattr(obj, 'rewrite', True)
815 auto_rewrite = getattr(obj, 'rewrite', True)
816
816
817 if esc == ESC_QUOTE:
817 if esc == ESC_QUOTE:
818 # Auto-quote splitting on whitespace
818 # Auto-quote splitting on whitespace
819 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
819 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
@@ -848,7 +848,7 b' class AutoHandler(PrefilterHandler):'
848
848
849 if auto_rewrite:
849 if auto_rewrite:
850 self.shell.auto_rewrite_input(newcmd)
850 self.shell.auto_rewrite_input(newcmd)
851
851
852 return newcmd
852 return newcmd
853
853
854
854
@@ -107,7 +107,7 b' class ProfileList(Application):'
107 )
107 )
108 ))
108 ))
109
109
110 ipython_dir = Unicode(get_ipython_dir(), config=True,
110 ipython_dir = Unicode(get_ipython_dir(), config=True,
111 help="""
111 help="""
112 The name of the IPython directory. This directory is used for logging
112 The name of the IPython directory. This directory is used for logging
113 configuration (through profiles), history storage, etc. The default
113 configuration (through profiles), history storage, etc. The default
@@ -115,7 +115,7 b' class ProfileList(Application):'
115 the environment variable IPYTHON_DIR.
115 the environment variable IPYTHON_DIR.
116 """
116 """
117 )
117 )
118
118
119 def list_profile_dirs(self):
119 def list_profile_dirs(self):
120 # Find the search paths
120 # Find the search paths
121 paths = [os.getcwdu(), self.ipython_dir]
121 paths = [os.getcwdu(), self.ipython_dir]
@@ -129,7 +129,7 b' class ProfileList(Application):'
129 profile = f.split('_',1)[-1]
129 profile = f.split('_',1)[-1]
130 start_cmd = 'ipython profile=%s' % profile
130 start_cmd = 'ipython profile=%s' % profile
131 print start_cmd + " ==> " + full_path
131 print start_cmd + " ==> " + full_path
132
132
133 def start(self):
133 def start(self):
134 self.list_profile_dirs()
134 self.list_profile_dirs()
135
135
@@ -150,15 +150,15 b' class ProfileCreate(BaseIPythonApplication):'
150 description = create_help
150 description = create_help
151 examples = _create_examples
151 examples = _create_examples
152 auto_create = Bool(True, config=False)
152 auto_create = Bool(True, config=False)
153
153
154 def _copy_config_files_default(self):
154 def _copy_config_files_default(self):
155 return True
155 return True
156
156
157 parallel = Bool(False, config=True,
157 parallel = Bool(False, config=True,
158 help="whether to include parallel computing config files")
158 help="whether to include parallel computing config files")
159 def _parallel_changed(self, name, old, new):
159 def _parallel_changed(self, name, old, new):
160 parallel_files = [ 'ipcontroller_config.py',
160 parallel_files = [ 'ipcontroller_config.py',
161 'ipengine_config.py',
161 'ipengine_config.py',
162 'ipcluster_config.py'
162 'ipcluster_config.py'
163 ]
163 ]
164 if new:
164 if new:
@@ -168,17 +168,17 b' class ProfileCreate(BaseIPythonApplication):'
168 for cf in parallel_files:
168 for cf in parallel_files:
169 if cf in self.config_files:
169 if cf in self.config_files:
170 self.config_files.remove(cf)
170 self.config_files.remove(cf)
171
171
172 def parse_command_line(self, argv):
172 def parse_command_line(self, argv):
173 super(ProfileCreate, self).parse_command_line(argv)
173 super(ProfileCreate, self).parse_command_line(argv)
174 # accept positional arg as profile name
174 # accept positional arg as profile name
175 if self.extra_args:
175 if self.extra_args:
176 self.profile = self.extra_args[0]
176 self.profile = self.extra_args[0]
177
177
178 flags = Dict(create_flags)
178 flags = Dict(create_flags)
179
179
180 classes = [ProfileDir]
180 classes = [ProfileDir]
181
181
182 def init_config_files(self):
182 def init_config_files(self):
183 super(ProfileCreate, self).init_config_files()
183 super(ProfileCreate, self).init_config_files()
184 # use local imports, since these classes may import from here
184 # use local imports, since these classes may import from here
@@ -223,7 +223,7 b' class ProfileCreate(BaseIPythonApplication):'
223 app.profile = self.profile
223 app.profile = self.profile
224 app.init_profile_dir()
224 app.init_profile_dir()
225 app.init_config_files()
225 app.init_config_files()
226
226
227 def stage_default_config_file(self):
227 def stage_default_config_file(self):
228 pass
228 pass
229
229
@@ -237,7 +237,7 b' class ProfileApp(Application):'
237 create = (ProfileCreate, "Create a new profile dir with default config files"),
237 create = (ProfileCreate, "Create a new profile dir with default config files"),
238 list = (ProfileList, "List existing profiles")
238 list = (ProfileList, "List existing profiles")
239 ))
239 ))
240
240
241 def start(self):
241 def start(self):
242 if self.subapp is None:
242 if self.subapp is None:
243 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
243 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
@@ -42,7 +42,7 b' PromptColors.add_scheme(coloransi.ColorScheme('
42 in_number = InputColors.NoColor, # Input prompt number
42 in_number = InputColors.NoColor, # Input prompt number
43 in_prompt2 = InputColors.NoColor, # Continuation prompt
43 in_prompt2 = InputColors.NoColor, # Continuation prompt
44 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
44 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
45
45
46 out_prompt = Colors.NoColor, # Output prompt
46 out_prompt = Colors.NoColor, # Output prompt
47 out_number = Colors.NoColor, # Output prompt number
47 out_number = Colors.NoColor, # Output prompt number
48
48
@@ -255,7 +255,7 b' class BasePrompt(object):'
255 # by all prompt classes through the cache. Nice OO spaghetti code!
255 # by all prompt classes through the cache. Nice OO spaghetti code!
256 self.cache = cache
256 self.cache = cache
257 self.sep = sep
257 self.sep = sep
258
258
259 # regexp to count the number of spaces at the end of a prompt
259 # regexp to count the number of spaces at the end of a prompt
260 # expression, useful for prompt auto-rewriting
260 # expression, useful for prompt auto-rewriting
261 self.rspace = re.compile(r'(\s*)$')
261 self.rspace = re.compile(r'(\s*)$')
@@ -281,7 +281,7 b' class BasePrompt(object):'
281 ('${self.sep}${self.col_p}',
281 ('${self.sep}${self.col_p}',
282 multiple_replace(prompt_specials, self.p_template),
282 multiple_replace(prompt_specials, self.p_template),
283 '${self.col_norm}'),self.cache.shell.user_ns,loc)
283 '${self.col_norm}'),self.cache.shell.user_ns,loc)
284
284
285 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
285 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
286 self.p_template),
286 self.p_template),
287 self.cache.shell.user_ns,loc)
287 self.cache.shell.user_ns,loc)
@@ -369,8 +369,8 b' class Prompt1(BasePrompt):'
369 self.col_norm = Colors.in_normal
369 self.col_norm = Colors.in_normal
370 # We need a non-input version of these escapes for the '--->'
370 # We need a non-input version of these escapes for the '--->'
371 # auto-call prompts used in the auto_rewrite() method.
371 # auto-call prompts used in the auto_rewrite() method.
372 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
372 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
373 self.col_norm_ni = Colors.normal
373 self.col_norm_ni = Colors.normal
374
374
375 def __str__(self):
375 def __str__(self):
376 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
376 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
@@ -405,7 +405,7 b' class PromptOut(BasePrompt):'
405
405
406 class Prompt2(BasePrompt):
406 class Prompt2(BasePrompt):
407 """Interactive continuation prompt."""
407 """Interactive continuation prompt."""
408
408
409 def __init__(self, cache, prompt=' .\\D.: ', pad_left=True):
409 def __init__(self, cache, prompt=' .\\D.: ', pad_left=True):
410 self.cache = cache
410 self.cache = cache
411 self.p_template = prompt
411 self.p_template = prompt
@@ -73,13 +73,13 b' The enhanced interactive Python shells have the following main features:'
73
73
74 * Easily embeddable in other Python programs and wxPython GUIs.
74 * Easily embeddable in other Python programs and wxPython GUIs.
75
75
76 * Integrated access to the pdb debugger and the Python profiler.
76 * Integrated access to the pdb debugger and the Python profiler.
77
77
78 The parallel computing architecture has the following main features:
78 The parallel computing architecture has the following main features:
79
79
80 * Quickly parallelize Python code from an interactive Python/IPython session.
80 * Quickly parallelize Python code from an interactive Python/IPython session.
81
81
82 * A flexible and dynamic process model that be deployed on anything from
82 * A flexible and dynamic process model that be deployed on anything from
83 multicore workstations to supercomputers.
83 multicore workstations to supercomputers.
84
84
85 * An architecture that supports many different styles of parallelism, from
85 * An architecture that supports many different styles of parallelism, from
@@ -90,7 +90,7 b' The parallel computing architecture has the following main features:'
90 * High level APIs that enable many things to be parallelized in a few lines
90 * High level APIs that enable many things to be parallelized in a few lines
91 of code.
91 of code.
92
92
93 * Share live parallel jobs with other users securely.
93 * Share live parallel jobs with other users securely.
94
94
95 * Dynamically load balanced task farming system.
95 * Dynamically load balanced task farming system.
96
96
@@ -37,7 +37,7 b' from IPython.utils import py3compat'
37
37
38 # Although it's not solely driven by the regex, note that:
38 # Although it's not solely driven by the regex, note that:
39 # ,;/% only trigger if they are the first character on the line
39 # ,;/% only trigger if they are the first character on the line
40 # ! and !! trigger if they are first char(s) *or* follow an indent
40 # ! and !! trigger if they are first char(s) *or* follow an indent
41 # ? triggers as first or last char.
41 # ? triggers as first or last char.
42
42
43 line_split = re.compile("""
43 line_split = re.compile("""
@@ -54,7 +54,7 b' def split_user_input(line, pattern=None):'
54 """
54 """
55 # We need to ensure that the rest of this routine deals only with unicode
55 # We need to ensure that the rest of this routine deals only with unicode
56 line = py3compat.cast_unicode(line, sys.stdin.encoding or 'utf-8')
56 line = py3compat.cast_unicode(line, sys.stdin.encoding or 'utf-8')
57
57
58 if pattern is None:
58 if pattern is None:
59 pattern = line_split
59 pattern = line_split
60 match = pattern.match(line)
60 match = pattern.match(line)
@@ -77,29 +77,29 b' def split_user_input(line, pattern=None):'
77 class LineInfo(object):
77 class LineInfo(object):
78 """A single line of input and associated info.
78 """A single line of input and associated info.
79
79
80 Includes the following as properties:
80 Includes the following as properties:
81
81
82 line
82 line
83 The original, raw line
83 The original, raw line
84
84
85 continue_prompt
85 continue_prompt
86 Is this line a continuation in a sequence of multiline input?
86 Is this line a continuation in a sequence of multiline input?
87
87
88 pre
88 pre
89 Any leading whitespace.
89 Any leading whitespace.
90
90
91 esc
91 esc
92 The escape character(s) in pre or the empty string if there isn't one.
92 The escape character(s) in pre or the empty string if there isn't one.
93 Note that '!!' and '??' are possible values for esc. Otherwise it will
93 Note that '!!' and '??' are possible values for esc. Otherwise it will
94 always be a single character.
94 always be a single character.
95
95
96 ifun
96 ifun
97 The 'function part', which is basically the maximal initial sequence
97 The 'function part', which is basically the maximal initial sequence
98 of valid python identifiers and the '.' character. This is what is
98 of valid python identifiers and the '.' character. This is what is
99 checked for alias and magic transformations, used for auto-calling,
99 checked for alias and magic transformations, used for auto-calling,
100 etc. In contrast to Python identifiers, it may start with "%" and contain
100 etc. In contrast to Python identifiers, it may start with "%" and contain
101 "*".
101 "*".
102
102
103 the_rest
103 the_rest
104 Everything else on the line.
104 Everything else on the line.
105 """
105 """
@@ -111,7 +111,7 b' class LineInfo(object):'
111 self.pre_char = self.pre.strip()
111 self.pre_char = self.pre.strip()
112 if self.pre_char:
112 if self.pre_char:
113 self.pre_whitespace = '' # No whitespace allowd before esc chars
113 self.pre_whitespace = '' # No whitespace allowd before esc chars
114 else:
114 else:
115 self.pre_whitespace = self.pre
115 self.pre_whitespace = self.pre
116
116
117 self._oinfo = None
117 self._oinfo = None
@@ -134,5 +134,5 b' class LineInfo(object):'
134 self._oinfo = ip.shell._ofind(self.ifun)
134 self._oinfo = ip.shell._ofind(self.ifun)
135 return self._oinfo
135 return self._oinfo
136
136
137 def __str__(self):
137 def __str__(self):
138 return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
138 return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
@@ -81,7 +81,7 b' def test_handlers():'
81 #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
81 #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
82
82
83 # post-esc-char whitespace goes inside
83 # post-esc-char whitespace goes inside
84 ("! true", 'get_ipython().system(u" true")'),
84 ("! true", 'get_ipython().system(u" true")'),
85
85
86 # handle_help
86 # handle_help
87
87
@@ -111,32 +111,32 b' def test_handlers():'
111 ('if 1:\n !!true', 'if 1:\n get_ipython().magic(u"sx true")'),
111 ('if 1:\n !!true', 'if 1:\n get_ipython().magic(u"sx true")'),
112
112
113 # Even with m_l_s on, autocall is off even with special chars
113 # Even with m_l_s on, autocall is off even with special chars
114 ('if 1:\n /fun 1 2', 'if 1:\n /fun 1 2'),
114 ('if 1:\n /fun 1 2', 'if 1:\n /fun 1 2'),
115 ('if 1:\n ;fun 1 2', 'if 1:\n ;fun 1 2'),
115 ('if 1:\n ;fun 1 2', 'if 1:\n ;fun 1 2'),
116 ('if 1:\n ,fun 1 2', 'if 1:\n ,fun 1 2'),
116 ('if 1:\n ,fun 1 2', 'if 1:\n ,fun 1 2'),
117 ('if 1:\n ?fun 1 2', 'if 1:\n ?fun 1 2'),
117 ('if 1:\n ?fun 1 2', 'if 1:\n ?fun 1 2'),
118 # What about !!
118 # What about !!
119 ])
119 ])
120
120
121 # Objects which are instances of IPyAutocall are *always* autocalled
121 # Objects which are instances of IPyAutocall are *always* autocalled
122 autocallable = Autocallable()
122 autocallable = Autocallable()
123 ip.user_ns['autocallable'] = autocallable
123 ip.user_ns['autocallable'] = autocallable
124
124
125 # auto
125 # auto
126 ip.magic('autocall 0')
126 ip.magic('autocall 0')
127 # Only explicit escapes or instances of IPyAutocallable should get
127 # Only explicit escapes or instances of IPyAutocallable should get
128 # expanded
128 # expanded
129 run([
129 run([
130 ('len "abc"', 'len "abc"'),
130 ('len "abc"', 'len "abc"'),
131 ('autocallable', 'autocallable()'),
131 ('autocallable', 'autocallable()'),
132 (",list 1 2 3", 'list("1", "2", "3")'),
132 (",list 1 2 3", 'list("1", "2", "3")'),
133 (";list 1 2 3", 'list("1 2 3")'),
133 (";list 1 2 3", 'list("1 2 3")'),
134 ("/len range(1,4)", 'len(range(1,4))'),
134 ("/len range(1,4)", 'len(range(1,4))'),
135 ])
135 ])
136 ip.magic('autocall 1')
136 ip.magic('autocall 1')
137 run([
137 run([
138 (",list 1 2 3", 'list("1", "2", "3")'),
138 (",list 1 2 3", 'list("1", "2", "3")'),
139 (";list 1 2 3", 'list("1 2 3")'),
139 (";list 1 2 3", 'list("1 2 3")'),
140 ("/len range(1,4)", 'len(range(1,4))'),
140 ("/len range(1,4)", 'len(range(1,4))'),
141 ('len "abc"', 'len("abc")'),
141 ('len "abc"', 'len("abc")'),
142 ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens
142 ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens
@@ -150,11 +150,11 b' def test_handlers():'
150 ip.magic('autocall 2')
150 ip.magic('autocall 2')
151 run([
151 run([
152 (",list 1 2 3", 'list("1", "2", "3")'),
152 (",list 1 2 3", 'list("1", "2", "3")'),
153 (";list 1 2 3", 'list("1 2 3")'),
153 (";list 1 2 3", 'list("1 2 3")'),
154 ("/len range(1,4)", 'len(range(1,4))'),
154 ("/len range(1,4)", 'len(range(1,4))'),
155 ('len "abc"', 'len("abc")'),
155 ('len "abc"', 'len("abc")'),
156 ('len "abc";', 'len("abc");'),
156 ('len "abc";', 'len("abc");'),
157 ('len [1,2]', 'len([1,2])'),
157 ('len [1,2]', 'len([1,2])'),
158 ('call_idx [1]', 'call_idx [1]'),
158 ('call_idx [1]', 'call_idx [1]'),
159 ('call_idx 1', 'call_idx(1)'),
159 ('call_idx 1', 'call_idx(1)'),
160 # This is what's different:
160 # This is what's different:
@@ -31,18 +31,18 b' def test_history():'
31 hist = ['a=1', 'def f():\n test = 1\n return test', u"b='€Æ¾÷ß'"]
31 hist = ['a=1', 'def f():\n test = 1\n return test', u"b='€Æ¾÷ß'"]
32 for i, h in enumerate(hist, start=1):
32 for i, h in enumerate(hist, start=1):
33 ip.history_manager.store_inputs(i, h)
33 ip.history_manager.store_inputs(i, h)
34
34
35 ip.history_manager.db_log_output = True
35 ip.history_manager.db_log_output = True
36 # Doesn't match the input, but we'll just check it's stored.
36 # Doesn't match the input, but we'll just check it's stored.
37 ip.history_manager.output_hist_reprs[3] = "spam"
37 ip.history_manager.output_hist_reprs[3] = "spam"
38 ip.history_manager.store_output(3)
38 ip.history_manager.store_output(3)
39
39
40 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
40 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
41
41
42 # Check whether specifying a range beyond the end of the current
42 # Check whether specifying a range beyond the end of the current
43 # session results in an error (gh-804)
43 # session results in an error (gh-804)
44 ip.magic('%hist 2-500')
44 ip.magic('%hist 2-500')
45
45
46 # New session
46 # New session
47 ip.history_manager.reset()
47 ip.history_manager.reset()
48 newcmds = ["z=5","class X(object):\n pass", "k='p'"]
48 newcmds = ["z=5","class X(object):\n pass", "k='p'"]
@@ -53,7 +53,7 b' def test_history():'
53 # Previous session:
53 # Previous session:
54 gothist = ip.history_manager.get_range(-1, 1, 4)
54 gothist = ip.history_manager.get_range(-1, 1, 4)
55 nt.assert_equal(list(gothist), zip([1,1,1],[1,2,3], hist))
55 nt.assert_equal(list(gothist), zip([1,1,1],[1,2,3], hist))
56
56
57 # Check get_hist_tail
57 # Check get_hist_tail
58 gothist = ip.history_manager.get_tail(4, output=True,
58 gothist = ip.history_manager.get_tail(4, output=True,
59 include_latest=True)
59 include_latest=True)
@@ -62,25 +62,25 b' def test_history():'
62 (2, 2, (newcmds[1], None)),
62 (2, 2, (newcmds[1], None)),
63 (2, 3, (newcmds[2], None)),]
63 (2, 3, (newcmds[2], None)),]
64 nt.assert_equal(list(gothist), expected)
64 nt.assert_equal(list(gothist), expected)
65
65
66 gothist = ip.history_manager.get_tail(2)
66 gothist = ip.history_manager.get_tail(2)
67 expected = [(2, 1, newcmds[0]),
67 expected = [(2, 1, newcmds[0]),
68 (2, 2, newcmds[1])]
68 (2, 2, newcmds[1])]
69 nt.assert_equal(list(gothist), expected)
69 nt.assert_equal(list(gothist), expected)
70
70
71 # Check get_hist_search
71 # Check get_hist_search
72 gothist = ip.history_manager.search("*test*")
72 gothist = ip.history_manager.search("*test*")
73 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
73 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
74 gothist = ip.history_manager.search("b*", output=True)
74 gothist = ip.history_manager.search("b*", output=True)
75 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
75 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
76
76
77 # Cross testing: check that magic %save can get previous session.
77 # Cross testing: check that magic %save can get previous session.
78 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
78 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
79 ip.magic_save(testfilename + " ~1/1-3")
79 ip.magic_save(testfilename + " ~1/1-3")
80 testfile = open(testfilename, "r")
80 testfile = open(testfilename, "r")
81 nt.assert_equal(testfile.read().decode("utf-8"),
81 nt.assert_equal(testfile.read().decode("utf-8"),
82 "# coding: utf-8\n" + "\n".join(hist))
82 "# coding: utf-8\n" + "\n".join(hist))
83
83
84 # Duplicate line numbers - check that it doesn't crash, and
84 # Duplicate line numbers - check that it doesn't crash, and
85 # gets a new session
85 # gets a new session
86 ip.history_manager.store_inputs(1, "rogue")
86 ip.history_manager.store_inputs(1, "rogue")
@@ -102,7 +102,7 b' def test_extract_hist_ranges():'
102 (-7, 1, 6)]
102 (-7, 1, 6)]
103 actual = list(extract_hist_ranges(instr))
103 actual = list(extract_hist_ranges(instr))
104 nt.assert_equal(actual, expected)
104 nt.assert_equal(actual, expected)
105
105
106 def test_magic_rerun():
106 def test_magic_rerun():
107 """Simple test for %rerun (no args -> rerun last line)"""
107 """Simple test for %rerun (no args -> rerun last line)"""
108 ip = get_ipython()
108 ip = get_ipython()
@@ -41,7 +41,7 b' def mini_interactive_loop(input_func):'
41 raw_input that simulates interactive input."""
41 raw_input that simulates interactive input."""
42
42
43 from IPython.core.inputsplitter import InputSplitter
43 from IPython.core.inputsplitter import InputSplitter
44
44
45 isp = InputSplitter()
45 isp = InputSplitter()
46 # In practice, this input loop would be wrapped in an outside loop to read
46 # In practice, this input loop would be wrapped in an outside loop to read
47 # input indefinitely, until some exit/quit command was issued. Here we
47 # input indefinitely, until some exit/quit command was issued. Here we
@@ -106,7 +106,7 b' def test_remove_comments():'
106 'line \nline\nline\nline \n\n'),
106 'line \nline\nline\nline \n\n'),
107 ]
107 ]
108 tt.check_pairs(isp.remove_comments, tests)
108 tt.check_pairs(isp.remove_comments, tests)
109
109
110 def test_has_comment():
110 def test_has_comment():
111 tests = [('text', False),
111 tests = [('text', False),
112 ('text #comment', True),
112 ('text #comment', True),
@@ -134,13 +134,13 b' class NoInputEncodingTestCase(unittest.TestCase):'
134 class X: pass
134 class X: pass
135 fake_stdin = X()
135 fake_stdin = X()
136 sys.stdin = fake_stdin
136 sys.stdin = fake_stdin
137
137
138 def test(self):
138 def test(self):
139 # Verify that if sys.stdin has no 'encoding' attribute we do the right
139 # Verify that if sys.stdin has no 'encoding' attribute we do the right
140 # thing
140 # thing
141 enc = isp.get_input_encoding()
141 enc = isp.get_input_encoding()
142 self.assertEqual(enc, 'ascii')
142 self.assertEqual(enc, 'ascii')
143
143
144 def tearDown(self):
144 def tearDown(self):
145 sys.stdin = self.old_stdin
145 sys.stdin = self.old_stdin
146
146
@@ -167,7 +167,7 b' class InputSplitterTestCase(unittest.TestCase):'
167 self.assertEqual(self.isp.source_reset(), '1\n2\n')
167 self.assertEqual(self.isp.source_reset(), '1\n2\n')
168 self.assertEqual(self.isp._buffer, [])
168 self.assertEqual(self.isp._buffer, [])
169 self.assertEqual(self.isp.source, '')
169 self.assertEqual(self.isp.source, '')
170
170
171 def test_indent(self):
171 def test_indent(self):
172 isp = self.isp # shorthand
172 isp = self.isp # shorthand
173 isp.push('x=1')
173 isp.push('x=1')
@@ -200,7 +200,7 b' class InputSplitterTestCase(unittest.TestCase):'
200 isp.push("if 1:")
200 isp.push("if 1:")
201 isp.push(" x = (1+\n 2)")
201 isp.push(" x = (1+\n 2)")
202 self.assertEqual(isp.indent_spaces, 4)
202 self.assertEqual(isp.indent_spaces, 4)
203
203
204 def test_indent4(self):
204 def test_indent4(self):
205 # In cell mode, inputs must be fed in whole blocks, so skip this test
205 # In cell mode, inputs must be fed in whole blocks, so skip this test
206 if self.isp.input_mode == 'cell': return
206 if self.isp.input_mode == 'cell': return
@@ -261,13 +261,13 b' class InputSplitterTestCase(unittest.TestCase):'
261 self.assertFalse(isp.push('if 1:'))
261 self.assertFalse(isp.push('if 1:'))
262 for line in [' x=1', '# a comment', ' y=2']:
262 for line in [' x=1', '# a comment', ' y=2']:
263 self.assertTrue(isp.push(line))
263 self.assertTrue(isp.push(line))
264
264
265 def test_push3(self):
265 def test_push3(self):
266 isp = self.isp
266 isp = self.isp
267 isp.push('if True:')
267 isp.push('if True:')
268 isp.push(' a = 1')
268 isp.push(' a = 1')
269 self.assertFalse(isp.push('b = [1,'))
269 self.assertFalse(isp.push('b = [1,'))
270
270
271 def test_replace_mode(self):
271 def test_replace_mode(self):
272 isp = self.isp
272 isp = self.isp
273 isp.input_mode = 'cell'
273 isp.input_mode = 'cell'
@@ -292,7 +292,7 b' class InputSplitterTestCase(unittest.TestCase):'
292 self.assertTrue(isp.push_accepts_more())
292 self.assertTrue(isp.push_accepts_more())
293 isp.push('')
293 isp.push('')
294 self.assertFalse(isp.push_accepts_more())
294 self.assertFalse(isp.push_accepts_more())
295
295
296 def test_push_accepts_more3(self):
296 def test_push_accepts_more3(self):
297 isp = self.isp
297 isp = self.isp
298 isp.push("x = (2+\n3)")
298 isp.push("x = (2+\n3)")
@@ -318,7 +318,7 b' class InputSplitterTestCase(unittest.TestCase):'
318 self.assertTrue(isp.push_accepts_more())
318 self.assertTrue(isp.push_accepts_more())
319 isp.push('')
319 isp.push('')
320 self.assertFalse(isp.push_accepts_more())
320 self.assertFalse(isp.push_accepts_more())
321
321
322 def test_push_accepts_more5(self):
322 def test_push_accepts_more5(self):
323 # In cell mode, inputs must be fed in whole blocks, so skip this test
323 # In cell mode, inputs must be fed in whole blocks, so skip this test
324 if self.isp.input_mode == 'cell': return
324 if self.isp.input_mode == 'cell': return
@@ -368,7 +368,7 b' class InteractiveLoopTestCase(unittest.TestCase):'
368 # we can check that the given dict is *contained* in test_ns
368 # we can check that the given dict is *contained* in test_ns
369 for k,v in ns.iteritems():
369 for k,v in ns.iteritems():
370 self.assertEqual(test_ns[k], v)
370 self.assertEqual(test_ns[k], v)
371
371
372 def test_simple(self):
372 def test_simple(self):
373 self.check_ns(['x=1'], dict(x=1))
373 self.check_ns(['x=1'], dict(x=1))
374
374
@@ -380,10 +380,10 b' class InteractiveLoopTestCase(unittest.TestCase):'
380
380
381 def test_abc(self):
381 def test_abc(self):
382 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
382 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
383
383
384 def test_multi(self):
384 def test_multi(self):
385 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
385 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
386
386
387
387
388 def test_LineInfo():
388 def test_LineInfo():
389 """Simple test for LineInfo construction and str()"""
389 """Simple test for LineInfo construction and str()"""
@@ -453,7 +453,7 b' syntax = \\'
453 ('?%hist', 'get_ipython().magic(u"pinfo %hist")'),
453 ('?%hist', 'get_ipython().magic(u"pinfo %hist")'),
454 ('?abc = qwe', 'get_ipython().magic(u"pinfo abc")'),
454 ('?abc = qwe', 'get_ipython().magic(u"pinfo abc")'),
455 ],
455 ],
456
456
457 end_help =
457 end_help =
458 [ ('x3?', 'get_ipython().magic(u"pinfo x3")'),
458 [ ('x3?', 'get_ipython().magic(u"pinfo x3")'),
459 ('x4??', 'get_ipython().magic(u"pinfo2 x4")'),
459 ('x4??', 'get_ipython().magic(u"pinfo2 x4")'),
@@ -473,7 +473,7 b' syntax = \\'
473 ('%cd /home', 'get_ipython().magic(u"cd /home")'),
473 ('%cd /home', 'get_ipython().magic(u"cd /home")'),
474 (' %magic', ' get_ipython().magic(u"magic")'),
474 (' %magic', ' get_ipython().magic(u"magic")'),
475 ],
475 ],
476
476
477 # Quoting with separate arguments
477 # Quoting with separate arguments
478 escaped_quote =
478 escaped_quote =
479 [ (',f', 'f("")'),
479 [ (',f', 'f("")'),
@@ -481,23 +481,23 b' syntax = \\'
481 (' ,f y', ' f("y")'),
481 (' ,f y', ' f("y")'),
482 (',f a b', 'f("a", "b")'),
482 (',f a b', 'f("a", "b")'),
483 ],
483 ],
484
484
485 # Quoting with single argument
485 # Quoting with single argument
486 escaped_quote2 =
486 escaped_quote2 =
487 [ (';f', 'f("")'),
487 [ (';f', 'f("")'),
488 (';f x', 'f("x")'),
488 (';f x', 'f("x")'),
489 (' ;f y', ' f("y")'),
489 (' ;f y', ' f("y")'),
490 (';f a b', 'f("a b")'),
490 (';f a b', 'f("a b")'),
491 ],
491 ],
492
492
493 # Simply apply parens
493 # Simply apply parens
494 escaped_paren =
494 escaped_paren =
495 [ ('/f', 'f()'),
495 [ ('/f', 'f()'),
496 ('/f x', 'f(x)'),
496 ('/f x', 'f(x)'),
497 (' /f y', ' f(y)'),
497 (' /f y', ' f(y)'),
498 ('/f a b', 'f(a, b)'),
498 ('/f a b', 'f(a, b)'),
499 ],
499 ],
500
500
501 # Check that we transform prompts before other transforms
501 # Check that we transform prompts before other transforms
502 mixed =
502 mixed =
503 [ ('In [1]: %lsmagic', 'get_ipython().magic(u"lsmagic")'),
503 [ ('In [1]: %lsmagic', 'get_ipython().magic(u"lsmagic")'),
@@ -532,7 +532,7 b' syntax_ml = \\'
532 def test_assign_system():
532 def test_assign_system():
533 tt.check_pairs(isp.transform_assign_system, syntax['assign_system'])
533 tt.check_pairs(isp.transform_assign_system, syntax['assign_system'])
534
534
535
535
536 def test_assign_magic():
536 def test_assign_magic():
537 tt.check_pairs(isp.transform_assign_magic, syntax['assign_magic'])
537 tt.check_pairs(isp.transform_assign_magic, syntax['assign_magic'])
538
538
@@ -541,7 +541,7 b' def test_classic_prompt():'
541 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
541 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
542 for example in syntax_ml['classic_prompt']:
542 for example in syntax_ml['classic_prompt']:
543 transform_checker(example, isp.transform_classic_prompt)
543 transform_checker(example, isp.transform_classic_prompt)
544
544
545
545
546 def test_ipy_prompt():
546 def test_ipy_prompt():
547 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
547 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
@@ -599,7 +599,7 b' class IPythonInputTestCase(InputSplitterTestCase):'
599 for raw, out_t in example:
599 for raw, out_t in example:
600 if raw.startswith(' '):
600 if raw.startswith(' '):
601 continue
601 continue
602
602
603 isp.push(raw)
603 isp.push(raw)
604 out, out_raw = isp.source_raw_reset()
604 out, out_raw = isp.source_raw_reset()
605 self.assertEqual(out.rstrip(), out_t,
605 self.assertEqual(out.rstrip(), out_t,
@@ -622,13 +622,13 b' class IPythonInputTestCase(InputSplitterTestCase):'
622 raw = '\n'.join(raw_parts).rstrip()
622 raw = '\n'.join(raw_parts).rstrip()
623 self.assertEqual(out.rstrip(), out_t)
623 self.assertEqual(out.rstrip(), out_t)
624 self.assertEqual(out_raw.rstrip(), raw)
624 self.assertEqual(out_raw.rstrip(), raw)
625
625
626
626
627 class BlockIPythonInputTestCase(IPythonInputTestCase):
627 class BlockIPythonInputTestCase(IPythonInputTestCase):
628
628
629 # Deactivate tests that don't make sense for the block mode
629 # Deactivate tests that don't make sense for the block mode
630 test_push3 = test_split = lambda s: None
630 test_push3 = test_split = lambda s: None
631
631
632 def setUp(self):
632 def setUp(self):
633 self.isp = isp.IPythonInputSplitter(input_mode='cell')
633 self.isp = isp.IPythonInputSplitter(input_mode='cell')
634
634
@@ -650,7 +650,7 b' class BlockIPythonInputTestCase(IPythonInputTestCase):'
650 # Match ignoring trailing whitespace
650 # Match ignoring trailing whitespace
651 self.assertEqual(out.rstrip(), out_t.rstrip())
651 self.assertEqual(out.rstrip(), out_t.rstrip())
652 self.assertEqual(out_raw.rstrip(), raw.rstrip())
652 self.assertEqual(out_raw.rstrip(), raw.rstrip())
653
653
654
654
655 #-----------------------------------------------------------------------------
655 #-----------------------------------------------------------------------------
656 # Main - use as a script, mostly for developer experiments
656 # Main - use as a script, mostly for developer experiments
@@ -667,7 +667,7 b" if __name__ == '__main__':"
667
667
668 autoindent = True
668 autoindent = True
669 #autoindent = False
669 #autoindent = False
670
670
671 try:
671 try:
672 while True:
672 while True:
673 prompt = start_prompt
673 prompt = start_prompt
@@ -45,7 +45,7 b' class Call(object):'
45
45
46 def method(self, x, z=2):
46 def method(self, x, z=2):
47 """Some method's docstring"""
47 """Some method's docstring"""
48
48
49 class OldStyle:
49 class OldStyle:
50 """An old-style class for testing."""
50 """An old-style class for testing."""
51 pass
51 pass
@@ -62,7 +62,7 b' def check_calltip(obj, name, call, docstring):'
62 info = inspector.info(obj, name)
62 info = inspector.info(obj, name)
63 call_line, ds = oinspect.call_tip(info)
63 call_line, ds = oinspect.call_tip(info)
64 nt.assert_equal(call_line, call)
64 nt.assert_equal(call_line, call)
65 nt.assert_equal(ds, docstring)
65 nt.assert_equal(ds, docstring)
66
66
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68 # Tests
68 # Tests
@@ -92,7 +92,7 b' def test_calltip_function2():'
92
92
93 def test_calltip_builtin():
93 def test_calltip_builtin():
94 check_calltip(sum, 'sum', None, sum.__doc__)
94 check_calltip(sum, 'sum', None, sum.__doc__)
95
95
96 def test_info():
96 def test_info():
97 "Check that Inspector.info fills out various fields as expected."
97 "Check that Inspector.info fills out various fields as expected."
98 i = inspector.info(Call, oname='Call')
98 i = inspector.info(Call, oname='Call')
@@ -112,11 +112,11 b' def test_info():'
112 nt.assert_true(i['isclass'])
112 nt.assert_true(i['isclass'])
113 nt.assert_equal(i['init_definition'], "Call(self, x, y=1)\n")
113 nt.assert_equal(i['init_definition'], "Call(self, x, y=1)\n")
114 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
114 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
115
115
116 i = inspector.info(Call, detail_level=1)
116 i = inspector.info(Call, detail_level=1)
117 nt.assert_not_equal(i['source'], None)
117 nt.assert_not_equal(i['source'], None)
118 nt.assert_equal(i['docstring'], None)
118 nt.assert_equal(i['docstring'], None)
119
119
120 c = Call(1)
120 c = Call(1)
121 c.__doc__ = "Modified instance docstring"
121 c.__doc__ = "Modified instance docstring"
122 i = inspector.info(c)
122 i = inspector.info(c)
@@ -125,12 +125,12 b' def test_info():'
125 nt.assert_equal(i['class_docstring'], Call.__doc__)
125 nt.assert_equal(i['class_docstring'], Call.__doc__)
126 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
126 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
127 nt.assert_equal(i['call_docstring'], c.__call__.__doc__)
127 nt.assert_equal(i['call_docstring'], c.__call__.__doc__)
128
128
129 # Test old-style classes, which for example may not have an __init__ method.
129 # Test old-style classes, which for example may not have an __init__ method.
130 if not py3compat.PY3:
130 if not py3compat.PY3:
131 i = inspector.info(OldStyle)
131 i = inspector.info(OldStyle)
132 nt.assert_equal(i['type_name'], 'classobj')
132 nt.assert_equal(i['type_name'], 'classobj')
133
133
134 i = inspector.info(OldStyle())
134 i = inspector.info(OldStyle())
135 nt.assert_equal(i['type_name'], 'instance')
135 nt.assert_equal(i['type_name'], 'instance')
136 nt.assert_equal(i['docstring'], OldStyle.__doc__)
136 nt.assert_equal(i['docstring'], OldStyle.__doc__)
@@ -32,7 +32,7 b' def doctest_refbug():'
32
32
33 In [1]: _ip.clear_main_mod_cache()
33 In [1]: _ip.clear_main_mod_cache()
34 # random
34 # random
35
35
36 In [2]: %run refbug
36 In [2]: %run refbug
37
37
38 In [3]: call_f()
38 In [3]: call_f()
@@ -81,7 +81,7 b' def doctest_run_builtins():'
81 ....: os.unlink(fname)
81 ....: os.unlink(fname)
82 ....: except:
82 ....: except:
83 ....: pass
83 ....: pass
84 ....:
84 ....:
85 """
85 """
86
86
87 def doctest_reset_del():
87 def doctest_reset_del():
@@ -90,7 +90,7 b' def doctest_reset_del():'
90 In [2]: class A(object):
90 In [2]: class A(object):
91 ...: def __del__(self):
91 ...: def __del__(self):
92 ...: print str("Hi")
92 ...: print str("Hi")
93 ...:
93 ...:
94
94
95 In [3]: a = A()
95 In [3]: a = A()
96
96
@@ -127,7 +127,7 b' class TestMagicRunPass(tt.TempFileMixin):'
127
127
128 def test_builtins_type(self):
128 def test_builtins_type(self):
129 """Check that the type of __builtins__ doesn't change with %run.
129 """Check that the type of __builtins__ doesn't change with %run.
130
130
131 However, the above could pass if __builtins__ was already modified to
131 However, the above could pass if __builtins__ was already modified to
132 be a dict (it should be a module) by a previous use of %run. So we
132 be a dict (it should be a module) by a previous use of %run. So we
133 also check explicitly that it really is a module:
133 also check explicitly that it really is a module:
@@ -168,8 +168,8 b' class TestMagicRunSimple(tt.TempFileMixin):'
168 "a = A()\n")
168 "a = A()\n")
169 self.mktmp(src)
169 self.mktmp(src)
170 tt.ipexec_validate(self.fname, 'object A deleted')
170 tt.ipexec_validate(self.fname, 'object A deleted')
171
171
172 @dec.skip_known_failure
172 @dec.skip_known_failure
173 def test_aggressive_namespace_cleanup(self):
173 def test_aggressive_namespace_cleanup(self):
174 """Test that namespace cleanup is not too aggressive GH-238
174 """Test that namespace cleanup is not too aggressive GH-238
175
175
@@ -12,7 +12,7 b' Installation instructions for ColorTB:'
12 import sys,ultratb
12 import sys,ultratb
13 sys.excepthook = ultratb.ColorTB()
13 sys.excepthook = ultratb.ColorTB()
14
14
15 * VerboseTB
15 * VerboseTB
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
18 and intended it for CGI programmers, but why should they have all the fun? I
18 and intended it for CGI programmers, but why should they have all the fun? I
@@ -34,7 +34,7 b' Note:'
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
35 variables (but otherwise includes the information and context given by
35 variables (but otherwise includes the information and context given by
36 Verbose).
36 Verbose).
37
37
38
38
39 Installation instructions for ColorTB:
39 Installation instructions for ColorTB:
40 import sys,ultratb
40 import sys,ultratb
@@ -121,7 +121,7 b' def inspect_error():'
121 """Print a message about internal inspect errors.
121 """Print a message about internal inspect errors.
122
122
123 These are unfortunately quite common."""
123 These are unfortunately quite common."""
124
124
125 error('Internal Python error in the inspect module.\n'
125 error('Internal Python error in the inspect module.\n'
126 'Below is the traceback from this internal error.\n')
126 'Below is the traceback from this internal error.\n')
127
127
@@ -197,7 +197,7 b' def findsource(object):'
197 while lnum > 0:
197 while lnum > 0:
198 if pmatch(lines[lnum]): break
198 if pmatch(lines[lnum]): break
199 lnum -= 1
199 lnum -= 1
200
200
201 return lines, lnum
201 return lines, lnum
202 raise IOError('could not find code object')
202 raise IOError('could not find code object')
203
203
@@ -262,7 +262,7 b' def _fixed_getinnerframes(etb, context=1,tb_offset=0):'
262 # (SyntaxErrors have to be treated specially because they have no traceback)
262 # (SyntaxErrors have to be treated specially because they have no traceback)
263
263
264 _parser = PyColorize.Parser()
264 _parser = PyColorize.Parser()
265
265
266 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
266 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
267 numbers_width = INDENT_SIZE - 1
267 numbers_width = INDENT_SIZE - 1
268 res = []
268 res = []
@@ -285,10 +285,10 b' def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):'
285 # is unicode-safe. So for now this is rather an ugly hack, but
285 # is unicode-safe. So for now this is rather an ugly hack, but
286 # necessary to at least have readable tracebacks. Improvements welcome!
286 # necessary to at least have readable tracebacks. Improvements welcome!
287 line = py3compat.cast_bytes_py2(line, 'utf-8')
287 line = py3compat.cast_bytes_py2(line, 'utf-8')
288
288
289 new_line, err = _line_format(line, 'str', scheme)
289 new_line, err = _line_format(line, 'str', scheme)
290 if not err: line = new_line
290 if not err: line = new_line
291
291
292 if i == lnum:
292 if i == lnum:
293 # This is the line with the error
293 # This is the line with the error
294 pad = numbers_width - len(str(i))
294 pad = numbers_width - len(str(i))
@@ -301,11 +301,11 b' def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):'
301 else:
301 else:
302 marker = ''
302 marker = ''
303 num = marker + str(i)
303 num = marker + str(i)
304 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
304 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
305 Colors.line, line, Colors.Normal)
305 Colors.line, line, Colors.Normal)
306 else:
306 else:
307 num = '%*s' % (numbers_width,i)
307 num = '%*s' % (numbers_width,i)
308 line = '%s%s%s %s' %(Colors.lineno, num,
308 line = '%s%s%s %s' %(Colors.lineno, num,
309 Colors.Normal, line)
309 Colors.Normal, line)
310
310
311 res.append(line)
311 res.append(line)
@@ -352,7 +352,7 b' class TBTools(object):'
352 """Output stream that exceptions are written to.
352 """Output stream that exceptions are written to.
353
353
354 Valid values are:
354 Valid values are:
355
355
356 - None: the default, which means that IPython will dynamically resolve
356 - None: the default, which means that IPython will dynamically resolve
357 to io.stdout. This ensures compatibility with most tools, including
357 to io.stdout. This ensures compatibility with most tools, including
358 Windows (where plain stdout doesn't recognize ANSI escapes).
358 Windows (where plain stdout doesn't recognize ANSI escapes).
@@ -364,7 +364,7 b' class TBTools(object):'
364 def _set_ostream(self, val):
364 def _set_ostream(self, val):
365 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
365 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
366 self._ostream = val
366 self._ostream = val
367
367
368 ostream = property(_get_ostream, _set_ostream)
368 ostream = property(_get_ostream, _set_ostream)
369
369
370 def set_colors(self,*args,**kw):
370 def set_colors(self,*args,**kw):
@@ -380,7 +380,7 b' class TBTools(object):'
380
380
381 def color_toggle(self):
381 def color_toggle(self):
382 """Toggle between the currently active color scheme and NoColor."""
382 """Toggle between the currently active color scheme and NoColor."""
383
383
384 if self.color_scheme_table.active_scheme_name == 'NoColor':
384 if self.color_scheme_table.active_scheme_name == 'NoColor':
385 self.color_scheme_table.set_active_scheme(self.old_scheme)
385 self.color_scheme_table.set_active_scheme(self.old_scheme)
386 self.Colors = self.color_scheme_table.active_colors
386 self.Colors = self.color_scheme_table.active_colors
@@ -414,7 +414,7 b' class TBTools(object):'
414 #---------------------------------------------------------------------------
414 #---------------------------------------------------------------------------
415 class ListTB(TBTools):
415 class ListTB(TBTools):
416 """Print traceback information from a traceback list, with optional color.
416 """Print traceback information from a traceback list, with optional color.
417
417
418 Calling: requires 3 arguments:
418 Calling: requires 3 arguments:
419 (etype, evalue, elist)
419 (etype, evalue, elist)
420 as would be obtained by:
420 as would be obtained by:
@@ -434,7 +434,7 b' class ListTB(TBTools):'
434 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
434 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
435 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
435 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
436 ostream=ostream)
436 ostream=ostream)
437
437
438 def __call__(self, etype, value, elist):
438 def __call__(self, etype, value, elist):
439 self.ostream.flush()
439 self.ostream.flush()
440 self.ostream.write(self.text(etype, value, elist))
440 self.ostream.write(self.text(etype, value, elist))
@@ -458,7 +458,7 b' class ListTB(TBTools):'
458 tb_offset : int, optional
458 tb_offset : int, optional
459 Number of frames in the traceback to skip. If not given, the
459 Number of frames in the traceback to skip. If not given, the
460 instance value is used (set in constructor).
460 instance value is used (set in constructor).
461
461
462 context : int, optional
462 context : int, optional
463 Number of lines of context information to print.
463 Number of lines of context information to print.
464
464
@@ -473,7 +473,7 b' class ListTB(TBTools):'
473
473
474 if tb_offset and len(elist) > tb_offset:
474 if tb_offset and len(elist) > tb_offset:
475 elist = elist[tb_offset:]
475 elist = elist[tb_offset:]
476
476
477 out_list.append('Traceback %s(most recent call last)%s:' %
477 out_list.append('Traceback %s(most recent call last)%s:' %
478 (Colors.normalEm, Colors.Normal) + '\n')
478 (Colors.normalEm, Colors.Normal) + '\n')
479 out_list.extend(self._format_list(elist))
479 out_list.extend(self._format_list(elist))
@@ -482,7 +482,7 b' class ListTB(TBTools):'
482 out_list.append(lines)
482 out_list.append(lines)
483
483
484 # Note: this code originally read:
484 # Note: this code originally read:
485
485
486 ## for line in lines[:-1]:
486 ## for line in lines[:-1]:
487 ## out_list.append(" "+line)
487 ## out_list.append(" "+line)
488 ## out_list.append(lines[-1])
488 ## out_list.append(lines[-1])
@@ -490,7 +490,7 b' class ListTB(TBTools):'
490 # This means it was indenting everything but the last line by a little
490 # This means it was indenting everything but the last line by a little
491 # bit. I've disabled this for now, but if we see ugliness somewhre we
491 # bit. I've disabled this for now, but if we see ugliness somewhre we
492 # can restore it.
492 # can restore it.
493
493
494 return out_list
494 return out_list
495
495
496 def _format_list(self, extracted_list):
496 def _format_list(self, extracted_list):
@@ -502,7 +502,7 b' class ListTB(TBTools):'
502 same index in the argument list. Each string ends in a newline;
502 same index in the argument list. Each string ends in a newline;
503 the strings may contain internal newlines as well, for those items
503 the strings may contain internal newlines as well, for those items
504 whose source text line is not None.
504 whose source text line is not None.
505
505
506 Lifted almost verbatim from traceback.py
506 Lifted almost verbatim from traceback.py
507 """
507 """
508
508
@@ -510,7 +510,7 b' class ListTB(TBTools):'
510 list = []
510 list = []
511 for filename, lineno, name, line in extracted_list[:-1]:
511 for filename, lineno, name, line in extracted_list[:-1]:
512 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
512 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
513 (Colors.filename, filename, Colors.Normal,
513 (Colors.filename, filename, Colors.Normal,
514 Colors.lineno, lineno, Colors.Normal,
514 Colors.lineno, lineno, Colors.Normal,
515 Colors.name, name, Colors.Normal)
515 Colors.name, name, Colors.Normal)
516 if line:
516 if line:
@@ -530,7 +530,7 b' class ListTB(TBTools):'
530 list.append(item)
530 list.append(item)
531 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
531 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
532 return list
532 return list
533
533
534 def _format_exception_only(self, etype, value):
534 def _format_exception_only(self, etype, value):
535 """Format the exception part of a traceback.
535 """Format the exception part of a traceback.
536
536
@@ -541,7 +541,7 b' class ListTB(TBTools):'
541 printed) display detailed information about where the syntax error
541 printed) display detailed information about where the syntax error
542 occurred. The message indicating which exception occurred is the
542 occurred. The message indicating which exception occurred is the
543 always last string in the list.
543 always last string in the list.
544
544
545 Also lifted nearly verbatim from traceback.py
545 Also lifted nearly verbatim from traceback.py
546 """
546 """
547
547
@@ -573,7 +573,7 b' class ListTB(TBTools):'
573 while i < len(line) and line[i].isspace():
573 while i < len(line) and line[i].isspace():
574 i = i+1
574 i = i+1
575 list.append('%s %s%s\n' % (Colors.line,
575 list.append('%s %s%s\n' % (Colors.line,
576 line.strip(),
576 line.strip(),
577 Colors.Normal))
577 Colors.Normal))
578 if offset is not None:
578 if offset is not None:
579 s = ' '
579 s = ' '
@@ -602,7 +602,7 b' class ListTB(TBTools):'
602
602
603 def get_exception_only(self, etype, value):
603 def get_exception_only(self, etype, value):
604 """Only print the exception type and message, without a traceback.
604 """Only print the exception type and message, without a traceback.
605
605
606 Parameters
606 Parameters
607 ----------
607 ----------
608 etype : exception type
608 etype : exception type
@@ -613,7 +613,7 b' class ListTB(TBTools):'
613
613
614 def show_exception_only(self, etype, evalue):
614 def show_exception_only(self, etype, evalue):
615 """Only print the exception type and message, without a traceback.
615 """Only print the exception type and message, without a traceback.
616
616
617 Parameters
617 Parameters
618 ----------
618 ----------
619 etype : exception type
619 etype : exception type
@@ -725,7 +725,7 b' class VerboseTB(TBTools):'
725 # Header with the exception type, python version, and date
725 # Header with the exception type, python version, and date
726 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
726 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
727 date = time.ctime(time.time())
727 date = time.ctime(time.time())
728
728
729 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
729 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
730 exc, ' '*(75-len(str(etype))-len(pyver)),
730 exc, ' '*(75-len(str(etype))-len(pyver)),
731 pyver, date.rjust(75) )
731 pyver, date.rjust(75) )
@@ -797,7 +797,7 b' class VerboseTB(TBTools):'
797 inspect_error()
797 inspect_error()
798 traceback.print_exc(file=self.ostream)
798 traceback.print_exc(file=self.ostream)
799 info("\nIPython's exception reporting continues...\n")
799 info("\nIPython's exception reporting continues...\n")
800
800
801 if func == '?':
801 if func == '?':
802 call = ''
802 call = ''
803 else:
803 else:
@@ -838,7 +838,7 b' class VerboseTB(TBTools):'
838 there is no way to disambguate partial dotted structures until
838 there is no way to disambguate partial dotted structures until
839 the full list is known. The caller is responsible for pruning
839 the full list is known. The caller is responsible for pruning
840 the final list of duplicates before using it."""
840 the final list of duplicates before using it."""
841
841
842 # build composite names
842 # build composite names
843 if token == '.':
843 if token == '.':
844 try:
844 try:
@@ -887,7 +887,7 b' class VerboseTB(TBTools):'
887 "The following traceback may be corrupted or invalid\n"
887 "The following traceback may be corrupted or invalid\n"
888 "The error message is: %s\n" % msg)
888 "The error message is: %s\n" % msg)
889 error(_m)
889 error(_m)
890
890
891 # prune names list of duplicates, but keep the right order
891 # prune names list of duplicates, but keep the right order
892 unique_names = uniq_stable(names)
892 unique_names = uniq_stable(names)
893
893
@@ -965,11 +965,11 b' class VerboseTB(TBTools):'
965 if ipinst is not None:
965 if ipinst is not None:
966 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
966 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
967 # vds: <<
967 # vds: <<
968
968
969 # return all our info assembled as a single string
969 # return all our info assembled as a single string
970 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
970 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
971 return [head] + frames + [''.join(exception[0])]
971 return [head] + frames + [''.join(exception[0])]
972
972
973 def debugger(self,force=False):
973 def debugger(self,force=False):
974 """Call up the pdb debugger if desired, always clean up the tb
974 """Call up the pdb debugger if desired, always clean up the tb
975 reference.
975 reference.
@@ -1048,9 +1048,9 b' class FormattedTB(VerboseTB, ListTB):'
1048 one needs to remove a number of topmost frames from the traceback (such as
1048 one needs to remove a number of topmost frames from the traceback (such as
1049 occurs with python programs that themselves execute other python code,
1049 occurs with python programs that themselves execute other python code,
1050 like Python shells). """
1050 like Python shells). """
1051
1051
1052 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1052 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1053 ostream=None,
1053 ostream=None,
1054 tb_offset=0, long_header=False, include_vars=False,
1054 tb_offset=0, long_header=False, include_vars=False,
1055 check_cache=None):
1055 check_cache=None):
1056
1056
@@ -1068,7 +1068,7 b' class FormattedTB(VerboseTB, ListTB):'
1068 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1068 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1069 # set_mode also sets the tb_join_char attribute
1069 # set_mode also sets the tb_join_char attribute
1070 self.set_mode(mode)
1070 self.set_mode(mode)
1071
1071
1072 def _extract_tb(self,tb):
1072 def _extract_tb(self,tb):
1073 if tb:
1073 if tb:
1074 return traceback.extract_tb(tb)
1074 return traceback.extract_tb(tb)
@@ -1096,7 +1096,7 b' class FormattedTB(VerboseTB, ListTB):'
1096 def stb2text(self, stb):
1096 def stb2text(self, stb):
1097 """Convert a structured traceback (a list) to a string."""
1097 """Convert a structured traceback (a list) to a string."""
1098 return self.tb_join_char.join(stb)
1098 return self.tb_join_char.join(stb)
1099
1099
1100
1100
1101 def set_mode(self,mode=None):
1101 def set_mode(self,mode=None):
1102 """Switch to the desired mode.
1102 """Switch to the desired mode.
@@ -1134,14 +1134,14 b' class AutoFormattedTB(FormattedTB):'
1134 It will find out about exceptions by itself.
1134 It will find out about exceptions by itself.
1135
1135
1136 A brief example:
1136 A brief example:
1137
1137
1138 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1138 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1139 try:
1139 try:
1140 ...
1140 ...
1141 except:
1141 except:
1142 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1142 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1143 """
1143 """
1144
1144
1145 def __call__(self,etype=None,evalue=None,etb=None,
1145 def __call__(self,etype=None,evalue=None,etb=None,
1146 out=None,tb_offset=None):
1146 out=None,tb_offset=None):
1147 """Print out a formatted exception traceback.
1147 """Print out a formatted exception traceback.
@@ -1153,7 +1153,7 b' class AutoFormattedTB(FormattedTB):'
1153 per-call basis (this overrides temporarily the instance's tb_offset
1153 per-call basis (this overrides temporarily the instance's tb_offset
1154 given at initialization time. """
1154 given at initialization time. """
1155
1155
1156
1156
1157 if out is None:
1157 if out is None:
1158 out = self.ostream
1158 out = self.ostream
1159 out.flush()
1159 out.flush()
@@ -1230,7 +1230,7 b' if __name__ == "__main__":'
1230 except:
1230 except:
1231 traceback.print_exc()
1231 traceback.print_exc()
1232 print ''
1232 print ''
1233
1233
1234 handler = ColorTB()
1234 handler = ColorTB()
1235 print '*** ColorTB ***'
1235 print '*** ColorTB ***'
1236 try:
1236 try:
@@ -1238,7 +1238,7 b' if __name__ == "__main__":'
1238 except:
1238 except:
1239 apply(handler, sys.exc_info() )
1239 apply(handler, sys.exc_info() )
1240 print ''
1240 print ''
1241
1241
1242 handler = VerboseTB()
1242 handler = VerboseTB()
1243 print '*** VerboseTB ***'
1243 print '*** VerboseTB ***'
1244 try:
1244 try:
@@ -1246,4 +1246,4 b' if __name__ == "__main__":'
1246 except:
1246 except:
1247 apply(handler, sys.exc_info() )
1247 apply(handler, sys.exc_info() )
1248 print ''
1248 print ''
1249
1249
@@ -36,7 +36,7 b' Usage'
36
36
37 Almost all configuration in IPython is available via the command-line. Do
37 Almost all configuration in IPython is available via the command-line. Do
38 `ipython --help-all` to see all available options. For persistent
38 `ipython --help-all` to see all available options. For persistent
39 configuration, look into your `ipython_config.py` configuration file for
39 configuration, look into your `ipython_config.py` configuration file for
40 details.
40 details.
41
41
42 This file is typically installed in the `IPYTHON_DIR` directory, and there
42 This file is typically installed in the `IPYTHON_DIR` directory, and there
@@ -45,11 +45,11 b' Usage'
45 IPYTHON_DIR defaults to `$HOME/.config/ipython`, and for other Unix systems
45 IPYTHON_DIR defaults to `$HOME/.config/ipython`, and for other Unix systems
46 to `$HOME/.ipython`. For Windows users, $HOME resolves to C:\\Documents
46 to `$HOME/.ipython`. For Windows users, $HOME resolves to C:\\Documents
47 and Settings\\YourUserName in most instances.
47 and Settings\\YourUserName in most instances.
48
48
49 To initialize a profile with the default configuration file, do::
49 To initialize a profile with the default configuration file, do::
50
50
51 $> ipython profile create
51 $> ipython profile create
52
52
53 and start editing `IPYTHON_DIR/profile_default/ipython_config.py`
53 and start editing `IPYTHON_DIR/profile_default/ipython_config.py`
54
54
55 In IPython's documentation, we will refer to this directory as
55 In IPython's documentation, we will refer to this directory as
@@ -129,7 +129,7 b' MAIN FEATURES'
129 * Persistent command history across sessions.
129 * Persistent command history across sessions.
130
130
131 * Logging of input with the ability to save and restore a working session.
131 * Logging of input with the ability to save and restore a working session.
132
132
133 * System escape with !. Typing !ls will run 'ls' in the current directory.
133 * System escape with !. Typing !ls will run 'ls' in the current directory.
134
134
135 * The reload command does a 'deep' reload of a module: changes made to the
135 * The reload command does a 'deep' reload of a module: changes made to the
@@ -201,7 +201,7 b' MAIN FEATURES'
201 Note that the '/' MUST be the first character on the line! This
201 Note that the '/' MUST be the first character on the line! This
202 won't work:
202 won't work:
203 >>> print /globals # syntax error
203 >>> print /globals # syntax error
204
204
205 In most cases the automatic algorithm should work, so you should
205 In most cases the automatic algorithm should work, so you should
206 rarely need to explicitly invoke /. One notable exception is if you
206 rarely need to explicitly invoke /. One notable exception is if you
207 are trying to call a function with a list of tuples as arguments (the
207 are trying to call a function with a list of tuples as arguments (the
@@ -210,12 +210,12 b' MAIN FEATURES'
210 but this will work:
210 but this will work:
211 In [2]: /zip (1,2,3),(4,5,6)
211 In [2]: /zip (1,2,3),(4,5,6)
212 ------> zip ((1,2,3),(4,5,6))
212 ------> zip ((1,2,3),(4,5,6))
213 Out[2]= [(1, 4), (2, 5), (3, 6)]
213 Out[2]= [(1, 4), (2, 5), (3, 6)]
214
214
215 IPython tells you that it has altered your command line by
215 IPython tells you that it has altered your command line by
216 displaying the new command line preceded by -->. e.g.:
216 displaying the new command line preceded by -->. e.g.:
217 In [18]: callable list
217 In [18]: callable list
218 -------> callable (list)
218 -------> callable (list)
219
219
220 2. Auto-Quoting
220 2. Auto-Quoting
221 You can force auto-quoting of a function's arguments by using ',' as
221 You can force auto-quoting of a function's arguments by using ',' as
@@ -255,7 +255,7 b' obj?, obj?? : Get help, or more help for object (also works as'
255
255
256 Magic functions are prefixed by %, and typically take their arguments without
256 Magic functions are prefixed by %, and typically take their arguments without
257 parentheses, quotes or even commas for convenience.
257 parentheses, quotes or even commas for convenience.
258
258
259 Example magic function calls:
259 Example magic function calls:
260
260
261 %alias d ls -F : 'd' is now an alias for 'ls -F'
261 %alias d ls -F : 'd' is now an alias for 'ls -F'
@@ -265,7 +265,7 b' cd /usr/share : Obvious. cd -<tab> to choose from visited dirs.'
265 %cd?? : See help AND source for magic %cd
265 %cd?? : See help AND source for magic %cd
266
266
267 System commands:
267 System commands:
268
268
269 !cp a.txt b/ : System command escape, calls os.system()
269 !cp a.txt b/ : System command escape, calls os.system()
270 cp a.txt b/ : after %rehashx, most system commands work without !
270 cp a.txt b/ : after %rehashx, most system commands work without !
271 cp ${f}.txt $bar : Variable expansion in magics and system commands
271 cp ${f}.txt $bar : Variable expansion in magics and system commands
@@ -277,7 +277,7 b' History:'
277 _i, _ii, _iii : Previous, next previous, next next previous input
277 _i, _ii, _iii : Previous, next previous, next next previous input
278 _i4, _ih[2:5] : Input history line 4, lines 2-4
278 _i4, _ih[2:5] : Input history line 4, lines 2-4
279 exec _i81 : Execute input history line #81 again
279 exec _i81 : Execute input history line #81 again
280 %rep 81 : Edit input history line #81
280 %rep 81 : Edit input history line #81
281 _, __, ___ : previous, next previous, next next previous output
281 _, __, ___ : previous, next previous, next next previous output
282 _dh : Directory history
282 _dh : Directory history
283 _oh : Output history
283 _oh : Output history
@@ -327,10 +327,10 b' blocks are evaluated once a single blank line is entered::'
327
327
328 In [1]: print "Hello IPython!" # Enter was pressed at the end of the line
328 In [1]: print "Hello IPython!" # Enter was pressed at the end of the line
329 Hello IPython!
329 Hello IPython!
330
330
331 In [2]: for i in range(10):
331 In [2]: for i in range(10):
332 ...: print i,
332 ...: print i,
333 ...:
333 ...:
334 0 1 2 3 4 5 6 7 8 9
334 0 1 2 3 4 5 6 7 8 9
335
335
336 If you want to enter more than one expression in a single input block
336 If you want to enter more than one expression in a single input block
@@ -348,7 +348,7 b' cell is executed as if it was a script. An example should clarify this::'
348 ...: z=3
348 ...: z=3
349 ...: x**2 # This does *not* produce an Out[] value
349 ...: x**2 # This does *not* produce an Out[] value
350 ...: x+y+z # Only the last expression does
350 ...: x+y+z # Only the last expression does
351 ...:
351 ...:
352 Out[3]: 6
352 Out[3]: 6
353
353
354 The behavior where an extra blank line forces execution is only active if you
354 The behavior where an extra blank line forces execution is only active if you
@@ -150,13 +150,13 b' def zip_items(items,titles=None):'
150 with their index. Leave other plot items alone."""
150 with their index. Leave other plot items alone."""
151
151
152 class StandaloneItem(Exception): pass
152 class StandaloneItem(Exception): pass
153
153
154 def get_titles(titles):
154 def get_titles(titles):
155 """Return the next title and the input titles array.
155 """Return the next title and the input titles array.
156
156
157 The input array may be changed to None when no titles are left to
157 The input array may be changed to None when no titles are left to
158 prevent extra unnecessary calls to this function."""
158 prevent extra unnecessary calls to this function."""
159
159
160 try:
160 try:
161 title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope
161 title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope
162 except IndexError:
162 except IndexError:
@@ -258,33 +258,33 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
258 explicit '' argument must be given as the limit to be kept.
258 explicit '' argument must be given as the limit to be kept.
259
259
260 Similar functions exist for [y{2}z{2}rtuv]range."""
260 Similar functions exist for [y{2}z{2}rtuv]range."""
261
261
262 self('set xrange [%s:%s]' % (min,max))
262 self('set xrange [%s:%s]' % (min,max))
263
263
264 def yrange(self,min='*',max='*'):
264 def yrange(self,min='*',max='*'):
265 self('set yrange [%s:%s]' % (min,max))
265 self('set yrange [%s:%s]' % (min,max))
266
266
267 def zrange(self,min='*',max='*'):
267 def zrange(self,min='*',max='*'):
268 self('set zrange [%s:%s]' % (min,max))
268 self('set zrange [%s:%s]' % (min,max))
269
269
270 def x2range(self,min='*',max='*'):
270 def x2range(self,min='*',max='*'):
271 self('set xrange [%s:%s]' % (min,max))
271 self('set xrange [%s:%s]' % (min,max))
272
272
273 def y2range(self,min='*',max='*'):
273 def y2range(self,min='*',max='*'):
274 self('set yrange [%s:%s]' % (min,max))
274 self('set yrange [%s:%s]' % (min,max))
275
275
276 def z2range(self,min='*',max='*'):
276 def z2range(self,min='*',max='*'):
277 self('set zrange [%s:%s]' % (min,max))
277 self('set zrange [%s:%s]' % (min,max))
278
278
279 def rrange(self,min='*',max='*'):
279 def rrange(self,min='*',max='*'):
280 self('set rrange [%s:%s]' % (min,max))
280 self('set rrange [%s:%s]' % (min,max))
281
281
282 def trange(self,min='*',max='*'):
282 def trange(self,min='*',max='*'):
283 self('set trange [%s:%s]' % (min,max))
283 self('set trange [%s:%s]' % (min,max))
284
284
285 def urange(self,min='*',max='*'):
285 def urange(self,min='*',max='*'):
286 self('set urange [%s:%s]' % (min,max))
286 self('set urange [%s:%s]' % (min,max))
287
287
288 def vrange(self,min='*',max='*'):
288 def vrange(self,min='*',max='*'):
289 self('set vrange [%s:%s]' % (min,max))
289 self('set vrange [%s:%s]' % (min,max))
290
290
@@ -319,7 +319,7 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
319 # Filter out other options the original plot doesn't know
319 # Filter out other options the original plot doesn't know
320 hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None)
320 hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None)
321 titles = popkey(keyw,'titles',0)
321 titles = popkey(keyw,'titles',0)
322
322
323 # the filename keyword should control hardcopy generation, this is an
323 # the filename keyword should control hardcopy generation, this is an
324 # override switch only which needs to be explicitly set to zero
324 # override switch only which needs to be explicitly set to zero
325 if hardcopy:
325 if hardcopy:
@@ -410,7 +410,7 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
410
410
411 - filename: a string, typically ending in .eps. If given, the plot is
411 - filename: a string, typically ending in .eps. If given, the plot is
412 sent to this file in PostScript format.
412 sent to this file in PostScript format.
413
413
414 - hardcopy: this can be set to 0 to override 'filename'. It does not
414 - hardcopy: this can be set to 0 to override 'filename'. It does not
415 need to be given to produce PostScript, its purpose is to allow
415 need to be given to produce PostScript, its purpose is to allow
416 switching PostScript output off globally in scripts without having to
416 switching PostScript output off globally in scripts without having to
@@ -421,7 +421,7 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
421 PostScript.
421 PostScript.
422
422
423 For example:
423 For example:
424
424
425 In [1]: x=frange(0,2*pi,npts=100)
425 In [1]: x=frange(0,2*pi,npts=100)
426
426
427 Generate a plot in file 'sin.eps':
427 Generate a plot in file 'sin.eps':
@@ -439,16 +439,16 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
439 PostScript generation through plot() is useful mainly for scripting
439 PostScript generation through plot() is useful mainly for scripting
440 uses where you are not interested in interactive plotting. For
440 uses where you are not interested in interactive plotting. For
441 interactive use, the hardcopy() function is typically more convenient:
441 interactive use, the hardcopy() function is typically more convenient:
442
442
443 In [5]: plot(x,sin(x))
443 In [5]: plot(x,sin(x))
444
444
445 In [6]: hardcopy('sin.eps') """
445 In [6]: hardcopy('sin.eps') """
446
446
447 self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw)
447 self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw)
448
448
449 def plot2(self,arg,**kw):
449 def plot2(self,arg,**kw):
450 """Plot the entries of a dictionary or a list/tuple of arrays.
450 """Plot the entries of a dictionary or a list/tuple of arrays.
451
451
452 This simple utility calls plot() with a list of Gnuplot.Data objects
452 This simple utility calls plot() with a list of Gnuplot.Data objects
453 constructed either from the values of the input dictionary, or the entries
453 constructed either from the values of the input dictionary, or the entries
454 in it if it is a tuple or list. Each item gets labeled with the key/index
454 in it if it is a tuple or list. Each item gets labeled with the key/index
@@ -493,7 +493,7 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
493 Gnuplot.py, this version has several enhancements, listed in the
493 Gnuplot.py, this version has several enhancements, listed in the
494 plot() documentation.
494 plot() documentation.
495 """
495 """
496
496
497 self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw)
497 self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw)
498
498
499 def replot(self, *items, **keyw):
499 def replot(self, *items, **keyw):
@@ -508,7 +508,7 b' class Gnuplot(Gnuplot_ori.Gnuplot):'
508 'filename' keyword argument in each call to replot. The Gnuplot python
508 'filename' keyword argument in each call to replot. The Gnuplot python
509 interface has no way of knowing that your previous call to
509 interface has no way of knowing that your previous call to
510 Gnuplot.plot() was meant for PostScript output."""
510 Gnuplot.plot() was meant for PostScript output."""
511
511
512 self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw)
512 self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw)
513
513
514 # The original hardcopy has a bug. See fix at the end. The rest of the code
514 # The original hardcopy has a bug. See fix at the end. The rest of the code
@@ -38,13 +38,13 b' class _Helper(object):'
38 def __repr__(self):
38 def __repr__(self):
39 return "Type help() for interactive help, " \
39 return "Type help() for interactive help, " \
40 "or help(object) for help about object."
40 "or help(object) for help about object."
41
41
42 def __call__(self, *args, **kwds):
42 def __call__(self, *args, **kwds):
43 class DummyWriter(object):
43 class DummyWriter(object):
44 '''Dumy class to handle help output'''
44 '''Dumy class to handle help output'''
45 def __init__(self, pager):
45 def __init__(self, pager):
46 self._pager = pager
46 self._pager = pager
47
47
48 def write(self, data):
48 def write(self, data):
49 '''hook to fill self._pager'''
49 '''hook to fill self._pager'''
50 self._pager(data)
50 self._pager(data)
@@ -52,17 +52,17 b' class _Helper(object):'
52 import pydoc
52 import pydoc
53 pydoc.help.output = DummyWriter(self._pager)
53 pydoc.help.output = DummyWriter(self._pager)
54 pydoc.help.interact = lambda :1
54 pydoc.help.interact = lambda :1
55
55
56 return pydoc.help(*args, **kwds)
56 return pydoc.help(*args, **kwds)
57
57
58
58
59 ##############################################################################
59 ##############################################################################
60 class _CodeExecutor(ThreadEx):
60 class _CodeExecutor(ThreadEx):
61 ''' Thread that execute ipython code '''
61 ''' Thread that execute ipython code '''
62 def __init__(self, instance):
62 def __init__(self, instance):
63 ThreadEx.__init__(self)
63 ThreadEx.__init__(self)
64 self.instance = instance
64 self.instance = instance
65
65
66 def run(self):
66 def run(self):
67 '''Thread main loop'''
67 '''Thread main loop'''
68 try:
68 try:
@@ -70,17 +70,17 b' class _CodeExecutor(ThreadEx):'
70 self.instance._help_text = None
70 self.instance._help_text = None
71 self.instance._execute()
71 self.instance._execute()
72 # used for uper class to generate event after execution
72 # used for uper class to generate event after execution
73 self.instance._after_execute()
73 self.instance._after_execute()
74
74
75 except KeyboardInterrupt:
75 except KeyboardInterrupt:
76 pass
76 pass
77
77
78
78
79 ##############################################################################
79 ##############################################################################
80 class NonBlockingIPShell(object):
80 class NonBlockingIPShell(object):
81 '''
81 '''
82 Create an IPython instance, running the commands in a separate,
82 Create an IPython instance, running the commands in a separate,
83 non-blocking thread.
83 non-blocking thread.
84 This allows embedding in any GUI without blockage.
84 This allows embedding in any GUI without blockage.
85
85
86 Note: The ThreadEx class supports asynchroneous function call
86 Note: The ThreadEx class supports asynchroneous function call
@@ -111,7 +111,7 b' class NonBlockingIPShell(object):'
111 self.init_ipython0(user_ns, user_global_ns,
111 self.init_ipython0(user_ns, user_global_ns,
112 cin, cout, cerr,
112 cin, cout, cerr,
113 ask_exit_handler)
113 ask_exit_handler)
114
114
115 #vars used by _execute
115 #vars used by _execute
116 self._iter_more = 0
116 self._iter_more = 0
117 self._history_level = 0
117 self._history_level = 0
@@ -121,7 +121,7 b' class NonBlockingIPShell(object):'
121 #thread working vars
121 #thread working vars
122 self._line_to_execute = ''
122 self._line_to_execute = ''
123 self._threading = True
123 self._threading = True
124
124
125 #vars that will be checked by GUI loop to handle thread states...
125 #vars that will be checked by GUI loop to handle thread states...
126 #will be replaced later by PostEvent GUI funtions...
126 #will be replaced later by PostEvent GUI funtions...
127 self._doc_text = None
127 self._doc_text = None
@@ -132,7 +132,7 b' class NonBlockingIPShell(object):'
132 cin=None, cout=None, cerr=None,
132 cin=None, cout=None, cerr=None,
133 ask_exit_handler=None):
133 ask_exit_handler=None):
134 ''' Initialize an ipython0 instance '''
134 ''' Initialize an ipython0 instance '''
135
135
136 #first we redefine in/out/error functions of IPython
136 #first we redefine in/out/error functions of IPython
137 #BUG: we've got a limitation form ipython0 there
137 #BUG: we've got a limitation form ipython0 there
138 #only one instance can be instanciated else tehre will be
138 #only one instance can be instanciated else tehre will be
@@ -143,7 +143,7 b' class NonBlockingIPShell(object):'
143 Term.cout = cout
143 Term.cout = cout
144 if cerr:
144 if cerr:
145 Term.cerr = cerr
145 Term.cerr = cerr
146
146
147 excepthook = sys.excepthook
147 excepthook = sys.excepthook
148
148
149 #Hack to save sys.displayhook, because ipython seems to overwrite it...
149 #Hack to save sys.displayhook, because ipython seems to overwrite it...
@@ -165,11 +165,11 b' class NonBlockingIPShell(object):'
165 self._IP.stdin_encoding = loc
165 self._IP.stdin_encoding = loc
166 #we replace the ipython default pager by our pager
166 #we replace the ipython default pager by our pager
167 self._IP.set_hook('show_in_pager', self._pager)
167 self._IP.set_hook('show_in_pager', self._pager)
168
168
169 #we replace the ipython default shell command caller
169 #we replace the ipython default shell command caller
170 #by our shell handler
170 #by our shell handler
171 self._IP.set_hook('shell_hook', self._shell)
171 self._IP.set_hook('shell_hook', self._shell)
172
172
173 #we replace the ipython default input command caller by our method
173 #we replace the ipython default input command caller by our method
174 iplib.raw_input_original = self._raw_input_original
174 iplib.raw_input_original = self._raw_input_original
175 #we replace the ipython default exit command by our method
175 #we replace the ipython default exit command by our method
@@ -184,10 +184,10 b' class NonBlockingIPShell(object):'
184
184
185 import __builtin__
185 import __builtin__
186 __builtin__.raw_input = self._raw_input
186 __builtin__.raw_input = self._raw_input
187
187
188 sys.excepthook = excepthook
188 sys.excepthook = excepthook
189
189
190 #----------------------- Thread management section ----------------------
190 #----------------------- Thread management section ----------------------
191 def do_execute(self, line):
191 def do_execute(self, line):
192 """
192 """
193 Tell the thread to process the 'line' command
193 Tell the thread to process the 'line' command
@@ -196,8 +196,8 b' class NonBlockingIPShell(object):'
196 self._line_to_execute = line
196 self._line_to_execute = line
197
197
198 if self._threading:
198 if self._threading:
199 #we launch the ipython line execution in a thread to make it
199 #we launch the ipython line execution in a thread to make it
200 #interruptible with include it in self namespace to be able
200 #interruptible with include it in self namespace to be able
201 #to call ce.raise_exc(KeyboardInterrupt)
201 #to call ce.raise_exc(KeyboardInterrupt)
202 self.ce = _CodeExecutor(self)
202 self.ce = _CodeExecutor(self)
203 self.ce.start()
203 self.ce.start()
@@ -207,8 +207,8 b' class NonBlockingIPShell(object):'
207 self._help_text = None
207 self._help_text = None
208 self._execute()
208 self._execute()
209 # used for uper class to generate event after execution
209 # used for uper class to generate event after execution
210 self._after_execute()
210 self._after_execute()
211
211
212 except KeyboardInterrupt:
212 except KeyboardInterrupt:
213 pass
213 pass
214
214
@@ -225,7 +225,7 b' class NonBlockingIPShell(object):'
225 @rtype: bool
225 @rtype: bool
226 """
226 """
227 return self._threading
227 return self._threading
228
228
229 def set_threading(self, state):
229 def set_threading(self, state):
230 """
230 """
231 Sets threading state, if set to True, then each command sent to
231 Sets threading state, if set to True, then each command sent to
@@ -247,7 +247,7 b' class NonBlockingIPShell(object):'
247 @rtype: string
247 @rtype: string
248 """
248 """
249 return self._doc_text
249 return self._doc_text
250
250
251 def get_help_text(self):
251 def get_help_text(self):
252 """
252 """
253 Returns the output of the processing that need to be paged via help pager(if any)
253 Returns the output of the processing that need to be paged via help pager(if any)
@@ -265,7 +265,7 b' class NonBlockingIPShell(object):'
265 @rtype: string
265 @rtype: string
266 """
266 """
267 return self._IP.banner
267 return self._IP.banner
268
268
269 def get_prompt_count(self):
269 def get_prompt_count(self):
270 """
270 """
271 Returns the prompt number.
271 Returns the prompt number.
@@ -295,7 +295,7 b' class NonBlockingIPShell(object):'
295 @rtype: int
295 @rtype: int
296 """
296 """
297 return self._IP.indent_current_nsp
297 return self._IP.indent_current_nsp
298
298
299 def update_namespace(self, ns_dict):
299 def update_namespace(self, ns_dict):
300 '''
300 '''
301 Add the current dictionary to the shell namespace.
301 Add the current dictionary to the shell namespace.
@@ -354,7 +354,7 b' class NonBlockingIPShell(object):'
354 while((history == '' or history == '\n') and self._history_level >0):
354 while((history == '' or history == '\n') and self._history_level >0):
355 if self._history_level >= 1:
355 if self._history_level >= 1:
356 self._history_level -= 1
356 self._history_level -= 1
357 history = self._get_history()
357 history = self._get_history()
358 return history
358 return history
359
359
360 def history_forward(self):
360 def history_forward(self):
@@ -406,7 +406,7 b' class NonBlockingIPShell(object):'
406 @rtype: int
406 @rtype: int
407 '''
407 '''
408 return len(self._IP.input_hist_raw)-1
408 return len(self._IP.input_hist_raw)-1
409
409
410 def _get_history(self):
410 def _get_history(self):
411 '''
411 '''
412 Get's the command string of the current history level.
412 Get's the command string of the current history level.
@@ -428,7 +428,7 b' class NonBlockingIPShell(object):'
428 self._help_text = text
428 self._help_text = text
429 else:
429 else:
430 self._help_text += text
430 self._help_text += text
431
431
432 def _pager(self, IP, text):
432 def _pager(self, IP, text):
433 '''
433 '''
434 This function is used as a callback replacment to IPython pager function
434 This function is used as a callback replacment to IPython pager function
@@ -437,7 +437,7 b' class NonBlockingIPShell(object):'
437 get_doc_text function.
437 get_doc_text function.
438 '''
438 '''
439 self._doc_text = text
439 self._doc_text = text
440
440
441 def _raw_input_original(self, prompt=''):
441 def _raw_input_original(self, prompt=''):
442 '''
442 '''
443 Custom raw_input() replacement. Get's current line from console buffer.
443 Custom raw_input() replacement. Get's current line from console buffer.
@@ -454,7 +454,7 b' class NonBlockingIPShell(object):'
454 """ A replacement from python's raw_input.
454 """ A replacement from python's raw_input.
455 """
455 """
456 raise NotImplementedError
456 raise NotImplementedError
457
457
458 def _execute(self):
458 def _execute(self):
459 '''
459 '''
460 Executes the current line provided by the shell object.
460 Executes the current line provided by the shell object.
@@ -464,7 +464,7 b' class NonBlockingIPShell(object):'
464 sys.stdout = Term.cout
464 sys.stdout = Term.cout
465 #self.sys_displayhook_ori = sys.displayhook
465 #self.sys_displayhook_ori = sys.displayhook
466 #sys.displayhook = self.displayhook
466 #sys.displayhook = self.displayhook
467
467
468 try:
468 try:
469 line = self._IP.raw_input(None, self._iter_more)
469 line = self._IP.raw_input(None, self._iter_more)
470 if self._IP.autoindent:
470 if self._IP.autoindent:
@@ -497,7 +497,7 b' class NonBlockingIPShell(object):'
497
497
498 sys.stdout = orig_stdout
498 sys.stdout = orig_stdout
499 #sys.displayhook = self.sys_displayhook_ori
499 #sys.displayhook = self.sys_displayhook_ori
500
500
501 def _shell(self, ip, cmd):
501 def _shell(self, ip, cmd):
502 '''
502 '''
503 Replacement method to allow shell commands without them blocking.
503 Replacement method to allow shell commands without them blocking.
@@ -16,12 +16,12 b' class IPythonHistoryPanel(wx.Panel):'
16
16
17 def __init__(self, parent,flt_empty=True,
17 def __init__(self, parent,flt_empty=True,
18 flt_doc=True,flt_cmd=True,flt_magic=True):
18 flt_doc=True,flt_cmd=True,flt_magic=True):
19
19
20 wx.Panel.__init__(self,parent,-1)
20 wx.Panel.__init__(self,parent,-1)
21 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
21 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
22 text_ctrl = PythonSTC(self, -1)
22 text_ctrl = PythonSTC(self, -1)
23
23
24
24
25 st_filt = wx.StaticText(self, -1, " Filter:")
25 st_filt = wx.StaticText(self, -1, " Filter:")
26
26
27 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
27 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
@@ -52,12 +52,12 b' class IPythonHistoryPanel(wx.Panel):'
52 self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter)
52 self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter)
53 self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter)
53 self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter)
54 self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter)
54 self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter)
55
55
56 #self.filter_empty.SetValue(flt_empty)
56 #self.filter_empty.SetValue(flt_empty)
57 #self.filter_doc.SetValue(flt_doc)
57 #self.filter_doc.SetValue(flt_doc)
58 #self.filter_cmd.SetValue(flt_cmd)
58 #self.filter_cmd.SetValue(flt_cmd)
59 #self.filter_magic.SetValue(flt_magic)
59 #self.filter_magic.SetValue(flt_magic)
60
60
61 sizer = wx.BoxSizer(wx.VERTICAL)
61 sizer = wx.BoxSizer(wx.VERTICAL)
62
62
63 sizer.Add(text_ctrl, 1, wx.EXPAND)
63 sizer.Add(text_ctrl, 1, wx.EXPAND)
@@ -83,7 +83,7 b' class IPythonHistoryPanel(wx.Panel):'
83 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
83 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
84 text_ctrl.SetMarginWidth(1, 15)
84 text_ctrl.SetMarginWidth(1, 15)
85
85
86
86
87 def write(self,history_line):
87 def write(self,history_line):
88 add = True
88 add = True
89 if self.filter_empty.GetValue() == True and history_line == '':
89 if self.filter_empty.GetValue() == True and history_line == '':
@@ -106,10 +106,10 b' class IPythonHistoryPanel(wx.Panel):'
106 self.options[name]['value']='False'
106 self.options[name]['value']='False'
107 self.updateOptionTracker(name,
107 self.updateOptionTracker(name,
108 self.options[name]['value'])
108 self.options[name]['value'])
109
109
110 def evtCheckEmptyFilter(self, event):
110 def evtCheckEmptyFilter(self, event):
111 self.processOptionCheckedEvt(event, 'filter_empty')
111 self.processOptionCheckedEvt(event, 'filter_empty')
112
112
113 def evtCheckDocFilter(self, event):
113 def evtCheckDocFilter(self, event):
114 self.processOptionCheckedEvt(event, 'filter_doc')
114 self.processOptionCheckedEvt(event, 'filter_doc')
115
115
@@ -118,10 +118,10 b' class IPythonHistoryPanel(wx.Panel):'
118
118
119 def evtCheckMagicFilter(self, event):
119 def evtCheckMagicFilter(self, event):
120 self.processOptionCheckedEvt(event, 'filter_magic')
120 self.processOptionCheckedEvt(event, 'filter_magic')
121
121
122 def getOptions(self):
122 def getOptions(self):
123 return self.options
123 return self.options
124
124
125 def reloadOptions(self,options):
125 def reloadOptions(self,options):
126 self.options = options
126 self.options = options
127 for key in self.options.keys():
127 for key in self.options.keys():
@@ -135,14 +135,14 b' class IPythonHistoryPanel(wx.Panel):'
135 Default history tracker (does nothing)
135 Default history tracker (does nothing)
136 '''
136 '''
137 pass
137 pass
138
138
139 def setOptionTrackerHook(self,func):
139 def setOptionTrackerHook(self,func):
140 '''
140 '''
141 Define a new history tracker
141 Define a new history tracker
142 '''
142 '''
143 self.updateOptionTracker = func
143 self.updateOptionTracker = func
144
144
145
145
146 #----------------------------------------------------------------------
146 #----------------------------------------------------------------------
147 # Font definition for Styled Text Control
147 # Font definition for Styled Text Control
148
148
@@ -177,7 +177,7 b' else:'
177 class PythonSTC(stc.StyledTextCtrl):
177 class PythonSTC(stc.StyledTextCtrl):
178
178
179 fold_symbols = 3
179 fold_symbols = 3
180
180
181 def __init__(self, parent, ID,
181 def __init__(self, parent, ID,
182 pos=wx.DefaultPosition, size=wx.DefaultSize,
182 pos=wx.DefaultPosition, size=wx.DefaultSize,
183 style=0):
183 style=0):
@@ -197,14 +197,14 b' class PythonSTC(stc.StyledTextCtrl):'
197 #self.SetViewEOL(True)
197 #self.SetViewEOL(True)
198 self.SetEOLMode(stc.STC_EOL_CRLF)
198 self.SetEOLMode(stc.STC_EOL_CRLF)
199 #self.SetUseAntiAliasing(True)
199 #self.SetUseAntiAliasing(True)
200
200
201 self.SetEdgeMode(stc.STC_EDGE_LINE)
201 self.SetEdgeMode(stc.STC_EDGE_LINE)
202 self.SetEdgeColumn(80)
202 self.SetEdgeColumn(80)
203 self.SetEdgeColour(wx.LIGHT_GREY)
203 self.SetEdgeColour(wx.LIGHT_GREY)
204 self.SetLayoutCache(stc.STC_CACHE_PAGE)
204 self.SetLayoutCache(stc.STC_CACHE_PAGE)
205
205
206 # Setup a margin to hold fold markers
206 # Setup a margin to hold fold markers
207 #self.SetFoldFlags(16)
207 #self.SetFoldFlags(16)
208 ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
208 ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
209 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
209 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
210 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
210 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
@@ -212,7 +212,7 b' class PythonSTC(stc.StyledTextCtrl):'
212 self.SetMarginWidth(2, 12)
212 self.SetMarginWidth(2, 12)
213
213
214 if self.fold_symbols == 0:
214 if self.fold_symbols == 0:
215 # Arrow pointing right for contracted folders,
215 # Arrow pointing right for contracted folders,
216 # arrow pointing down for expanded
216 # arrow pointing down for expanded
217 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
217 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
218 stc.STC_MARK_ARROWDOWN, "black", "black")
218 stc.STC_MARK_ARROWDOWN, "black", "black")
@@ -228,7 +228,7 b' class PythonSTC(stc.StyledTextCtrl):'
228 stc.STC_MARK_EMPTY, "white", "black")
228 stc.STC_MARK_EMPTY, "white", "black")
229 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
229 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
230 stc.STC_MARK_EMPTY, "white", "black")
230 stc.STC_MARK_EMPTY, "white", "black")
231
231
232 elif self.fold_symbols == 1:
232 elif self.fold_symbols == 1:
233 # Plus for contracted folders, minus for expanded
233 # Plus for contracted folders, minus for expanded
234 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
234 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
@@ -301,7 +301,7 b' class PythonSTC(stc.StyledTextCtrl):'
301 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
301 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
302
302
303 # Python styles
303 # Python styles
304 # Default
304 # Default
305 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
305 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
306 # Comments
306 # Comments
307 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
307 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
@@ -335,9 +335,9 b' class PythonSTC(stc.StyledTextCtrl):'
335
335
336 # register some images for use in the AutoComplete box.
336 # register some images for use in the AutoComplete box.
337 #self.RegisterImage(1, images.getSmilesBitmap())
337 #self.RegisterImage(1, images.getSmilesBitmap())
338 #self.RegisterImage(2,
338 #self.RegisterImage(2,
339 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
339 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
340 #self.RegisterImage(3,
340 #self.RegisterImage(3,
341 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
341 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
342
342
343
343
@@ -365,7 +365,7 b' class PythonSTC(stc.StyledTextCtrl):'
365 #self.AutoCompShow(0, st)
365 #self.AutoCompShow(0, st)
366
366
367 kw = keyword.kwlist[:]
367 kw = keyword.kwlist[:]
368
368
369 kw.sort() # Python sorts are case sensitive
369 kw.sort() # Python sorts are case sensitive
370 self.AutoCompSetIgnoreCase(False) # so this needs to match
370 self.AutoCompSetIgnoreCase(False) # so this needs to match
371
371
@@ -398,7 +398,7 b' class PythonSTC(stc.StyledTextCtrl):'
398 if braceAtCaret < 0:
398 if braceAtCaret < 0:
399 charAfter = self.GetCharAt(caretPos)
399 charAfter = self.GetCharAt(caretPos)
400 styleAfter = self.GetStyleAt(caretPos)
400 styleAfter = self.GetStyleAt(caretPos)
401
401
402 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
402 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
403 braceAtCaret = caretPos
403 braceAtCaret = caretPos
404
404
@@ -53,11 +53,11 b' class WxNonBlockingIPShell(NonBlockingIPShell):'
53 '''
53 '''
54 An NonBlockingIPShell Thread that is WX dependent.
54 An NonBlockingIPShell Thread that is WX dependent.
55 '''
55 '''
56 def __init__(self, parent,
56 def __init__(self, parent,
57 argv=[],user_ns={},user_global_ns=None,
57 argv=[],user_ns={},user_global_ns=None,
58 cin=None, cout=None, cerr=None,
58 cin=None, cout=None, cerr=None,
59 ask_exit_handler=None):
59 ask_exit_handler=None):
60
60
61 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
61 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
62 cin, cout, cerr,
62 cin, cout, cerr,
63 ask_exit_handler)
63 ask_exit_handler)
@@ -68,22 +68,22 b' class WxNonBlockingIPShell(NonBlockingIPShell):'
68 self._IP.exit = self._ask_exit
68 self._IP.exit = self._ask_exit
69
69
70 def addGUIShortcut(self, text, func):
70 def addGUIShortcut(self, text, func):
71 wx.CallAfter(self.parent.add_button_handler,
71 wx.CallAfter(self.parent.add_button_handler,
72 button_info={ 'text':text,
72 button_info={ 'text':text,
73 'func':self.parent.doExecuteLine(func)})
73 'func':self.parent.doExecuteLine(func)})
74
74
75 def _raw_input(self, prompt=''):
75 def _raw_input(self, prompt=''):
76 """ A replacement from python's raw_input.
76 """ A replacement from python's raw_input.
77 """
77 """
78 self.answer = None
78 self.answer = None
79 if(self._threading == True):
79 if(self._threading == True):
80 wx.CallAfter(self._yesNoBox, prompt)
80 wx.CallAfter(self._yesNoBox, prompt)
81 while self.answer is None:
81 while self.answer is None:
82 time.sleep(.1)
82 time.sleep(.1)
83 else:
83 else:
84 self._yesNoBox(prompt)
84 self._yesNoBox(prompt)
85 return self.answer
85 return self.answer
86
86
87 def _yesNoBox(self, prompt):
87 def _yesNoBox(self, prompt):
88 """ yes/no box managed with wx.CallAfter jsut in case caler is executed in a thread"""
88 """ yes/no box managed with wx.CallAfter jsut in case caler is executed in a thread"""
89 dlg = wx.TextEntryDialog(
89 dlg = wx.TextEntryDialog(
@@ -94,21 +94,21 b' class WxNonBlockingIPShell(NonBlockingIPShell):'
94 answer = ''
94 answer = ''
95 if dlg.ShowModal() == wx.ID_OK:
95 if dlg.ShowModal() == wx.ID_OK:
96 answer = dlg.GetValue()
96 answer = dlg.GetValue()
97
97
98 dlg.Destroy()
98 dlg.Destroy()
99 self.answer = answer
99 self.answer = answer
100
100
101 def _ask_exit(self):
101 def _ask_exit(self):
102 wx.CallAfter(self.ask_exit_callback, ())
102 wx.CallAfter(self.ask_exit_callback, ())
103
103
104 def _after_execute(self):
104 def _after_execute(self):
105 wx.CallAfter(self.parent.evtStateExecuteDone, ())
105 wx.CallAfter(self.parent.evtStateExecuteDone, ())
106
106
107
107
108 class WxConsoleView(stc.StyledTextCtrl):
108 class WxConsoleView(stc.StyledTextCtrl):
109 '''
109 '''
110 Specialized styled text control view for console-like workflow.
110 Specialized styled text control view for console-like workflow.
111 We use here a scintilla frontend thus it can be reused in any GUI that
111 We use here a scintilla frontend thus it can be reused in any GUI that
112 supports scintilla with less work.
112 supports scintilla with less work.
113
113
114 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
114 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
@@ -128,7 +128,7 b' class WxConsoleView(stc.StyledTextCtrl):'
128 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
128 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
129 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
129 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
130 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
130 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
131 '1;34': [12, 'LIGHT BLUE'], '1;35':
131 '1;34': [12, 'LIGHT BLUE'], '1;35':
132 [13, 'MEDIUM VIOLET RED'],
132 [13, 'MEDIUM VIOLET RED'],
133 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
133 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
134
134
@@ -163,8 +163,8 b' class WxConsoleView(stc.StyledTextCtrl):'
163 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
163 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
164
164
165 ####### Scintilla configuration ###################################
165 ####### Scintilla configuration ###################################
166
166
167 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
167 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
168 # the widget
168 # the widget
169 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
169 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
170 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
170 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
@@ -188,7 +188,7 b' class WxConsoleView(stc.StyledTextCtrl):'
188 self.SetTabWidth(4)
188 self.SetTabWidth(4)
189
189
190 self.EnsureCaretVisible()
190 self.EnsureCaretVisible()
191
191
192 self.SetMargins(3, 3) #text is moved away from border with 3px
192 self.SetMargins(3, 3) #text is moved away from border with 3px
193 # Suppressing Scintilla margins
193 # Suppressing Scintilla margins
194 self.SetMarginWidth(0, 0)
194 self.SetMarginWidth(0, 0)
@@ -197,19 +197,19 b' class WxConsoleView(stc.StyledTextCtrl):'
197
197
198 self.background_color = background_color
198 self.background_color = background_color
199 self.buildStyles()
199 self.buildStyles()
200
200
201 self.indent = 0
201 self.indent = 0
202 self.prompt_count = 0
202 self.prompt_count = 0
203 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
203 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
204
204
205 self.write(intro)
205 self.write(intro)
206 self.setPrompt(prompt)
206 self.setPrompt(prompt)
207 self.showPrompt()
207 self.showPrompt()
208
208
209 self.autocomplete_mode = autocomplete_mode
209 self.autocomplete_mode = autocomplete_mode
210
210
211 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
211 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
212
212
213 def buildStyles(self):
213 def buildStyles(self):
214 #we define platform specific fonts
214 #we define platform specific fonts
215 if wx.Platform == '__WXMSW__':
215 if wx.Platform == '__WXMSW__':
@@ -246,29 +246,29 b' class WxConsoleView(stc.StyledTextCtrl):'
246 self.SetCaretForeground("WHITE")
246 self.SetCaretForeground("WHITE")
247 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
247 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
248
248
249 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
249 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
250 "fore:%s,back:%s,size:%d,face:%s"
250 "fore:%s,back:%s,size:%d,face:%s"
251 % (self.ANSI_STYLES['0;30'][1],
251 % (self.ANSI_STYLES['0;30'][1],
252 self.background_color,
252 self.background_color,
253 faces['size'], faces['mono']))
253 faces['size'], faces['mono']))
254 self.StyleClearAll()
254 self.StyleClearAll()
255 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
255 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
256 "fore:#FF0000,back:#0000FF,bold")
256 "fore:#FF0000,back:#0000FF,bold")
257 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
257 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
258 "fore:#000000,back:#FF0000,bold")
258 "fore:#000000,back:#FF0000,bold")
259
259
260 for style in self.ANSI_STYLES.values():
260 for style in self.ANSI_STYLES.values():
261 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
261 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
262
262
263 #######################################################################
263 #######################################################################
264
264
265 def setBackgroundColor(self, color):
265 def setBackgroundColor(self, color):
266 self.background_color = color
266 self.background_color = color
267 self.buildStyles()
267 self.buildStyles()
268
268
269 def getBackgroundColor(self, color):
269 def getBackgroundColor(self, color):
270 return self.background_color
270 return self.background_color
271
271
272 def asyncWrite(self, text):
272 def asyncWrite(self, text):
273 '''
273 '''
274 Write given text to buffer in an asynchroneous way.
274 Write given text to buffer in an asynchroneous way.
@@ -278,16 +278,16 b' class WxConsoleView(stc.StyledTextCtrl):'
278 '''
278 '''
279 try:
279 try:
280 wx.MutexGuiEnter()
280 wx.MutexGuiEnter()
281
281
282 #be sure not to be interrutpted before the MutexGuiLeave!
282 #be sure not to be interrutpted before the MutexGuiLeave!
283 self.write(text)
283 self.write(text)
284
284
285 except KeyboardInterrupt:
285 except KeyboardInterrupt:
286 wx.MutexGuiLeave()
286 wx.MutexGuiLeave()
287 raise KeyboardInterrupt
287 raise KeyboardInterrupt
288 wx.MutexGuiLeave()
288 wx.MutexGuiLeave()
289
289
290
290
291 def write(self, text):
291 def write(self, text):
292 '''
292 '''
293 Write given text to buffer.
293 Write given text to buffer.
@@ -299,7 +299,7 b' class WxConsoleView(stc.StyledTextCtrl):'
299 segment = segments.pop(0)
299 segment = segments.pop(0)
300 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
300 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
301 self.AppendText(segment)
301 self.AppendText(segment)
302
302
303 if segments:
303 if segments:
304 ansi_tags = self.color_pat.findall(text)
304 ansi_tags = self.color_pat.findall(text)
305
305
@@ -312,9 +312,9 b' class WxConsoleView(stc.StyledTextCtrl):'
312 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
312 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
313
313
314 segments.pop(i)
314 segments.pop(i)
315
315
316 self.moveCursor(self.getCurrentLineEnd())
316 self.moveCursor(self.getCurrentLineEnd())
317
317
318 def getPromptLen(self):
318 def getPromptLen(self):
319 '''
319 '''
320 Return the length of current prompt
320 Return the length of current prompt
@@ -326,10 +326,10 b' class WxConsoleView(stc.StyledTextCtrl):'
326
326
327 def setIndentation(self, indentation):
327 def setIndentation(self, indentation):
328 self.indent = indentation
328 self.indent = indentation
329
329
330 def setPromptCount(self, count):
330 def setPromptCount(self, count):
331 self.prompt_count = count
331 self.prompt_count = count
332
332
333 def showPrompt(self):
333 def showPrompt(self):
334 '''
334 '''
335 Prints prompt at start of line.
335 Prints prompt at start of line.
@@ -340,11 +340,11 b' class WxConsoleView(stc.StyledTextCtrl):'
340 self.write(self.prompt)
340 self.write(self.prompt)
341 #now we update the position of end of prompt
341 #now we update the position of end of prompt
342 self.current_start = self.getCurrentLineEnd()
342 self.current_start = self.getCurrentLineEnd()
343
343
344 autoindent = self.indent*' '
344 autoindent = self.indent*' '
345 autoindent = autoindent.replace(' ','\t')
345 autoindent = autoindent.replace(' ','\t')
346 self.write(autoindent)
346 self.write(autoindent)
347
347
348 def changeLine(self, text):
348 def changeLine(self, text):
349 '''
349 '''
350 Replace currently entered command line with given text.
350 Replace currently entered command line with given text.
@@ -379,15 +379,15 b' class WxConsoleView(stc.StyledTextCtrl):'
379 #If cursor is at wrong position put it at last line...
379 #If cursor is at wrong position put it at last line...
380 if self.GetCurrentPos() < self.getCurrentPromptStart():
380 if self.GetCurrentPos() < self.getCurrentPromptStart():
381 self.GotoPos(self.getCurrentPromptStart())
381 self.GotoPos(self.getCurrentPromptStart())
382
382
383 def removeFromTo(self, from_pos, to_pos):
383 def removeFromTo(self, from_pos, to_pos):
384 if from_pos < to_pos:
384 if from_pos < to_pos:
385 self.SetSelection(from_pos, to_pos)
385 self.SetSelection(from_pos, to_pos)
386 self.DeleteBack()
386 self.DeleteBack()
387
387
388 def removeCurrentLine(self):
388 def removeCurrentLine(self):
389 self.LineDelete()
389 self.LineDelete()
390
390
391 def moveCursor(self, position):
391 def moveCursor(self, position):
392 self.GotoPos(position)
392 self.GotoPos(position)
393
393
@@ -397,7 +397,7 b' class WxConsoleView(stc.StyledTextCtrl):'
397 def selectFromTo(self, from_pos, to_pos):
397 def selectFromTo(self, from_pos, to_pos):
398 self.SetSelectionStart(from_pos)
398 self.SetSelectionStart(from_pos)
399 self.SetSelectionEnd(to_pos)
399 self.SetSelectionEnd(to_pos)
400
400
401 def writeHistory(self, history):
401 def writeHistory(self, history):
402 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
402 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
403 self.changeLine(history)
403 self.changeLine(history)
@@ -410,19 +410,19 b' class WxConsoleView(stc.StyledTextCtrl):'
410
410
411 def getCompletionMethod(self, completion):
411 def getCompletionMethod(self, completion):
412 return self.autocomplete_mode
412 return self.autocomplete_mode
413
413
414 def writeCompletion(self, possibilities):
414 def writeCompletion(self, possibilities):
415 if self.autocomplete_mode == 'IPYTHON':
415 if self.autocomplete_mode == 'IPYTHON':
416 max_len = len(max(possibilities, key=len))
416 max_len = len(max(possibilities, key=len))
417 max_symbol = ' '*max_len
417 max_symbol = ' '*max_len
418
418
419 #now we check how much symbol we can put on a line...
419 #now we check how much symbol we can put on a line...
420 test_buffer = max_symbol + ' '*4
420 test_buffer = max_symbol + ' '*4
421
421
422 allowed_symbols = 80/len(test_buffer)
422 allowed_symbols = 80/len(test_buffer)
423 if allowed_symbols == 0:
423 if allowed_symbols == 0:
424 allowed_symbols = 1
424 allowed_symbols = 1
425
425
426 pos = 1
426 pos = 1
427 buf = ''
427 buf = ''
428 for symbol in possibilities:
428 for symbol in possibilities:
@@ -445,7 +445,7 b' class WxConsoleView(stc.StyledTextCtrl):'
445 for breaker in splitter:
445 for breaker in splitter:
446 last_word = last_word.split(breaker)[-1]
446 last_word = last_word.split(breaker)[-1]
447 self.AutoCompShow(len(last_word), " ".join(possibilities))
447 self.AutoCompShow(len(last_word), " ".join(possibilities))
448
448
449 def _onKeypress(self, event, skip=True):
449 def _onKeypress(self, event, skip=True):
450 '''
450 '''
451 Key press callback used for correcting behavior for console-like
451 Key press callback used for correcting behavior for console-like
@@ -476,7 +476,7 b' class WxConsoleView(stc.StyledTextCtrl):'
476 elif event.GetKeyCode() == wx.WXK_LEFT:
476 elif event.GetKeyCode() == wx.WXK_LEFT:
477 if event.Modifiers == wx.MOD_NONE:
477 if event.Modifiers == wx.MOD_NONE:
478 self.moveCursorOnNewValidKey()
478 self.moveCursorOnNewValidKey()
479
479
480 self.moveCursor(self.getCursorPos()-1)
480 self.moveCursor(self.getCursorPos()-1)
481 if self.getCursorPos() < self.getCurrentPromptStart():
481 if self.getCursorPos() < self.getCurrentPromptStart():
482 self.moveCursor(self.getCurrentPromptStart())
482 self.moveCursor(self.getCurrentPromptStart())
@@ -487,18 +487,18 b' class WxConsoleView(stc.StyledTextCtrl):'
487 if self.getCursorPos() > self.getCurrentPromptStart():
487 if self.getCursorPos() > self.getCurrentPromptStart():
488 event.Skip()
488 event.Skip()
489 return True
489 return True
490
490
491 if skip:
491 if skip:
492 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
492 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
493 and event.Modifiers == wx.MOD_NONE:
493 and event.Modifiers == wx.MOD_NONE:
494 self.moveCursorOnNewValidKey()
494 self.moveCursorOnNewValidKey()
495
495
496 event.Skip()
496 event.Skip()
497 return True
497 return True
498 return False
498 return False
499 else:
499 else:
500 event.Skip()
500 event.Skip()
501
501
502 def OnUpdateUI(self, evt):
502 def OnUpdateUI(self, evt):
503 # check for matching braces
503 # check for matching braces
504 braceAtCaret = -1
504 braceAtCaret = -1
@@ -533,19 +533,19 b' class WxConsoleView(stc.StyledTextCtrl):'
533 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
533 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
534 #print pt
534 #print pt
535 #self.Refresh(False)
535 #self.Refresh(False)
536
536
537 class IPShellWidget(wx.Panel):
537 class IPShellWidget(wx.Panel):
538 '''
538 '''
539 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
539 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
540 If you want to port this to any other GUI toolkit, just replace the
540 If you want to port this to any other GUI toolkit, just replace the
541 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
541 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
542 from whatever container you want. I've choosed to derivate from a wx.Panel
542 from whatever container you want. I've choosed to derivate from a wx.Panel
543 because it seems to be more useful
543 because it seems to be more useful
544 Any idea to make it more 'generic' welcomed.
544 Any idea to make it more 'generic' welcomed.
545 '''
545 '''
546
546
547 def __init__(self, parent, intro=None,
547 def __init__(self, parent, intro=None,
548 background_color="BLACK", add_button_handler=None,
548 background_color="BLACK", add_button_handler=None,
549 wx_ip_shell=None, user_ns={},user_global_ns=None,
549 wx_ip_shell=None, user_ns={},user_global_ns=None,
550 ):
550 ):
551 '''
551 '''
@@ -570,7 +570,7 b' class IPShellWidget(wx.Panel):'
570
570
571 ### IPython wx console view instanciation ###
571 ### IPython wx console view instanciation ###
572 #If user didn't defined an intro text, we create one for him
572 #If user didn't defined an intro text, we create one for him
573 #If you really wnat an empty intro just call wxIPythonViewPanel
573 #If you really wnat an empty intro just call wxIPythonViewPanel
574 #with intro=''
574 #with intro=''
575 if intro is None:
575 if intro is None:
576 welcome_text = "Welcome to WxIPython Shell.\n\n"
576 welcome_text = "Welcome to WxIPython Shell.\n\n"
@@ -598,7 +598,7 b' class IPShellWidget(wx.Panel):'
598 self.threading_option.SetToolTip(wx.ToolTip(
598 self.threading_option.SetToolTip(wx.ToolTip(
599 "Use threading: infinite loop don't freeze the GUI and commands can be breaked\nNo threading: maximum compatibility"))
599 "Use threading: infinite loop don't freeze the GUI and commands can be breaked\nNo threading: maximum compatibility"))
600 #self.threading_option.SetValue(False)
600 #self.threading_option.SetValue(False)
601
601
602 self.options={'completion':{'value':'IPYTHON',
602 self.options={'completion':{'value':'IPYTHON',
603 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
603 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
604 'setfunc':self.text_ctrl.setCompletionMethod},
604 'setfunc':self.text_ctrl.setCompletionMethod},
@@ -614,12 +614,12 b' class IPShellWidget(wx.Panel):'
614 self.cout.write = self.text_ctrl.asyncWrite
614 self.cout.write = self.text_ctrl.asyncWrite
615 #we reloard options
615 #we reloard options
616 self.reloadOptions(self.options)
616 self.reloadOptions(self.options)
617
617
618 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
618 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
619 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
619 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
620 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
620 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
621 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
621 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
622
622
623 ### making the layout of the panel ###
623 ### making the layout of the panel ###
624 sizer = wx.BoxSizer(wx.VERTICAL)
624 sizer = wx.BoxSizer(wx.VERTICAL)
625 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
625 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
@@ -648,7 +648,7 b' class IPShellWidget(wx.Panel):'
648
648
649 def askExitCallback(self, event):
649 def askExitCallback(self, event):
650 self.askExitHandler(event)
650 self.askExitHandler(event)
651
651
652 #---------------------- IPython Thread Management ------------------------
652 #---------------------- IPython Thread Management ------------------------
653 def stateDoExecuteLine(self):
653 def stateDoExecuteLine(self):
654 lines=self.text_ctrl.getCurrentLine()
654 lines=self.text_ctrl.getCurrentLine()
@@ -660,7 +660,7 b' class IPShellWidget(wx.Panel):'
660 if(self.text_ctrl.getCursorPos()!=0):
660 if(self.text_ctrl.getCursorPos()!=0):
661 self.text_ctrl.removeCurrentLine()
661 self.text_ctrl.removeCurrentLine()
662 self.setCurrentState('WAIT_END_OF_EXECUTION')
662 self.setCurrentState('WAIT_END_OF_EXECUTION')
663
663
664 def evtStateExecuteDone(self,evt):
664 def evtStateExecuteDone(self,evt):
665 self.doc = self.IP.get_doc_text()
665 self.doc = self.IP.get_doc_text()
666 self.help = self.IP.get_help_text()
666 self.help = self.IP.get_help_text()
@@ -673,7 +673,7 b' class IPShellWidget(wx.Panel):'
673 self.pager_lines = self.help.split('\n')
673 self.pager_lines = self.help.split('\n')
674 self.pager_state = 'INIT'
674 self.pager_state = 'INIT'
675 self.setCurrentState('SHOW_DOC')
675 self.setCurrentState('SHOW_DOC')
676 self.pager(self.help)
676 self.pager(self.help)
677 else:
677 else:
678 if(self.text_ctrl.getCursorPos()!=0):
678 if(self.text_ctrl.getCursorPos()!=0):
679 self.text_ctrl.removeCurrentLine()
679 self.text_ctrl.removeCurrentLine()
@@ -714,7 +714,7 b' class IPShellWidget(wx.Panel):'
714 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
714 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
715 else:
715 else:
716 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
716 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
717
717
718 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
718 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
719 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
719 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
720 self.pager_index += 10
720 self.pager_index += 10
@@ -730,7 +730,7 b' class IPShellWidget(wx.Panel):'
730 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
730 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
731 else:
731 else:
732 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
732 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
733
733
734 self.pager_index += 1
734 self.pager_index += 1
735 self.pager_nb_lines -= 1
735 self.pager_nb_lines -= 1
736 if self.pager_nb_lines > 0:
736 if self.pager_nb_lines > 0:
@@ -739,7 +739,7 b' class IPShellWidget(wx.Panel):'
739 self.pager_nb_lines = 0
739 self.pager_nb_lines = 0
740 self.pager_state = 'DONE'
740 self.pager_state = 'DONE'
741 self.stateShowPrompt()
741 self.stateShowPrompt()
742
742
743 #------------------------ Key Handler ------------------------------------
743 #------------------------ Key Handler ------------------------------------
744 def keyPress(self, event):
744 def keyPress(self, event):
745 '''
745 '''
@@ -752,7 +752,7 b' class IPShellWidget(wx.Panel):'
752 #we raise an exception inside the IPython thread container
752 #we raise an exception inside the IPython thread container
753 self.IP.ce.raise_exc(KeyboardInterrupt)
753 self.IP.ce.raise_exc(KeyboardInterrupt)
754 return
754 return
755
755
756 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
756 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
757 #mode if AutoComp has been set as inactive
757 #mode if AutoComp has been set as inactive
758 if self.cur_state == 'COMPLETING':
758 if self.cur_state == 'COMPLETING':
@@ -772,13 +772,13 b' class IPShellWidget(wx.Panel):'
772 self.pager_state = 'PROCESS_LINES'
772 self.pager_state = 'PROCESS_LINES'
773 self.pager(self.doc)
773 self.pager(self.doc)
774 return
774 return
775
775
776 if self.cur_state == 'WAITING_USER_INPUT':
776 if self.cur_state == 'WAITING_USER_INPUT':
777 line=self.text_ctrl.getCurrentLine()
777 line=self.text_ctrl.getCurrentLine()
778 self.text_ctrl.write('\n')
778 self.text_ctrl.write('\n')
779 self.setCurrentState('WAIT_END_OF_EXECUTION')
779 self.setCurrentState('WAIT_END_OF_EXECUTION')
780 return
780 return
781
781
782 if event.GetKeyCode() in [ord('q'),ord('Q')]:
782 if event.GetKeyCode() in [ord('q'),ord('Q')]:
783 if self.pager_state == 'WAITING':
783 if self.pager_state == 'WAITING':
784 self.pager_state = 'DONE'
784 self.pager_state = 'DONE'
@@ -787,8 +787,8 b' class IPShellWidget(wx.Panel):'
787 return
787 return
788
788
789 if self.cur_state == 'WAITING_USER_INPUT':
789 if self.cur_state == 'WAITING_USER_INPUT':
790 event.Skip()
790 event.Skip()
791
791
792 if self.cur_state == 'IDLE':
792 if self.cur_state == 'IDLE':
793 if event.KeyCode == wx.WXK_UP:
793 if event.KeyCode == wx.WXK_UP:
794 history = self.IP.history_back()
794 history = self.IP.history_back()
@@ -805,7 +805,7 b' class IPShellWidget(wx.Panel):'
805 return
805 return
806 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
806 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
807 if len(possibilities) > 1:
807 if len(possibilities) > 1:
808 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
808 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
809 cur_slice = self.text_ctrl.getCurrentLine()
809 cur_slice = self.text_ctrl.getCurrentLine()
810 self.text_ctrl.write('\n')
810 self.text_ctrl.write('\n')
811 self.text_ctrl.writeCompletion(possibilities)
811 self.text_ctrl.writeCompletion(possibilities)
@@ -855,31 +855,31 b' class IPShellWidget(wx.Panel):'
855 self.updateOptionTracker('threading',
855 self.updateOptionTracker('threading',
856 self.options['threading']['value'])
856 self.options['threading']['value'])
857 self.text_ctrl.SetFocus()
857 self.text_ctrl.SetFocus()
858
858
859 def getOptions(self):
859 def getOptions(self):
860 return self.options
860 return self.options
861
861
862 def reloadOptions(self,options):
862 def reloadOptions(self,options):
863 self.options = options
863 self.options = options
864 for key in self.options.keys():
864 for key in self.options.keys():
865 value = self.options[key]['value']
865 value = self.options[key]['value']
866 self.options[key]['checkbox'].SetValue(self.options[key][value])
866 self.options[key]['checkbox'].SetValue(self.options[key][value])
867 self.options[key]['setfunc'](value)
867 self.options[key]['setfunc'](value)
868
868
869 if self.options['threading']['value']=='True':
869 if self.options['threading']['value']=='True':
870 self.IP.set_threading(True)
870 self.IP.set_threading(True)
871 self.cout.write = self.text_ctrl.asyncWrite
871 self.cout.write = self.text_ctrl.asyncWrite
872 else:
872 else:
873 self.IP.set_threading(False)
873 self.IP.set_threading(False)
874 self.cout.write = self.text_ctrl.write
874 self.cout.write = self.text_ctrl.write
875
875
876 #------------------------ Hook Section -----------------------------------
876 #------------------------ Hook Section -----------------------------------
877 def updateOptionTracker(self,name,value):
877 def updateOptionTracker(self,name,value):
878 '''
878 '''
879 Default history tracker (does nothing)
879 Default history tracker (does nothing)
880 '''
880 '''
881 pass
881 pass
882
882
883 def setOptionTrackerHook(self,func):
883 def setOptionTrackerHook(self,func):
884 '''
884 '''
885 Define a new history tracker
885 Define a new history tracker
@@ -891,7 +891,7 b' class IPShellWidget(wx.Panel):'
891 Default history tracker (does nothing)
891 Default history tracker (does nothing)
892 '''
892 '''
893 pass
893 pass
894
894
895 def setHistoryTrackerHook(self,func):
895 def setHistoryTrackerHook(self,func):
896 '''
896 '''
897 Define a new history tracker
897 Define a new history tracker
@@ -903,7 +903,7 b' class IPShellWidget(wx.Panel):'
903 Default status tracker (does nothing)
903 Default status tracker (does nothing)
904 '''
904 '''
905 pass
905 pass
906
906
907 def setStatusTrackerHook(self,func):
907 def setStatusTrackerHook(self,func):
908 '''
908 '''
909 Define a new status tracker
909 Define a new status tracker
@@ -6,8 +6,8 b' raise_exc.'
6 import threading
6 import threading
7 import inspect
7 import inspect
8 import ctypes
8 import ctypes
9
9
10
10
11 def _async_raise(tid, exctype):
11 def _async_raise(tid, exctype):
12 """raises the exception, performs cleanup if needed"""
12 """raises the exception, performs cleanup if needed"""
13 if not inspect.isclass(exctype):
13 if not inspect.isclass(exctype):
@@ -16,35 +16,35 b' def _async_raise(tid, exctype):'
16 if res == 0:
16 if res == 0:
17 raise ValueError("invalid thread id")
17 raise ValueError("invalid thread id")
18 elif res != 1:
18 elif res != 1:
19 # """if it returns a number greater than one, you're in trouble,
19 # """if it returns a number greater than one, you're in trouble,
20 # and you should call it again with exc=NULL to revert the effect"""
20 # and you should call it again with exc=NULL to revert the effect"""
21 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
21 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
22 raise SystemError("PyThreadState_SetAsyncExc failed")
22 raise SystemError("PyThreadState_SetAsyncExc failed")
23
23
24
24
25 class ThreadEx(threading.Thread):
25 class ThreadEx(threading.Thread):
26 def _get_my_tid(self):
26 def _get_my_tid(self):
27 """determines this (self's) thread id"""
27 """determines this (self's) thread id"""
28 if not self.isAlive():
28 if not self.isAlive():
29 raise threading.ThreadError("the thread is not active")
29 raise threading.ThreadError("the thread is not active")
30
30
31 # do we have it cached?
31 # do we have it cached?
32 if hasattr(self, "_thread_id"):
32 if hasattr(self, "_thread_id"):
33 return self._thread_id
33 return self._thread_id
34
34
35 # no, look for it in the _active dict
35 # no, look for it in the _active dict
36 for tid, tobj in threading._active.items():
36 for tid, tobj in threading._active.items():
37 if tobj is self:
37 if tobj is self:
38 self._thread_id = tid
38 self._thread_id = tid
39 return tid
39 return tid
40
40
41 raise AssertionError("could not determine the thread's id")
41 raise AssertionError("could not determine the thread's id")
42
42
43 def raise_exc(self, exctype):
43 def raise_exc(self, exctype):
44 """raises the given exception type in the context of this thread"""
44 """raises the given exception type in the context of this thread"""
45 _async_raise(self._get_my_tid(), exctype)
45 _async_raise(self._get_my_tid(), exctype)
46
46
47 def kill(self):
47 def kill(self):
48 """raises SystemExit in the context of the given thread, which should
48 """raises SystemExit in the context of the given thread, which should
49 cause the thread to exit silently (unless caught)"""
49 cause the thread to exit silently (unless caught)"""
50 self.raise_exc(SystemExit)
50 self.raise_exc(SystemExit)
@@ -27,26 +27,26 b' __email__ = "laurent.dufrechou _at_ gmail.com"'
27 __license__ = "BSD"
27 __license__ = "BSD"
28
28
29 #-----------------------------------------
29 #-----------------------------------------
30 # Creating one main frame for our
30 # Creating one main frame for our
31 # application with movables windows
31 # application with movables windows
32 #-----------------------------------------
32 #-----------------------------------------
33 class MyFrame(wx.Frame):
33 class MyFrame(wx.Frame):
34 """Creating one main frame for our
34 """Creating one main frame for our
35 application with movables windows"""
35 application with movables windows"""
36 def __init__(self, parent=None, id=-1, title="WxIPython",
36 def __init__(self, parent=None, id=-1, title="WxIPython",
37 pos=wx.DefaultPosition,
37 pos=wx.DefaultPosition,
38 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE, sync_ok=False):
38 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE, sync_ok=False):
39 wx.Frame.__init__(self, parent, id, title, pos, size, style)
39 wx.Frame.__init__(self, parent, id, title, pos, size, style)
40 self._mgr = wx.aui.AuiManager()
40 self._mgr = wx.aui.AuiManager()
41
41
42 # notify PyAUI which frame to use
42 # notify PyAUI which frame to use
43 self._mgr.SetManagedWindow(self)
43 self._mgr.SetManagedWindow(self)
44
44
45 #create differents panels and make them persistant
45 #create differents panels and make them persistant
46 self.history_panel = IPythonHistoryPanel(self)
46 self.history_panel = IPythonHistoryPanel(self)
47
47
48 self.history_panel.setOptionTrackerHook(self.optionSave)
48 self.history_panel.setOptionTrackerHook(self.optionSave)
49
49
50 self.ipython_panel = IPShellWidget(self,background_color = "BLACK")
50 self.ipython_panel = IPShellWidget(self,background_color = "BLACK")
51 #self.ipython_panel = IPShellWidget(self,background_color = "WHITE")
51 #self.ipython_panel = IPShellWidget(self,background_color = "WHITE")
52 if(sync_ok):
52 if(sync_ok):
@@ -60,12 +60,12 b' class MyFrame(wx.Frame):'
60
60
61 #Create a notebook to display different IPython shell implementations
61 #Create a notebook to display different IPython shell implementations
62 self.nb = wx.aui.AuiNotebook(self)
62 self.nb = wx.aui.AuiNotebook(self)
63
63
64 self.optionLoad()
64 self.optionLoad()
65
65
66 self.statusbar = self.createStatus()
66 self.statusbar = self.createStatus()
67 self.createMenu()
67 self.createMenu()
68
68
69 ########################################################################
69 ########################################################################
70 ### add the panes to the manager
70 ### add the panes to the manager
71 # main panels
71 # main panels
@@ -75,12 +75,12 b' class MyFrame(wx.Frame):'
75 self.nb.AddPage(self.ipython_panel2, "IPython1 Synchroneous Shell")
75 self.nb.AddPage(self.ipython_panel2, "IPython1 Synchroneous Shell")
76
76
77 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
77 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
78
78
79 # now we specify some panel characteristics
79 # now we specify some panel characteristics
80 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
80 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
81 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
81 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
82 self._mgr.GetPane(self.history_panel).MinSize((200,400));
82 self._mgr.GetPane(self.history_panel).MinSize((200,400));
83
83
84 # tell the manager to "commit" all the changes just made
84 # tell the manager to "commit" all the changes just made
85 self._mgr.Update()
85 self._mgr.Update()
86
86
@@ -91,14 +91,14 b' class MyFrame(wx.Frame):'
91 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
91 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
92 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
92 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
93 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
93 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
94
94
95 warn_text = 'Hello from IPython and wxPython.\n'
95 warn_text = 'Hello from IPython and wxPython.\n'
96 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
96 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
97 warn_text +='It does NOT emulate currently all the IPython functions.\n'
97 warn_text +='It does NOT emulate currently all the IPython functions.\n'
98 warn_text +="\nIf you use MATPLOTLIB with show() you'll need to deactivate the THREADING option.\n"
98 warn_text +="\nIf you use MATPLOTLIB with show() you'll need to deactivate the THREADING option.\n"
99 if(not sync_ok):
99 if(not sync_ok):
100 warn_text +="\n->No twisted package detected, IPython1 example deactivated."
100 warn_text +="\n->No twisted package detected, IPython1 example deactivated."
101
101
102 dlg = wx.MessageDialog(self,
102 dlg = wx.MessageDialog(self,
103 warn_text,
103 warn_text,
104 'Warning Box',
104 'Warning Box',
@@ -120,9 +120,9 b' class MyFrame(wx.Frame):'
120 opt.write(key + '=' + options_ipython_panel[key]['value']+'\n')
120 opt.write(key + '=' + options_ipython_panel[key]['value']+'\n')
121 for key in options_history_panel.keys():
121 for key in options_history_panel.keys():
122 opt.write(key + '=' + options_history_panel[key]['value']+'\n')
122 opt.write(key + '=' + options_history_panel[key]['value']+'\n')
123 finally:
123 finally:
124 opt.close()
124 opt.close()
125
125
126 def optionLoad(self):
126 def optionLoad(self):
127 try:
127 try:
128 ip = get()
128 ip = get()
@@ -130,10 +130,10 b' class MyFrame(wx.Frame):'
130 opt = open(path + '/options.conf','r')
130 opt = open(path + '/options.conf','r')
131 lines = opt.readlines()
131 lines = opt.readlines()
132 opt.close()
132 opt.close()
133
133
134 options_ipython_panel = self.ipython_panel.getOptions()
134 options_ipython_panel = self.ipython_panel.getOptions()
135 options_history_panel = self.history_panel.getOptions()
135 options_history_panel = self.history_panel.getOptions()
136
136
137 for line in lines:
137 for line in lines:
138 key = line.split('=')[0]
138 key = line.split('=')[0]
139 value = line.split('=')[1].replace('\n','').replace('\r','')
139 value = line.split('=')[1].replace('\n','').replace('\r','')
@@ -145,25 +145,25 b' class MyFrame(wx.Frame):'
145 print >>sys.__stdout__,"Warning: key ",key,"not found in widget options. Check Options.conf"
145 print >>sys.__stdout__,"Warning: key ",key,"not found in widget options. Check Options.conf"
146 self.ipython_panel.reloadOptions(options_ipython_panel)
146 self.ipython_panel.reloadOptions(options_ipython_panel)
147 self.history_panel.reloadOptions(options_history_panel)
147 self.history_panel.reloadOptions(options_history_panel)
148
148
149 except IOError:
149 except IOError:
150 print >>sys.__stdout__,"Could not open Options.conf, defaulting to default values."
150 print >>sys.__stdout__,"Could not open Options.conf, defaulting to default values."
151
151
152
152
153 def createMenu(self):
153 def createMenu(self):
154 """local method used to create one menu bar"""
154 """local method used to create one menu bar"""
155
155
156 mb = wx.MenuBar()
156 mb = wx.MenuBar()
157
157
158 file_menu = wx.Menu()
158 file_menu = wx.Menu()
159 file_menu.Append(wx.ID_EXIT, "Exit")
159 file_menu.Append(wx.ID_EXIT, "Exit")
160
160
161 view_menu = wx.Menu()
161 view_menu = wx.Menu()
162 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
162 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
163 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
163 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
164 view_menu.AppendSeparator()
164 view_menu.AppendSeparator()
165 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
165 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
166
166
167 about_menu = wx.Menu()
167 about_menu = wx.Menu()
168 about_menu.Append(wx.ID_HIGHEST+3, "About")
168 about_menu.Append(wx.ID_HIGHEST+3, "About")
169
169
@@ -171,7 +171,7 b' class MyFrame(wx.Frame):'
171 mb.Append(view_menu, "View")
171 mb.Append(view_menu, "View")
172 mb.Append(about_menu, "About")
172 mb.Append(about_menu, "About")
173 #mb.Append(options_menu, "Options")
173 #mb.Append(options_menu, "Options")
174
174
175 self.SetMenuBar(mb)
175 self.SetMenuBar(mb)
176
176
177 def createStatus(self):
177 def createStatus(self):
@@ -189,14 +189,14 b' class MyFrame(wx.Frame):'
189 'SHOW_DOC':'Showing doc',
189 'SHOW_DOC':'Showing doc',
190 'SHOW_PROMPT':'Showing prompt'}
190 'SHOW_PROMPT':'Showing prompt'}
191 self.statusbar.SetStatusText(states[text], 0)
191 self.statusbar.SetStatusText(states[text], 0)
192
192
193 def OnClose(self, event):
193 def OnClose(self, event):
194 """#event used to close program """
194 """#event used to close program """
195 # deinitialize the frame manager
195 # deinitialize the frame manager
196 self._mgr.UnInit()
196 self._mgr.UnInit()
197 self.Destroy()
197 self.Destroy()
198 event.Skip()
198 event.Skip()
199
199
200 def OnExitDlg(self, event):
200 def OnExitDlg(self, event):
201 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
201 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
202 'WxIPython exit',
202 'WxIPython exit',
@@ -208,17 +208,17 b' class MyFrame(wx.Frame):'
208 self._mgr.UnInit()
208 self._mgr.UnInit()
209 self.Destroy()
209 self.Destroy()
210 dlg.Destroy()
210 dlg.Destroy()
211
211
212 #event to display IPython pannel
212 #event to display IPython pannel
213 def OnShowIPythonPanel(self,event):
213 def OnShowIPythonPanel(self,event):
214 """ #event to display Boxpannel """
214 """ #event to display Boxpannel """
215 self._mgr.GetPane(self.ipython_panel).Show(True)
215 self._mgr.GetPane(self.ipython_panel).Show(True)
216 self._mgr.Update()
216 self._mgr.Update()
217 #event to display History pannel
217 #event to display History pannel
218 def OnShowHistoryPanel(self,event):
218 def OnShowHistoryPanel(self,event):
219 self._mgr.GetPane(self.history_panel).Show(True)
219 self._mgr.GetPane(self.history_panel).Show(True)
220 self._mgr.Update()
220 self._mgr.Update()
221
221
222 def OnShowAllPanel(self,event):
222 def OnShowAllPanel(self,event):
223 """#event to display all Pannels"""
223 """#event to display all Pannels"""
224 self._mgr.GetPane(self.ipython_panel).Show(True)
224 self._mgr.GetPane(self.ipython_panel).Show(True)
@@ -240,22 +240,22 b' class MyFrame(wx.Frame):'
240 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
240 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
241
241
242 # Then we call wx.AboutBox giving it that info object
242 # Then we call wx.AboutBox giving it that info object
243 wx.AboutBox(info)
243 wx.AboutBox(info)
244
244
245 #-----------------------------------------
245 #-----------------------------------------
246 #Creating our application
246 #Creating our application
247 #-----------------------------------------
247 #-----------------------------------------
248 class MyApp(wx.PySimpleApp):
248 class MyApp(wx.PySimpleApp):
249 """Creating our application"""
249 """Creating our application"""
250 def __init__(self, sync_ok=False):
250 def __init__(self, sync_ok=False):
251 wx.PySimpleApp.__init__(self)
251 wx.PySimpleApp.__init__(self)
252
252
253 self.frame = MyFrame(sync_ok=sync_ok)
253 self.frame = MyFrame(sync_ok=sync_ok)
254 self.frame.Show()
254 self.frame.Show()
255
255
256 #-----------------------------------------
256 #-----------------------------------------
257 #Main loop
257 #Main loop
258 #-----------------------------------------
258 #-----------------------------------------
259 def main():
259 def main():
260 app = MyApp(is_sync_frontend_ok)
260 app = MyApp(is_sync_frontend_ok)
261 app.SetTopWindow(app.frame)
261 app.SetTopWindow(app.frame)
@@ -3,18 +3,18 b''
3
3
4 Definition of Fundamental Physical Constants, CODATA Recommended Values
4 Definition of Fundamental Physical Constants, CODATA Recommended Values
5
5
6 Source, Peter J. Mohr and Barry N. Taylor,
6 Source, Peter J. Mohr and Barry N. Taylor,
7 CODATA Recommended Values of the Fundamental
7 CODATA Recommended Values of the Fundamental
8 Physical Constants, 1998
8 Physical Constants, 1998
9
9
10 Website: physics.nist.gov/constants
10 Website: physics.nist.gov/constants
11 """
11 """
12 # License: BSD-like
12 # License: BSD-like
13 # Copyright: Gael Varoquaux (gael.varoquaux@normalesup.org)
13 # Copyright: Gael Varoquaux (gael.varoquaux@normalesup.org)
14
14
15 # inspired by maxima's physconst.mac by Cliff Yapp
15 # inspired by maxima's physconst.mac by Cliff Yapp
16
16
17 #from math import * # math MUST be imported BEFORE PhysicalQInteractive
17 #from math import * # math MUST be imported BEFORE PhysicalQInteractive
18 from IPython.extensions.PhysicalQInteractive import PhysicalQuantityInteractive
18 from IPython.extensions.PhysicalQInteractive import PhysicalQuantityInteractive
19
19
20 # Math constants:
20 # Math constants:
@@ -111,7 +111,7 b' ueVT_N.__doc__ = """nuclear magneton in eV T-1 """'
111 # Atomic and Nuclear Constants
111 # Atomic and Nuclear Constants
112 # General
112 # General
113 #-------------------------------------------------------------------------
113 #-------------------------------------------------------------------------
114 # fine-structure constant
114 # fine-structure constant
115 alpha = 7.297352533E-3
115 alpha = 7.297352533E-3
116
116
117
117
@@ -163,19 +163,19 b' me_MeV.__doc__ = """electron mass - energy equivalent in MeV"""'
163 # electron-muon mass ratio
163 # electron-muon mass ratio
164 memu = 4.83633210E-3
164 memu = 4.83633210E-3
165
165
166 # electron-tau mass ratio
166 # electron-tau mass ratio
167 metau = 2.87555E-4
167 metau = 2.87555E-4
168
168
169 # electron-proton mass ratio
169 # electron-proton mass ratio
170 memp = 5.446170232E-4
170 memp = 5.446170232E-4
171
171
172 # electron-neutron mass ratio
172 # electron-neutron mass ratio
173 memn = 5.438673462E-4
173 memn = 5.438673462E-4
174
174
175 # electron-deuteron mass ratio
175 # electron-deuteron mass ratio
176 memd = 2.7244371170E-4
176 memd = 2.7244371170E-4
177
177
178 # electron to alpha particle mass ratio
178 # electron to alpha particle mass ratio
179 memalpha = 1.3709335611E-4
179 memalpha = 1.3709335611E-4
180
180
181
181
@@ -202,31 +202,31 b' sigma_e.__doc__ = """Thomson cross section """'
202 u_e = PhysicalQuantityInteractive(-928.476362E-26 , 'J/T')
202 u_e = PhysicalQuantityInteractive(-928.476362E-26 , 'J/T')
203 u_e.__doc__ = """electron magnetic moment """
203 u_e.__doc__ = """electron magnetic moment """
204
204
205 # electron magnetic moment to Bohr magneton ratio
205 # electron magnetic moment to Bohr magneton ratio
206 ueuB = -1.0011596521869
206 ueuB = -1.0011596521869
207
207
208 # electron magnetic moment to nuclear magneton ratio
208 # electron magnetic moment to nuclear magneton ratio
209 ueuN = -1838.2819660
209 ueuN = -1838.2819660
210
210
211 # electron magnetic moment anomaly |ue|/uB - 1
211 # electron magnetic moment anomaly |ue|/uB - 1
212 a_e = 1.1596521869E-3
212 a_e = 1.1596521869E-3
213
213
214 # electron g-factor
214 # electron g-factor
215 g_e = -2.0023193043737
215 g_e = -2.0023193043737
216
216
217 # electron-muon magnetic moment ratio
217 # electron-muon magnetic moment ratio
218 ueuu = 206.7669720
218 ueuu = 206.7669720
219
219
220 # electron-proton magnetic moment ratio
220 # electron-proton magnetic moment ratio
221 ueup = -658.2106875
221 ueup = -658.2106875
222
222
223 # electron to shielded proton magnetic moment ratio (H2O, sphere, 25 C)
223 # electron to shielded proton magnetic moment ratio (H2O, sphere, 25 C)
224 ueusp = -658.2275954
224 ueusp = -658.2275954
225
225
226 # electron-neutron magnetic moment ratio
226 # electron-neutron magnetic moment ratio
227 ueun = 960.92050
227 ueun = 960.92050
228
228
229 # electron-deuteron magnetic moment ratio
229 # electron-deuteron magnetic moment ratio
230 ueud = -2143.923498
230 ueud = -2143.923498
231
231
232 # electron to shielded helione magnetic moment ratio (gas, sphere, 25 C)
232 # electron to shielded helione magnetic moment ratio (gas, sphere, 25 C)
@@ -252,7 +252,7 b' muc2_J.__doc__ = """energy equivalent """'
252 muc2_MeV = PhysicalQuantityInteractive(105.6583568 , 'MeV')
252 muc2_MeV = PhysicalQuantityInteractive(105.6583568 , 'MeV')
253 muc2_MeV.__doc__ = """energy equivalent in MeV """
253 muc2_MeV.__doc__ = """energy equivalent in MeV """
254
254
255 # muon-electron mass ratio
255 # muon-electron mass ratio
256 mume = 206.7682657
256 mume = 206.7682657
257
257
258 # muon-tau mass ratio
258 # muon-tau mass ratio
@@ -261,7 +261,7 b' mum = 5.94572E-2'
261 # muon-proton mass ratio
261 # muon-proton mass ratio
262 mump = 0.1126095173
262 mump = 0.1126095173
263
263
264 # muon-neutron mass ratio
264 # muon-neutron mass ratio
265 mumn = 0.1124545079
265 mumn = 0.1124545079
266
266
267
267
@@ -276,19 +276,19 b' lambda_C_u.__doc__ = """muon Compton wavelength """'
276 uu = PhysicalQuantityInteractive(-4.49044813E-26 , 'J/T')
276 uu = PhysicalQuantityInteractive(-4.49044813E-26 , 'J/T')
277 uu.__doc__ = """muon magnetic moment """
277 uu.__doc__ = """muon magnetic moment """
278
278
279 # ratio of muon magnetic moment to Bohr magneton ratio
279 # ratio of muon magnetic moment to Bohr magneton ratio
280 uuuB = -4.84197085E-3
280 uuuB = -4.84197085E-3
281
281
282 # ratio of muon magnetic moment to nuclear magneton ratio
282 # ratio of muon magnetic moment to nuclear magneton ratio
283 uuuN = -8.89059770
283 uuuN = -8.89059770
284
284
285 # muon magnetic moment anomaly |uu|/(e /2mu) - 1
285 # muon magnetic moment anomaly |uu|/(e /2mu) - 1
286 a_u = 1.16591602E-3
286 a_u = 1.16591602E-3
287
287
288 # muon g-factor -2(1 + au)
288 # muon g-factor -2(1 + au)
289 g_u = -2.0023318320
289 g_u = -2.0023318320
290
290
291 # muon-proton magnetic moment ratio
291 # muon-proton magnetic moment ratio
292 uuup = -3.18334539
292 uuup = -3.18334539
293
293
294 # Tau, tau-
294 # Tau, tau-
@@ -308,16 +308,16 b' mtauc2_J.__doc__ = """tau mass energy equivalent """'
308 mtauc2_MeV = PhysicalQuantityInteractive(1777.05 , 'MeV')
308 mtauc2_MeV = PhysicalQuantityInteractive(1777.05 , 'MeV')
309 mtauc2_MeV.__doc__ = """tau mass energy equivalent in MeV """
309 mtauc2_MeV.__doc__ = """tau mass energy equivalent in MeV """
310
310
311 # tau-electron mass ratio
311 # tau-electron mass ratio
312 mtaume = 3477.60
312 mtaume = 3477.60
313
313
314 # tau-muon mass ratio
314 # tau-muon mass ratio
315 mtaumu = 16.8188
315 mtaumu = 16.8188
316
316
317 # tau-proton mass ratio
317 # tau-proton mass ratio
318 mtaump = 1.89396
318 mtaump = 1.89396
319
319
320 # tau-neutron mass ratio
320 # tau-neutron mass ratio
321 mtaumn = 1.89135
321 mtaumn = 1.89135
322
322
323
323
@@ -344,16 +344,16 b' mpc2_J.__doc__ = """energy equivalent """'
344 mpc2_MeV = PhysicalQuantityInteractive(938.271998 , 'MeV')
344 mpc2_MeV = PhysicalQuantityInteractive(938.271998 , 'MeV')
345 mpc2_MeV.__doc__ = """energy equivalent in MeV """
345 mpc2_MeV.__doc__ = """energy equivalent in MeV """
346
346
347 # proton-electron mass ratio
347 # proton-electron mass ratio
348 mpme = 1836.1526675
348 mpme = 1836.1526675
349
349
350 # proton-muon mass ratio
350 # proton-muon mass ratio
351 mpmu = 8.88024408
351 mpmu = 8.88024408
352
352
353 # proton-tau mass ratio
353 # proton-tau mass ratio
354 mpmtau = 0.527994
354 mpmtau = 0.527994
355
355
356 # proton-neutron mass ratio
356 # proton-neutron mass ratio
357 mpmn = 0.99862347855
357 mpmn = 0.99862347855
358
358
359
359
@@ -372,26 +372,26 b' lambda_C_p.__doc__ = """proton Compton wavelength h/mpc """'
372 up = PhysicalQuantityInteractive(1.410606633E-26 , 'J/T')
372 up = PhysicalQuantityInteractive(1.410606633E-26 , 'J/T')
373 up.__doc__ = """proton magnetic moment """
373 up.__doc__ = """proton magnetic moment """
374
374
375 # proton magnetic moment to Bohr magneton ratio
375 # proton magnetic moment to Bohr magneton ratio
376 upuB = 1.521032203E-3
376 upuB = 1.521032203E-3
377
377
378 # proton magnetic moment to nuclear magneton ratio
378 # proton magnetic moment to nuclear magneton ratio
379 upuN = 2.792847337
379 upuN = 2.792847337
380
380
381 # proton g-factor 2up/uN
381 # proton g-factor 2up/uN
382 g_p = 5.585694675
382 g_p = 5.585694675
383
383
384 # proton-neutron magnetic moment ratio
384 # proton-neutron magnetic moment ratio
385 upun = -1.45989805
385 upun = -1.45989805
386
386
387
387
388 usp = PhysicalQuantityInteractive(1.410570399E-26 , 'J/T')
388 usp = PhysicalQuantityInteractive(1.410570399E-26 , 'J/T')
389 usp.__doc__ = """shielded proton magnetic moment (H2O, sphere, 25 C)"""
389 usp.__doc__ = """shielded proton magnetic moment (H2O, sphere, 25 C)"""
390
390
391 # shielded proton magnetic moment to Bohr magneton ratio
391 # shielded proton magnetic moment to Bohr magneton ratio
392 uspuB = 1.520993132E-3
392 uspuB = 1.520993132E-3
393
393
394 # shielded proton magnetic moment to nuclear magneton ratio
394 # shielded proton magnetic moment to nuclear magneton ratio
395 uspuN = 2.792775597
395 uspuN = 2.792775597
396
396
397 # proton magnetic shielding correction 1 - u p/up (H2O, sphere, 25 C)
397 # proton magnetic shielding correction 1 - u p/up (H2O, sphere, 25 C)
@@ -422,16 +422,16 b' mnc2_J.__doc__ = """neutron mass energy equivalent """'
422 mnc2_MeV = PhysicalQuantityInteractive(939.565330 , 'MeV')
422 mnc2_MeV = PhysicalQuantityInteractive(939.565330 , 'MeV')
423 mnc2_MeV.__doc__ = """neutron mass energy equivalent in MeV """
423 mnc2_MeV.__doc__ = """neutron mass energy equivalent in MeV """
424
424
425 # neutron-electron mass ratio
425 # neutron-electron mass ratio
426 mnme = 1838.6836550
426 mnme = 1838.6836550
427
427
428 # neutron-muon mass ratio
428 # neutron-muon mass ratio
429 mnmu = 8.89248478
429 mnmu = 8.89248478
430
430
431 # neutron-tau mass ratio
431 # neutron-tau mass ratio
432 mnm = 0.528722
432 mnm = 0.528722
433
433
434 # neutron-proton mass ratio
434 # neutron-proton mass ratio
435 mnmp = 1.00137841887
435 mnmp = 1.00137841887
436
436
437
437
@@ -446,19 +446,19 b' lambda_C_n.__doc__ = """neutron Compton wavelength"""'
446 un = PhysicalQuantityInteractive(-0.96623640E-26 , 'J/T')
446 un = PhysicalQuantityInteractive(-0.96623640E-26 , 'J/T')
447 un.__doc__ = """neutron magnetic moment """
447 un.__doc__ = """neutron magnetic moment """
448
448
449 # neutron magnetic moment to Bohr magneton ratio
449 # neutron magnetic moment to Bohr magneton ratio
450 unuB = -1.04187563E-3
450 unuB = -1.04187563E-3
451
451
452 # neutron magnetic moment to nuclear magneton ratio
452 # neutron magnetic moment to nuclear magneton ratio
453 unuN = -1.91304272
453 unuN = -1.91304272
454
454
455 # neutron g-factor
455 # neutron g-factor
456 g_n = -3.82608545
456 g_n = -3.82608545
457
457
458 # neutron-electron magnetic moment ratio
458 # neutron-electron magnetic moment ratio
459 unue = 1.04066882E-3
459 unue = 1.04066882E-3
460
460
461 # neutron-proton magnetic moment ratio
461 # neutron-proton magnetic moment ratio
462 unup = -0.68497934
462 unup = -0.68497934
463
463
464 # neutron to shielded proton magnetic moment ratio (H2O, sphere, 25 C)
464 # neutron to shielded proton magnetic moment ratio (H2O, sphere, 25 C)
@@ -486,10 +486,10 b' mdc2_J.__doc__ = """deuteron mass energy equivalent """'
486 mdc2_eV = PhysicalQuantityInteractive(1875.612762 , 'MeV')
486 mdc2_eV = PhysicalQuantityInteractive(1875.612762 , 'MeV')
487 mdc2_eV.__doc__ = """deuteron mass energy equivalent in MeV """
487 mdc2_eV.__doc__ = """deuteron mass energy equivalent in MeV """
488
488
489 # deuteron-electron mass ratio
489 # deuteron-electron mass ratio
490 mdme = 3670.4829550
490 mdme = 3670.4829550
491
491
492 # deuteron-proton mass ratio
492 # deuteron-proton mass ratio
493 mdmp = 1.99900750083
493 mdmp = 1.99900750083
494
494
495
495
@@ -500,19 +500,19 b' Molar_d.__doc__ = """deuteron molar mass """'
500 ud = PhysicalQuantityInteractive(0.433073457E-26 , 'J/T')
500 ud = PhysicalQuantityInteractive(0.433073457E-26 , 'J/T')
501 ud.__doc__ = """deuteron magnetic moment """
501 ud.__doc__ = """deuteron magnetic moment """
502
502
503 # deuteron magnetic moment to Bohr magneton ratio
503 # deuteron magnetic moment to Bohr magneton ratio
504 uduB = 0.4669754556E-3
504 uduB = 0.4669754556E-3
505
505
506 # deuteron magnetic moment to nuclear magneton ratio
506 # deuteron magnetic moment to nuclear magneton ratio
507 uduN = 0.8574382284
507 uduN = 0.8574382284
508
508
509 # deuteron-electron magnetic moment ratio
509 # deuteron-electron magnetic moment ratio
510 udue = -4.664345537E-4
510 udue = -4.664345537E-4
511
511
512 # deuteron-proton magnetic moment ratio
512 # deuteron-proton magnetic moment ratio
513 udup = 0.3070122083
513 udup = 0.3070122083
514
514
515 # deuteron-neutron magnetic moment ratio
515 # deuteron-neutron magnetic moment ratio
516 udun = -0.44820652
516 udun = -0.44820652
517
517
518 # Helion, h
518 # Helion, h
@@ -532,10 +532,10 b' mhc2_J.__doc__ = """helion mass energy equivalent """'
532 mhc2_MeV = PhysicalQuantityInteractive(2808.39132 , 'MeV')
532 mhc2_MeV = PhysicalQuantityInteractive(2808.39132 , 'MeV')
533 mhc2_MeV.__doc__ = """helion mass energy equivalent in MeV """
533 mhc2_MeV.__doc__ = """helion mass energy equivalent in MeV """
534
534
535 # helion-electron mass ratio
535 # helion-electron mass ratio
536 mhme = 5495.885238
536 mhme = 5495.885238
537
537
538 # helion-proton mass ratio
538 # helion-proton mass ratio
539 mhmp = 2.99315265850
539 mhmp = 2.99315265850
540
540
541
541
@@ -546,10 +546,10 b' Molar_h.__doc__ = """helion molar mass """'
546 ush = PhysicalQuantityInteractive(-1.074552967E-26 , 'J/T')
546 ush = PhysicalQuantityInteractive(-1.074552967E-26 , 'J/T')
547 ush.__doc__ = """shielded helion magnetic moment (gas, sphere, 25 C)"""
547 ush.__doc__ = """shielded helion magnetic moment (gas, sphere, 25 C)"""
548
548
549 # shielded helion magnetic moment to Bohr magneton ratio
549 # shielded helion magnetic moment to Bohr magneton ratio
550 ushuB = -1.158671474E-3
550 ushuB = -1.158671474E-3
551
551
552 # shielded helion magnetic moment to nuclear magneton ratio
552 # shielded helion magnetic moment to nuclear magneton ratio
553 ushuN = -2.127497718
553 ushuN = -2.127497718
554
554
555 # shielded helion to proton magnetic moment ratio (gas, sphere, 25 C)
555 # shielded helion to proton magnetic moment ratio (gas, sphere, 25 C)
@@ -562,7 +562,7 b' ushusp = -0.7617861313'
562 gamma_h = PhysicalQuantityInteractive(2.037894764E8 , '1/(s*T)')
562 gamma_h = PhysicalQuantityInteractive(2.037894764E8 , '1/(s*T)')
563 gamma_h.__doc__ = """shielded helion gyromagnetic (gas, sphere, 25 C) """
563 gamma_h.__doc__ = """shielded helion gyromagnetic (gas, sphere, 25 C) """
564
564
565 # Alpha particle,
565 # Alpha particle,
566 #-------------------------------------------------------------------------
566 #-------------------------------------------------------------------------
567
567
568 m_alpha = PhysicalQuantityInteractive(6.64465598E-27 , 'kg')
568 m_alpha = PhysicalQuantityInteractive(6.64465598E-27 , 'kg')
@@ -579,10 +579,10 b' malphac2_J.__doc__ = """alpha particle mass energy equivalent """'
579 malphac2_MeV = PhysicalQuantityInteractive(3727.37904 , 'MeV')
579 malphac2_MeV = PhysicalQuantityInteractive(3727.37904 , 'MeV')
580 malphac2_MeV.__doc__ = """alpha particle mass energy equivalent in MeV """
580 malphac2_MeV.__doc__ = """alpha particle mass energy equivalent in MeV """
581
581
582 # alpha particle to electron mass ratio
582 # alpha particle to electron mass ratio
583 malphame = 7294.299508
583 malphame = 7294.299508
584
584
585 # alpha particle to proton mass ratio
585 # alpha particle to proton mass ratio
586 malphamp = 3.9725996846
586 malphamp = 3.9725996846
587
587
588
588
@@ -642,9 +642,9 b" Vm_2 = PhysicalQuantityInteractive(22.710981E-3 , 'm**3/mol')"
642 Vm_2.__doc__ = """molar volume of ideal gas RT/p T = 273.15 K, p = 100 kPa """
642 Vm_2.__doc__ = """molar volume of ideal gas RT/p T = 273.15 K, p = 100 kPa """
643
643
644 # Sackur-Tetrode constant (absolute entropy constant) 52 + ln_(2 mukT1/h2)3/2kT1/p0
644 # Sackur-Tetrode constant (absolute entropy constant) 52 + ln_(2 mukT1/h2)3/2kT1/p0
645 # T1 = 1 K, p0 = 100 kPa
645 # T1 = 1 K, p0 = 100 kPa
646 S_0R_1 = -1.1517048
646 S_0R_1 = -1.1517048
647 # T1 = 1 K, p0 = 101.325 kPa
647 # T1 = 1 K, p0 = 101.325 kPa
648 S_0R_2 = -1.1648678
648 S_0R_2 = -1.1648678
649
649
650
650
@@ -1,10 +1,10 b''
1 """ Set default options for IPython.
1 """ Set default options for IPython.
2
2
3 Just import this module to get reasonable defaults for everything.
3 Just import this module to get reasonable defaults for everything.
4
4
5 These configurations used to be performed in ipythonrc (or ipythonrc.ini).
5 These configurations used to be performed in ipythonrc (or ipythonrc.ini).
6 Therefore importing this in your config files makes ipython basically
6 Therefore importing this in your config files makes ipython basically
7 ignore your ipythonrc. This is *not* imported by default, you need to import
7 ignore your ipythonrc. This is *not* imported by default, you need to import
8 this manually in one of your config files.
8 this manually in one of your config files.
9
9
10 You can further override these defaults in e.g. your ipy_user_config.py,
10 You can further override these defaults in e.g. your ipy_user_config.py,
@@ -58,5 +58,5 b' set show-all-if-ambiguous on'
58 if readline.have_readline:
58 if readline.have_readline:
59 for cmd in rlopts.split('\n'):
59 for cmd in rlopts.split('\n'):
60 readline.parse_and_bind(cmd)
60 readline.parse_and_bind(cmd)
61
61
62
62
@@ -2,7 +2,7 b''
2
2
3 Start ipython in shell mode by invoking "ipython -p sh"
3 Start ipython in shell mode by invoking "ipython -p sh"
4
4
5 (the old version, "ipython -p pysh" still works but this is the more "modern"
5 (the old version, "ipython -p pysh" still works but this is the more "modern"
6 shell mode and is recommended for users who don't care about pysh-mode
6 shell mode and is recommended for users who don't care about pysh-mode
7 compatibility)
7 compatibility)
8 """
8 """
@@ -16,29 +16,29 b' import os,re,textwrap'
16
16
17 import ipy_defaults
17 import ipy_defaults
18
18
19 def main():
19 def main():
20 ip = ipapi.get()
20 ip = ipapi.get()
21 o = ip.options
21 o = ip.options
22 # autocall to "full" mode (smart mode is default, I like full mode)
22 # autocall to "full" mode (smart mode is default, I like full mode)
23
23
24 o.autocall = 2
24 o.autocall = 2
25
25
26 # Jason Orendorff's path class is handy to have in user namespace
26 # Jason Orendorff's path class is handy to have in user namespace
27 # if you are doing shell-like stuff
27 # if you are doing shell-like stuff
28 try:
28 try:
29 ip.ex("from IPython.external.path import path" )
29 ip.ex("from IPython.external.path import path" )
30 except ImportError:
30 except ImportError:
31 pass
31 pass
32
32
33 # beefed up %env is handy in shell mode
33 # beefed up %env is handy in shell mode
34 import envpersist
34 import envpersist
35
35
36 # To see where mycmd resides (in path/aliases), do %which mycmd
36 # To see where mycmd resides (in path/aliases), do %which mycmd
37 import ipy_which
37 import ipy_which
38
38
39 # tab completers for hg, svn, ...
39 # tab completers for hg, svn, ...
40 import ipy_app_completers
40 import ipy_app_completers
41
41
42 # To make executables foo and bar in mybin usable without PATH change, do:
42 # To make executables foo and bar in mybin usable without PATH change, do:
43 # %rehashdir c:/mybin
43 # %rehashdir c:/mybin
44 # %store foo
44 # %store foo
@@ -47,45 +47,45 b' def main():'
47
47
48 # does not work without subprocess module!
48 # does not work without subprocess module!
49 #import ipy_signals
49 #import ipy_signals
50
50
51 ip.ex('import os')
51 ip.ex('import os')
52 ip.ex("def up(): os.chdir('..')")
52 ip.ex("def up(): os.chdir('..')")
53 ip.user_ns['LA'] = LastArgFinder()
53 ip.user_ns['LA'] = LastArgFinder()
54
54
55 # You can assign to _prompt_title variable
55 # You can assign to _prompt_title variable
56 # to provide some extra information for prompt
56 # to provide some extra information for prompt
57 # (e.g. the current mode, host/username...)
57 # (e.g. the current mode, host/username...)
58
58
59 ip.user_ns['_prompt_title'] = ''
59 ip.user_ns['_prompt_title'] = ''
60
60
61 # Nice prompt
61 # Nice prompt
62 o.prompt_in1= r'\C_Green${_prompt_title}\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
62 o.prompt_in1= r'\C_Green${_prompt_title}\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
63 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
63 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
64 o.prompt_out= '<\#> '
64 o.prompt_out= '<\#> '
65
65
66 from IPython.core import release
66 from IPython.core import release
67
67
68 import sys
68 import sys
69 # Non-chatty banner
69 # Non-chatty banner
70 o.banner = "IPython %s [on Py %s]\n" % (release.version,sys.version.split(None,1)[0])
70 o.banner = "IPython %s [on Py %s]\n" % (release.version,sys.version.split(None,1)[0])
71
71
72
72
73 ip.default_option('cd','-q')
73 ip.default_option('cd','-q')
74 ip.default_option('macro', '-r')
74 ip.default_option('macro', '-r')
75 # If you only rarely want to execute the things you %edit...
75 # If you only rarely want to execute the things you %edit...
76 #ip.default_option('edit','-x')
76 #ip.default_option('edit','-x')
77
77
78
78
79 o.prompts_pad_left="1"
79 o.prompts_pad_left="1"
80 # Remove all blank lines in between prompts, like a normal shell.
80 # Remove all blank lines in between prompts, like a normal shell.
81 o.separate_in="0"
81 o.separate_in="0"
82 o.separate_out="0"
82 o.separate_out="0"
83 o.separate_out2="0"
83 o.separate_out2="0"
84
84
85 # now alias all syscommands
85 # now alias all syscommands
86
86
87 db = ip.db
87 db = ip.db
88
88
89 syscmds = db.get("syscmdlist",[] )
89 syscmds = db.get("syscmdlist",[] )
90 if not syscmds:
90 if not syscmds:
91 print textwrap.dedent("""
91 print textwrap.dedent("""
@@ -95,47 +95,47 b' def main():'
95 """)
95 """)
96 ip.magic('rehashx')
96 ip.magic('rehashx')
97 syscmds = db.get("syscmdlist")
97 syscmds = db.get("syscmdlist")
98
98
99 # lowcase aliases on win32 only
99 # lowcase aliases on win32 only
100 if os.name == 'posix':
100 if os.name == 'posix':
101 mapper = lambda s:s
101 mapper = lambda s:s
102 else:
102 else:
103 def mapper(s): return s.lower()
103 def mapper(s): return s.lower()
104
104
105 for cmd in syscmds:
105 for cmd in syscmds:
106 # print "sys",cmd #dbg
106 # print "sys",cmd #dbg
107 noext, ext = os.path.splitext(cmd)
107 noext, ext = os.path.splitext(cmd)
108 if ext.lower() == '.exe':
108 if ext.lower() == '.exe':
109 cmd = noext
109 cmd = noext
110
110
111 key = mapper(cmd)
111 key = mapper(cmd)
112 if key not in ip.alias_manager.alias_table:
112 if key not in ip.alias_manager.alias_table:
113 # Dots will be removed from alias names, since ipython
113 # Dots will be removed from alias names, since ipython
114 # assumes names with dots to be python code
114 # assumes names with dots to be python code
115
115
116 ip.define_alias(key.replace('.',''), cmd)
116 ip.define_alias(key.replace('.',''), cmd)
117
117
118 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
118 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
119 ip.load("IPython.external.mglob")
119 ip.load("IPython.external.mglob")
120
120
121 # win32 is crippled w/o cygwin, try to help it a little bit
121 # win32 is crippled w/o cygwin, try to help it a little bit
122 if sys.platform == 'win32':
122 if sys.platform == 'win32':
123 if 'cygwin' in os.environ['PATH'].lower():
123 if 'cygwin' in os.environ['PATH'].lower():
124 # use the colors of cygwin ls (recommended)
124 # use the colors of cygwin ls (recommended)
125 ip.define_alias('d', 'ls -F --color=auto')
125 ip.define_alias('d', 'ls -F --color=auto')
126 else:
126 else:
127 # get icp, imv, imkdir, igrep, irm,...
127 # get icp, imv, imkdir, igrep, irm,...
128 ip.load('ipy_fsops')
128 ip.load('ipy_fsops')
129
129
130 # and the next best thing to real 'ls -F'
130 # and the next best thing to real 'ls -F'
131 ip.define_alias('d','dir /w /og /on')
131 ip.define_alias('d','dir /w /og /on')
132
132
133 ip.set_hook('input_prefilter', slash_prefilter_f)
133 ip.set_hook('input_prefilter', slash_prefilter_f)
134 extend_shell_behavior(ip)
134 extend_shell_behavior(ip)
135
135
136 class LastArgFinder:
136 class LastArgFinder:
137 """ Allow $LA to work as "last argument of previous command", like $! in bash
137 """ Allow $LA to work as "last argument of previous command", like $! in bash
138
138
139 To call this in normal IPython code, do LA()
139 To call this in normal IPython code, do LA()
140 """
140 """
141 def __call__(self, hist_idx = None):
141 def __call__(self, hist_idx = None):
@@ -144,7 +144,7 b' class LastArgFinder:'
144 return str(self)
144 return str(self)
145 return ip.input_hist_raw[hist_idx].strip().split()[-1]
145 return ip.input_hist_raw[hist_idx].strip().split()[-1]
146 def __str__(self):
146 def __str__(self):
147 ip = ipapi.get()
147 ip = ipapi.get()
148 for cmd in reversed(ip.input_hist_raw):
148 for cmd in reversed(ip.input_hist_raw):
149 parts = cmd.strip().split()
149 parts = cmd.strip().split()
150 if len(parts) < 2 or parts[-1] in ['$LA', 'LA()']:
150 if len(parts) < 2 or parts[-1] in ['$LA', 'LA()']:
@@ -154,7 +154,7 b' class LastArgFinder:'
154
154
155 def slash_prefilter_f(self,line):
155 def slash_prefilter_f(self,line):
156 """ ./foo, ~/foo and /bin/foo now run foo as system command
156 """ ./foo, ~/foo and /bin/foo now run foo as system command
157
157
158 Removes the need for doing !./foo, !~/foo or !/bin/foo
158 Removes the need for doing !./foo, !~/foo or !/bin/foo
159 """
159 """
160 from IPython.utils import genutils
160 from IPython.utils import genutils
@@ -253,7 +253,7 b' def extend_shell_behavior(ip):'
253 if command or more:
253 if command or more:
254 # push to raw history, so hist line numbers stay in sync
254 # push to raw history, so hist line numbers stay in sync
255 ip.input_hist_raw.append("# " + command + "\n")
255 ip.input_hist_raw.append("# " + command + "\n")
256
256
257 more = ip.push_line(ip.prefilter(command,more))
257 more = ip.push_line(ip.prefilter(command,more))
258 command = ''
258 command = ''
259 # IPython's runsource returns None if there was an error
259 # IPython's runsource returns None if there was an error
@@ -186,7 +186,7 b' class ZopeDebug(object):'
186 query = {}
186 query = {}
187 if indexes.get('path'):
187 if indexes.get('path'):
188 from string import join
188 from string import join
189 path = join(obj.getPhysicalPath(), '/')
189 path = join(obj.getPhysicalPath(), '/')
190 query.update({'path': path})
190 query.update({'path': path})
191 if indexes.get('getID'):
191 if indexes.get('getID'):
192 query.update({'getID': obj.id, })
192 query.update({'getID': obj.id, })
@@ -288,7 +288,7 b' def main():'
288 ip = ipapi.get()
288 ip = ipapi.get()
289 o = ip.options
289 o = ip.options
290 # autocall to "full" mode (smart mode is default, I like full mode)
290 # autocall to "full" mode (smart mode is default, I like full mode)
291
291
292 SOFTWARE_HOME = os.environ.get( "SOFTWARE_HOME" )
292 SOFTWARE_HOME = os.environ.get( "SOFTWARE_HOME" )
293 sys.path.append( SOFTWARE_HOME )
293 sys.path.append( SOFTWARE_HOME )
294 print "SOFTWARE_HOME=%s\n" % SOFTWARE_HOME
294 print "SOFTWARE_HOME=%s\n" % SOFTWARE_HOME
@@ -310,7 +310,7 b' def main():'
310 app
310 app
311 portal
311 portal
312 utils.{ %s }
312 utils.{ %s }
313
313
314 Uses the $SOFTWARE_HOME and $CONFIG_FILE environment
314 Uses the $SOFTWARE_HOME and $CONFIG_FILE environment
315 variables.
315 variables.
316 """ % ( ",".join([ x for x in dir(zope_debug.utils) if not x.startswith("_") ] ) ) )
316 """ % ( ",".join([ x for x in dir(zope_debug.utils) if not x.startswith("_") ] ) ) )
@@ -1,10 +1,10 b''
1 """ Integration with gvim, by Erich Heine
1 """ Integration with gvim, by Erich Heine
2
2
3 Provides a %vim magic command, and reuses the same vim session. Uses
3 Provides a %vim magic command, and reuses the same vim session. Uses
4 unix domain sockets for communication between vim and IPython. ipy.vim is
4 unix domain sockets for communication between vim and IPython. ipy.vim is
5 available in doc/examples of the IPython distribution.
5 available in doc/examples of the IPython distribution.
6
6
7 Slightly touched up email announcement (and description how to use it) by
7 Slightly touched up email announcement (and description how to use it) by
8 Erich Heine is here:
8 Erich Heine is here:
9
9
10 Ive recently been playing with ipython, and like it quite a bit. I did
10 Ive recently been playing with ipython, and like it quite a bit. I did
@@ -22,7 +22,7 b' for having gvim and ipython work very nicely together. Ive attached'
22 both to this email (hoping of course that the mailing list allows such
22 both to this email (hoping of course that the mailing list allows such
23 things).
23 things).
24
24
25 There are 2 files:
25 There are 2 files:
26
26
27 ipy_vimserver.py -- this file contains the ipython stuff
27 ipy_vimserver.py -- this file contains the ipython stuff
28 ipy.vim -- this file contains the gvim stuff
28 ipy.vim -- this file contains the gvim stuff
@@ -40,7 +40,7 b' from Numeric import *'
40 inf = infty = Infinity = (array([1])/0.0)[0]
40 inf = infty = Infinity = (array([1])/0.0)[0]
41
41
42 #****************************************************************************
42 #****************************************************************************
43 # function definitions
43 # function definitions
44 exp_safe_MIN = math.log(2.2250738585072014e-308)
44 exp_safe_MIN = math.log(2.2250738585072014e-308)
45 exp_safe_MAX = 1.7976931348623157e+308
45 exp_safe_MAX = 1.7976931348623157e+308
46
46
@@ -140,12 +140,12 b' def norm(a,p=2):'
140
140
141 Ref: http://mathworld.wolfram.com/VectorNorm.html
141 Ref: http://mathworld.wolfram.com/VectorNorm.html
142 http://mathworld.wolfram.com/L-Infinity-Norm.html"""
142 http://mathworld.wolfram.com/L-Infinity-Norm.html"""
143
143
144 if p in ('inf','Infinity'):
144 if p in ('inf','Infinity'):
145 return max(absolute(a).flat)
145 return max(absolute(a).flat)
146 else:
146 else:
147 return (sum_flat(absolute(a)**p))**(1.0/p)
147 return (sum_flat(absolute(a)**p))**(1.0/p)
148
148
149 def frange(xini,xfin=None,delta=None,**kw):
149 def frange(xini,xfin=None,delta=None,**kw):
150 """frange([start,] stop[, step, keywords]) -> array of floats
150 """frange([start,] stop[, step, keywords]) -> array of floats
151
151
@@ -183,14 +183,14 b' def frange(xini,xfin=None,delta=None,**kw):'
183 #defaults
183 #defaults
184 kw.setdefault('closed',1)
184 kw.setdefault('closed',1)
185 endpoint = kw['closed'] != 0
185 endpoint = kw['closed'] != 0
186
186
187 # funny logic to allow the *first* argument to be optional (like range())
187 # funny logic to allow the *first* argument to be optional (like range())
188 # This was modified with a simpler version from a similar frange() found
188 # This was modified with a simpler version from a similar frange() found
189 # at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66472
189 # at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66472
190 if xfin == None:
190 if xfin == None:
191 xfin = xini + 0.0
191 xfin = xini + 0.0
192 xini = 0.0
192 xini = 0.0
193
193
194 if delta == None:
194 if delta == None:
195 delta = 1.0
195 delta = 1.0
196
196
@@ -223,7 +223,7 b" def identity(n,rank=2,typecode='l'):"
223
223
224 Since rank defaults to 2, this function behaves in the default case (when
224 Since rank defaults to 2, this function behaves in the default case (when
225 only n is given) like the Numeric identity function."""
225 only n is given) like the Numeric identity function."""
226
226
227 iden = zeros((n,)*rank,typecode=typecode)
227 iden = zeros((n,)*rank,typecode=typecode)
228 for i in range(n):
228 for i in range(n):
229 idx = (i,)*rank
229 idx = (i,)*rank
@@ -250,7 +250,7 b' def binary_repr(number, max_length = 1025):'
250 Increase the value of max_length for very large numbers. Note that on
250 Increase the value of max_length for very large numbers. Note that on
251 32-bit machines, 2**1023 is the largest integer power of 2 which can be
251 32-bit machines, 2**1023 is the largest integer power of 2 which can be
252 converted to a Python float."""
252 converted to a Python float."""
253
253
254 assert number < 2L << max_length
254 assert number < 2L << max_length
255 shifts = map (operator.rshift, max_length * [number], \
255 shifts = map (operator.rshift, max_length * [number], \
256 range (max_length - 1, -1, -1))
256 range (max_length - 1, -1, -1))
@@ -261,7 +261,7 b' def binary_repr(number, max_length = 1025):'
261
261
262 def log2(x,ln2 = math.log(2.0)):
262 def log2(x,ln2 = math.log(2.0)):
263 """Return the log(x) in base 2.
263 """Return the log(x) in base 2.
264
264
265 This is a _slow_ function but which is guaranteed to return the correct
265 This is a _slow_ function but which is guaranteed to return the correct
266 integer value if the input is an ineger exact power of 2."""
266 integer value if the input is an ineger exact power of 2."""
267
267
@@ -288,7 +288,7 b' def ispower2(n):'
288
288
289 def fromfunction_kw(function, dimensions, **kwargs):
289 def fromfunction_kw(function, dimensions, **kwargs):
290 """Drop-in replacement for fromfunction() from Numerical Python.
290 """Drop-in replacement for fromfunction() from Numerical Python.
291
291
292 Allows passing keyword arguments to the desired function.
292 Allows passing keyword arguments to the desired function.
293
293
294 Call it as (keywords are optional):
294 Call it as (keywords are optional):
@@ -38,10 +38,10 b' class AsyncFrontEndBase(FrontEndBase):'
38 Overrides FrontEndBase to wrap execute in a deferred result.
38 Overrides FrontEndBase to wrap execute in a deferred result.
39 All callbacks are made as callbacks on the deferred result.
39 All callbacks are made as callbacks on the deferred result.
40 """
40 """
41
41
42 implements(IFrontEnd)
42 implements(IFrontEnd)
43 classProvides(IFrontEndFactory)
43 classProvides(IFrontEndFactory)
44
44
45 def __init__(self, engine=None, history=None):
45 def __init__(self, engine=None, history=None):
46 assert(engine==None or IEngineCore.providedBy(engine))
46 assert(engine==None or IEngineCore.providedBy(engine))
47 self.engine = IEngineCore(engine)
47 self.engine = IEngineCore(engine)
@@ -49,26 +49,26 b' class AsyncFrontEndBase(FrontEndBase):'
49 self.history = FrontEndHistory(input_cache=[''])
49 self.history = FrontEndHistory(input_cache=[''])
50 else:
50 else:
51 self.history = history
51 self.history = history
52
52
53 def execute(self, block, blockID=None):
53 def execute(self, block, blockID=None):
54 """Execute the block and return the deferred result.
54 """Execute the block and return the deferred result.
55
55
56 Parameters:
56 Parameters:
57 block : {str, AST}
57 block : {str, AST}
58 blockID : any
58 blockID : any
59 Caller may provide an ID to identify this block.
59 Caller may provide an ID to identify this block.
60 result['blockID'] := blockID
60 result['blockID'] := blockID
61
61
62 Result:
62 Result:
63 Deferred result of self.interpreter.execute
63 Deferred result of self.interpreter.execute
64 """
64 """
65
65
66 if(not self.is_complete(block)):
66 if(not self.is_complete(block)):
67 return Failure(Exception("Block is not compilable"))
67 return Failure(Exception("Block is not compilable"))
68
68
69 if(blockID == None):
69 if(blockID == None):
70 blockID = guid.generate()
70 blockID = guid.generate()
71
71
72 d = self.engine.execute(block)
72 d = self.engine.execute(block)
73 d.addCallback(self._add_history, block=block)
73 d.addCallback(self._add_history, block=block)
74 d.addCallbacks(self._add_block_id_for_result,
74 d.addCallbacks(self._add_block_id_for_result,
@@ -76,7 +76,7 b' class AsyncFrontEndBase(FrontEndBase):'
76 callbackArgs=(blockID,),
76 callbackArgs=(blockID,),
77 errbackArgs=(blockID,))
77 errbackArgs=(blockID,))
78 d.addBoth(self.update_cell_prompt, blockID=blockID)
78 d.addBoth(self.update_cell_prompt, blockID=blockID)
79 d.addCallbacks(self.render_result,
79 d.addCallbacks(self.render_result,
80 errback=self.render_error)
80 errback=self.render_error)
81
81
82 return d
82 return d
@@ -1,10 +1,10 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
3
3
4 """PyObjC classes to provide a Cocoa frontend to the
4 """PyObjC classes to provide a Cocoa frontend to the
5 IPython.kernel.engineservice.IEngineBase.
5 IPython.kernel.engineservice.IEngineBase.
6
6
7 To add an IPython interpreter to a cocoa app, instantiate an
7 To add an IPython interpreter to a cocoa app, instantiate an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
9 NSTextView instance in your UI. That's it.
9 NSTextView instance in your UI. That's it.
10
10
@@ -32,7 +32,7 b' from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\\'
32 NSLog, NSNotificationCenter, NSMakeRange,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
33 NSLocalizedString, NSIntersectionRange,\
33 NSLocalizedString, NSIntersectionRange,\
34 NSString, NSAutoreleasePool
34 NSString, NSAutoreleasePool
35
35
36 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
36 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
37 NSTextView, NSRulerView, NSVerticalRuler
37 NSTextView, NSRulerView, NSVerticalRuler
38
38
@@ -49,14 +49,14 b' from twisted.python.failure import Failure'
49 # Classes to implement the Cocoa frontend
49 # Classes to implement the Cocoa frontend
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 # TODO:
52 # TODO:
53 # 1. use MultiEngineClient and out-of-process engine rather than
53 # 1. use MultiEngineClient and out-of-process engine rather than
54 # ThreadedEngineService?
54 # ThreadedEngineService?
55 # 2. integrate Xgrid launching of engines
55 # 2. integrate Xgrid launching of engines
56
56
57 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
57 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
58 """Wrap all blocks in an NSAutoreleasePool"""
58 """Wrap all blocks in an NSAutoreleasePool"""
59
59
60 def wrapped_execute(self, msg, lines):
60 def wrapped_execute(self, msg, lines):
61 """wrapped_execute"""
61 """wrapped_execute"""
62 try:
62 try:
@@ -65,9 +65,9 b' class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):'
65 self).wrapped_execute(msg, lines)
65 self).wrapped_execute(msg, lines)
66 finally:
66 finally:
67 p.drain()
67 p.drain()
68
68
69 return result
69 return result
70
70
71
71
72
72
73 class Cell(NSObject):
73 class Cell(NSObject):
@@ -75,20 +75,20 b' class Cell(NSObject):'
75 Representation of the prompts, input and output of a cell in the
75 Representation of the prompts, input and output of a cell in the
76 frontend
76 frontend
77 """
77 """
78
78
79 blockNumber = objc.ivar().unsigned_long()
79 blockNumber = objc.ivar().unsigned_long()
80 blockID = objc.ivar()
80 blockID = objc.ivar()
81 inputBlock = objc.ivar()
81 inputBlock = objc.ivar()
82 output = objc.ivar()
82 output = objc.ivar()
83
84
83
85
84
85
86 class CellBlock(object):
86 class CellBlock(object):
87 """
87 """
88 Storage for information about text ranges relating to a single cell
88 Storage for information about text ranges relating to a single cell
89 """
89 """
90
90
91
91
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
93 outputRange=None):
93 outputRange=None):
94 super(CellBlock, self).__init__()
94 super(CellBlock, self).__init__()
@@ -96,10 +96,10 b' class CellBlock(object):'
96 self.inputRange = inputRange
96 self.inputRange = inputRange
97 self.outputPromptRange = outputPromptRange
97 self.outputPromptRange = outputPromptRange
98 self.outputRange = outputRange
98 self.outputRange = outputRange
99
99
100 def update_ranges_for_insertion(self, text, textRange):
100 def update_ranges_for_insertion(self, text, textRange):
101 """Update ranges for text insertion at textRange"""
101 """Update ranges for text insertion at textRange"""
102
102
103 for r in [self.inputPromptRange,self.inputRange,
103 for r in [self.inputPromptRange,self.inputRange,
104 self.outputPromptRange, self.outputRange]:
104 self.outputPromptRange, self.outputRange]:
105 if(r == None):
105 if(r == None):
@@ -117,11 +117,11 b' class CellBlock(object):'
117 r.length += len(text) - intersection.length
117 r.length += len(text) - intersection.length
118 else:
118 else:
119 r.length -= intersection.length
119 r.length -= intersection.length
120
120
121
121
122 def update_ranges_for_deletion(self, textRange):
122 def update_ranges_for_deletion(self, textRange):
123 """Update ranges for text deletion at textRange"""
123 """Update ranges for text deletion at textRange"""
124
124
125 for r in [self.inputPromptRange,self.inputRange,
125 for r in [self.inputPromptRange,self.inputRange,
126 self.outputPromptRange, self.outputRange]:
126 self.outputPromptRange, self.outputRange]:
127 if(r==None):
127 if(r==None):
@@ -139,52 +139,52 b' class CellBlock(object):'
139 r.length += intersection.length
139 r.length += intersection.length
140 else:
140 else:
141 r.length -= intersection.length
141 r.length -= intersection.length
142
142
143 def __repr__(self):
143 def __repr__(self):
144 return 'CellBlock('+ str((self.inputPromptRange,
144 return 'CellBlock('+ str((self.inputPromptRange,
145 self.inputRange,
145 self.inputRange,
146 self.outputPromptRange,
146 self.outputPromptRange,
147 self.outputRange)) + ')'
147 self.outputRange)) + ')'
148
149
148
150
149
151
150
151
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
154 waitingForEngine = objc.ivar().bool()
154 waitingForEngine = objc.ivar().bool()
155 textView = objc.IBOutlet()
155 textView = objc.IBOutlet()
156
156
157 def init(self):
157 def init(self):
158 self = super(IPythonCocoaController, self).init()
158 self = super(IPythonCocoaController, self).init()
159 AsyncFrontEndBase.__init__(self,
159 AsyncFrontEndBase.__init__(self,
160 engine=AutoreleasePoolWrappedThreadedEngineService())
160 engine=AutoreleasePoolWrappedThreadedEngineService())
161 if(self != None):
161 if(self != None):
162 self._common_init()
162 self._common_init()
163
163
164 return self
164 return self
165
165
166 def _common_init(self):
166 def _common_init(self):
167 """_common_init"""
167 """_common_init"""
168
168
169 self.userNS = NSMutableDictionary.dictionary()
169 self.userNS = NSMutableDictionary.dictionary()
170 self.waitingForEngine = False
170 self.waitingForEngine = False
171
171
172 self.lines = {}
172 self.lines = {}
173 self.tabSpaces = 4
173 self.tabSpaces = 4
174 self.tabUsesSpaces = True
174 self.tabUsesSpaces = True
175 self.currentBlockID = self.next_block_ID()
175 self.currentBlockID = self.next_block_ID()
176 self.blockRanges = {} # blockID=>CellBlock
176 self.blockRanges = {} # blockID=>CellBlock
177
177
178
178
179 def awakeFromNib(self):
179 def awakeFromNib(self):
180 """awakeFromNib"""
180 """awakeFromNib"""
181
181
182 self._common_init()
182 self._common_init()
183
183
184 # Start the IPython engine
184 # Start the IPython engine
185 self.engine.startService()
185 self.engine.startService()
186 NSLog('IPython engine started')
186 NSLog('IPython engine started')
187
187
188 # Register for app termination
188 # Register for app termination
189 nc = NSNotificationCenter.defaultCenter()
189 nc = NSNotificationCenter.defaultCenter()
190 nc.addObserver_selector_name_object_(
190 nc.addObserver_selector_name_object_(
@@ -192,7 +192,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
192 'appWillTerminate:',
192 'appWillTerminate:',
193 NSApplicationWillTerminateNotification,
193 NSApplicationWillTerminateNotification,
194 None)
194 None)
195
195
196 self.textView.setDelegate_(self)
196 self.textView.setDelegate_(self)
197 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
197 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
198 r = NSRulerView.alloc().initWithScrollView_orientation_(
198 r = NSRulerView.alloc().initWithScrollView_orientation_(
@@ -202,30 +202,30 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
202 self.verticalRulerView.setClientView_(self.textView)
202 self.verticalRulerView.setClientView_(self.textView)
203 self._start_cli_banner()
203 self._start_cli_banner()
204 self.start_new_block()
204 self.start_new_block()
205
205
206
206
207 def appWillTerminate_(self, notification):
207 def appWillTerminate_(self, notification):
208 """appWillTerminate"""
208 """appWillTerminate"""
209
209
210 self.engine.stopService()
210 self.engine.stopService()
211
211
212
212
213 def complete(self, token):
213 def complete(self, token):
214 """Complete token in engine's user_ns
214 """Complete token in engine's user_ns
215
215
216 Parameters
216 Parameters
217 ----------
217 ----------
218 token : string
218 token : string
219
219
220 Result
220 Result
221 ------
221 ------
222 Deferred result of
222 Deferred result of
223 IPython.kernel.engineservice.IEngineBase.complete
223 IPython.kernel.engineservice.IEngineBase.complete
224 """
224 """
225
225
226 return self.engine.complete(token)
226 return self.engine.complete(token)
227
227
228
228
229 def execute(self, block, blockID=None):
229 def execute(self, block, blockID=None):
230 self.waitingForEngine = True
230 self.waitingForEngine = True
231 self.willChangeValueForKey_('commandHistory')
231 self.willChangeValueForKey_('commandHistory')
@@ -233,87 +233,87 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
233 blockID)
233 blockID)
234 d.addBoth(self._engine_done)
234 d.addBoth(self._engine_done)
235 d.addCallback(self._update_user_ns)
235 d.addCallback(self._update_user_ns)
236
236
237 return d
237 return d
238
238
239
239
240 def push_(self, namespace):
240 def push_(self, namespace):
241 """Push dictionary of key=>values to python namespace"""
241 """Push dictionary of key=>values to python namespace"""
242
242
243 self.waitingForEngine = True
243 self.waitingForEngine = True
244 self.willChangeValueForKey_('commandHistory')
244 self.willChangeValueForKey_('commandHistory')
245 d = self.engine.push(namespace)
245 d = self.engine.push(namespace)
246 d.addBoth(self._engine_done)
246 d.addBoth(self._engine_done)
247 d.addCallback(self._update_user_ns)
247 d.addCallback(self._update_user_ns)
248
248
249
249
250 def pull_(self, keys):
250 def pull_(self, keys):
251 """Pull keys from python namespace"""
251 """Pull keys from python namespace"""
252
252
253 self.waitingForEngine = True
253 self.waitingForEngine = True
254 result = blockingCallFromThread(self.engine.pull, keys)
254 result = blockingCallFromThread(self.engine.pull, keys)
255 self.waitingForEngine = False
255 self.waitingForEngine = False
256
256
257 @objc.signature('v@:@I')
257 @objc.signature('v@:@I')
258 def executeFileAtPath_encoding_(self, path, encoding):
258 def executeFileAtPath_encoding_(self, path, encoding):
259 """Execute file at path in an empty namespace. Update the engine
259 """Execute file at path in an empty namespace. Update the engine
260 user_ns with the resulting locals."""
260 user_ns with the resulting locals."""
261
261
262 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
262 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
263 path,
263 path,
264 encoding,
264 encoding,
265 None)
265 None)
266 self.engine.execute(lines)
266 self.engine.execute(lines)
267
267
268
268
269 def _engine_done(self, x):
269 def _engine_done(self, x):
270 self.waitingForEngine = False
270 self.waitingForEngine = False
271 self.didChangeValueForKey_('commandHistory')
271 self.didChangeValueForKey_('commandHistory')
272 return x
272 return x
273
273
274 def _update_user_ns(self, result):
274 def _update_user_ns(self, result):
275 """Update self.userNS from self.engine's namespace"""
275 """Update self.userNS from self.engine's namespace"""
276 d = self.engine.keys()
276 d = self.engine.keys()
277 d.addCallback(self._get_engine_namespace_values_for_keys)
277 d.addCallback(self._get_engine_namespace_values_for_keys)
278
278
279 return result
279 return result
280
280
281
281
282 def _get_engine_namespace_values_for_keys(self, keys):
282 def _get_engine_namespace_values_for_keys(self, keys):
283 d = self.engine.pull(keys)
283 d = self.engine.pull(keys)
284 d.addCallback(self._store_engine_namespace_values, keys=keys)
284 d.addCallback(self._store_engine_namespace_values, keys=keys)
285
285
286
286
287 def _store_engine_namespace_values(self, values, keys=[]):
287 def _store_engine_namespace_values(self, values, keys=[]):
288 assert(len(values) == len(keys))
288 assert(len(values) == len(keys))
289 self.willChangeValueForKey_('userNS')
289 self.willChangeValueForKey_('userNS')
290 for (k,v) in zip(keys,values):
290 for (k,v) in zip(keys,values):
291 self.userNS[k] = saferepr(v)
291 self.userNS[k] = saferepr(v)
292 self.didChangeValueForKey_('userNS')
292 self.didChangeValueForKey_('userNS')
293
293
294
294
295 def update_cell_prompt(self, result, blockID=None):
295 def update_cell_prompt(self, result, blockID=None):
296 print self.blockRanges
296 print self.blockRanges
297 if(isinstance(result, Failure)):
297 if(isinstance(result, Failure)):
298 prompt = self.input_prompt()
298 prompt = self.input_prompt()
299
299
300 else:
300 else:
301 prompt = self.input_prompt(number=result['number'])
301 prompt = self.input_prompt(number=result['number'])
302
302
303 r = self.blockRanges[blockID].inputPromptRange
303 r = self.blockRanges[blockID].inputPromptRange
304 self.insert_text(prompt,
304 self.insert_text(prompt,
305 textRange=r,
305 textRange=r,
306 scrollToVisible=False
306 scrollToVisible=False
307 )
307 )
308
308
309 return result
309 return result
310
310
311
311
312 def render_result(self, result):
312 def render_result(self, result):
313 blockID = result['blockID']
313 blockID = result['blockID']
314 inputRange = self.blockRanges[blockID].inputRange
314 inputRange = self.blockRanges[blockID].inputRange
315 del self.blockRanges[blockID]
315 del self.blockRanges[blockID]
316
316
317 #print inputRange,self.current_block_range()
317 #print inputRange,self.current_block_range()
318 self.insert_text('\n' +
318 self.insert_text('\n' +
319 self.output_prompt(number=result['number']) +
319 self.output_prompt(number=result['number']) +
@@ -322,8 +322,8 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
322 textRange=NSMakeRange(inputRange.location+inputRange.length,
322 textRange=NSMakeRange(inputRange.location+inputRange.length,
323 0))
323 0))
324 return result
324 return result
325
325
326
326
327 def render_error(self, failure):
327 def render_error(self, failure):
328 print failure
328 print failure
329 blockID = failure.blockID
329 blockID = failure.blockID
@@ -338,82 +338,82 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
338 0))
338 0))
339 self.start_new_block()
339 self.start_new_block()
340 return failure
340 return failure
341
341
342
342
343 def _start_cli_banner(self):
343 def _start_cli_banner(self):
344 """Print banner"""
344 """Print banner"""
345
345
346 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
346 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
347 IPython.__version__
347 IPython.__version__
348
348
349 self.insert_text(banner + '\n\n')
349 self.insert_text(banner + '\n\n')
350
350
351
351
352 def start_new_block(self):
352 def start_new_block(self):
353 """"""
353 """"""
354
354
355 self.currentBlockID = self.next_block_ID()
355 self.currentBlockID = self.next_block_ID()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
357 self.insert_text(self.input_prompt(),
357 self.insert_text(self.input_prompt(),
358 textRange=self.current_block_range().inputPromptRange)
358 textRange=self.current_block_range().inputPromptRange)
359
359
360
360
361
361
362 def next_block_ID(self):
362 def next_block_ID(self):
363
363
364 return guid.generate()
364 return guid.generate()
365
365
366 def new_cell_block(self):
366 def new_cell_block(self):
367 """A new CellBlock at the end of self.textView.textStorage()"""
367 """A new CellBlock at the end of self.textView.textStorage()"""
368
368
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
370 0), #len(self.input_prompt())),
370 0), #len(self.input_prompt())),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
372 0))
372 0))
373
373
374
374
375 def current_block_range(self):
375 def current_block_range(self):
376 return self.blockRanges.get(self.currentBlockID,
376 return self.blockRanges.get(self.currentBlockID,
377 self.new_cell_block())
377 self.new_cell_block())
378
378
379 def current_block(self):
379 def current_block(self):
380 """The current block's text"""
380 """The current block's text"""
381
381
382 return self.text_for_range(self.current_block_range().inputRange)
382 return self.text_for_range(self.current_block_range().inputRange)
383
383
384 def text_for_range(self, textRange):
384 def text_for_range(self, textRange):
385 """text_for_range"""
385 """text_for_range"""
386
386
387 ts = self.textView.textStorage()
387 ts = self.textView.textStorage()
388 return ts.string().substringWithRange_(textRange)
388 return ts.string().substringWithRange_(textRange)
389
389
390 def current_line(self):
390 def current_line(self):
391 block = self.text_for_range(self.current_block_range().inputRange)
391 block = self.text_for_range(self.current_block_range().inputRange)
392 block = block.split('\n')
392 block = block.split('\n')
393 return block[-1]
393 return block[-1]
394
394
395
395
396 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
396 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
397 """Insert text into textView at textRange, updating blockRanges
397 """Insert text into textView at textRange, updating blockRanges
398 as necessary
398 as necessary
399 """
399 """
400 if(textRange == None):
400 if(textRange == None):
401 #range for end of text
401 #range for end of text
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
403
403
404
404
405 self.textView.replaceCharactersInRange_withString_(
405 self.textView.replaceCharactersInRange_withString_(
406 textRange, string)
406 textRange, string)
407
407
408 for r in self.blockRanges.itervalues():
408 for r in self.blockRanges.itervalues():
409 r.update_ranges_for_insertion(string, textRange)
409 r.update_ranges_for_insertion(string, textRange)
410
410
411 self.textView.setSelectedRange_(textRange)
411 self.textView.setSelectedRange_(textRange)
412 if(scrollToVisible):
412 if(scrollToVisible):
413 self.textView.scrollRangeToVisible_(textRange)
413 self.textView.scrollRangeToVisible_(textRange)
414
414
415
415
416
416
417 def replace_current_block_with_string(self, textView, string):
417 def replace_current_block_with_string(self, textView, string):
418 textView.replaceCharactersInRange_withString_(
418 textView.replaceCharactersInRange_withString_(
419 self.current_block_range().inputRange,
419 self.current_block_range().inputRange,
@@ -422,53 +422,53 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
422 r = NSMakeRange(textView.textStorage().length(), 0)
422 r = NSMakeRange(textView.textStorage().length(), 0)
423 textView.scrollRangeToVisible_(r)
423 textView.scrollRangeToVisible_(r)
424 textView.setSelectedRange_(r)
424 textView.setSelectedRange_(r)
425
425
426
426
427 def current_indent_string(self):
427 def current_indent_string(self):
428 """returns string for indent or None if no indent"""
428 """returns string for indent or None if no indent"""
429
429
430 return self._indent_for_block(self.current_block())
430 return self._indent_for_block(self.current_block())
431
431
432
432
433 def _indent_for_block(self, block):
433 def _indent_for_block(self, block):
434 lines = block.split('\n')
434 lines = block.split('\n')
435 if(len(lines) > 1):
435 if(len(lines) > 1):
436 currentIndent = len(lines[-1]) - len(lines[-1].lstrip())
436 currentIndent = len(lines[-1]) - len(lines[-1].lstrip())
437 if(currentIndent == 0):
437 if(currentIndent == 0):
438 currentIndent = self.tabSpaces
438 currentIndent = self.tabSpaces
439
439
440 if(self.tabUsesSpaces):
440 if(self.tabUsesSpaces):
441 result = ' ' * currentIndent
441 result = ' ' * currentIndent
442 else:
442 else:
443 result = '\t' * (currentIndent/self.tabSpaces)
443 result = '\t' * (currentIndent/self.tabSpaces)
444 else:
444 else:
445 result = None
445 result = None
446
446
447 return result
447 return result
448
448
449
449
450 # NSTextView delegate methods...
450 # NSTextView delegate methods...
451 def textView_doCommandBySelector_(self, textView, selector):
451 def textView_doCommandBySelector_(self, textView, selector):
452 assert(textView == self.textView)
452 assert(textView == self.textView)
453 NSLog("textView_doCommandBySelector_: "+selector)
453 NSLog("textView_doCommandBySelector_: "+selector)
454
454
455
455
456 if(selector == 'insertNewline:'):
456 if(selector == 'insertNewline:'):
457 indent = self.current_indent_string()
457 indent = self.current_indent_string()
458 if(indent):
458 if(indent):
459 line = indent + self.current_line()
459 line = indent + self.current_line()
460 else:
460 else:
461 line = self.current_line()
461 line = self.current_line()
462
462
463 if(self.is_complete(self.current_block())):
463 if(self.is_complete(self.current_block())):
464 self.execute(self.current_block(),
464 self.execute(self.current_block(),
465 blockID=self.currentBlockID)
465 blockID=self.currentBlockID)
466 self.start_new_block()
466 self.start_new_block()
467
467
468 return True
468 return True
469
469
470 return False
470 return False
471
471
472 elif(selector == 'moveUp:'):
472 elif(selector == 'moveUp:'):
473 prevBlock = self.get_history_previous(self.current_block())
473 prevBlock = self.get_history_previous(self.current_block())
474 if(prevBlock != None):
474 if(prevBlock != None):
@@ -476,7 +476,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
476 else:
476 else:
477 NSBeep()
477 NSBeep()
478 return True
478 return True
479
479
480 elif(selector == 'moveDown:'):
480 elif(selector == 'moveDown:'):
481 nextBlock = self.get_history_next()
481 nextBlock = self.get_history_next()
482 if(nextBlock != None):
482 if(nextBlock != None):
@@ -484,10 +484,10 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
484 else:
484 else:
485 NSBeep()
485 NSBeep()
486 return True
486 return True
487
487
488 elif(selector == 'moveToBeginningOfParagraph:'):
488 elif(selector == 'moveToBeginningOfParagraph:'):
489 textView.setSelectedRange_(NSMakeRange(
489 textView.setSelectedRange_(NSMakeRange(
490 self.current_block_range().inputRange.location,
490 self.current_block_range().inputRange.location,
491 0))
491 0))
492 return True
492 return True
493 elif(selector == 'moveToEndOfParagraph:'):
493 elif(selector == 'moveToEndOfParagraph:'):
@@ -499,16 +499,16 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
499 if(textView.selectedRange().location <= \
499 if(textView.selectedRange().location <= \
500 self.current_block_range().location):
500 self.current_block_range().location):
501 raise NotImplemented()
501 raise NotImplemented()
502
502
503 return False # don't actually handle the delete
503 return False # don't actually handle the delete
504
504
505 elif(selector == 'insertTab:'):
505 elif(selector == 'insertTab:'):
506 if(len(self.current_line().strip()) == 0): #only white space
506 if(len(self.current_line().strip()) == 0): #only white space
507 return False
507 return False
508 else:
508 else:
509 self.textView.complete_(self)
509 self.textView.complete_(self)
510 return True
510 return True
511
511
512 elif(selector == 'deleteBackward:'):
512 elif(selector == 'deleteBackward:'):
513 #if we're at the beginning of the current block, ignore
513 #if we're at the beginning of the current block, ignore
514 if(textView.selectedRange().location == \
514 if(textView.selectedRange().location == \
@@ -523,17 +523,17 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
523 r.update_ranges_for_deletion(deleteRange)
523 r.update_ranges_for_deletion(deleteRange)
524 return False
524 return False
525 return False
525 return False
526
526
527
527
528 def textView_shouldChangeTextInRanges_replacementStrings_(self,
528 def textView_shouldChangeTextInRanges_replacementStrings_(self,
529 textView, ranges, replacementStrings):
529 textView, ranges, replacementStrings):
530 """
530 """
531 Delegate method for NSTextView.
531 Delegate method for NSTextView.
532
532
533 Refuse change text in ranges not at end, but make those changes at
533 Refuse change text in ranges not at end, but make those changes at
534 end.
534 end.
535 """
535 """
536
536
537 assert(len(ranges) == len(replacementStrings))
537 assert(len(ranges) == len(replacementStrings))
538 allow = True
538 allow = True
539 for r,s in zip(ranges, replacementStrings):
539 for r,s in zip(ranges, replacementStrings):
@@ -542,10 +542,10 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
542 r.location < self.current_block_range().inputRange.location):
542 r.location < self.current_block_range().inputRange.location):
543 self.insert_text(s)
543 self.insert_text(s)
544 allow = False
544 allow = False
545
545
546 return allow
546 return allow
547
547
548 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
548 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
549 textView, words, charRange, index):
549 textView, words, charRange, index):
550 try:
550 try:
551 ts = textView.textStorage()
551 ts = textView.textStorage()
@@ -554,7 +554,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
554 except:
554 except:
555 completions = objc.nil
555 completions = objc.nil
556 NSBeep()
556 NSBeep()
557
557
558 return (completions,0)
558 return (completions,0)
559
559
560
560
@@ -2,7 +2,7 b''
2 """
2 """
3 setup.py
3 setup.py
4
4
5 Setuptools installer script for generating a Cocoa plugin for the
5 Setuptools installer script for generating a Cocoa plugin for the
6 IPython cocoa frontend
6 IPython cocoa frontend
7
7
8 Author: Barry Wark
8 Author: Barry Wark
@@ -1,18 +1,18 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """This file contains unittests for the
2 """This file contains unittests for the
3 IPython.frontend.cocoa.cocoa_frontend module.
3 IPython.frontend.cocoa.cocoa_frontend module.
4 """
4 """
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #---------------------------------------------------------------------------
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2005 The IPython Development Team
8 # Copyright (C) 2005 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
12 #---------------------------------------------------------------------------
13
13
14 #---------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #---------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17
17
18 # Tell nose to skip this module
18 # Tell nose to skip this module
@@ -84,14 +84,14 b' class TestIPythonCocoaControler(unittest.TestCase):'
84 """test that current_indent_string returns current indent or None.
84 """test that current_indent_string returns current indent or None.
85 Uses _indent_for_block for direct unit testing.
85 Uses _indent_for_block for direct unit testing.
86 """
86 """
87
87
88 self.controller.tabUsesSpaces = True
88 self.controller.tabUsesSpaces = True
89 self.assert_(self.controller._indent_for_block("""a=3""") == None)
89 self.assert_(self.controller._indent_for_block("""a=3""") == None)
90 self.assert_(self.controller._indent_for_block("") == None)
90 self.assert_(self.controller._indent_for_block("") == None)
91 block = """def test():\n a=3"""
91 block = """def test():\n a=3"""
92 self.assert_(self.controller._indent_for_block(block) == \
92 self.assert_(self.controller._indent_for_block(block) == \
93 ' ' * self.controller.tabSpaces)
93 ' ' * self.controller.tabSpaces)
94
94
95 block = """if(True):\n%sif(False):\n%spass""" % \
95 block = """if(True):\n%sif(False):\n%spass""" % \
96 (' '*self.controller.tabSpaces,
96 (' '*self.controller.tabSpaces,
97 2*' '*self.controller.tabSpaces)
97 2*' '*self.controller.tabSpaces)
@@ -1,10 +1,10 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
3 """
3 """
4 frontendbase provides an interface and base class for GUI frontends for
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
5 IPython.kernel/IPython.kernel.core.
6
6
7 Frontend implementations will likely want to subclass FrontEndBase.
7 Frontend implementations will likely want to subclass FrontEndBase.
8
8
9 Author: Barry Wark
9 Author: Barry Wark
10 """
10 """
@@ -26,8 +26,8 b' from IPython.external import guid'
26
26
27
27
28 from IPython.frontend.zopeinterface import (
28 from IPython.frontend.zopeinterface import (
29 Interface,
29 Interface,
30 Attribute,
30 Attribute,
31 )
31 )
32 from IPython.kernel.core.history import FrontEndHistory
32 from IPython.kernel.core.history import FrontEndHistory
33 from IPython.kernel.core.util import Bunch
33 from IPython.kernel.core.util import Bunch
@@ -47,103 +47,103 b" rc.prompt_out = r'Out [$number]: '"
47
47
48 class IFrontEndFactory(Interface):
48 class IFrontEndFactory(Interface):
49 """Factory interface for frontends."""
49 """Factory interface for frontends."""
50
50
51 def __call__(engine=None, history=None):
51 def __call__(engine=None, history=None):
52 """
52 """
53 Parameters:
53 Parameters:
54 interpreter : IPython.kernel.engineservice.IEngineCore
54 interpreter : IPython.kernel.engineservice.IEngineCore
55 """
55 """
56
56
57 pass
57 pass
58
58
59
59
60 class IFrontEnd(Interface):
60 class IFrontEnd(Interface):
61 """Interface for frontends. All methods return t.i.d.Deferred"""
61 """Interface for frontends. All methods return t.i.d.Deferred"""
62
62
63 Attribute("input_prompt_template", "string.Template instance\
63 Attribute("input_prompt_template", "string.Template instance\
64 substituteable with execute result.")
64 substituteable with execute result.")
65 Attribute("output_prompt_template", "string.Template instance\
65 Attribute("output_prompt_template", "string.Template instance\
66 substituteable with execute result.")
66 substituteable with execute result.")
67 Attribute("continuation_prompt_template", "string.Template instance\
67 Attribute("continuation_prompt_template", "string.Template instance\
68 substituteable with execute result.")
68 substituteable with execute result.")
69
69
70 def update_cell_prompt(result, blockID=None):
70 def update_cell_prompt(result, blockID=None):
71 """Subclass may override to update the input prompt for a block.
71 """Subclass may override to update the input prompt for a block.
72
72
73 In asynchronous frontends, this method will be called as a
73 In asynchronous frontends, this method will be called as a
74 twisted.internet.defer.Deferred's callback/errback.
74 twisted.internet.defer.Deferred's callback/errback.
75 Implementations should thus return result when finished.
75 Implementations should thus return result when finished.
76
76
77 Result is a result dict in case of success, and a
77 Result is a result dict in case of success, and a
78 twisted.python.util.failure.Failure in case of an error
78 twisted.python.util.failure.Failure in case of an error
79 """
79 """
80
80
81 pass
81 pass
82
82
83 def render_result(result):
83 def render_result(result):
84 """Render the result of an execute call. Implementors may choose the
84 """Render the result of an execute call. Implementors may choose the
85 method of rendering.
85 method of rendering.
86 For example, a notebook-style frontend might render a Chaco plot
86 For example, a notebook-style frontend might render a Chaco plot
87 inline.
87 inline.
88
88
89 Parameters:
89 Parameters:
90 result : dict (result of IEngineBase.execute )
90 result : dict (result of IEngineBase.execute )
91 blockID = result['blockID']
91 blockID = result['blockID']
92
92
93 Result:
93 Result:
94 Output of frontend rendering
94 Output of frontend rendering
95 """
95 """
96
96
97 pass
97 pass
98
98
99 def render_error(failure):
99 def render_error(failure):
100 """Subclasses must override to render the failure.
100 """Subclasses must override to render the failure.
101
101
102 In asynchronous frontend, since this method will be called as a
102 In asynchronous frontend, since this method will be called as a
103 twisted.internet.defer.Deferred's callback. Implementations
103 twisted.internet.defer.Deferred's callback. Implementations
104 should thus return result when finished.
104 should thus return result when finished.
105
105
106 blockID = failure.blockID
106 blockID = failure.blockID
107 """
107 """
108
108
109 pass
109 pass
110
110
111 def input_prompt(number=''):
111 def input_prompt(number=''):
112 """Returns the input prompt by subsituting into
112 """Returns the input prompt by subsituting into
113 self.input_prompt_template
113 self.input_prompt_template
114 """
114 """
115 pass
115 pass
116
116
117 def output_prompt(number=''):
117 def output_prompt(number=''):
118 """Returns the output prompt by subsituting into
118 """Returns the output prompt by subsituting into
119 self.output_prompt_template
119 self.output_prompt_template
120 """
120 """
121
121
122 pass
122 pass
123
123
124 def continuation_prompt():
124 def continuation_prompt():
125 """Returns the continuation prompt by subsituting into
125 """Returns the continuation prompt by subsituting into
126 self.continuation_prompt_template
126 self.continuation_prompt_template
127 """
127 """
128
128
129 pass
129 pass
130
130
131 def is_complete(block):
131 def is_complete(block):
132 """Returns True if block is complete, False otherwise."""
132 """Returns True if block is complete, False otherwise."""
133
133
134 pass
134 pass
135
135
136
136
137 def get_history_previous(current_block):
137 def get_history_previous(current_block):
138 """Returns the block previous in the history. Saves currentBlock if
138 """Returns the block previous in the history. Saves currentBlock if
139 the history_cursor is currently at the end of the input history"""
139 the history_cursor is currently at the end of the input history"""
140 pass
140 pass
141
141
142 def get_history_next():
142 def get_history_next():
143 """Returns the next block in the history."""
143 """Returns the next block in the history."""
144
144
145 pass
145 pass
146
146
147 def complete(self, line):
147 def complete(self, line):
148 """Returns the list of possible completions, and the completed
148 """Returns the list of possible completions, and the completed
149 line.
149 line.
@@ -156,7 +156,7 b' class IFrontEnd(Interface):'
156
156
157
157
158 ##############################################################################
158 ##############################################################################
159 # Base class for all the frontends.
159 # Base class for all the frontends.
160 ##############################################################################
160 ##############################################################################
161
161
162 class FrontEndBase(object):
162 class FrontEndBase(object):
@@ -164,86 +164,86 b' class FrontEndBase(object):'
164 FrontEndBase manages the state tasks for a CLI frontend:
164 FrontEndBase manages the state tasks for a CLI frontend:
165 - Input and output history management
165 - Input and output history management
166 - Input/continuation and output prompt generation
166 - Input/continuation and output prompt generation
167
167
168 Some issues (due to possibly unavailable engine):
168 Some issues (due to possibly unavailable engine):
169 - How do we get the current cell number for the engine?
169 - How do we get the current cell number for the engine?
170 - How do we handle completions?
170 - How do we handle completions?
171 """
171 """
172
172
173 history_cursor = 0
173 history_cursor = 0
174
174
175 input_prompt_template = string.Template(rc.prompt_in1)
175 input_prompt_template = string.Template(rc.prompt_in1)
176 output_prompt_template = string.Template(rc.prompt_out)
176 output_prompt_template = string.Template(rc.prompt_out)
177 continuation_prompt_template = string.Template(rc.prompt_in2)
177 continuation_prompt_template = string.Template(rc.prompt_in2)
178
178
179 def __init__(self, shell=None, history=None):
179 def __init__(self, shell=None, history=None):
180 self.shell = shell
180 self.shell = shell
181 if history is None:
181 if history is None:
182 self.history = FrontEndHistory(input_cache=[''])
182 self.history = FrontEndHistory(input_cache=[''])
183 else:
183 else:
184 self.history = history
184 self.history = history
185
185
186
186
187 def input_prompt(self, number=''):
187 def input_prompt(self, number=''):
188 """Returns the current input prompt
188 """Returns the current input prompt
189
189
190 It would be great to use ipython1.core.prompts.Prompt1 here
190 It would be great to use ipython1.core.prompts.Prompt1 here
191 """
191 """
192 return self.input_prompt_template.safe_substitute({'number':number})
192 return self.input_prompt_template.safe_substitute({'number':number})
193
193
194
194
195 def continuation_prompt(self):
195 def continuation_prompt(self):
196 """Returns the current continuation prompt"""
196 """Returns the current continuation prompt"""
197
197
198 return self.continuation_prompt_template.safe_substitute()
198 return self.continuation_prompt_template.safe_substitute()
199
199
200 def output_prompt(self, number=''):
200 def output_prompt(self, number=''):
201 """Returns the output prompt for result"""
201 """Returns the output prompt for result"""
202
202
203 return self.output_prompt_template.safe_substitute({'number':number})
203 return self.output_prompt_template.safe_substitute({'number':number})
204
204
205
205
206 def is_complete(self, block):
206 def is_complete(self, block):
207 """Determine if block is complete.
207 """Determine if block is complete.
208
208
209 Parameters
209 Parameters
210 block : string
210 block : string
211
211
212 Result
212 Result
213 True if block can be sent to the engine without compile errors.
213 True if block can be sent to the engine without compile errors.
214 False otherwise.
214 False otherwise.
215 """
215 """
216
216
217 try:
217 try:
218 is_complete = codeop.compile_command(block.rstrip() + '\n\n',
218 is_complete = codeop.compile_command(block.rstrip() + '\n\n',
219 "<string>", "exec")
219 "<string>", "exec")
220 except:
220 except:
221 return False
221 return False
222
222
223 lines = block.split('\n')
223 lines = block.split('\n')
224 return ((is_complete is not None)
224 return ((is_complete is not None)
225 and (len(lines)==1 or str(lines[-1])==''))
225 and (len(lines)==1 or str(lines[-1])==''))
226
226
227
227
228 def execute(self, block, blockID=None):
228 def execute(self, block, blockID=None):
229 """Execute the block and return the result.
229 """Execute the block and return the result.
230
230
231 Parameters:
231 Parameters:
232 block : {str, AST}
232 block : {str, AST}
233 blockID : any
233 blockID : any
234 Caller may provide an ID to identify this block.
234 Caller may provide an ID to identify this block.
235 result['blockID'] := blockID
235 result['blockID'] := blockID
236
236
237 Result:
237 Result:
238 Deferred result of self.interpreter.execute
238 Deferred result of self.interpreter.execute
239 """
239 """
240
240
241 if(not self.is_complete(block)):
241 if(not self.is_complete(block)):
242 raise Exception("Block is not compilable")
242 raise Exception("Block is not compilable")
243
243
244 if(blockID == None):
244 if(blockID == None):
245 blockID = guid.generate()
245 blockID = guid.generate()
246
246
247 try:
247 try:
248 result = self.shell.execute(block)
248 result = self.shell.execute(block)
249 except Exception,e:
249 except Exception,e:
@@ -254,90 +254,90 b' class FrontEndBase(object):'
254 result = self._add_block_id_for_result(result, blockID=blockID)
254 result = self._add_block_id_for_result(result, blockID=blockID)
255 result = self.update_cell_prompt(result, blockID=blockID)
255 result = self.update_cell_prompt(result, blockID=blockID)
256 result = self.render_result(result)
256 result = self.render_result(result)
257
257
258 return result
258 return result
259
259
260
260
261 def _add_block_id_for_result(self, result, blockID):
261 def _add_block_id_for_result(self, result, blockID):
262 """Add the blockID to result or failure. Unfortunatley, we have to
262 """Add the blockID to result or failure. Unfortunatley, we have to
263 treat failures differently than result dicts.
263 treat failures differently than result dicts.
264 """
264 """
265
265
266 result['blockID'] = blockID
266 result['blockID'] = blockID
267
267
268 return result
268 return result
269
269
270 def _add_block_id_for_failure(self, failure, blockID):
270 def _add_block_id_for_failure(self, failure, blockID):
271 """_add_block_id_for_failure"""
271 """_add_block_id_for_failure"""
272 failure.blockID = blockID
272 failure.blockID = blockID
273 return failure
273 return failure
274
274
275
275
276 def _add_history(self, result, block=None):
276 def _add_history(self, result, block=None):
277 """Add block to the history"""
277 """Add block to the history"""
278
278
279 assert(block != None)
279 assert(block != None)
280 self.history.add_items([block])
280 self.history.add_items([block])
281 self.history_cursor += 1
281 self.history_cursor += 1
282
282
283 return result
283 return result
284
284
285
285
286 def get_history_previous(self, current_block):
286 def get_history_previous(self, current_block):
287 """ Returns previous history string and decrement history cursor.
287 """ Returns previous history string and decrement history cursor.
288 """
288 """
289 command = self.history.get_history_item(self.history_cursor - 1)
289 command = self.history.get_history_item(self.history_cursor - 1)
290
290
291 if command is not None:
291 if command is not None:
292 if(self.history_cursor+1 == len(self.history.input_cache)):
292 if(self.history_cursor+1 == len(self.history.input_cache)):
293 self.history.input_cache[self.history_cursor] = current_block
293 self.history.input_cache[self.history_cursor] = current_block
294 self.history_cursor -= 1
294 self.history_cursor -= 1
295 return command
295 return command
296
296
297
297
298 def get_history_next(self):
298 def get_history_next(self):
299 """ Returns next history string and increment history cursor.
299 """ Returns next history string and increment history cursor.
300 """
300 """
301 command = self.history.get_history_item(self.history_cursor+1)
301 command = self.history.get_history_item(self.history_cursor+1)
302
302
303 if command is not None:
303 if command is not None:
304 self.history_cursor += 1
304 self.history_cursor += 1
305 return command
305 return command
306
306
307 ###
307 ###
308 # Subclasses probably want to override these methods...
308 # Subclasses probably want to override these methods...
309 ###
309 ###
310
310
311 def update_cell_prompt(self, result, blockID=None):
311 def update_cell_prompt(self, result, blockID=None):
312 """Subclass may override to update the input prompt for a block.
312 """Subclass may override to update the input prompt for a block.
313
313
314 This method only really makes sens in asyncrhonous frontend.
314 This method only really makes sens in asyncrhonous frontend.
315 Since this method will be called as a
315 Since this method will be called as a
316 twisted.internet.defer.Deferred's callback, implementations should
316 twisted.internet.defer.Deferred's callback, implementations should
317 return result when finished.
317 return result when finished.
318 """
318 """
319
319
320 raise NotImplementedError
320 raise NotImplementedError
321
321
322
322
323 def render_result(self, result):
323 def render_result(self, result):
324 """Subclasses must override to render result.
324 """Subclasses must override to render result.
325
325
326 In asynchronous frontends, this method will be called as a
326 In asynchronous frontends, this method will be called as a
327 twisted.internet.defer.Deferred's callback. Implementations
327 twisted.internet.defer.Deferred's callback. Implementations
328 should thus return result when finished.
328 should thus return result when finished.
329 """
329 """
330
330
331 raise NotImplementedError
331 raise NotImplementedError
332
332
333
333
334 def render_error(self, failure):
334 def render_error(self, failure):
335 """Subclasses must override to render the failure.
335 """Subclasses must override to render the failure.
336
336
337 In asynchronous frontends, this method will be called as a
337 In asynchronous frontends, this method will be called as a
338 twisted.internet.defer.Deferred's callback. Implementations
338 twisted.internet.defer.Deferred's callback. Implementations
339 should thus return result when finished.
339 should thus return result when finished.
340 """
340 """
341
341
342 raise NotImplementedError
342 raise NotImplementedError
343
343
@@ -48,13 +48,13 b' class LineFrontEndBase(FrontEndBase):'
48 to be the base class behind all the frontend that are line-oriented,
48 to be the base class behind all the frontend that are line-oriented,
49 rather than block-oriented.
49 rather than block-oriented.
50 """
50 """
51
51
52 # We need to keep the prompt number, to be able to increment
52 # We need to keep the prompt number, to be able to increment
53 # it when there is an exception.
53 # it when there is an exception.
54 prompt_number = 1
54 prompt_number = 1
55
55
56 # We keep a reference to the last result: it helps testing and
56 # We keep a reference to the last result: it helps testing and
57 # programatic control of the frontend.
57 # programatic control of the frontend.
58 last_result = dict(number=0)
58 last_result = dict(number=0)
59
59
60 # The last prompt displayed. Useful for continuation prompts.
60 # The last prompt displayed. Useful for continuation prompts.
@@ -93,11 +93,11 b' class LineFrontEndBase(FrontEndBase):'
93
93
94 def complete(self, line):
94 def complete(self, line):
95 """Complete line in engine's user_ns
95 """Complete line in engine's user_ns
96
96
97 Parameters
97 Parameters
98 ----------
98 ----------
99 line : string
99 line : string
100
100
101 Returns
101 Returns
102 -------
102 -------
103 The replacement for the line and the list of possible completions.
103 The replacement for the line and the list of possible completions.
@@ -105,11 +105,11 b' class LineFrontEndBase(FrontEndBase):'
105 completions = self.shell.complete(line)
105 completions = self.shell.complete(line)
106 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
106 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
107 if completions:
107 if completions:
108 prefix = common_prefix(completions)
108 prefix = common_prefix(completions)
109 residual = complete_sep.split(line)[:-1]
109 residual = complete_sep.split(line)[:-1]
110 line = line[:-len(residual)] + prefix
110 line = line[:-len(residual)] + prefix
111 return line, completions
111 return line, completions
112
112
113
113
114 def render_result(self, result):
114 def render_result(self, result):
115 """ Frontend-specific rendering of the result of a calculation
115 """ Frontend-specific rendering of the result of a calculation
@@ -118,15 +118,15 b' class LineFrontEndBase(FrontEndBase):'
118 if 'stdout' in result and result['stdout']:
118 if 'stdout' in result and result['stdout']:
119 self.write('\n' + result['stdout'])
119 self.write('\n' + result['stdout'])
120 if 'display' in result and result['display']:
120 if 'display' in result and result['display']:
121 self.write("%s%s\n" % (
121 self.write("%s%s\n" % (
122 self.output_prompt_template.substitute(
122 self.output_prompt_template.substitute(
123 number=result['number']),
123 number=result['number']),
124 result['display']['pprint']
124 result['display']['pprint']
125 ) )
125 ) )
126
126
127
127
128 def render_error(self, failure):
128 def render_error(self, failure):
129 """ Frontend-specific rendering of error.
129 """ Frontend-specific rendering of error.
130 """
130 """
131 self.write('\n\n'+str(failure)+'\n\n')
131 self.write('\n\n'+str(failure)+'\n\n')
132 return failure
132 return failure
@@ -146,7 +146,7 b' class LineFrontEndBase(FrontEndBase):'
146 # thus want to consider an empty string as a complete
146 # thus want to consider an empty string as a complete
147 # statement.
147 # statement.
148 return True
148 return True
149 elif ( len(self.input_buffer.split('\n'))>2
149 elif ( len(self.input_buffer.split('\n'))>2
150 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
150 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
151 return False
151 return False
152 else:
152 else:
@@ -157,7 +157,7 b' class LineFrontEndBase(FrontEndBase):'
157 # This should probably be done in a different place (like
157 # This should probably be done in a different place (like
158 # maybe 'prefilter_input' method? For now, this works.
158 # maybe 'prefilter_input' method? For now, this works.
159 clean_string = string.rstrip('\n')
159 clean_string = string.rstrip('\n')
160 if not clean_string.endswith('\\'): clean_string +='\n\n'
160 if not clean_string.endswith('\\'): clean_string +='\n\n'
161 is_complete = codeop.compile_command(clean_string,
161 is_complete = codeop.compile_command(clean_string,
162 "<string>", "exec")
162 "<string>", "exec")
163 self.release_output()
163 self.release_output()
@@ -228,10 +228,10 b' class LineFrontEndBase(FrontEndBase):'
228
228
229
229
230 def complete_current_input(self):
230 def complete_current_input(self):
231 """ Do code completion on current line.
231 """ Do code completion on current line.
232 """
232 """
233 if self.debug:
233 if self.debug:
234 print >>sys.__stdout__, "complete_current_input",
234 print >>sys.__stdout__, "complete_current_input",
235 line = self.input_buffer
235 line = self.input_buffer
236 new_line, completions = self.complete(line)
236 new_line, completions = self.complete(line)
237 if len(completions)>1:
237 if len(completions)>1:
@@ -241,7 +241,7 b' class LineFrontEndBase(FrontEndBase):'
241 if self.debug:
241 if self.debug:
242 print >>sys.__stdout__, 'line', line
242 print >>sys.__stdout__, 'line', line
243 print >>sys.__stdout__, 'new_line', new_line
243 print >>sys.__stdout__, 'new_line', new_line
244 print >>sys.__stdout__, completions
244 print >>sys.__stdout__, completions
245
245
246
246
247 def get_line_width(self):
247 def get_line_width(self):
@@ -259,10 +259,10 b' class LineFrontEndBase(FrontEndBase):'
259 """
259 """
260 if new_line is None:
260 if new_line is None:
261 new_line = self.input_buffer
261 new_line = self.input_buffer
262
262
263 self.write('\n')
263 self.write('\n')
264 max_len = len(max(possibilities, key=len)) + 1
264 max_len = len(max(possibilities, key=len)) + 1
265
265
266 # Now we check how much symbol we can put on a line...
266 # Now we check how much symbol we can put on a line...
267 chars_per_line = self.get_line_width()
267 chars_per_line = self.get_line_width()
268 symbols_per_line = max(1, chars_per_line/max_len)
268 symbols_per_line = max(1, chars_per_line/max_len)
@@ -283,7 +283,7 b' class LineFrontEndBase(FrontEndBase):'
283
283
284
284
285 def new_prompt(self, prompt):
285 def new_prompt(self, prompt):
286 """ Prints a prompt and starts a new editing buffer.
286 """ Prints a prompt and starts a new editing buffer.
287
287
288 Subclasses should use this method to make sure that the
288 Subclasses should use this method to make sure that the
289 terminal is put in a state favorable for a new line
289 terminal is put in a state favorable for a new line
@@ -297,7 +297,7 b' class LineFrontEndBase(FrontEndBase):'
297 """Returns the current continuation prompt.
297 """Returns the current continuation prompt.
298 """
298 """
299 return ("."*(len(self.last_prompt)-2) + ': ')
299 return ("."*(len(self.last_prompt)-2) + ': ')
300
300
301
301
302 def execute_command(self, command, hidden=False):
302 def execute_command(self, command, hidden=False):
303 """ Execute a command, not only in the model, but also in the
303 """ Execute a command, not only in the model, but also in the
@@ -308,7 +308,7 b' class LineFrontEndBase(FrontEndBase):'
308 #--------------------------------------------------------------------------
308 #--------------------------------------------------------------------------
309 # Private API
309 # Private API
310 #--------------------------------------------------------------------------
310 #--------------------------------------------------------------------------
311
311
312 def _on_enter(self, new_line_pos=0):
312 def _on_enter(self, new_line_pos=0):
313 """ Called when the return key is pressed in a line editing
313 """ Called when the return key is pressed in a line editing
314 buffer.
314 buffer.
@@ -339,7 +339,7 b' class LineFrontEndBase(FrontEndBase):'
339 new_line_pos = -new_line_pos
339 new_line_pos = -new_line_pos
340 lines = current_buffer.split('\n')[:-1]
340 lines = current_buffer.split('\n')[:-1]
341 prompt_less_lines = prompt_less_buffer.split('\n')
341 prompt_less_lines = prompt_less_buffer.split('\n')
342 # Create the new line, with the continuation prompt, and the
342 # Create the new line, with the continuation prompt, and the
343 # same amount of indent than the line above it.
343 # same amount of indent than the line above it.
344 new_line = self.continuation_prompt() + \
344 new_line = self.continuation_prompt() + \
345 self._get_indent_string('\n'.join(
345 self._get_indent_string('\n'.join(
@@ -356,7 +356,7 b' class LineFrontEndBase(FrontEndBase):'
356 else:
356 else:
357 lines.insert(new_line_pos, new_line)
357 lines.insert(new_line_pos, new_line)
358 self.input_buffer = '\n'.join(lines)
358 self.input_buffer = '\n'.join(lines)
359
359
360
360
361 def _get_indent_string(self, string):
361 def _get_indent_string(self, string):
362 """ Return the string of whitespace that prefixes a line. Used to
362 """ Return the string of whitespace that prefixes a line. Used to
@@ -369,5 +369,5 b' class LineFrontEndBase(FrontEndBase):'
369 ' '*(indent_chars % 4)
369 ' '*(indent_chars % 4)
370
370
371 return indent_string
371 return indent_string
372
372
373
373
@@ -51,7 +51,7 b' def mk_system_call(system_call_function, command):'
51 return my_system_call
51 return my_system_call
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Frontend class using ipython0 to do the prefiltering.
54 # Frontend class using ipython0 to do the prefiltering.
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 class PrefilterFrontEnd(LineFrontEndBase):
57 class PrefilterFrontEnd(LineFrontEndBase):
@@ -65,7 +65,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
65 """
65 """
66
66
67 debug = False
67 debug = False
68
68
69 def __init__(self, ipython0=None, *args, **kwargs):
69 def __init__(self, ipython0=None, *args, **kwargs):
70 """ Parameters
70 """ Parameters
71 ----------
71 ----------
@@ -99,7 +99,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
99 __builtin__.raw_input = old_rawinput
99 __builtin__.raw_input = old_rawinput
100 self.ipython0 = ipython0
100 self.ipython0 = ipython0
101 # Set the pager:
101 # Set the pager:
102 self.ipython0.set_hook('show_in_pager',
102 self.ipython0.set_hook('show_in_pager',
103 lambda s, string: self.write("\n" + string))
103 lambda s, string: self.write("\n" + string))
104 self.ipython0.write = self.write
104 self.ipython0.write = self.write
105 self._ip = _ip = self.ipython0
105 self._ip = _ip = self.ipython0
@@ -109,7 +109,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
109 # XXX: Muck around with magics so that they work better
109 # XXX: Muck around with magics so that they work better
110 # in our environment
110 # in our environment
111 if not sys.platform.startswith('win'):
111 if not sys.platform.startswith('win'):
112 self.ipython0.magic_ls = mk_system_call(self.system_call,
112 self.ipython0.magic_ls = mk_system_call(self.system_call,
113 'ls -CF')
113 'ls -CF')
114 # And now clean up the mess created by ipython0
114 # And now clean up the mess created by ipython0
115 self.release_output()
115 self.release_output()
@@ -122,7 +122,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
122 self.start()
122 self.start()
123
123
124 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
125 # FrontEndBase interface
125 # FrontEndBase interface
126 #--------------------------------------------------------------------------
126 #--------------------------------------------------------------------------
127
127
128 def show_traceback(self):
128 def show_traceback(self):
@@ -147,8 +147,8 b' class PrefilterFrontEnd(LineFrontEndBase):'
147
147
148 def save_output_hooks(self):
148 def save_output_hooks(self):
149 """ Store all the output hooks we can think of, to be able to
149 """ Store all the output hooks we can think of, to be able to
150 restore them.
150 restore them.
151
151
152 We need to do this early, as starting the ipython0 instance will
152 We need to do this early, as starting the ipython0 instance will
153 screw ouput hooks.
153 screw ouput hooks.
154 """
154 """
@@ -178,8 +178,8 b' class PrefilterFrontEnd(LineFrontEndBase):'
178 Term.cerr.write = self.__old_cerr_write
178 Term.cerr.write = self.__old_cerr_write
179 sys.stdout = self.__old_stdout
179 sys.stdout = self.__old_stdout
180 sys.stderr = self.__old_stderr
180 sys.stderr = self.__old_stderr
181 pydoc.help.output = self.__old_help_output
181 pydoc.help.output = self.__old_help_output
182 sys.displayhook = self.__old_display_hook
182 sys.displayhook = self.__old_display_hook
183
183
184
184
185 def complete(self, line):
185 def complete(self, line):
@@ -191,12 +191,12 b' class PrefilterFrontEnd(LineFrontEndBase):'
191 key = lambda x: x.replace('_', '')
191 key = lambda x: x.replace('_', '')
192 completions.sort(key=key)
192 completions.sort(key=key)
193 if completions:
193 if completions:
194 prefix = common_prefix(completions)
194 prefix = common_prefix(completions)
195 line = line[:-len(word)] + prefix
195 line = line[:-len(word)] + prefix
196 return line, completions
196 return line, completions
197
197
198 #--------------------------------------------------------------------------
198 #--------------------------------------------------------------------------
199 # LineFrontEndBase interface
199 # LineFrontEndBase interface
200 #--------------------------------------------------------------------------
200 #--------------------------------------------------------------------------
201
201
202 def prefilter_input(self, input_string):
202 def prefilter_input(self, input_string):
@@ -209,7 +209,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
209 # capture it.
209 # capture it.
210 self.capture_output()
210 self.capture_output()
211 self.last_result = dict(number=self.prompt_number)
211 self.last_result = dict(number=self.prompt_number)
212
212
213 try:
213 try:
214 try:
214 try:
215 for line in input_string.split('\n'):
215 for line in input_string.split('\n'):
@@ -227,9 +227,9 b' class PrefilterFrontEnd(LineFrontEndBase):'
227 return filtered_string
227 return filtered_string
228
228
229 #--------------------------------------------------------------------------
229 #--------------------------------------------------------------------------
230 # PrefilterFrontEnd interface
230 # PrefilterFrontEnd interface
231 #--------------------------------------------------------------------------
231 #--------------------------------------------------------------------------
232
232
233 def system_call(self, command_string):
233 def system_call(self, command_string):
234 """ Allows for frontend to define their own system call, to be
234 """ Allows for frontend to define their own system call, to be
235 able capture output and redirect input.
235 able capture output and redirect input.
@@ -250,7 +250,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
250 # that in the 'pyreadline' module (modes/basemode.py) where we break at
250 # that in the 'pyreadline' module (modes/basemode.py) where we break at
251 # each delimiter and try to complete the residual line, until we get a
251 # each delimiter and try to complete the residual line, until we get a
252 # successful list of completions.
252 # successful list of completions.
253 expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})'
253 expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})'
254 complete_sep = re.compile(expression)
254 complete_sep = re.compile(expression)
255 text = complete_sep.split(line)[-1]
255 text = complete_sep.split(line)[-1]
256 return text
256 return text
@@ -1,4 +1,4 b''
1 # Addapted from killableprocess.py.
1 # Addapted from killableprocess.py.
2 #______________________________________________________________________________
2 #______________________________________________________________________________
3 #
3 #
4 # killableprocess - subprocesses which can be reliably killed
4 # killableprocess - subprocesses which can be reliably killed
@@ -118,10 +118,10 b' else:'
118
118
119 if startupinfo is None:
119 if startupinfo is None:
120 startupinfo = winprocess.STARTUPINFO()
120 startupinfo = winprocess.STARTUPINFO()
121
121
122 if None not in (p2cread, c2pwrite, errwrite):
122 if None not in (p2cread, c2pwrite, errwrite):
123 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
123 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
124
124
125 startupinfo.hStdInput = int(p2cread)
125 startupinfo.hStdInput = int(p2cread)
126 startupinfo.hStdOutput = int(c2pwrite)
126 startupinfo.hStdOutput = int(c2pwrite)
127 startupinfo.hStdError = int(errwrite)
127 startupinfo.hStdError = int(errwrite)
@@ -132,7 +132,7 b' else:'
132 args = comspec + " /c " + args
132 args = comspec + " /c " + args
133
133
134 # We create a new job for this process, so that we can kill
134 # We create a new job for this process, so that we can kill
135 # the process and any sub-processes
135 # the process and any sub-processes
136 self._job = winprocess.CreateJobObject()
136 self._job = winprocess.CreateJobObject()
137
137
138 creationflags |= winprocess.CREATE_SUSPENDED
138 creationflags |= winprocess.CREATE_SUSPENDED
@@ -145,7 +145,7 b' else:'
145 creationflags,
145 creationflags,
146 winprocess.EnvironmentBlock(env),
146 winprocess.EnvironmentBlock(env),
147 cwd, startupinfo)
147 cwd, startupinfo)
148
148
149 self._child_created = True
149 self._child_created = True
150 self._handle = hp
150 self._handle = hp
151 self._thread = ht
151 self._thread = ht
@@ -173,7 +173,7 b' else:'
173 winprocess.TerminateJobObject(self._job, 127)
173 winprocess.TerminateJobObject(self._job, 127)
174 else:
174 else:
175 winprocess.TerminateProcess(self._handle, 127)
175 winprocess.TerminateProcess(self._handle, 127)
176 self.returncode = 127
176 self.returncode = 127
177 else:
177 else:
178 if group:
178 if group:
179 os.killpg(self.pid, signal.SIGKILL)
179 os.killpg(self.pid, signal.SIGKILL)
@@ -1,6 +1,6 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Object for encapsulating process execution by using callbacks for stdout,
3 Object for encapsulating process execution by using callbacks for stdout,
4 stderr and stdin.
4 stderr and stdin.
5 """
5 """
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
@@ -26,10 +26,10 b' class PipedProcess(Thread):'
26 killing it.
26 killing it.
27 """
27 """
28
28
29 def __init__(self, command_string, out_callback,
29 def __init__(self, command_string, out_callback,
30 end_callback=None,):
30 end_callback=None,):
31 """ command_string: the command line executed to start the
31 """ command_string: the command line executed to start the
32 process.
32 process.
33
33
34 out_callback: the python callable called on stdout/stderr.
34 out_callback: the python callable called on stdout/stderr.
35
35
@@ -43,7 +43,7 b' class PipedProcess(Thread):'
43 self.out_callback = out_callback
43 self.out_callback = out_callback
44 self.end_callback = end_callback
44 self.end_callback = end_callback
45 Thread.__init__(self)
45 Thread.__init__(self)
46
46
47
47
48 def run(self):
48 def run(self):
49 """ Start the process and hook up the callbacks.
49 """ Start the process and hook up the callbacks.
@@ -70,5 +70,5 b' class PipedProcess(Thread):'
70
70
71 if self.end_callback is not None:
71 if self.end_callback is not None:
72 self.end_callback()
72 self.end_callback()
73
73
74
74
@@ -2,20 +2,20 b''
2 """This file contains unittests for the asyncfrontendbase module."""
2 """This file contains unittests for the asyncfrontendbase module."""
3
3
4 #---------------------------------------------------------------------------
4 #---------------------------------------------------------------------------
5 # Copyright (C) 2008-2009 The IPython Development Team
5 # Copyright (C) 2008-2009 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #---------------------------------------------------------------------------
9 #---------------------------------------------------------------------------
10
10
11 #---------------------------------------------------------------------------
11 #---------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #---------------------------------------------------------------------------
13 #---------------------------------------------------------------------------
14
14
15 from twisted.trial import unittest
15 from twisted.trial import unittest
16
16
17 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
17 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
18 from IPython.frontend import frontendbase
18 from IPython.frontend import frontendbase
19 from IPython.kernel.engineservice import EngineService
19 from IPython.kernel.engineservice import EngineService
20 from IPython.testing.parametric import Parametric, parametric
20 from IPython.testing.parametric import Parametric, parametric
21
21
@@ -26,20 +26,20 b' from IPython.testing.parametric import Parametric, parametric'
26 class FrontEndCallbackChecker(AsyncFrontEndBase):
26 class FrontEndCallbackChecker(AsyncFrontEndBase):
27 """FrontEndBase subclass for checking callbacks"""
27 """FrontEndBase subclass for checking callbacks"""
28 def __init__(self, engine=None, history=None):
28 def __init__(self, engine=None, history=None):
29 super(FrontEndCallbackChecker, self).__init__(engine=engine,
29 super(FrontEndCallbackChecker, self).__init__(engine=engine,
30 history=history)
30 history=history)
31 self.updateCalled = False
31 self.updateCalled = False
32 self.renderResultCalled = False
32 self.renderResultCalled = False
33 self.renderErrorCalled = False
33 self.renderErrorCalled = False
34
34
35 def update_cell_prompt(self, result, blockID=None):
35 def update_cell_prompt(self, result, blockID=None):
36 self.updateCalled = True
36 self.updateCalled = True
37 return result
37 return result
38
38
39 def render_result(self, result):
39 def render_result(self, result):
40 self.renderResultCalled = True
40 self.renderResultCalled = True
41 return result
41 return result
42
42
43 def render_error(self, failure):
43 def render_error(self, failure):
44 self.renderErrorCalled = True
44 self.renderErrorCalled = True
45 return failure
45 return failure
@@ -48,50 +48,50 b' class FrontEndCallbackChecker(AsyncFrontEndBase):'
48 class TestAsyncFrontendBase(unittest.TestCase):
48 class TestAsyncFrontendBase(unittest.TestCase):
49 def setUp(self):
49 def setUp(self):
50 """Setup the EngineService and FrontEndBase"""
50 """Setup the EngineService and FrontEndBase"""
51
51
52 self.fb = FrontEndCallbackChecker(engine=EngineService())
52 self.fb = FrontEndCallbackChecker(engine=EngineService())
53
53
54 def test_implements_IFrontEnd(self):
54 def test_implements_IFrontEnd(self):
55 self.assert_(frontendbase.IFrontEnd.implementedBy(
55 self.assert_(frontendbase.IFrontEnd.implementedBy(
56 AsyncFrontEndBase))
56 AsyncFrontEndBase))
57
57
58 def test_is_complete_returns_False_for_incomplete_block(self):
58 def test_is_complete_returns_False_for_incomplete_block(self):
59 block = """def test(a):"""
59 block = """def test(a):"""
60 self.assert_(self.fb.is_complete(block) == False)
60 self.assert_(self.fb.is_complete(block) == False)
61
61
62 def test_is_complete_returns_True_for_complete_block(self):
62 def test_is_complete_returns_True_for_complete_block(self):
63 block = """def test(a): pass"""
63 block = """def test(a): pass"""
64 self.assert_(self.fb.is_complete(block))
64 self.assert_(self.fb.is_complete(block))
65 block = """a=3"""
65 block = """a=3"""
66 self.assert_(self.fb.is_complete(block))
66 self.assert_(self.fb.is_complete(block))
67
67
68 def test_blockID_added_to_result(self):
68 def test_blockID_added_to_result(self):
69 block = """3+3"""
69 block = """3+3"""
70 d = self.fb.execute(block, blockID='TEST_ID')
70 d = self.fb.execute(block, blockID='TEST_ID')
71 d.addCallback(lambda r: self.assert_(r['blockID']=='TEST_ID'))
71 d.addCallback(lambda r: self.assert_(r['blockID']=='TEST_ID'))
72 return d
72 return d
73
73
74 def test_blockID_added_to_failure(self):
74 def test_blockID_added_to_failure(self):
75 block = "raise Exception()"
75 block = "raise Exception()"
76 d = self.fb.execute(block,blockID='TEST_ID')
76 d = self.fb.execute(block,blockID='TEST_ID')
77 d.addErrback(lambda f: self.assert_(f.blockID=='TEST_ID'))
77 d.addErrback(lambda f: self.assert_(f.blockID=='TEST_ID'))
78 return d
78 return d
79
79
80 def test_callbacks_added_to_execute(self):
80 def test_callbacks_added_to_execute(self):
81 d = self.fb.execute("10+10")
81 d = self.fb.execute("10+10")
82 d.addCallback(lambda r: self.assert_(self.fb.updateCalled and self.fb.renderResultCalled))
82 d.addCallback(lambda r: self.assert_(self.fb.updateCalled and self.fb.renderResultCalled))
83 return d
83 return d
84
84
85 def test_error_callback_added_to_execute(self):
85 def test_error_callback_added_to_execute(self):
86 """Test that render_error called on execution error."""
86 """Test that render_error called on execution error."""
87
87
88 d = self.fb.execute("raise Exception()")
88 d = self.fb.execute("raise Exception()")
89 d.addErrback(lambda f: self.assert_(self.fb.renderErrorCalled))
89 d.addErrback(lambda f: self.assert_(self.fb.renderErrorCalled))
90 return d
90 return d
91
91
92 def test_history_returns_expected_block(self):
92 def test_history_returns_expected_block(self):
93 """Make sure history browsing doesn't fail."""
93 """Make sure history browsing doesn't fail."""
94
94
95 blocks = ["a=1","a=2","a=3"]
95 blocks = ["a=1","a=2","a=3"]
96 d = self.fb.execute(blocks[0])
96 d = self.fb.execute(blocks[0])
97 d.addCallback(lambda _: self.fb.execute(blocks[1]))
97 d.addCallback(lambda _: self.fb.execute(blocks[1]))
@@ -100,7 +100,7 b' class TestAsyncFrontendBase(unittest.TestCase):'
100 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-3]))
100 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-3]))
101 d.addCallback(lambda _: self.assert_(self.fb.get_history_next()==blocks[-2]))
101 d.addCallback(lambda _: self.assert_(self.fb.get_history_next()==blocks[-2]))
102 return d
102 return d
103
103
104 def test_history_returns_none_at_startup(self):
104 def test_history_returns_none_at_startup(self):
105 self.assert_(self.fb.get_history_previous("")==None)
105 self.assert_(self.fb.get_history_previous("")==None)
106 self.assert_(self.fb.get_history_next()==None)
106 self.assert_(self.fb.get_history_next()==None)
@@ -1,6 +1,6 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test the basic functionality of frontendbase.
3 Test the basic functionality of frontendbase.
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
@@ -15,7 +15,7 b' __docformat__ = "restructuredtext en"'
15 from IPython.frontend.frontendbase import FrontEndBase
15 from IPython.frontend.frontendbase import FrontEndBase
16
16
17 def test_iscomplete():
17 def test_iscomplete():
18 """ Check that is_complete works.
18 """ Check that is_complete works.
19 """
19 """
20 f = FrontEndBase()
20 f = FrontEndBase()
21 assert f.is_complete('(a + a)')
21 assert f.is_complete('(a + a)')
@@ -1,6 +1,6 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test the LineFrontEnd
3 Test the LineFrontEnd
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
@@ -27,7 +27,7 b' from IPython.testing.globalipapp import get_ipython'
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 class TestPrefilterFrontEnd(PrefilterFrontEnd):
29 class TestPrefilterFrontEnd(PrefilterFrontEnd):
30
30
31 input_prompt_template = string.Template('')
31 input_prompt_template = string.Template('')
32 output_prompt_template = string.Template('')
32 output_prompt_template = string.Template('')
33 banner = ''
33 banner = ''
@@ -42,7 +42,7 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
42 self.out.truncate()
42 self.out.truncate()
43
43
44 def write(self, string, *args, **kwargs):
44 def write(self, string, *args, **kwargs):
45 self.out.write(string)
45 self.out.write(string)
46
46
47 def _on_enter(self):
47 def _on_enter(self):
48 self.input_buffer += '\n'
48 self.input_buffer += '\n'
@@ -151,11 +151,11 b' def test_capture():'
151 out_value = f.out.getvalue()
151 out_value = f.out.getvalue()
152 yield assert_equal, out_value, '1'
152 yield assert_equal, out_value, '1'
153
153
154
154
155 @isolate_ipython0
155 @isolate_ipython0
156 def test_magic():
156 def test_magic():
157 """ Test the magic expansion and history.
157 """ Test the magic expansion and history.
158
158
159 This test is fairly fragile and will break when magics change.
159 This test is fairly fragile and will break when magics change.
160 """
160 """
161 f = TestPrefilterFrontEnd()
161 f = TestPrefilterFrontEnd()
@@ -188,7 +188,7 b' def test_help():'
188 assert 'traceback' not in f.last_result
188 assert 'traceback' not in f.last_result
189 ## XXX: ipython doctest magic breaks this. I have no clue why
189 ## XXX: ipython doctest magic breaks this. I have no clue why
190 #out_value = f.out.getvalue()
190 #out_value = f.out.getvalue()
191 #assert out_value.split()[-1] == 'foobar'
191 #assert out_value.split()[-1] == 'foobar'
192
192
193
193
194 @isolate_ipython0
194 @isolate_ipython0
@@ -35,7 +35,7 b' def test_io():'
35 """ Checks that we can send characters on stdin to the process.
35 """ Checks that we can send characters on stdin to the process.
36 """
36 """
37 s = StringIO()
37 s = StringIO()
38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
39 out_callback=s.write, )
39 out_callback=s.write, )
40 p.start()
40 p.start()
41 test_string = '12345\n'
41 test_string = '12345\n'
@@ -52,12 +52,12 b' def test_kill():'
52 """ Check that we can kill a process, and its subprocess.
52 """ Check that we can kill a process, and its subprocess.
53 """
53 """
54 s = StringIO()
54 s = StringIO()
55 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
55 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
56 out_callback=s.write, )
56 out_callback=s.write, )
57 p.start()
57 p.start()
58 while not hasattr(p, 'process'):
58 while not hasattr(p, 'process'):
59 sleep(0.1)
59 sleep(0.1)
60 p.process.kill()
60 p.process.kill()
61 assert p.process.poll() is not None
61 assert p.process.poll() is not None
62
62
63
63
@@ -36,7 +36,7 b' import re'
36 # FIXME: Need to provide an API for non user-generated display on the
36 # FIXME: Need to provide an API for non user-generated display on the
37 # screen: this should not be editable by the user.
37 # screen: this should not be editable by the user.
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39 # Constants
39 # Constants
40 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
41 _COMPLETE_BUFFER_MARKER = 31
41 _COMPLETE_BUFFER_MARKER = 31
42 _ERROR_MARKER = 30
42 _ERROR_MARKER = 30
@@ -75,7 +75,7 b' _DEFAULT_STYLE = {'
75 # Default scintilla settings
75 # Default scintilla settings
76 'antialiasing' : True,
76 'antialiasing' : True,
77 'carret_color' : 'BLACK',
77 'carret_color' : 'BLACK',
78 'background_color' :'WHITE',
78 'background_color' :'WHITE',
79
79
80 #prompt definition
80 #prompt definition
81 'prompt_in1' : \
81 'prompt_in1' : \
@@ -94,14 +94,14 b' _TRACE_STYLE = 17'
94 # system colors
94 # system colors
95 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
95 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
96
96
97 # Translation table from ANSI escape sequences to color.
97 # Translation table from ANSI escape sequences to color.
98 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
98 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
99 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
99 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
100 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
100 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
101 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
101 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
102 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
102 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
103 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
103 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
104 '1;34': [12, 'LIGHT BLUE'], '1;35':
104 '1;34': [12, 'LIGHT BLUE'], '1;35':
105 [13, 'MEDIUM VIOLET RED'],
105 [13, 'MEDIUM VIOLET RED'],
106 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
106 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
107
107
@@ -133,7 +133,7 b' else:'
133 'size' : 10,
133 'size' : 10,
134 'size2': 8,
134 'size2': 8,
135 }
135 }
136
136
137
137
138 #-----------------------------------------------------------------------------
138 #-----------------------------------------------------------------------------
139 # The console widget class
139 # The console widget class
@@ -174,36 +174,36 b' class ConsoleWidget(editwindow.EditWindow):'
174 # Translation table from ANSI escape sequences to color. Override
174 # Translation table from ANSI escape sequences to color. Override
175 # this to specify your colors.
175 # this to specify your colors.
176 ANSI_STYLES = ANSI_STYLES.copy()
176 ANSI_STYLES = ANSI_STYLES.copy()
177
177
178 # Font faces
178 # Font faces
179 faces = FACES.copy()
179 faces = FACES.copy()
180
180
181 # Store the last time a refresh was done
181 # Store the last time a refresh was done
182 _last_refresh_time = 0
182 _last_refresh_time = 0
183
183
184 #--------------------------------------------------------------------------
184 #--------------------------------------------------------------------------
185 # Public API
185 # Public API
186 #--------------------------------------------------------------------------
186 #--------------------------------------------------------------------------
187
187
188 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
188 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
189 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
189 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
190 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
190 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
191 self.configure_scintilla()
191 self.configure_scintilla()
192 # Track if 'enter' key as ever been processed
192 # Track if 'enter' key as ever been processed
193 # This variable will only be reallowed until key goes up
193 # This variable will only be reallowed until key goes up
194 self.enter_catched = False
194 self.enter_catched = False
195 self.current_prompt_pos = 0
195 self.current_prompt_pos = 0
196
196
197 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
197 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
198 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
198 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
199
199
200
200
201 def write(self, text, refresh=True):
201 def write(self, text, refresh=True):
202 """ Write given text to buffer, while translating the ansi escape
202 """ Write given text to buffer, while translating the ansi escape
203 sequences.
203 sequences.
204 """
204 """
205 # XXX: do not put print statements to sys.stdout/sys.stderr in
205 # XXX: do not put print statements to sys.stdout/sys.stderr in
206 # this method, the print statements will call this method, as
206 # this method, the print statements will call this method, as
207 # you will end up with an infinit loop
207 # you will end up with an infinit loop
208 title = self.title_pat.split(text)
208 title = self.title_pat.split(text)
209 if len(title)>1:
209 if len(title)>1:
@@ -219,7 +219,7 b' class ConsoleWidget(editwindow.EditWindow):'
219 except UnicodeDecodeError:
219 except UnicodeDecodeError:
220 # XXX: Do I really want to skip the exception?
220 # XXX: Do I really want to skip the exception?
221 pass
221 pass
222
222
223 if segments:
223 if segments:
224 for ansi_tag, text in zip(segments[::2], segments[1::2]):
224 for ansi_tag, text in zip(segments[::2], segments[1::2]):
225 self.StartStyling(self.GetLength(), 0xFF)
225 self.StartStyling(self.GetLength(), 0xFF)
@@ -234,8 +234,8 b' class ConsoleWidget(editwindow.EditWindow):'
234 else:
234 else:
235 style = self.ANSI_STYLES[ansi_tag][0]
235 style = self.ANSI_STYLES[ansi_tag][0]
236
236
237 self.SetStyling(len(text), style)
237 self.SetStyling(len(text), style)
238
238
239 self.GotoPos(self.GetLength())
239 self.GotoPos(self.GetLength())
240 if refresh:
240 if refresh:
241 current_time = time.time()
241 current_time = time.time()
@@ -245,9 +245,9 b' class ConsoleWidget(editwindow.EditWindow):'
245 else:
245 else:
246 wx.Yield()
246 wx.Yield()
247 # self.ProcessEvent(wx.PaintEvent())
247 # self.ProcessEvent(wx.PaintEvent())
248 self._last_refresh_time = current_time
248 self._last_refresh_time = current_time
249
249
250
250
251 def new_prompt(self, prompt):
251 def new_prompt(self, prompt):
252 """ Prints a prompt at start of line, and move the start of the
252 """ Prints a prompt at start of line, and move the start of the
253 current block there.
253 current block there.
@@ -270,7 +270,7 b' class ConsoleWidget(editwindow.EditWindow):'
270 # ASCII-less prompt
270 # ASCII-less prompt
271 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
271 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
272 return "."*(len(ascii_less)-2) + ': '
272 return "."*(len(ascii_less)-2) + ': '
273
273
274
274
275 def scroll_to_bottom(self):
275 def scroll_to_bottom(self):
276 maxrange = self.GetScrollRange(wx.VERTICAL)
276 maxrange = self.GetScrollRange(wx.VERTICAL)
@@ -299,7 +299,7 b' class ConsoleWidget(editwindow.EditWindow):'
299 widget.
299 widget.
300 """
300 """
301 p = self.style.copy()
301 p = self.style.copy()
302
302
303 # Marker for complete buffer.
303 # Marker for complete buffer.
304 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
304 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
305 background=p['trace'])
305 background=p['trace'])
@@ -313,14 +313,14 b' class ConsoleWidget(editwindow.EditWindow):'
313
313
314 self.SetEOLMode(stc.STC_EOL_LF)
314 self.SetEOLMode(stc.STC_EOL_LF)
315
315
316 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
316 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
317 # the widget
317 # the widget
318 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
318 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
319 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
319 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
320 # Also allow Ctrl Shift "=" for poor non US keyboard users.
320 # Also allow Ctrl Shift "=" for poor non US keyboard users.
321 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
321 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
322 stc.STC_CMD_ZOOMIN)
322 stc.STC_CMD_ZOOMIN)
323
323
324 # Keys: we need to clear some of the keys the that don't play
324 # Keys: we need to clear some of the keys the that don't play
325 # well with a console.
325 # well with a console.
326 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
326 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
@@ -341,7 +341,7 b' class ConsoleWidget(editwindow.EditWindow):'
341 self.SetIndent(4)
341 self.SetIndent(4)
342 self.SetTabWidth(4)
342 self.SetTabWidth(4)
343
343
344 # we don't want scintilla's autocompletion to choose
344 # we don't want scintilla's autocompletion to choose
345 # automaticaly out of a single choice list, as we pop it up
345 # automaticaly out of a single choice list, as we pop it up
346 # automaticaly
346 # automaticaly
347 self.AutoCompSetChooseSingle(False)
347 self.AutoCompSetChooseSingle(False)
@@ -360,11 +360,11 b' class ConsoleWidget(editwindow.EditWindow):'
360 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
360 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
361
361
362 # styles
362 # styles
363
363
364 self.SetCaretForeground(p['carret_color'])
364 self.SetCaretForeground(p['carret_color'])
365
365
366 background_color = p['background_color']
366 background_color = p['background_color']
367
367
368 if 'default' in p:
368 if 'default' in p:
369 if 'back' not in p['default']:
369 if 'back' not in p['default']:
370 p['default'] += ',back:%s' % background_color
370 p['default'] += ',back:%s' % background_color
@@ -372,20 +372,20 b' class ConsoleWidget(editwindow.EditWindow):'
372 p['default'] += ',size:%s' % self.faces['size']
372 p['default'] += ',size:%s' % self.faces['size']
373 if 'face' not in p['default']:
373 if 'face' not in p['default']:
374 p['default'] += ',face:%s' % self.faces['mono']
374 p['default'] += ',face:%s' % self.faces['mono']
375
375
376 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
376 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
377 else:
377 else:
378 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
378 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
379 "fore:%s,back:%s,size:%d,face:%s"
379 "fore:%s,back:%s,size:%d,face:%s"
380 % (self.ANSI_STYLES['0;30'][1],
380 % (self.ANSI_STYLES['0;30'][1],
381 background_color,
381 background_color,
382 self.faces['size'], self.faces['mono']))
382 self.faces['size'], self.faces['mono']))
383
383
384 self.StyleClearAll()
384 self.StyleClearAll()
385
385
386 # XXX: two lines below are usefull if not using the lexer
386 # XXX: two lines below are usefull if not using the lexer
387 #for style in self.ANSI_STYLES.values():
387 #for style in self.ANSI_STYLES.values():
388 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
388 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
389
389
390 # prompt definition
390 # prompt definition
391 self.prompt_in1 = p['prompt_in1']
391 self.prompt_in1 = p['prompt_in1']
@@ -417,25 +417,25 b' class ConsoleWidget(editwindow.EditWindow):'
417 #we add a vertical line to console widget
417 #we add a vertical line to console widget
418 self.SetEdgeMode(stc.STC_EDGE_LINE)
418 self.SetEdgeMode(stc.STC_EDGE_LINE)
419 self.SetEdgeColumn(edge_column)
419 self.SetEdgeColumn(edge_column)
420
420
421
421
422 #--------------------------------------------------------------------------
422 #--------------------------------------------------------------------------
423 # EditWindow API
423 # EditWindow API
424 #--------------------------------------------------------------------------
424 #--------------------------------------------------------------------------
425
425
426 def OnUpdateUI(self, event):
426 def OnUpdateUI(self, event):
427 """ Override the OnUpdateUI of the EditWindow class, to prevent
427 """ Override the OnUpdateUI of the EditWindow class, to prevent
428 syntax highlighting both for faster redraw, and for more
428 syntax highlighting both for faster redraw, and for more
429 consistent look and feel.
429 consistent look and feel.
430 """
430 """
431
431
432
432
433 #--------------------------------------------------------------------------
433 #--------------------------------------------------------------------------
434 # Private API
434 # Private API
435 #--------------------------------------------------------------------------
435 #--------------------------------------------------------------------------
436
436
437 def _on_key_down(self, event, skip=True):
437 def _on_key_down(self, event, skip=True):
438 """ Key press callback used for correcting behavior for
438 """ Key press callback used for correcting behavior for
439 console-like interfaces: the cursor is constraint to be after
439 console-like interfaces: the cursor is constraint to be after
440 the last prompt.
440 the last prompt.
441
441
@@ -487,7 +487,7 b' class ConsoleWidget(editwindow.EditWindow):'
487 if event.ShiftDown():
487 if event.ShiftDown():
488 # Try to force execution
488 # Try to force execution
489 self.GotoPos(self.GetLength())
489 self.GotoPos(self.GetLength())
490 self.write('\n' + self.continuation_prompt(),
490 self.write('\n' + self.continuation_prompt(),
491 refresh=False)
491 refresh=False)
492 self._on_enter()
492 self._on_enter()
493 else:
493 else:
@@ -501,7 +501,7 b' class ConsoleWidget(editwindow.EditWindow):'
501 else:
501 else:
502 # FIXME: This behavior is not ideal: if the selection
502 # FIXME: This behavior is not ideal: if the selection
503 # is already started, it will jump.
503 # is already started, it will jump.
504 self.SetSelectionStart(self.current_prompt_pos)
504 self.SetSelectionStart(self.current_prompt_pos)
505 self.SetSelectionEnd(self.GetCurrentPos())
505 self.SetSelectionEnd(self.GetCurrentPos())
506 catched = True
506 catched = True
507
507
@@ -591,15 +591,15 b' class ConsoleWidget(editwindow.EditWindow):'
591 # Jump back up
591 # Jump back up
592 self.GotoPos(self.GetLineEndPosition(line_num-1))
592 self.GotoPos(self.GetLineEndPosition(line_num-1))
593 return True
593 return True
594 elif ( current_pos > self.GetLineEndPosition(line_num)
594 elif ( current_pos > self.GetLineEndPosition(line_num)
595 and not current_pos == self.GetLength()):
595 and not current_pos == self.GetLength()):
596 # Jump to next line
596 # Jump to next line
597 self.GotoPos(current_pos + 1 +
597 self.GotoPos(current_pos + 1 +
598 len(continuation_prompt))
598 len(continuation_prompt))
599 return True
599 return True
600
600
601 # We re-allow enter event processing
601 # We re-allow enter event processing
602 self.enter_catched = False
602 self.enter_catched = False
603 return False
603 return False
604
604
605
605
@@ -38,7 +38,7 b' class IPythonXController(WxController):'
38 self._input_state == 'readline':
38 self._input_state == 'readline':
39 wx.CallAfter(self.ask_exit)
39 wx.CallAfter(self.ask_exit)
40 else:
40 else:
41 WxController._on_key_down(self, event, skip=skip)
41 WxController._on_key_down(self, event, skip=skip)
42
42
43
43
44 def ask_exit(self):
44 def ask_exit(self):
@@ -56,7 +56,7 b' class IPythonXController(WxController):'
56 else:
56 else:
57 wx.CallAfter(wx.GetApp().Exit)
57 wx.CallAfter(wx.GetApp().Exit)
58 self.write('Exiting ...', refresh=False)
58 self.write('Exiting ...', refresh=False)
59
59
60
60
61 def do_exit(self):
61 def do_exit(self):
62 """ Exits the interpreter, kills the windows.
62 """ Exits the interpreter, kills the windows.
@@ -81,10 +81,10 b' class IPythonX(wx.Frame):'
81 self.Show(True)
81 self.Show(True)
82 wx.EVT_CLOSE(self, self.on_close)
82 wx.EVT_CLOSE(self, self.on_close)
83
83
84
84
85 def on_close(self, event):
85 def on_close(self, event):
86 """ Called on closing the windows.
86 """ Called on closing the windows.
87
87
88 Stops the event loop, to close all the child windows.
88 Stops the event loop, to close all the child windows.
89 """
89 """
90 wx.CallAfter(wx.Exit)
90 wx.CallAfter(wx.Exit)
@@ -47,7 +47,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
47 This class inherits from ConsoleWidget, that provides a console-like
47 This class inherits from ConsoleWidget, that provides a console-like
48 widget to provide a text-rendering widget suitable for a terminal.
48 widget to provide a text-rendering widget suitable for a terminal.
49 """
49 """
50
50
51 # Print debug info on what is happening to the console.
51 # Print debug info on what is happening to the console.
52 debug = False
52 debug = False
53
53
@@ -83,7 +83,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
83
83
84 # A flag governing the behavior of the input. Can be:
84 # A flag governing the behavior of the input. Can be:
85 #
85 #
86 # 'readline' for readline-like behavior with a prompt
86 # 'readline' for readline-like behavior with a prompt
87 # and an edit buffer.
87 # and an edit buffer.
88 # 'raw_input' similar to readline, but triggered by a raw-input
88 # 'raw_input' similar to readline, but triggered by a raw-input
89 # call. Can be used by subclasses to act differently.
89 # call. Can be used by subclasses to act differently.
@@ -111,7 +111,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
111 #--------------------------------------------------------------------------
111 #--------------------------------------------------------------------------
112 # Public API
112 # Public API
113 #--------------------------------------------------------------------------
113 #--------------------------------------------------------------------------
114
114
115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
116 size=wx.DefaultSize,
116 size=wx.DefaultSize,
117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
@@ -129,7 +129,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
129 self.style = styledef
129 self.style = styledef
130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
131 PrefilterFrontEnd.__init__(self, **kwds)
131 PrefilterFrontEnd.__init__(self, **kwds)
132
132
133 # Stick in our own raw_input:
133 # Stick in our own raw_input:
134 self.ipython0.raw_input = self.raw_input
134 self.ipython0.raw_input = self.raw_input
135
135
@@ -147,14 +147,14 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
147 self.shell.user_ns['self'] = self
147 self.shell.user_ns['self'] = self
148 # Inject our own raw_input in namespace
148 # Inject our own raw_input in namespace
149 self.shell.user_ns['raw_input'] = self.raw_input
149 self.shell.user_ns['raw_input'] = self.raw_input
150
150
151 def raw_input(self, prompt=''):
151 def raw_input(self, prompt=''):
152 """ A replacement from python's raw_input.
152 """ A replacement from python's raw_input.
153 """
153 """
154 self.new_prompt(prompt)
154 self.new_prompt(prompt)
155 self._input_state = 'raw_input'
155 self._input_state = 'raw_input'
156 if hasattr(self, '_cursor'):
156 if hasattr(self, '_cursor'):
157 del self._cursor
157 del self._cursor
158 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
158 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
159 self.__old_on_enter = self._on_enter
159 self.__old_on_enter = self._on_enter
160 event_loop = wx.EventLoop()
160 event_loop = wx.EventLoop()
@@ -162,7 +162,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
162 event_loop.Exit()
162 event_loop.Exit()
163 self._on_enter = my_on_enter
163 self._on_enter = my_on_enter
164 # XXX: Running a separate event_loop. Ugly.
164 # XXX: Running a separate event_loop. Ugly.
165 event_loop.Run()
165 event_loop.Run()
166 self._on_enter = self.__old_on_enter
166 self._on_enter = self.__old_on_enter
167 self._input_state = 'buffering'
167 self._input_state = 'buffering'
168 self._cursor = wx.BusyCursor()
168 self._cursor = wx.BusyCursor()
@@ -177,12 +177,12 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
177 self._running_process = False
177 self._running_process = False
178 event_loop.Exit()
178 event_loop.Exit()
179
179
180 self._running_process = PipedProcess(command_string,
180 self._running_process = PipedProcess(command_string,
181 out_callback=self.buffered_write,
181 out_callback=self.buffered_write,
182 end_callback = _end_system_call)
182 end_callback = _end_system_call)
183 self._running_process.start()
183 self._running_process.start()
184 # XXX: Running a separate event_loop. Ugly.
184 # XXX: Running a separate event_loop. Ugly.
185 event_loop.Run()
185 event_loop.Run()
186 # Be sure to flush the buffer.
186 # Be sure to flush the buffer.
187 self._buffer_flush(event=None)
187 self._buffer_flush(event=None)
188
188
@@ -191,7 +191,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
191 """ Analyse current and displays useful calltip for it.
191 """ Analyse current and displays useful calltip for it.
192 """
192 """
193 if self.debug:
193 if self.debug:
194 print >>sys.__stdout__, "do_calltip"
194 print >>sys.__stdout__, "do_calltip"
195 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
195 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
196 symbol = self.input_buffer
196 symbol = self.input_buffer
197 symbol_string = separators.split(symbol)[-1]
197 symbol_string = separators.split(symbol)[-1]
@@ -217,11 +217,11 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
217
217
218
218
219 def _popup_completion(self, create=False):
219 def _popup_completion(self, create=False):
220 """ Updates the popup completion menu if it exists. If create is
220 """ Updates the popup completion menu if it exists. If create is
221 true, open the menu.
221 true, open the menu.
222 """
222 """
223 if self.debug:
223 if self.debug:
224 print >>sys.__stdout__, "_popup_completion"
224 print >>sys.__stdout__, "_popup_completion"
225 line = self.input_buffer
225 line = self.input_buffer
226 if (self.AutoCompActive() and line and not line[-1] == '.') \
226 if (self.AutoCompActive() and line and not line[-1] == '.') \
227 or create==True:
227 or create==True:
@@ -230,7 +230,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
230 offset = len(self._get_completion_text(line))
230 offset = len(self._get_completion_text(line))
231 self.pop_completion(completions, offset=offset)
231 self.pop_completion(completions, offset=offset)
232 if self.debug:
232 if self.debug:
233 print >>sys.__stdout__, completions
233 print >>sys.__stdout__, completions
234
234
235
235
236 def buffered_write(self, text):
236 def buffered_write(self, text):
@@ -244,7 +244,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
244 self._out_buffer.append(text)
244 self._out_buffer.append(text)
245 self._out_buffer_lock.release()
245 self._out_buffer_lock.release()
246 if not self._buffer_flush_timer.IsRunning():
246 if not self._buffer_flush_timer.IsRunning():
247 wx.CallAfter(self._buffer_flush_timer.Start,
247 wx.CallAfter(self._buffer_flush_timer.Start,
248 milliseconds=100, oneShot=True)
248 milliseconds=100, oneShot=True)
249
249
250
250
@@ -257,9 +257,9 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
257
257
258
258
259 #--------------------------------------------------------------------------
259 #--------------------------------------------------------------------------
260 # LineFrontEnd interface
260 # LineFrontEnd interface
261 #--------------------------------------------------------------------------
261 #--------------------------------------------------------------------------
262
262
263 def execute(self, python_string, raw_string=None):
263 def execute(self, python_string, raw_string=None):
264 self._input_state = 'buffering'
264 self._input_state = 'buffering'
265 self.CallTipCancel()
265 self.CallTipCancel()
@@ -275,7 +275,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
275 # Use a callafter to update the display robustly under windows
275 # Use a callafter to update the display robustly under windows
276 def callback():
276 def callback():
277 self.GotoPos(self.GetLength())
277 self.GotoPos(self.GetLength())
278 PrefilterFrontEnd.execute(self, python_string,
278 PrefilterFrontEnd.execute(self, python_string,
279 raw_string=raw_string)
279 raw_string=raw_string)
280 wx.CallAfter(callback)
280 wx.CallAfter(callback)
281
281
@@ -314,7 +314,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
314 return True
314 return True
315
315
316
316
317 def save_output_hooks(self):
317 def save_output_hooks(self):
318 self.__old_raw_input = __builtin__.raw_input
318 self.__old_raw_input = __builtin__.raw_input
319 PrefilterFrontEnd.save_output_hooks(self)
319 PrefilterFrontEnd.save_output_hooks(self)
320
320
@@ -322,8 +322,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
322 self.SetLexer(stc.STC_LEX_NULL)
322 self.SetLexer(stc.STC_LEX_NULL)
323 PrefilterFrontEnd.capture_output(self)
323 PrefilterFrontEnd.capture_output(self)
324 __builtin__.raw_input = self.raw_input
324 __builtin__.raw_input = self.raw_input
325
325
326
326
327 def release_output(self):
327 def release_output(self):
328 __builtin__.raw_input = self.__old_raw_input
328 __builtin__.raw_input = self.__old_raw_input
329 PrefilterFrontEnd.release_output(self)
329 PrefilterFrontEnd.release_output(self)
@@ -346,11 +346,11 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
346 for i in range(start_line, self.GetCurrentLine()):
346 for i in range(start_line, self.GetCurrentLine()):
347 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
347 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
348
348
349
349
350 #--------------------------------------------------------------------------
350 #--------------------------------------------------------------------------
351 # FrontEndBase interface
351 # FrontEndBase interface
352 #--------------------------------------------------------------------------
352 #--------------------------------------------------------------------------
353
353
354 def render_error(self, e):
354 def render_error(self, e):
355 start_line = self.GetCurrentLine()
355 start_line = self.GetCurrentLine()
356 self.write('\n' + e + '\n')
356 self.write('\n' + e + '\n')
@@ -359,7 +359,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
359
359
360
360
361 #--------------------------------------------------------------------------
361 #--------------------------------------------------------------------------
362 # ConsoleWidget interface
362 # ConsoleWidget interface
363 #--------------------------------------------------------------------------
363 #--------------------------------------------------------------------------
364
364
365 def new_prompt(self, prompt):
365 def new_prompt(self, prompt):
@@ -405,7 +405,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
405 # get back to a prompt.
405 # get back to a prompt.
406 elif self._input_state == 'subprocess' and (
406 elif self._input_state == 'subprocess' and (
407 ( key_code <256 and not event.ControlDown() )
407 ( key_code <256 and not event.ControlDown() )
408 or
408 or
409 ( key_code in (ord('d'), ord('D')) and
409 ( key_code in (ord('d'), ord('D')) and
410 event.ControlDown())):
410 event.ControlDown())):
411 # We are running a process, we redirect keys.
411 # We are running a process, we redirect keys.
@@ -426,7 +426,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
426 self.do_calltip()
426 self.do_calltip()
427 elif self.AutoCompActive() and not key_code == ord('\t'):
427 elif self.AutoCompActive() and not key_code == ord('\t'):
428 event.Skip()
428 event.Skip()
429 if key_code in (wx.WXK_BACK, wx.WXK_DELETE):
429 if key_code in (wx.WXK_BACK, wx.WXK_DELETE):
430 wx.CallAfter(self._popup_completion, create=True)
430 wx.CallAfter(self._popup_completion, create=True)
431 elif not key_code in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
431 elif not key_code in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
432 wx.WXK_RIGHT, wx.WXK_ESCAPE):
432 wx.WXK_RIGHT, wx.WXK_ESCAPE):
@@ -455,7 +455,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
455 # Tab-completion
455 # Tab-completion
456 elif key_code == ord('\t'):
456 elif key_code == ord('\t'):
457 current_line, current_line_num = self.CurLine
457 current_line, current_line_num = self.CurLine
458 if not re.match(r'^%s\s*$' % self.continuation_prompt(),
458 if not re.match(r'^%s\s*$' % self.continuation_prompt(),
459 current_line):
459 current_line):
460 self.complete_current_input()
460 self.complete_current_input()
461 if self.AutoCompActive():
461 if self.AutoCompActive():
@@ -491,7 +491,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
491 ConsoleWidget._on_key_down(self, event, skip=skip)
491 ConsoleWidget._on_key_down(self, event, skip=skip)
492 else:
492 else:
493 ConsoleWidget._on_key_down(self, event, skip=skip)
493 ConsoleWidget._on_key_down(self, event, skip=skip)
494
494
495
495
496
496
497 def _on_key_up(self, event, skip=True):
497 def _on_key_up(self, event, skip=True):
@@ -503,7 +503,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
503 wx.CallAfter(self._popup_completion, create=True)
503 wx.CallAfter(self._popup_completion, create=True)
504 else:
504 else:
505 ConsoleWidget._on_key_up(self, event, skip=skip)
505 ConsoleWidget._on_key_up(self, event, skip=skip)
506 # Make sure the continuation_prompts are always followed by a
506 # Make sure the continuation_prompts are always followed by a
507 # whitespace
507 # whitespace
508 new_lines = []
508 new_lines = []
509 if self._input_state == 'readline':
509 if self._input_state == 'readline':
@@ -531,10 +531,10 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
531 if sys.platform == 'win32':
531 if sys.platform == 'win32':
532 self.input_buffer = self.input_buffer
532 self.input_buffer = self.input_buffer
533 old_prompt_num = self.current_prompt_pos
533 old_prompt_num = self.current_prompt_pos
534 has_executed = PrefilterFrontEnd._on_enter(self,
534 has_executed = PrefilterFrontEnd._on_enter(self,
535 new_line_pos=new_line_pos)
535 new_line_pos=new_line_pos)
536 if old_prompt_num == self.current_prompt_pos:
536 if old_prompt_num == self.current_prompt_pos:
537 # No execution has happened
537 # No execution has happened
538 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
538 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
539 return has_executed
539 return has_executed
540
540
@@ -544,7 +544,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
544 #--------------------------------------------------------------------------
544 #--------------------------------------------------------------------------
545
545
546 def OnUpdateUI(self, event):
546 def OnUpdateUI(self, event):
547 """ Override the OnUpdateUI of the EditWindow class, to prevent
547 """ Override the OnUpdateUI of the EditWindow class, to prevent
548 syntax highlighting both for faster redraw, and for more
548 syntax highlighting both for faster redraw, and for more
549 consistent look and feel.
549 consistent look and feel.
550 """
550 """
@@ -554,10 +554,10 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
554 #--------------------------------------------------------------------------
554 #--------------------------------------------------------------------------
555 # Private API
555 # Private API
556 #--------------------------------------------------------------------------
556 #--------------------------------------------------------------------------
557
557
558 def _buffer_flush(self, event):
558 def _buffer_flush(self, event):
559 """ Called by the timer to flush the write buffer.
559 """ Called by the timer to flush the write buffer.
560
560
561 This is always called in the mainloop, by the wx timer.
561 This is always called in the mainloop, by the wx timer.
562 """
562 """
563 self._out_buffer_lock.acquire()
563 self._out_buffer_lock.acquire()
@@ -4,7 +4,7 b''
4 zope.interface mock. If zope is installed, this module provides a zope
4 zope.interface mock. If zope is installed, this module provides a zope
5 interface classes, if not it provides mocks for them.
5 interface classes, if not it provides mocks for them.
6
6
7 Classes provided:
7 Classes provided:
8 Interface, Attribute, implements, classProvides
8 Interface, Attribute, implements, classProvides
9 """
9 """
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
@@ -23,7 +23,7 b' if len(sys.argv) > 1:'
23 if sys.argv[1] == '-v':
23 if sys.argv[1] == '-v':
24 sys.argv = sys.argv[:-1] # IPython is confused by -v, apparently
24 sys.argv = sys.argv[:-1] # IPython is confused by -v, apparently
25 verbose = True
25 verbose = True
26
26
27 IPython.Shell.start()
27 IPython.Shell.start()
28
28
29 ip = IPython.ipapi.get()
29 ip = IPython.ipapi.get()
@@ -41,7 +41,7 b' def install_mock_handler(name):'
41 handler func always returns '', which causes ipython to cease handling
41 handler func always returns '', which causes ipython to cease handling
42 the string immediately. That way, that it doesn't echo output, raise
42 the string immediately. That way, that it doesn't echo output, raise
43 exceptions, etc. But do note that testing multiline strings thus gets
43 exceptions, etc. But do note that testing multiline strings thus gets
44 a bit hard."""
44 a bit hard."""
45 def mock_handler(self, line, continue_prompt=None,
45 def mock_handler(self, line, continue_prompt=None,
46 pre=None,iFun=None,theRest=None,
46 pre=None,iFun=None,theRest=None,
47 obj=None):
47 obj=None):
@@ -76,7 +76,7 b' def reset_esc_handlers():'
76 s.ESC_SH_CAP : s.handle_shell_escape,
76 s.ESC_SH_CAP : s.handle_shell_escape,
77 }
77 }
78 reset_esc_handlers()
78 reset_esc_handlers()
79
79
80 # This is so I don't have to quote over and over. Gotta be a better way.
80 # This is so I don't have to quote over and over. Gotta be a better way.
81 handle_normal = 'handle_normal'
81 handle_normal = 'handle_normal'
82 handle_auto = 'handle_auto'
82 handle_auto = 'handle_auto'
@@ -96,18 +96,18 b' def check(assertion, failure_msg):'
96 if assertion:
96 if assertion:
97 if verbose:
97 if verbose:
98 sys.stdout.write('.')
98 sys.stdout.write('.')
99 sys.stdout.flush()
99 sys.stdout.flush()
100 else:
100 else:
101 if verbose:
101 if verbose:
102 sys.stdout.write('F')
102 sys.stdout.write('F')
103 sys.stdout.flush()
103 sys.stdout.flush()
104 failures.append(failure_msg)
104 failures.append(failure_msg)
105
105
106
106
107 def check_handler(expected_handler, line):
107 def check_handler(expected_handler, line):
108 """Verify that the expected hander was called (for the given line,
108 """Verify that the expected hander was called (for the given line,
109 passed in for failure reporting).
109 passed in for failure reporting).
110
110
111 Pulled out to its own function so that tests which don't use
111 Pulled out to its own function so that tests which don't use
112 run_handler_tests can still take advantage of it."""
112 run_handler_tests can still take advantage of it."""
113 check(handler_called == expected_handler,
113 check(handler_called == expected_handler,
@@ -115,16 +115,16 b' def check_handler(expected_handler, line):'
115 "instead %s called" % (expected_handler,
115 "instead %s called" % (expected_handler,
116 repr(line),
116 repr(line),
117 handler_called))
117 handler_called))
118
118
119
119
120 def run_handler_tests(h_tests):
120 def run_handler_tests(h_tests):
121 """Loop through a series of (input_line, handler_name) pairs, verifying
121 """Loop through a series of (input_line, handler_name) pairs, verifying
122 that, for each ip calls the given handler for the given line.
122 that, for each ip calls the given handler for the given line.
123
123
124 The verbose complaint includes the line passed in, so if that line can
124 The verbose complaint includes the line passed in, so if that line can
125 include enough info to find the error, the tests are modestly
125 include enough info to find the error, the tests are modestly
126 self-documenting.
126 self-documenting.
127 """
127 """
128 for ln, expected_handler in h_tests:
128 for ln, expected_handler in h_tests:
129 global handler_called
129 global handler_called
130 handler_called = None
130 handler_called = None
@@ -133,7 +133,7 b' def run_handler_tests(h_tests):'
133
133
134 def run_one_test(ln, expected_handler):
134 def run_one_test(ln, expected_handler):
135 run_handler_tests([(ln, expected_handler)])
135 run_handler_tests([(ln, expected_handler)])
136
136
137
137
138 # =========================================
138 # =========================================
139 # Tests
139 # Tests
@@ -153,12 +153,12 b' esc_handler_tests = ['
153 ( '%magic', handle_magic),
153 ( '%magic', handle_magic),
154 # XXX Possibly, add test for /,; once those are unhooked from %autocall
154 # XXX Possibly, add test for /,; once those are unhooked from %autocall
155 ( 'emacs_mode # PYTHON-MODE', handle_emacs ),
155 ( 'emacs_mode # PYTHON-MODE', handle_emacs ),
156 ( ' ', handle_normal),
156 ( ' ', handle_normal),
157
157
158 # Trailing qmark combos. Odd special cases abound
158 # Trailing qmark combos. Odd special cases abound
159
159
160 # ! always takes priority!
160 # ! always takes priority!
161 ( '!thing?', handle_shell_escape),
161 ( '!thing?', handle_shell_escape),
162 ( '!thing arg?', handle_shell_escape),
162 ( '!thing arg?', handle_shell_escape),
163 ( '!!thing?', handle_shell_escape),
163 ( '!!thing?', handle_shell_escape),
164 ( '!!thing arg?', handle_shell_escape),
164 ( '!!thing arg?', handle_shell_escape),
@@ -186,8 +186,8 b' run_handler_tests(esc_handler_tests)'
186 old_mls = ip.options.multi_line_specials
186 old_mls = ip.options.multi_line_specials
187 for ln in [ ' !ls $f multi_line_specials %s',
187 for ln in [ ' !ls $f multi_line_specials %s',
188 ' !!ls $f multi_line_specials %s', # !! escapes work on mls
188 ' !!ls $f multi_line_specials %s', # !! escapes work on mls
189 # Trailing ? doesn't trigger help:
189 # Trailing ? doesn't trigger help:
190 ' !ls $f multi_line_specials %s ?',
190 ' !ls $f multi_line_specials %s ?',
191 ' !!ls $f multi_line_specials %s ?',
191 ' !!ls $f multi_line_specials %s ?',
192 ]:
192 ]:
193 ip.options.multi_line_specials = 1
193 ip.options.multi_line_specials = 1
@@ -271,16 +271,16 b' class AttributeMutator(object):'
271 attr_mutator = AttributeMutator()
271 attr_mutator = AttributeMutator()
272 ip.to_user_ns('attr_mutator')
272 ip.to_user_ns('attr_mutator')
273
273
274 ip.options.autocall = 1
274 ip.options.autocall = 1
275
275
276 run_one_test('attr_mutator.foo should mutate', handle_normal)
276 run_one_test('attr_mutator.foo should mutate', handle_normal)
277 check(attr_mutator.called, 'ofind should be called in absence of assign characters')
277 check(attr_mutator.called, 'ofind should be called in absence of assign characters')
278
278
279 for c in list('!=()<>+*/%^&|'):
279 for c in list('!=()<>+*/%^&|'):
280 attr_mutator.called = False
280 attr_mutator.called = False
281 run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal)
281 run_one_test('attr_mutator.foo %s should *not* mutate' % c, handle_normal)
282 run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal)
282 run_one_test('attr_mutator.foo%s should *not* mutate' % c, handle_normal)
283
283
284 check(not attr_mutator.called,
284 check(not attr_mutator.called,
285 'ofind should not be called near character %s' % c)
285 'ofind should not be called near character %s' % c)
286
286
@@ -302,7 +302,7 b' for ac_state in [0,1]:'
302 run_handler_tests([
302 run_handler_tests([
303 ("alias_cmd", handle_alias),
303 ("alias_cmd", handle_alias),
304 # XXX See note above
304 # XXX See note above
305 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
305 #("alias_head.with_dot unshadowed, autocall=%s" % ac_state, handle_alias),
306 ("alias_cmd.something aliases must match whole expr", handle_normal),
306 ("alias_cmd.something aliases must match whole expr", handle_normal),
307 ("alias_cmd /", handle_alias),
307 ("alias_cmd /", handle_alias),
308 ])
308 ])
@@ -331,7 +331,7 b' import IPython.ipapi'
331 class Autocallable(IPython.ipapi.IPyAutocall):
331 class Autocallable(IPython.ipapi.IPyAutocall):
332 def __call__(self):
332 def __call__(self):
333 return "called"
333 return "called"
334
334
335 autocallable = Autocallable()
335 autocallable = Autocallable()
336 ip.to_user_ns('autocallable')
336 ip.to_user_ns('autocallable')
337
337
@@ -344,13 +344,13 b' run_handler_tests( ['
344 ( 'len autocall_0', handle_normal),
344 ( 'len autocall_0', handle_normal),
345 ( 'thing autocall_0', handle_normal),
345 ( 'thing autocall_0', handle_normal),
346 ( 'autocallable', handle_auto),
346 ( 'autocallable', handle_auto),
347
347
348 # With explicit escapes, callable and non-callables both get expanded,
348 # With explicit escapes, callable and non-callables both get expanded,
349 # regardless of the %autocall setting:
349 # regardless of the %autocall setting:
350 ( '/len autocall_0', handle_auto),
350 ( '/len autocall_0', handle_auto),
351 ( ',len autocall_0 b0', handle_auto),
351 ( ',len autocall_0 b0', handle_auto),
352 ( ';len autocall_0 b0', handle_auto),
352 ( ';len autocall_0 b0', handle_auto),
353
353
354 ( '/thing autocall_0', handle_auto),
354 ( '/thing autocall_0', handle_auto),
355 ( ',thing autocall_0 b0', handle_auto),
355 ( ',thing autocall_0 b0', handle_auto),
356 ( ';thing autocall_0 b0', handle_auto),
356 ( ';thing autocall_0 b0', handle_auto),
@@ -370,7 +370,7 b' run_handler_tests( ['
370
370
371
371
372 # Now, with autocall in default, 'smart' mode
372 # Now, with autocall in default, 'smart' mode
373 ip.options.autocall = 1
373 ip.options.autocall = 1
374 run_handler_tests( [
374 run_handler_tests( [
375 # Autocalls without escapes -- only expand if it's callable
375 # Autocalls without escapes -- only expand if it's callable
376 ( 'len a1', handle_auto),
376 ( 'len a1', handle_auto),
@@ -416,8 +416,8 b' for b in bin_ops:'
416 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
416 bin_tests.append((';len %s binop_autocall' % b, handle_auto))
417 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
417 bin_tests.append((',len %s binop_autocall' % b, handle_auto))
418 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
418 bin_tests.append(('/len %s binop_autocall' % b, handle_auto))
419
419
420 # Who loves auto-generating tests?
420 # Who loves auto-generating tests?
421 run_handler_tests(bin_tests)
421 run_handler_tests(bin_tests)
422
422
423
423
@@ -431,7 +431,7 b' run_handler_tests(bin_tests)'
431 # ============
431 # ============
432 num_f = len(failures)
432 num_f = len(failures)
433 if verbose:
433 if verbose:
434 print
434 print
435 print "%s tests run, %s failure%s" % (num_tests,
435 print "%s tests run, %s failure%s" % (num_tests,
436 num_f,
436 num_f,
437 num_f != 1 and "s" or "")
437 num_f != 1 and "s" or "")
@@ -10,7 +10,7 b' import sys'
10 from twisted.internet import reactor, threads
10 from twisted.internet import reactor, threads
11
11
12 from IPython.core.ipmaker import make_IPython
12 from IPython.core.ipmaker import make_IPython
13 from IPython.core.iplib import InteractiveShell
13 from IPython.core.iplib import InteractiveShell
14 from IPython.utils.ipstruct import Struct
14 from IPython.utils.ipstruct import Struct
15 import Queue,thread,threading,signal
15 import Queue,thread,threading,signal
16 from signal import signal, SIGINT
16 from signal import signal, SIGINT
@@ -43,7 +43,7 b' def hijack_reactor():'
43 return getattr(orig_reactor, name)
43 return getattr(orig_reactor, name)
44 def __setattr__(self, name, value):
44 def __setattr__(self, name, value):
45 return setattr(orig_reactor, name, value)
45 return setattr(orig_reactor, name, value)
46
46
47 internet.reactor = DummyReactor()
47 internet.reactor = DummyReactor()
48 return orig_reactor
48 return orig_reactor
49
49
@@ -62,12 +62,12 b' class TwistedInteractiveShell(InteractiveShell):'
62 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
62 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
63 user_ns=None,user_global_ns=None,banner2='',**kw):
63 user_ns=None,user_global_ns=None,banner2='',**kw):
64 """Similar to the normal InteractiveShell, but with threading control"""
64 """Similar to the normal InteractiveShell, but with threading control"""
65
65
66 InteractiveShell.__init__(self,name,usage,rc,user_ns,
66 InteractiveShell.__init__(self,name,usage,rc,user_ns,
67 user_global_ns,banner2)
67 user_global_ns,banner2)
68
68
69
69
70 # A queue to hold the code to be executed.
70 # A queue to hold the code to be executed.
71 self.code_queue = Queue.Queue()
71 self.code_queue = Queue.Queue()
72
72
73 # Stuff to do at closing time
73 # Stuff to do at closing time
@@ -82,13 +82,13 b' class TwistedInteractiveShell(InteractiveShell):'
82 self.worker_ident = None
82 self.worker_ident = None
83 self.reactor_started = False
83 self.reactor_started = False
84 self.first_run = True
84 self.first_run = True
85
85
86 def runsource(self, source, filename="<input>", symbol="single"):
86 def runsource(self, source, filename="<input>", symbol="single"):
87 """Compile and run some source in the interpreter.
87 """Compile and run some source in the interpreter.
88
88
89 Modified version of code.py's runsource(), to handle threading issues.
89 Modified version of code.py's runsource(), to handle threading issues.
90 See the original for full docstring details."""
90 See the original for full docstring details."""
91
91
92 # If Ctrl-C was typed, we reset the flag and return right away
92 # If Ctrl-C was typed, we reset the flag and return right away
93 if shellglobals.KBINT:
93 if shellglobals.KBINT:
94 shellglobals.KBINT = False
94 shellglobals.KBINT = False
@@ -97,7 +97,7 b' class TwistedInteractiveShell(InteractiveShell):'
97 if self._kill:
97 if self._kill:
98 # can't queue new code if we are being killed
98 # can't queue new code if we are being killed
99 return True
99 return True
100
100
101 try:
101 try:
102 code = self.compile(source, filename, symbol)
102 code = self.compile(source, filename, symbol)
103 except (OverflowError, SyntaxError, ValueError):
103 except (OverflowError, SyntaxError, ValueError):
@@ -109,21 +109,21 b' class TwistedInteractiveShell(InteractiveShell):'
109 # Case 2
109 # Case 2
110 return True
110 return True
111
111
112 # shortcut - if we are in worker thread, or the worker thread is not running,
112 # shortcut - if we are in worker thread, or the worker thread is not running,
113 # execute directly (to allow recursion and prevent deadlock if code is run early
113 # execute directly (to allow recursion and prevent deadlock if code is run early
114 # in IPython construction)
114 # in IPython construction)
115
115
116 if (not self.reactor_started or (self.worker_ident is None and not self.first_run)
116 if (not self.reactor_started or (self.worker_ident is None and not self.first_run)
117 or self.worker_ident == thread.get_ident() or shellglobals.run_in_frontend(source)):
117 or self.worker_ident == thread.get_ident() or shellglobals.run_in_frontend(source)):
118 InteractiveShell.runcode(self,code)
118 InteractiveShell.runcode(self,code)
119 return
119 return
120
120
121 # Case 3
121 # Case 3
122 # Store code in queue, so the execution thread can handle it.
122 # Store code in queue, so the execution thread can handle it.
123
123
124 self.first_run = False
124 self.first_run = False
125 completed_ev, received_ev = threading.Event(), threading.Event()
125 completed_ev, received_ev = threading.Event(), threading.Event()
126
126
127 self.code_queue.put((code,completed_ev, received_ev))
127 self.code_queue.put((code,completed_ev, received_ev))
128
128
129 reactor.callLater(0.0,self.runcode)
129 reactor.callLater(0.0,self.runcode)
@@ -133,18 +133,18 b' class TwistedInteractiveShell(InteractiveShell):'
133 print "Warning: Timeout for mainloop thread exceeded"
133 print "Warning: Timeout for mainloop thread exceeded"
134 print "switching to nonthreaded mode (until mainloop wakes up again)"
134 print "switching to nonthreaded mode (until mainloop wakes up again)"
135 self.worker_ident = None
135 self.worker_ident = None
136 else:
136 else:
137 completed_ev.wait()
137 completed_ev.wait()
138
138
139 return False
139 return False
140
140
141 def runcode(self):
141 def runcode(self):
142 """Execute a code object.
142 """Execute a code object.
143
143
144 Multithreaded wrapper around IPython's runcode()."""
144 Multithreaded wrapper around IPython's runcode()."""
145
145
146
146
147 # we are in worker thread, stash out the id for runsource()
147 # we are in worker thread, stash out the id for runsource()
148 self.worker_ident = thread.get_ident()
148 self.worker_ident = thread.get_ident()
149
149
150 if self._kill:
150 if self._kill:
@@ -172,12 +172,12 b' class TwistedInteractiveShell(InteractiveShell):'
172 code_to_run = None
172 code_to_run = None
173 while 1:
173 while 1:
174 try:
174 try:
175 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
175 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
176 except Queue.Empty:
176 except Queue.Empty:
177 break
177 break
178 received_ev.set()
178 received_ev.set()
179
179
180
180
181 # Exceptions need to be raised differently depending on which
181 # Exceptions need to be raised differently depending on which
182 # thread is active. This convoluted try/except is only there to
182 # thread is active. This convoluted try/except is only there to
183 # protect against asynchronous exceptions, to ensure that a shellglobals.KBINT
183 # protect against asynchronous exceptions, to ensure that a shellglobals.KBINT
@@ -196,8 +196,8 b' class TwistedInteractiveShell(InteractiveShell):'
196 finally:
196 finally:
197 shellglobals.CODE_RUN = False
197 shellglobals.CODE_RUN = False
198 # allow runsource() return from wait
198 # allow runsource() return from wait
199 completed_ev.set()
199 completed_ev.set()
200
200
201 # This MUST return true for gtk threading to work
201 # This MUST return true for gtk threading to work
202 return True
202 return True
203
203
@@ -237,7 +237,7 b' class IPShellTwisted:'
237 while True and not self.quitting:
237 while True and not self.quitting:
238 reactorrun_orig()
238 reactorrun_orig()
239 self.reactor.run = reactorrun
239 self.reactor.run = reactorrun
240
240
241 self.IP = make_IPython(argv, user_ns=user_ns, debug=debug,
241 self.IP = make_IPython(argv, user_ns=user_ns, debug=debug,
242 shell_class=shell_class,
242 shell_class=shell_class,
243 on_kill=[mainquit])
243 on_kill=[mainquit])
@@ -258,8 +258,8 b' class IPShellTwisted:'
258 reactor.callWhenRunning(spawnMainloopThread)
258 reactor.callWhenRunning(spawnMainloopThread)
259 self.IP.reactor_started = True
259 self.IP.reactor_started = True
260 self.reactor.run()
260 self.reactor.run()
261 print "mainloop ending...."
261 print "mainloop ending...."
262
262
263 exists = True
263 exists = True
264
264
265
265
@@ -53,19 +53,19 b' class ParalleMagic(Plugin):'
53 def magic_result(self, ipself, parameter_s=''):
53 def magic_result(self, ipself, parameter_s=''):
54 """Print the result of command i on all engines..
54 """Print the result of command i on all engines..
55
55
56 To use this a :class:`DirectView` instance must be created
56 To use this a :class:`DirectView` instance must be created
57 and then activated by calling its :meth:`activate` method.
57 and then activated by calling its :meth:`activate` method.
58
58
59 Then you can do the following::
59 Then you can do the following::
60
60
61 In [23]: %result
61 In [23]: %result
62 Out[23]:
62 Out[23]:
63 <Results List>
63 <Results List>
64 [0] In [6]: a = 10
64 [0] In [6]: a = 10
65 [1] In [6]: a = 10
65 [1] In [6]: a = 10
66
66
67 In [22]: %result 6
67 In [22]: %result 6
68 Out[22]:
68 Out[22]:
69 <Results List>
69 <Results List>
70 [0] In [6]: a = 10
70 [0] In [6]: a = 10
71 [1] In [6]: a = 10
71 [1] In [6]: a = 10
@@ -85,14 +85,14 b' class ParalleMagic(Plugin):'
85 def magic_px(self, ipself, parameter_s=''):
85 def magic_px(self, ipself, parameter_s=''):
86 """Executes the given python command in parallel.
86 """Executes the given python command in parallel.
87
87
88 To use this a :class:`DirectView` instance must be created
88 To use this a :class:`DirectView` instance must be created
89 and then activated by calling its :meth:`activate` method.
89 and then activated by calling its :meth:`activate` method.
90
90
91 Then you can do the following::
91 Then you can do the following::
92
92
93 In [24]: %px a = 5
93 In [24]: %px a = 5
94 Parallel execution on engine(s): all
94 Parallel execution on engine(s): all
95 Out[24]:
95 Out[24]:
96 <Results List>
96 <Results List>
97 [0] In [7]: a = 5
97 [0] In [7]: a = 5
98 [1] In [7]: a = 5
98 [1] In [7]: a = 5
@@ -111,7 +111,7 b' class ParalleMagic(Plugin):'
111 def magic_autopx(self, ipself, parameter_s=''):
111 def magic_autopx(self, ipself, parameter_s=''):
112 """Toggles auto parallel mode.
112 """Toggles auto parallel mode.
113
113
114 To use this a :class:`DirectView` instance must be created
114 To use this a :class:`DirectView` instance must be created
115 and then activated by calling its :meth:`activate` method. Once this
115 and then activated by calling its :meth:`activate` method. Once this
116 is called, all commands typed at the command line are send to
116 is called, all commands typed at the command line are send to
117 the engines to be executed in parallel. To control which engine
117 the engines to be executed in parallel. To control which engine
@@ -142,7 +142,7 b' class ParalleMagic(Plugin):'
142 self._enable_autopx()
142 self._enable_autopx()
143
143
144 def _enable_autopx(self):
144 def _enable_autopx(self):
145 """Enable %autopx mode by saving the original run_cell and installing
145 """Enable %autopx mode by saving the original run_cell and installing
146 pxrun_cell.
146 pxrun_cell.
147 """
147 """
148 if self.active_view is None:
148 if self.active_view is None:
@@ -157,7 +157,7 b' class ParalleMagic(Plugin):'
157
157
158 self.autopx = True
158 self.autopx = True
159 print "%autopx enabled"
159 print "%autopx enabled"
160
160
161 def _disable_autopx(self):
161 def _disable_autopx(self):
162 """Disable %autopx by restoring the original InteractiveShell.run_cell.
162 """Disable %autopx by restoring the original InteractiveShell.run_cell.
163 """
163 """
@@ -178,7 +178,7 b' class ParalleMagic(Plugin):'
178 stdouts = [result.stdout.rstrip()]
178 stdouts = [result.stdout.rstrip()]
179 else:
179 else:
180 stdouts = [s.rstrip() for s in result.stdout]
180 stdouts = [s.rstrip() for s in result.stdout]
181
181
182 targets = self.active_view.targets
182 targets = self.active_view.targets
183 if isinstance(targets, int):
183 if isinstance(targets, int):
184 targets = [targets]
184 targets = [targets]
@@ -192,29 +192,29 b' class ParalleMagic(Plugin):'
192
192
193 def pxrun_cell(self, raw_cell, store_history=True):
193 def pxrun_cell(self, raw_cell, store_history=True):
194 """drop-in replacement for InteractiveShell.run_cell.
194 """drop-in replacement for InteractiveShell.run_cell.
195
195
196 This executes code remotely, instead of in the local namespace.
196 This executes code remotely, instead of in the local namespace.
197
197
198 See InteractiveShell.run_cell for details.
198 See InteractiveShell.run_cell for details.
199 """
199 """
200
200
201 if (not raw_cell) or raw_cell.isspace():
201 if (not raw_cell) or raw_cell.isspace():
202 return
202 return
203
203
204 ipself = self.shell
204 ipself = self.shell
205
205
206 with ipself.builtin_trap:
206 with ipself.builtin_trap:
207 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
207 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
208
208
209 # Store raw and processed history
209 # Store raw and processed history
210 if store_history:
210 if store_history:
211 ipself.history_manager.store_inputs(ipself.execution_count,
211 ipself.history_manager.store_inputs(ipself.execution_count,
212 cell, raw_cell)
212 cell, raw_cell)
213
213
214 # ipself.logger.log(cell, raw_cell)
214 # ipself.logger.log(cell, raw_cell)
215
215
216 cell_name = ipself.compile.cache(cell, ipself.execution_count)
216 cell_name = ipself.compile.cache(cell, ipself.execution_count)
217
217
218 try:
218 try:
219 code_ast = ast.parse(cell, filename=cell_name)
219 code_ast = ast.parse(cell, filename=cell_name)
220 except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError):
220 except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError):
@@ -232,7 +232,7 b' class ParalleMagic(Plugin):'
232 ipself.history_manager.store_output(ipself.execution_count)
232 ipself.history_manager.store_output(ipself.execution_count)
233 # Each cell is a *single* input, regardless of how many lines it has
233 # Each cell is a *single* input, regardless of how many lines it has
234 ipself.execution_count += 1
234 ipself.execution_count += 1
235
235
236 if re.search(r'get_ipython\(\)\.magic\(u?"%?autopx', cell):
236 if re.search(r'get_ipython\(\)\.magic\(u?"%?autopx', cell):
237 self._disable_autopx()
237 self._disable_autopx()
238 return False
238 return False
@@ -94,7 +94,7 b' def matchorfail(text, pos):'
94
94
95 class Itpl:
95 class Itpl:
96 """Class representing a string with interpolation abilities.
96 """Class representing a string with interpolation abilities.
97
97
98 Upon creation, an instance works out what parts of the format
98 Upon creation, an instance works out what parts of the format
99 string are literal and what parts need to be evaluated. The
99 string are literal and what parts need to be evaluated. The
100 evaluation and substitution happens in the namespace of the
100 evaluation and substitution happens in the namespace of the
@@ -106,10 +106,10 b' class Itpl:'
106
106
107 The format string is parsed according to the following rules:
107 The format string is parsed according to the following rules:
108
108
109 1. A dollar sign and a name, possibly followed by any of:
109 1. A dollar sign and a name, possibly followed by any of:
110 - an open-paren, and anything up to the matching paren
110 - an open-paren, and anything up to the matching paren
111 - an open-bracket, and anything up to the matching bracket
111 - an open-bracket, and anything up to the matching bracket
112 - a period and a name
112 - a period and a name
113 any number of times, is evaluated as a Python expression.
113 any number of times, is evaluated as a Python expression.
114
114
115 2. A dollar sign immediately followed by an open-brace, and
115 2. A dollar sign immediately followed by an open-brace, and
@@ -135,7 +135,7 b' class Itpl:'
135 self.format = format
135 self.format = format
136 self.codec = codec
136 self.codec = codec
137 self.encoding_errors = encoding_errors
137 self.encoding_errors = encoding_errors
138
138
139 namechars = "abcdefghijklmnopqrstuvwxyz" \
139 namechars = "abcdefghijklmnopqrstuvwxyz" \
140 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
140 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
141 chunks = []
141 chunks = []
@@ -212,7 +212,7 b' class Itpl:'
212 loc, glob = frame.f_locals, frame.f_globals
212 loc, glob = frame.f_locals, frame.f_globals
213
213
214 return self._str(glob,loc)
214 return self._str(glob,loc)
215
215
216 class ItplNS(Itpl):
216 class ItplNS(Itpl):
217 """Class representing a string with interpolation abilities.
217 """Class representing a string with interpolation abilities.
218
218
@@ -221,7 +221,7 b' class ItplNS(Itpl):'
221 efficient, as no traceback needs to be extracte. It also allows the
221 efficient, as no traceback needs to be extracte. It also allows the
222 caller to supply a different namespace for the interpolation to occur than
222 caller to supply a different namespace for the interpolation to occur than
223 its own."""
223 its own."""
224
224
225 def __init__(self, format,globals,locals=None,
225 def __init__(self, format,globals,locals=None,
226 codec='utf_8',encoding_errors='backslashreplace'):
226 codec='utf_8',encoding_errors='backslashreplace'):
227 """ItplNS(format,globals[,locals]) -> interpolating string instance.
227 """ItplNS(format,globals[,locals]) -> interpolating string instance.
@@ -236,7 +236,7 b' class ItplNS(Itpl):'
236 self.globals = globals
236 self.globals = globals
237 self.locals = locals
237 self.locals = locals
238 Itpl.__init__(self,format,codec,encoding_errors)
238 Itpl.__init__(self,format,codec,encoding_errors)
239
239
240 def __str__(self):
240 def __str__(self):
241 """Evaluate and substitute the appropriate parts of the string."""
241 """Evaluate and substitute the appropriate parts of the string."""
242 return self._str(self.globals,self.locals)
242 return self._str(self.globals,self.locals)
@@ -260,7 +260,7 b' class ItplFile:'
260
260
261 def filter(file=sys.stdout):
261 def filter(file=sys.stdout):
262 """Return an ItplFile that filters writes to the given file object.
262 """Return an ItplFile that filters writes to the given file object.
263
263
264 'file = filter(file)' replaces 'file' with a filtered object that
264 'file = filter(file)' replaces 'file' with a filtered object that
265 has a write() method. When called with no argument, this creates
265 has a write() method. When called with no argument, this creates
266 a filter to sys.stdout."""
266 a filter to sys.stdout."""
@@ -268,7 +268,7 b' def filter(file=sys.stdout):'
268
268
269 def unfilter(ifile=None):
269 def unfilter(ifile=None):
270 """Return the original file that corresponds to the given ItplFile.
270 """Return the original file that corresponds to the given ItplFile.
271
271
272 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
272 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
273 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
273 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
274 return ifile and ifile.file or sys.stdout.file
274 return ifile and ifile.file or sys.stdout.file
@@ -3,12 +3,12 b''
3 ## Copyright (c) 2005, Michele Simionato
3 ## Copyright (c) 2005, Michele Simionato
4 ## All rights reserved.
4 ## All rights reserved.
5 ##
5 ##
6 ## Redistributions of source code must retain the above copyright
6 ## Redistributions of source code must retain the above copyright
7 ## notice, this list of conditions and the following disclaimer.
7 ## notice, this list of conditions and the following disclaimer.
8 ## Redistributions in bytecode form must reproduce the above copyright
8 ## Redistributions in bytecode form must reproduce the above copyright
9 ## notice, this list of conditions and the following disclaimer in
9 ## notice, this list of conditions and the following disclaimer in
10 ## the documentation and/or other materials provided with the
10 ## the documentation and/or other materials provided with the
11 ## distribution.
11 ## distribution.
12
12
13 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
13 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
14 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -39,7 +39,7 b' except ImportError: # for Python version < 2.5'
39 "A simple replacement of functools.partial"
39 "A simple replacement of functools.partial"
40 def __init__(self, func, *args, **kw):
40 def __init__(self, func, *args, **kw):
41 self.func = func
41 self.func = func
42 self.args = args
42 self.args = args
43 self.keywords = kw
43 self.keywords = kw
44 def __call__(self, *otherargs, **otherkw):
44 def __call__(self, *otherargs, **otherkw):
45 kw = self.keywords.copy()
45 kw = self.keywords.copy()
@@ -61,7 +61,7 b' class FunctionMaker(object):'
61 # func can be a class or a callable, but not an instance method
61 # func can be a class or a callable, but not an instance method
62 self.name = func.__name__
62 self.name = func.__name__
63 if self.name == '<lambda>': # small hack for lambda functions
63 if self.name == '<lambda>': # small hack for lambda functions
64 self.name = '_lambda_'
64 self.name = '_lambda_'
65 self.doc = func.__doc__
65 self.doc = func.__doc__
66 self.module = func.__module__
66 self.module = func.__module__
67 if inspect.isfunction(func):
67 if inspect.isfunction(func):
@@ -138,7 +138,7 b' class FunctionMaker(object):'
138 """
138 """
139 if isinstance(obj, str): # "name(signature)"
139 if isinstance(obj, str): # "name(signature)"
140 name, rest = obj.strip().split('(', 1)
140 name, rest = obj.strip().split('(', 1)
141 signature = rest[:-1] #strip a right parens
141 signature = rest[:-1] #strip a right parens
142 func = None
142 func = None
143 else: # a function
143 else: # a function
144 name = None
144 name = None
@@ -146,9 +146,9 b' class FunctionMaker(object):'
146 func = obj
146 func = obj
147 fun = cls(func, name, signature, defaults, doc, module)
147 fun = cls(func, name, signature, defaults, doc, module)
148 ibody = '\n'.join(' ' + line for line in body.splitlines())
148 ibody = '\n'.join(' ' + line for line in body.splitlines())
149 return fun.make('def %(name)s(%(signature)s):\n' + ibody,
149 return fun.make('def %(name)s(%(signature)s):\n' + ibody,
150 evaldict, addsource, **attrs)
150 evaldict, addsource, **attrs)
151
151
152 def decorator(caller, func=None):
152 def decorator(caller, func=None):
153 """
153 """
154 decorator(caller) converts a caller function into a decorator;
154 decorator(caller) converts a caller function into a decorator;
@@ -164,7 +164,7 b' def decorator(caller, func=None):'
164 # otherwise assume caller is a function
164 # otherwise assume caller is a function
165 f = inspect.getargspec(caller)[0][0] # first arg
165 f = inspect.getargspec(caller)[0][0] # first arg
166 return FunctionMaker.create(
166 return FunctionMaker.create(
167 '%s(%s)' % (caller.__name__, f),
167 '%s(%s)' % (caller.__name__, f),
168 'return decorator(_call_, %s)' % f,
168 'return decorator(_call_, %s)' % f,
169 dict(_call_=caller, decorator=decorator), undecorated=caller,
169 dict(_call_=caller, decorator=decorator), undecorated=caller,
170 doc=caller.__doc__, module=caller.__module__)
170 doc=caller.__doc__, module=caller.__module__)
@@ -191,7 +191,7 b' def getinfo(func):'
191 - doc (the docstring : str)
191 - doc (the docstring : str)
192 - module (the module name : str)
192 - module (the module name : str)
193 - dict (the function __dict__ : str)
193 - dict (the function __dict__ : str)
194
194
195 >>> def f(self, x=1, y=2, *args, **kw): pass
195 >>> def f(self, x=1, y=2, *args, **kw): pass
196
196
197 >>> info = getinfo(f)
197 >>> info = getinfo(f)
@@ -200,7 +200,7 b' def getinfo(func):'
200 'f'
200 'f'
201 >>> info["argnames"]
201 >>> info["argnames"]
202 ['self', 'x', 'y', 'args', 'kw']
202 ['self', 'x', 'y', 'args', 'kw']
203
203
204 >>> info["defaults"]
204 >>> info["defaults"]
205 (1, 2)
205 (1, 2)
206
206
@@ -237,7 +237,7 b' def update_wrapper(wrapper, model, infodict=None):'
237 def new_wrapper(wrapper, model):
237 def new_wrapper(wrapper, model):
238 """
238 """
239 An improvement over functools.update_wrapper. The wrapper is a generic
239 An improvement over functools.update_wrapper. The wrapper is a generic
240 callable object. It works by generating a copy of the wrapper with the
240 callable object. It works by generating a copy of the wrapper with the
241 right signature and by updating the copy, not the original.
241 right signature and by updating the copy, not the original.
242 Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module',
242 Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module',
243 'dict', 'defaults'.
243 'dict', 'defaults'.
@@ -138,9 +138,9 b' def skipif(skip_condition, msg=None):'
138
138
139 def get_msg(func,msg=None):
139 def get_msg(func,msg=None):
140 """Skip message with information about function being skipped."""
140 """Skip message with information about function being skipped."""
141 if msg is None:
141 if msg is None:
142 out = 'Test skipped due to test condition'
142 out = 'Test skipped due to test condition'
143 else:
143 else:
144 out = '\n'+msg
144 out = '\n'+msg
145
145
146 return "Skipping test: %s%s" % (func.__name__,out)
146 return "Skipping test: %s%s" % (func.__name__,out)
@@ -167,7 +167,7 b' def skipif(skip_condition, msg=None):'
167 skipper = skipper_gen
167 skipper = skipper_gen
168 else:
168 else:
169 skipper = skipper_func
169 skipper = skipper_func
170
170
171 return nose.tools.make_decorator(f)(skipper)
171 return nose.tools.make_decorator(f)(skipper)
172
172
173 return skip_decorator
173 return skip_decorator
@@ -5,41 +5,41 b''
5 #
5 #
6 # Copyright (c) 2006 Conan C. Albrecht
6 # Copyright (c) 2006 Conan C. Albrecht
7 #
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is furnished
12 # copies of the Software, and to permit persons to whom the Software is furnished
13 # to do so, subject to the following conditions:
13 # to do so, subject to the following conditions:
14 #
14 #
15 # The above copyright notice and this permission notice shall be included in all
15 # The above copyright notice and this permission notice shall be included in all
16 # copies or substantial portions of the Software.
16 # copies or substantial portions of the Software.
17 #
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
19 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
19 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
20 # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
20 # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
21 # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 # DEALINGS IN THE SOFTWARE.
23 # DEALINGS IN THE SOFTWARE.
24
24
25
25
26
26
27 ##################################################################################################
27 ##################################################################################################
28 ### A globally-unique identifier made up of time and ip and 8 digits for a counter:
28 ### A globally-unique identifier made up of time and ip and 8 digits for a counter:
29 ### each GUID is 40 characters wide
29 ### each GUID is 40 characters wide
30 ###
30 ###
31 ### A globally unique identifier that combines ip, time, and a counter. Since the
31 ### A globally unique identifier that combines ip, time, and a counter. Since the
32 ### time is listed first, you can sort records by guid. You can also extract the time
32 ### time is listed first, you can sort records by guid. You can also extract the time
33 ### and ip if needed.
33 ### and ip if needed.
34 ###
34 ###
35 ### Since the counter has eight hex characters, you can create up to
35 ### Since the counter has eight hex characters, you can create up to
36 ### 0xffffffff (4294967295) GUIDs every millisecond. If your processor
36 ### 0xffffffff (4294967295) GUIDs every millisecond. If your processor
37 ### is somehow fast enough to create more than that in a millisecond (looking
37 ### is somehow fast enough to create more than that in a millisecond (looking
38 ### toward the future, of course), the function will wait until the next
38 ### toward the future, of course), the function will wait until the next
39 ### millisecond to return.
39 ### millisecond to return.
40 ###
40 ###
41 ### GUIDs make wonderful database keys. They require no access to the
41 ### GUIDs make wonderful database keys. They require no access to the
42 ### database (to get the max index number), they are extremely unique, and they sort
42 ### database (to get the max index number), they are extremely unique, and they sort
43 ### automatically by time. GUIDs prevent key clashes when merging
43 ### automatically by time. GUIDs prevent key clashes when merging
44 ### two databases together, combining data, or generating keys in distributed
44 ### two databases together, combining data, or generating keys in distributed
45 ### systems.
45 ### systems.
@@ -57,9 +57,9 b''
57 # December 2, 2003 Fixed duplicating GUIDs. Sometimes they duplicate if multiples are created
57 # December 2, 2003 Fixed duplicating GUIDs. Sometimes they duplicate if multiples are created
58 # in the same millisecond (it checks the last 100 GUIDs now and has a larger random part)
58 # in the same millisecond (it checks the last 100 GUIDs now and has a larger random part)
59 # December 9, 2003 Fixed MAX_RANDOM, which was going over sys.maxint
59 # December 9, 2003 Fixed MAX_RANDOM, which was going over sys.maxint
60 # June 12, 2004 Allowed a custom IP address to be sent in rather than always using the
60 # June 12, 2004 Allowed a custom IP address to be sent in rather than always using the
61 # local IP address.
61 # local IP address.
62 # November 4, 2005 Changed the random part to a counter variable. Now GUIDs are totally
62 # November 4, 2005 Changed the random part to a counter variable. Now GUIDs are totally
63 # unique and more efficient, as long as they are created by only
63 # unique and more efficient, as long as they are created by only
64 # on runtime on a given machine. The counter part is after the time
64 # on runtime on a given machine. The counter part is after the time
65 # part so it sorts correctly.
65 # part so it sorts correctly.
@@ -87,7 +87,7 b' import threading'
87
87
88 #Makes a hex IP from a decimal dot-separated ip (eg: 127.0.0.1)
88 #Makes a hex IP from a decimal dot-separated ip (eg: 127.0.0.1)
89 make_hexip = lambda ip: ''.join(["%04x" % long(i) for i in ip.split('.')]) # leave space for ip v6 (65K in each sub)
89 make_hexip = lambda ip: ''.join(["%04x" % long(i) for i in ip.split('.')]) # leave space for ip v6 (65K in each sub)
90
90
91 MAX_COUNTER = 0xfffffffe
91 MAX_COUNTER = 0xfffffffe
92 counter = 0L
92 counter = 0L
93 firstcounter = MAX_COUNTER
93 firstcounter = MAX_COUNTER
@@ -104,7 +104,7 b" except: # if we don't have an ip, default to someting in the 10.x.x.x private ra"
104 ip += '.' + str(rand.randrange(1, 0xffff)) # might as well use IPv6 range if we're making it up
104 ip += '.' + str(rand.randrange(1, 0xffff)) # might as well use IPv6 range if we're making it up
105 hexip = make_hexip(ip)
105 hexip = make_hexip(ip)
106
106
107
107
108 #################################
108 #################################
109 ### Public module functions
109 ### Public module functions
110
110
@@ -121,7 +121,7 b' def generate(ip=None):'
121
121
122 # do we need to wait for the next millisecond (are we out of counters?)
122 # do we need to wait for the next millisecond (are we out of counters?)
123 now = long(time.time() * 1000)
123 now = long(time.time() * 1000)
124 while lasttime == now and counter == firstcounter:
124 while lasttime == now and counter == firstcounter:
125 time.sleep(.01)
125 time.sleep(.01)
126 now = long(time.time() * 1000)
126 now = long(time.time() * 1000)
127
127
@@ -136,7 +136,7 b' def generate(ip=None):'
136 if counter > MAX_COUNTER:
136 if counter > MAX_COUNTER:
137 counter = 0
137 counter = 0
138 lasttime = now
138 lasttime = now
139 parts.append("%08x" % (counter))
139 parts.append("%08x" % (counter))
140
140
141 # ip part
141 # ip part
142 parts.append(hexip)
142 parts.append(hexip)
@@ -145,10 +145,10 b' def generate(ip=None):'
145 return ''.join(parts)
145 return ''.join(parts)
146 finally:
146 finally:
147 lock.release()
147 lock.release()
148
148
149
149
150 def extract_time(guid):
150 def extract_time(guid):
151 '''Extracts the time portion out of the guid and returns the
151 '''Extracts the time portion out of the guid and returns the
152 number of seconds since the epoch as a float'''
152 number of seconds since the epoch as a float'''
153 return float(long(guid[0:16], 16)) / 1000.0
153 return float(long(guid[0:16], 16)) / 1000.0
154
154
@@ -1,26 +1,26 b''
1 r""" mglob - enhanced file list expansion module
1 r""" mglob - enhanced file list expansion module
2
2
3 Use as stand-alone utility (for xargs, `backticks` etc.),
3 Use as stand-alone utility (for xargs, `backticks` etc.),
4 or a globbing library for own python programs. Globbing the sys.argv is something
4 or a globbing library for own python programs. Globbing the sys.argv is something
5 that almost every Windows script has to perform manually, and this module is here
5 that almost every Windows script has to perform manually, and this module is here
6 to help with that task. Also Unix users will benefit from enhanced modes
6 to help with that task. Also Unix users will benefit from enhanced modes
7 such as recursion, exclusion, directory omission...
7 such as recursion, exclusion, directory omission...
8
8
9 Unlike glob.glob, directories are not included in the glob unless specified
9 Unlike glob.glob, directories are not included in the glob unless specified
10 with 'dir:'
10 with 'dir:'
11
11
12 'expand' is the function to use in python programs. Typical use
12 'expand' is the function to use in python programs. Typical use
13 to expand argv (esp. in windows)::
13 to expand argv (esp. in windows)::
14
14
15 try:
15 try:
16 import mglob
16 import mglob
17 files = mglob.expand(sys.argv[1:])
17 files = mglob.expand(sys.argv[1:])
18 except ImportError:
18 except ImportError:
19 print "mglob not found; try 'easy_install mglob' for extra features"
19 print "mglob not found; try 'easy_install mglob' for extra features"
20 files = sys.argv[1:]
20 files = sys.argv[1:]
21
21
22 Note that for unix, shell expands *normal* wildcards (*.cpp, etc.) in argv.
22 Note that for unix, shell expands *normal* wildcards (*.cpp, etc.) in argv.
23 Therefore, you might want to use quotes with normal wildcards to prevent this
23 Therefore, you might want to use quotes with normal wildcards to prevent this
24 expansion, in order for mglob to see the wildcards and get the wanted behaviour.
24 expansion, in order for mglob to see the wildcards and get the wanted behaviour.
25 Not quoting the wildcards is harmless and typically has equivalent results, though.
25 Not quoting the wildcards is harmless and typically has equivalent results, though.
26
26
@@ -34,27 +34,27 b' License: MIT Open Source license'
34 globsyntax = """\
34 globsyntax = """\
35 This program allows specifying filenames with "mglob" mechanism.
35 This program allows specifying filenames with "mglob" mechanism.
36 Supported syntax in globs (wilcard matching patterns)::
36 Supported syntax in globs (wilcard matching patterns)::
37
37
38 *.cpp ?ellowo*
38 *.cpp ?ellowo*
39 - obvious. Differs from normal glob in that dirs are not included.
39 - obvious. Differs from normal glob in that dirs are not included.
40 Unix users might want to write this as: "*.cpp" "?ellowo*"
40 Unix users might want to write this as: "*.cpp" "?ellowo*"
41 rec:/usr/share=*.txt,*.doc
41 rec:/usr/share=*.txt,*.doc
42 - get all *.txt and *.doc under /usr/share,
42 - get all *.txt and *.doc under /usr/share,
43 recursively
43 recursively
44 rec:/usr/share
44 rec:/usr/share
45 - All files under /usr/share, recursively
45 - All files under /usr/share, recursively
46 rec:*.py
46 rec:*.py
47 - All .py files under current working dir, recursively
47 - All .py files under current working dir, recursively
48 foo
48 foo
49 - File or dir foo
49 - File or dir foo
50 !*.bak readme*
50 !*.bak readme*
51 - readme*, exclude files ending with .bak
51 - readme*, exclude files ending with .bak
52 !.svn/ !.hg/ !*_Data/ rec:.
52 !.svn/ !.hg/ !*_Data/ rec:.
53 - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse.
53 - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse.
54 Trailing / is the key, \ does not work! Use !.*/ for all hidden.
54 Trailing / is the key, \ does not work! Use !.*/ for all hidden.
55 dir:foo
55 dir:foo
56 - the directory foo if it exists (not files in foo)
56 - the directory foo if it exists (not files in foo)
57 dir:*
57 dir:*
58 - all directories in current folder
58 - all directories in current folder
59 foo.py bar.* !h* rec:*.py
59 foo.py bar.* !h* rec:*.py
60 - Obvious. !h* exclusion only applies for rec:*.py.
60 - Obvious. !h* exclusion only applies for rec:*.py.
@@ -71,16 +71,16 b' __version__ = "0.2"'
71
71
72
72
73 import os,glob,fnmatch,sys,re
73 import os,glob,fnmatch,sys,re
74
74
75 def expand(flist,exp_dirs = False):
75 def expand(flist,exp_dirs = False):
76 """ Expand the glob(s) in flist.
76 """ Expand the glob(s) in flist.
77
77
78 flist may be either a whitespace-separated list of globs/files
78 flist may be either a whitespace-separated list of globs/files
79 or an array of globs/files.
79 or an array of globs/files.
80
80
81 if exp_dirs is true, directory names in glob are expanded to the files
81 if exp_dirs is true, directory names in glob are expanded to the files
82 contained in them - otherwise, directory names are returned as is.
82 contained in them - otherwise, directory names are returned as is.
83
83
84 """
84 """
85 if isinstance(flist, basestring):
85 if isinstance(flist, basestring):
86 import shlex
86 import shlex
@@ -89,7 +89,7 b' def expand(flist,exp_dirs = False):'
89 denied_set = set()
89 denied_set = set()
90 cont_set = set()
90 cont_set = set()
91 cur_rejected_dirs = set()
91 cur_rejected_dirs = set()
92
92
93 def recfind(p, pats = ["*"]):
93 def recfind(p, pats = ["*"]):
94 denied_dirs = [os.path.dirname(d) for d in denied_set if d.endswith("/")]
94 denied_dirs = [os.path.dirname(d) for d in denied_set if d.endswith("/")]
95 for (dp,dnames,fnames) in os.walk(p):
95 for (dp,dnames,fnames) in os.walk(p):
@@ -103,7 +103,7 b' def expand(flist,exp_dirs = False):'
103 break
103 break
104 if deny:
104 if deny:
105 continue
105 continue
106
106
107
107
108 #print "dp",dp
108 #print "dp",dp
109 bname = os.path.basename(dp)
109 bname = os.path.basename(dp)
@@ -115,7 +115,7 b' def expand(flist,exp_dirs = False):'
115 if deny:
115 if deny:
116 continue
116 continue
117
117
118
118
119 for f in fnames:
119 for f in fnames:
120 matched = False
120 matched = False
121 for p in pats:
121 for p in pats:
@@ -123,7 +123,7 b' def expand(flist,exp_dirs = False):'
123 matched = True
123 matched = True
124 break
124 break
125 if matched:
125 if matched:
126 yield os.path.join(dp,f)
126 yield os.path.join(dp,f)
127
127
128 def once_filter(seq):
128 def once_filter(seq):
129 for it in seq:
129 for it in seq:
@@ -146,30 +146,30 b' def expand(flist,exp_dirs = False):'
146 if not re.search(pat,cont, re.IGNORECASE):
146 if not re.search(pat,cont, re.IGNORECASE):
147 deny = True
147 deny = True
148 break
148 break
149
149
150 if not deny:
150 if not deny:
151 yield it
151 yield it
152 return
152 return
153
153
154 res = []
154 res = []
155
155
156 for ent in flist:
156 for ent in flist:
157 ent = os.path.expanduser(os.path.expandvars(ent))
157 ent = os.path.expanduser(os.path.expandvars(ent))
158 if ent.lower().startswith('rec:'):
158 if ent.lower().startswith('rec:'):
159 fields = ent[4:].split('=')
159 fields = ent[4:].split('=')
160 if len(fields) == 2:
160 if len(fields) == 2:
161 pth, patlist = fields
161 pth, patlist = fields
162 elif len(fields) == 1:
162 elif len(fields) == 1:
163 if os.path.isdir(fields[0]):
163 if os.path.isdir(fields[0]):
164 # single arg is dir
164 # single arg is dir
165 pth, patlist = fields[0], '*'
165 pth, patlist = fields[0], '*'
166 else:
166 else:
167 # single arg is pattern
167 # single arg is pattern
168 pth, patlist = '.', fields[0]
168 pth, patlist = '.', fields[0]
169
169
170 elif len(fields) == 0:
170 elif len(fields) == 0:
171 pth, pathlist = '.','*'
171 pth, pathlist = '.','*'
172
172
173 pats = patlist.split(',')
173 pats = patlist.split(',')
174 res.extend(once_filter(recfind(pth, pats)))
174 res.extend(once_filter(recfind(pth, pats)))
175 # filelist
175 # filelist
@@ -186,7 +186,7 b' def expand(flist,exp_dirs = False):'
186 # get all files in the specified dir
186 # get all files in the specified dir
187 elif os.path.isdir(ent) and exp_dirs:
187 elif os.path.isdir(ent) and exp_dirs:
188 res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*"))))
188 res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*"))))
189
189
190 # glob only files
190 # glob only files
191
191
192 elif '*' in ent or '?' in ent:
192 elif '*' in ent or '?' in ent:
@@ -195,19 +195,19 b' def expand(flist,exp_dirs = False):'
195 else:
195 else:
196 res.extend(once_filter([ent]))
196 res.extend(once_filter([ent]))
197 return res
197 return res
198
198
199
199
200 def test():
200 def test():
201 assert (
201 assert (
202 expand("*.py ~/.ipython/*.py rec:/usr/share/doc-base") ==
202 expand("*.py ~/.ipython/*.py rec:/usr/share/doc-base") ==
203 expand( ['*.py', '~/.ipython/*.py', 'rec:/usr/share/doc-base'] )
203 expand( ['*.py', '~/.ipython/*.py', 'rec:/usr/share/doc-base'] )
204 )
204 )
205
205
206 def main():
206 def main():
207 if len(sys.argv) < 2:
207 if len(sys.argv) < 2:
208 print globsyntax
208 print globsyntax
209 return
209 return
210
210
211 print "\n".join(expand(sys.argv[1:])),
211 print "\n".join(expand(sys.argv[1:])),
212
212
213 def mglob_f(self, arg):
213 def mglob_f(self, arg):
@@ -220,8 +220,8 b' def mglob_f(self, arg):'
220 def init_ipython(ip):
220 def init_ipython(ip):
221 """ register %mglob for IPython """
221 """ register %mglob for IPython """
222 mglob_f.__doc__ = globsyntax
222 mglob_f.__doc__ = globsyntax
223 ip.define_magic("mglob",mglob_f)
223 ip.define_magic("mglob",mglob_f)
224
224
225 # test()
225 # test()
226 if __name__ == "__main__":
226 if __name__ == "__main__":
227 main()
227 main()
@@ -86,7 +86,7 b' __all__ = ['
86 'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno',
86 'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno',
87 'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
87 'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
88 'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
88 'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
89 'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity',
89 'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity',
90 'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
90 'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
91 'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
91 'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
92 'indentedBlock', 'originalTextFor',
92 'indentedBlock', 'originalTextFor',
@@ -430,7 +430,7 b' class ParseResults(object):'
430 self[k] = v
430 self[k] = v
431 if isinstance(v[0],ParseResults):
431 if isinstance(v[0],ParseResults):
432 v[0].__parent = wkref(self)
432 v[0].__parent = wkref(self)
433
433
434 self.__toklist += other.__toklist
434 self.__toklist += other.__toklist
435 self.__accumNames.update( other.__accumNames )
435 self.__accumNames.update( other.__accumNames )
436 del other
436 del other
@@ -3269,12 +3269,12 b' def originalTextFor(expr, asString=True):'
3269 restore the parsed fields of an HTML start tag into the raw tag text itself, or to
3269 restore the parsed fields of an HTML start tag into the raw tag text itself, or to
3270 revert separate tokens with intervening whitespace back to the original matching
3270 revert separate tokens with intervening whitespace back to the original matching
3271 input text. Simpler to use than the parse action keepOriginalText, and does not
3271 input text. Simpler to use than the parse action keepOriginalText, and does not
3272 require the inspect module to chase up the call stack. By default, returns a
3272 require the inspect module to chase up the call stack. By default, returns a
3273 string containing the original parsed text.
3273 string containing the original parsed text.
3274
3274
3275 If the optional asString argument is passed as False, then the return value is a
3275 If the optional asString argument is passed as False, then the return value is a
3276 ParseResults containing any results names that were originally matched, and a
3276 ParseResults containing any results names that were originally matched, and a
3277 single token containing the original matched text from the input string. So if
3277 single token containing the original matched text from the input string. So if
3278 the expression passed to originalTextFor contains expressions with defined
3278 the expression passed to originalTextFor contains expressions with defined
3279 results names, you must set asString to False if you want to preserve those
3279 results names, you must set asString to False if you want to preserve those
3280 results name values."""
3280 results name values."""
@@ -3290,7 +3290,7 b' def originalTextFor(expr, asString=True):'
3290 del t["_original_end"]
3290 del t["_original_end"]
3291 matchExpr.setParseAction(extractText)
3291 matchExpr.setParseAction(extractText)
3292 return matchExpr
3292 return matchExpr
3293
3293
3294 # convenience constants for positional expressions
3294 # convenience constants for positional expressions
3295 empty = Empty().setName("empty")
3295 empty = Empty().setName("empty")
3296 lineStart = LineStart().setName("lineStart")
3296 lineStart = LineStart().setName("lineStart")
@@ -3570,7 +3570,7 b' def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString):'
3570 ).setParseAction(lambda t:t[0].strip()))
3570 ).setParseAction(lambda t:t[0].strip()))
3571 else:
3571 else:
3572 if ignoreExpr is not None:
3572 if ignoreExpr is not None:
3573 content = (Combine(OneOrMore(~ignoreExpr +
3573 content = (Combine(OneOrMore(~ignoreExpr +
3574 ~Literal(opener) + ~Literal(closer) +
3574 ~Literal(opener) + ~Literal(closer) +
3575 CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1))
3575 CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1))
3576 ).setParseAction(lambda t:t[0].strip()))
3576 ).setParseAction(lambda t:t[0].strip()))
@@ -40,7 +40,7 b' if QT_API is None:'
40 QT_API = QT_API_PYQT
40 QT_API = QT_API_PYQT
41 except ImportError:
41 except ImportError:
42 raise ImportError('Cannot import PySide >= 1.0.3 or PyQt4 >= 4.7')
42 raise ImportError('Cannot import PySide >= 1.0.3 or PyQt4 >= 4.7')
43
43
44 elif QT_API == QT_API_PYQT:
44 elif QT_API == QT_API_PYQT:
45 # Note: This must be called *before* PyQt4 is imported.
45 # Note: This must be called *before* PyQt4 is imported.
46 prepare_pyqt4()
46 prepare_pyqt4()
@@ -62,5 +62,5 b' elif QT_API == QT_API_PYSIDE:'
62 from PySide import QtCore, QtGui, QtSvg
62 from PySide import QtCore, QtGui, QtSvg
63
63
64 else:
64 else:
65 raise RuntimeError('Invalid Qt API %r, valid values are: %r or %r' %
65 raise RuntimeError('Invalid Qt API %r, valid values are: %r or %r' %
66 (QT_API, QT_API_PYQT, QT_API_PYSIDE))
66 (QT_API, QT_API_PYQT, QT_API_PYSIDE))
@@ -75,7 +75,7 b' def try_passwordless_ssh(server, keyfile, paramiko=None):'
75 """Attempt to make an ssh connection without a password.
75 """Attempt to make an ssh connection without a password.
76 This is mainly used for requiring password input only once
76 This is mainly used for requiring password input only once
77 when many tunnels may be connected to the same server.
77 when many tunnels may be connected to the same server.
78
78
79 If paramiko is None, the default for the platform is chosen.
79 If paramiko is None, the default for the platform is chosen.
80 """
80 """
81 if paramiko is None:
81 if paramiko is None:
@@ -130,12 +130,12 b' def _try_passwordless_paramiko(server, keyfile):'
130
130
131 def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
131 def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
132 """Connect a socket to an address via an ssh tunnel.
132 """Connect a socket to an address via an ssh tunnel.
133
133
134 This is a wrapper for socket.connect(addr), when addr is not accessible
134 This is a wrapper for socket.connect(addr), when addr is not accessible
135 from the local machine. It simply creates an ssh tunnel using the remaining args,
135 from the local machine. It simply creates an ssh tunnel using the remaining args,
136 and calls socket.connect('tcp://localhost:lport') where lport is the randomly
136 and calls socket.connect('tcp://localhost:lport') where lport is the randomly
137 selected local port of the tunnel.
137 selected local port of the tunnel.
138
138
139 """
139 """
140 new_url, tunnel = open_tunnel(addr, server, keyfile=keyfile, password=password, paramiko=paramiko, timeout=timeout)
140 new_url, tunnel = open_tunnel(addr, server, keyfile=keyfile, password=password, paramiko=paramiko, timeout=timeout)
141 socket.connect(new_url)
141 socket.connect(new_url)
@@ -144,15 +144,15 b' def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramik'
144
144
145 def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
145 def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
146 """Open a tunneled connection from a 0MQ url.
146 """Open a tunneled connection from a 0MQ url.
147
147
148 For use inside tunnel_connection.
148 For use inside tunnel_connection.
149
149
150 Returns
150 Returns
151 -------
151 -------
152
152
153 (url, tunnel): The 0MQ url that has been forwarded, and the tunnel object
153 (url, tunnel): The 0MQ url that has been forwarded, and the tunnel object
154 """
154 """
155
155
156 lport = select_random_ports(1)[0]
156 lport = select_random_ports(1)[0]
157 transport, addr = addr.split('://')
157 transport, addr = addr.split('://')
158 ip,rport = addr.split(':')
158 ip,rport = addr.split(':')
@@ -163,7 +163,7 b' def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeou'
163 tunnelf = paramiko_tunnel
163 tunnelf = paramiko_tunnel
164 else:
164 else:
165 tunnelf = openssh_tunnel
165 tunnelf = openssh_tunnel
166
166
167 tunnel = tunnelf(lport, rport, server, remoteip=ip, keyfile=keyfile, password=password, timeout=timeout)
167 tunnel = tunnelf(lport, rport, server, remoteip=ip, keyfile=keyfile, password=password, timeout=timeout)
168 return 'tcp://127.0.0.1:%i'%lport, tunnel
168 return 'tcp://127.0.0.1:%i'%lport, tunnel
169
169
@@ -172,15 +172,15 b" def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, pas"
172 on this machine to localhost:rport on server. The tunnel
172 on this machine to localhost:rport on server. The tunnel
173 will automatically close when not in use, remaining open
173 will automatically close when not in use, remaining open
174 for a minimum of timeout seconds for an initial connection.
174 for a minimum of timeout seconds for an initial connection.
175
175
176 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
176 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
177 as seen from `server`.
177 as seen from `server`.
178
178
179 keyfile and password may be specified, but ssh config is checked for defaults.
179 keyfile and password may be specified, but ssh config is checked for defaults.
180
180
181 Parameters
181 Parameters
182 ----------
182 ----------
183
183
184 lport : int
184 lport : int
185 local port for connecting to the tunnel from this machine.
185 local port for connecting to the tunnel from this machine.
186 rport : int
186 rport : int
@@ -192,11 +192,11 b" def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, pas"
192 The remote ip, specifying the destination of the tunnel.
192 The remote ip, specifying the destination of the tunnel.
193 Default is localhost, which means that the tunnel would redirect
193 Default is localhost, which means that the tunnel would redirect
194 localhost:lport on this machine to localhost:rport on the *server*.
194 localhost:lport on this machine to localhost:rport on the *server*.
195
195
196 keyfile : str; path to public key file
196 keyfile : str; path to public key file
197 This specifies a key to be used in ssh login, default None.
197 This specifies a key to be used in ssh login, default None.
198 Regular default ssh keys will be used without specifying this argument.
198 Regular default ssh keys will be used without specifying this argument.
199 password : str;
199 password : str;
200 Your ssh password to the ssh server. Note that if this is left None,
200 Your ssh password to the ssh server. Note that if this is left None,
201 you will be prompted for it if passwordless key based login is unavailable.
201 you will be prompted for it if passwordless key based login is unavailable.
202 timeout : int [default: 60]
202 timeout : int [default: 60]
@@ -207,7 +207,7 b" def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, pas"
207 raise ImportError("pexpect unavailable, use paramiko_tunnel")
207 raise ImportError("pexpect unavailable, use paramiko_tunnel")
208 ssh="ssh "
208 ssh="ssh "
209 if keyfile:
209 if keyfile:
210 ssh += "-i " + keyfile
210 ssh += "-i " + keyfile
211 cmd = ssh + " -f -L 127.0.0.1:%i:%s:%i %s sleep %i"%(lport, remoteip, rport, server, timeout)
211 cmd = ssh + " -f -L 127.0.0.1:%i:%s:%i %s sleep %i"%(lport, remoteip, rport, server, timeout)
212 tunnel = pexpect.spawn(cmd)
212 tunnel = pexpect.spawn(cmd)
213 failed = False
213 failed = False
@@ -232,7 +232,7 b" def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, pas"
232 password = getpass("%s's password: "%(server))
232 password = getpass("%s's password: "%(server))
233 tunnel.sendline(password)
233 tunnel.sendline(password)
234 failed = True
234 failed = True
235
235
236 def _split_server(server):
236 def _split_server(server):
237 if '@' in server:
237 if '@' in server:
238 username,server = server.split('@', 1)
238 username,server = server.split('@', 1)
@@ -248,20 +248,20 b' def _split_server(server):'
248 def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
248 def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
249 """launch a tunner with paramiko in a subprocess. This should only be used
249 """launch a tunner with paramiko in a subprocess. This should only be used
250 when shell ssh is unavailable (e.g. Windows).
250 when shell ssh is unavailable (e.g. Windows).
251
251
252 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
252 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
253 as seen from `server`.
253 as seen from `server`.
254
254
255 If you are familiar with ssh tunnels, this creates the tunnel:
255 If you are familiar with ssh tunnels, this creates the tunnel:
256
256
257 ssh server -L localhost:lport:remoteip:rport
257 ssh server -L localhost:lport:remoteip:rport
258
258
259 keyfile and password may be specified, but ssh config is checked for defaults.
259 keyfile and password may be specified, but ssh config is checked for defaults.
260
260
261
261
262 Parameters
262 Parameters
263 ----------
263 ----------
264
264
265 lport : int
265 lport : int
266 local port for connecting to the tunnel from this machine.
266 local port for connecting to the tunnel from this machine.
267 rport : int
267 rport : int
@@ -273,33 +273,33 b" def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, pa"
273 The remote ip, specifying the destination of the tunnel.
273 The remote ip, specifying the destination of the tunnel.
274 Default is localhost, which means that the tunnel would redirect
274 Default is localhost, which means that the tunnel would redirect
275 localhost:lport on this machine to localhost:rport on the *server*.
275 localhost:lport on this machine to localhost:rport on the *server*.
276
276
277 keyfile : str; path to public key file
277 keyfile : str; path to public key file
278 This specifies a key to be used in ssh login, default None.
278 This specifies a key to be used in ssh login, default None.
279 Regular default ssh keys will be used without specifying this argument.
279 Regular default ssh keys will be used without specifying this argument.
280 password : str;
280 password : str;
281 Your ssh password to the ssh server. Note that if this is left None,
281 Your ssh password to the ssh server. Note that if this is left None,
282 you will be prompted for it if passwordless key based login is unavailable.
282 you will be prompted for it if passwordless key based login is unavailable.
283 timeout : int [default: 60]
283 timeout : int [default: 60]
284 The time (in seconds) after which no activity will result in the tunnel
284 The time (in seconds) after which no activity will result in the tunnel
285 closing. This prevents orphaned tunnels from running forever.
285 closing. This prevents orphaned tunnels from running forever.
286
286
287 """
287 """
288 if paramiko is None:
288 if paramiko is None:
289 raise ImportError("Paramiko not available")
289 raise ImportError("Paramiko not available")
290
290
291 if password is None:
291 if password is None:
292 if not _try_passwordless_paramiko(server, keyfile):
292 if not _try_passwordless_paramiko(server, keyfile):
293 password = getpass("%s's password: "%(server))
293 password = getpass("%s's password: "%(server))
294
294
295 p = Process(target=_paramiko_tunnel,
295 p = Process(target=_paramiko_tunnel,
296 args=(lport, rport, server, remoteip),
296 args=(lport, rport, server, remoteip),
297 kwargs=dict(keyfile=keyfile, password=password))
297 kwargs=dict(keyfile=keyfile, password=password))
298 p.daemon=False
298 p.daemon=False
299 p.start()
299 p.start()
300 atexit.register(_shutdown_process, p)
300 atexit.register(_shutdown_process, p)
301 return p
301 return p
302
302
303 def _shutdown_process(p):
303 def _shutdown_process(p):
304 if p.isalive():
304 if p.isalive():
305 p.terminate()
305 p.terminate()
@@ -342,7 +342,7 b" if sys.platform == 'win32':"
342 else:
342 else:
343 ssh_tunnel = openssh_tunnel
343 ssh_tunnel = openssh_tunnel
344
344
345
345
346 __all__ = ['tunnel_connection', 'ssh_tunnel', 'openssh_tunnel', 'paramiko_tunnel', 'try_passwordless_ssh']
346 __all__ = ['tunnel_connection', 'ssh_tunnel', 'openssh_tunnel', 'paramiko_tunnel', 'try_passwordless_ssh']
347
347
348
348
@@ -50,7 +50,7 b' class AuthenticatedHandler(web.RequestHandler):'
50 if not self.application.password:
50 if not self.application.password:
51 user_id = 'anonymous'
51 user_id = 'anonymous'
52 return user_id
52 return user_id
53
53
54
54
55 class NBBrowserHandler(AuthenticatedHandler):
55 class NBBrowserHandler(AuthenticatedHandler):
56 @web.authenticated
56 @web.authenticated
@@ -176,13 +176,13 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler):'
176 self.session = Session()
176 self.session = Session()
177 self.save_on_message = self.on_message
177 self.save_on_message = self.on_message
178 self.on_message = self.on_first_message
178 self.on_message = self.on_first_message
179
179
180 def get_current_user(self):
180 def get_current_user(self):
181 user_id = self.get_secure_cookie("user")
181 user_id = self.get_secure_cookie("user")
182 if user_id == '' or (user_id is None and not self.application.password):
182 if user_id == '' or (user_id is None and not self.application.password):
183 user_id = 'anonymous'
183 user_id = 'anonymous'
184 return user_id
184 return user_id
185
185
186 def _inject_cookie_message(self, msg):
186 def _inject_cookie_message(self, msg):
187 """Inject the first message, which is the document cookie,
187 """Inject the first message, which is the document cookie,
188 for authentication."""
188 for authentication."""
@@ -193,14 +193,14 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler):'
193 self._cookies = Cookie.SimpleCookie(msg)
193 self._cookies = Cookie.SimpleCookie(msg)
194 except:
194 except:
195 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
195 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
196
196
197 def on_first_message(self, msg):
197 def on_first_message(self, msg):
198 self._inject_cookie_message(msg)
198 self._inject_cookie_message(msg)
199 if self.get_current_user() is None:
199 if self.get_current_user() is None:
200 logging.warn("Couldn't authenticate WebSocket connection")
200 logging.warn("Couldn't authenticate WebSocket connection")
201 raise web.HTTPError(403)
201 raise web.HTTPError(403)
202 self.on_message = self.save_on_message
202 self.on_message = self.save_on_message
203
203
204
204
205 class IOPubHandler(AuthenticatedZMQStreamHandler):
205 class IOPubHandler(AuthenticatedZMQStreamHandler):
206
206
@@ -209,7 +209,7 b' class IOPubHandler(AuthenticatedZMQStreamHandler):'
209 self._beating = False
209 self._beating = False
210 self.iopub_stream = None
210 self.iopub_stream = None
211 self.hb_stream = None
211 self.hb_stream = None
212
212
213 def on_first_message(self, msg):
213 def on_first_message(self, msg):
214 try:
214 try:
215 super(IOPubHandler, self).on_first_message(msg)
215 super(IOPubHandler, self).on_first_message(msg)
@@ -231,12 +231,12 b' class IOPubHandler(AuthenticatedZMQStreamHandler):'
231 else:
231 else:
232 self.iopub_stream.on_recv(self._on_zmq_reply)
232 self.iopub_stream.on_recv(self._on_zmq_reply)
233 self.start_hb(self.kernel_died)
233 self.start_hb(self.kernel_died)
234
234
235 def on_message(self, msg):
235 def on_message(self, msg):
236 pass
236 pass
237
237
238 def on_close(self):
238 def on_close(self):
239 # This method can be called twice, once by self.kernel_died and once
239 # This method can be called twice, once by self.kernel_died and once
240 # from the WebSocket close event. If the WebSocket connection is
240 # from the WebSocket close event. If the WebSocket connection is
241 # closed before the ZMQ streams are setup, they could be None.
241 # closed before the ZMQ streams are setup, they could be None.
242 self.stop_hb()
242 self.stop_hb()
@@ -245,7 +245,7 b' class IOPubHandler(AuthenticatedZMQStreamHandler):'
245 self.iopub_stream.close()
245 self.iopub_stream.close()
246 if self.hb_stream is not None and not self.hb_stream.closed():
246 if self.hb_stream is not None and not self.hb_stream.closed():
247 self.hb_stream.close()
247 self.hb_stream.close()
248
248
249 def start_hb(self, callback):
249 def start_hb(self, callback):
250 """Start the heartbeating and call the callback if the kernel dies."""
250 """Start the heartbeating and call the callback if the kernel dies."""
251 if not self._beating:
251 if not self._beating:
@@ -12,7 +12,7 b' class BaseFrontendMixin(object):'
12 #---------------------------------------------------------------------------
12 #---------------------------------------------------------------------------
13 # 'BaseFrontendMixin' concrete interface
13 # 'BaseFrontendMixin' concrete interface
14 #---------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15
15
16 def _get_kernel_manager(self):
16 def _get_kernel_manager(self):
17 """ Returns the current kernel manager.
17 """ Returns the current kernel manager.
18 """
18 """
@@ -34,7 +34,7 b' class BaseFrontendMixin(object):'
34 old_manager.stdin_channel.message_received.disconnect(self._dispatch)
34 old_manager.stdin_channel.message_received.disconnect(self._dispatch)
35 old_manager.hb_channel.kernel_died.disconnect(
35 old_manager.hb_channel.kernel_died.disconnect(
36 self._handle_kernel_died)
36 self._handle_kernel_died)
37
37
38 # Handle the case where the old kernel manager is still listening.
38 # Handle the case where the old kernel manager is still listening.
39 if old_manager.channels_running:
39 if old_manager.channels_running:
40 self._stopped_channels()
40 self._stopped_channels()
@@ -77,9 +77,9 b' class BaseFrontendMixin(object):'
77 since_last_heartbeat : float
77 since_last_heartbeat : float
78 The time since the heartbeat was last received.
78 The time since the heartbeat was last received.
79 """
79 """
80
80
81 def _started_channels(self):
81 def _started_channels(self):
82 """ Called when the KernelManager channels have started listening or
82 """ Called when the KernelManager channels have started listening or
83 when the frontend is assigned an already listening KernelManager.
83 when the frontend is assigned an already listening KernelManager.
84 """
84 """
85
85
@@ -93,7 +93,7 b' class BaseFrontendMixin(object):'
93 #---------------------------------------------------------------------------
93 #---------------------------------------------------------------------------
94
94
95 def _dispatch(self, msg):
95 def _dispatch(self, msg):
96 """ Calls the frontend handler associated with the message type of the
96 """ Calls the frontend handler associated with the message type of the
97 given message.
97 given message.
98 """
98 """
99 msg_type = msg['header']['msg_type']
99 msg_type = msg['header']['msg_type']
@@ -112,7 +112,7 b' class AnsiCodeProcessor(object):'
112 ----------
112 ----------
113 command : str
113 command : str
114 The code identifier, i.e. the final character in the sequence.
114 The code identifier, i.e. the final character in the sequence.
115
115
116 params : sequence of integers, optional
116 params : sequence of integers, optional
117 The parameter codes for the command.
117 The parameter codes for the command.
118 """
118 """
@@ -143,7 +143,7 b' class AnsiCodeProcessor(object):'
143
143
144 def set_osc_code(self, params):
144 def set_osc_code(self, params):
145 """ Set attributes based on OSC (Operating System Command) parameters.
145 """ Set attributes based on OSC (Operating System Command) parameters.
146
146
147 Parameters
147 Parameters
148 ----------
148 ----------
149 params : sequence of str
149 params : sequence of str
@@ -162,7 +162,7 b' class AnsiCodeProcessor(object):'
162 self.color_map[color] = self._parse_xterm_color_spec(spec)
162 self.color_map[color] = self._parse_xterm_color_spec(spec)
163 except (IndexError, ValueError):
163 except (IndexError, ValueError):
164 pass
164 pass
165
165
166 def set_sgr_code(self, params):
166 def set_sgr_code(self, params):
167 """ Set attributes based on SGR (Select Graphic Rendition) codes.
167 """ Set attributes based on SGR (Select Graphic Rendition) codes.
168
168
@@ -200,7 +200,7 b' class AnsiCodeProcessor(object):'
200 self.underline = False
200 self.underline = False
201 elif code >= 30 and code <= 37:
201 elif code >= 30 and code <= 37:
202 self.foreground_color = code - 30
202 self.foreground_color = code - 30
203 elif code == 38 and params and params.pop(0) == 5:
203 elif code == 38 and params and params.pop(0) == 5:
204 # xterm-specific: 256 color support.
204 # xterm-specific: 256 color support.
205 if params:
205 if params:
206 self.foreground_color = params.pop(0)
206 self.foreground_color = params.pop(0)
@@ -226,7 +226,7 b' class AnsiCodeProcessor(object):'
226 if spec.startswith('rgb:'):
226 if spec.startswith('rgb:'):
227 return tuple(map(lambda x: int(x, 16), spec[4:].split('/')))
227 return tuple(map(lambda x: int(x, 16), spec[4:].split('/')))
228 elif spec.startswith('rgbi:'):
228 elif spec.startswith('rgbi:'):
229 return tuple(map(lambda x: int(float(x) * 255),
229 return tuple(map(lambda x: int(float(x) * 255),
230 spec[5:].split('/')))
230 spec[5:].split('/')))
231 elif spec == '?':
231 elif spec == '?':
232 raise ValueError('Unsupported xterm color spec')
232 raise ValueError('Unsupported xterm color spec')
@@ -237,7 +237,7 b' class AnsiCodeProcessor(object):'
237 if special == '\f':
237 if special == '\f':
238 self.actions.append(ScrollAction('scroll', 'down', 'page', 1))
238 self.actions.append(ScrollAction('scroll', 'down', 'page', 1))
239 return ''
239 return ''
240
240
241
241
242 class QtAnsiCodeProcessor(AnsiCodeProcessor):
242 class QtAnsiCodeProcessor(AnsiCodeProcessor):
243 """ Translates ANSI escape codes into QTextCharFormats.
243 """ Translates ANSI escape codes into QTextCharFormats.
@@ -288,7 +288,7 b' class QtAnsiCodeProcessor(AnsiCodeProcessor):'
288 return QtGui.QColor(*constructor)
288 return QtGui.QColor(*constructor)
289
289
290 return None
290 return None
291
291
292 def get_format(self):
292 def get_format(self):
293 """ Returns a QTextCharFormat that encodes the current style attributes.
293 """ Returns a QTextCharFormat that encodes the current style attributes.
294 """
294 """
@@ -322,7 +322,7 b' class QtAnsiCodeProcessor(AnsiCodeProcessor):'
322 self.default_color_map = self.darkbg_color_map.copy()
322 self.default_color_map = self.darkbg_color_map.copy()
323
323
324 if color.value() >= 127:
324 if color.value() >= 127:
325 # Colors appropriate for a terminal with a light background. For
325 # Colors appropriate for a terminal with a light background. For
326 # now, only use non-bright colors...
326 # now, only use non-bright colors...
327 for i in xrange(8):
327 for i in xrange(8):
328 self.default_color_map[i + 8] = self.default_color_map[i]
328 self.default_color_map[i + 8] = self.default_color_map[i]
@@ -9,7 +9,7 b' class BracketMatcher(QtCore.QObject):'
9 """ Matches square brackets, braces, and parentheses based on cursor
9 """ Matches square brackets, braces, and parentheses based on cursor
10 position.
10 position.
11 """
11 """
12
12
13 # Protected class variables.
13 # Protected class variables.
14 _opening_map = { '(':')', '{':'}', '[':']' }
14 _opening_map = { '(':')', '{':'}', '[':']' }
15 _closing_map = { ')':'(', '}':'{', ']':'[' }
15 _closing_map = { ')':'(', '}':'{', ']':'[' }
@@ -75,7 +75,7 b' class BracketMatcher(QtCore.QObject):'
75 selection = QtGui.QTextEdit.ExtraSelection()
75 selection = QtGui.QTextEdit.ExtraSelection()
76 cursor = self._text_edit.textCursor()
76 cursor = self._text_edit.textCursor()
77 cursor.setPosition(position)
77 cursor.setPosition(position)
78 cursor.movePosition(QtGui.QTextCursor.NextCharacter,
78 cursor.movePosition(QtGui.QTextCursor.NextCharacter,
79 QtGui.QTextCursor.KeepAnchor)
79 QtGui.QTextCursor.KeepAnchor)
80 selection.cursor = cursor
80 selection.cursor = cursor
81 selection.format = self.format
81 selection.format = self.format
@@ -134,7 +134,7 b' class CallTipWidget(QtGui.QLabel):'
134 doc = doc[:match.end()] + '\n[Documentation continues...]'
134 doc = doc[:match.end()] + '\n[Documentation continues...]'
135 else:
135 else:
136 doc = ''
136 doc = ''
137
137
138 if call_line:
138 if call_line:
139 doc = '\n\n'.join([call_line, doc])
139 doc = '\n\n'.join([call_line, doc])
140 return self.show_tip(doc)
140 return self.show_tip(doc)
@@ -147,7 +147,7 b' class CallTipWidget(QtGui.QLabel):'
147 document = text_edit.document()
147 document = text_edit.document()
148 cursor = text_edit.textCursor()
148 cursor = text_edit.textCursor()
149 search_pos = cursor.position() - 1
149 search_pos = cursor.position() - 1
150 self._start_position, _ = self._find_parenthesis(search_pos,
150 self._start_position, _ = self._find_parenthesis(search_pos,
151 forward=False)
151 forward=False)
152 if self._start_position == -1:
152 if self._start_position == -1:
153 return False
153 return False
@@ -155,7 +155,7 b' class CallTipWidget(QtGui.QLabel):'
155 # Set the text and resize the widget accordingly.
155 # Set the text and resize the widget accordingly.
156 self.setText(tip)
156 self.setText(tip)
157 self.resize(self.sizeHint())
157 self.resize(self.sizeHint())
158
158
159 # Locate and show the widget. Place the tip below the current line
159 # Locate and show the widget. Place the tip below the current line
160 # unless it would be off the screen. In that case, place it above
160 # unless it would be off the screen. In that case, place it above
161 # the current line.
161 # the current line.
@@ -171,7 +171,7 b' class CallTipWidget(QtGui.QLabel):'
171 self.move(point)
171 self.move(point)
172 self.show()
172 self.show()
173 return True
173 return True
174
174
175 #--------------------------------------------------------------------------
175 #--------------------------------------------------------------------------
176 # Protected interface
176 # Protected interface
177 #--------------------------------------------------------------------------
177 #--------------------------------------------------------------------------
@@ -3,7 +3,7 b' from pygments.token import Token, is_token_subtype'
3
3
4
4
5 class CompletionLexer(object):
5 class CompletionLexer(object):
6 """ Uses Pygments and some auxillary information to lex code snippets for
6 """ Uses Pygments and some auxillary information to lex code snippets for
7 symbol contexts.
7 symbol contexts.
8 """
8 """
9
9
@@ -30,7 +30,7 b' class CompletionLexer(object):'
30 if reversed_tokens and reversed_tokens[0][1].endswith('\n') and \
30 if reversed_tokens and reversed_tokens[0][1].endswith('\n') and \
31 not string.endswith('\n'):
31 not string.endswith('\n'):
32 reversed_tokens.pop(0)
32 reversed_tokens.pop(0)
33
33
34 current_op = ''
34 current_op = ''
35 for token, text in reversed_tokens:
35 for token, text in reversed_tokens:
36
36
@@ -71,4 +71,4 b' class CompletionLexer(object):'
71 self._name_separators = list(name_separators)
71 self._name_separators = list(name_separators)
72
72
73 lexer = property(get_lexer, set_lexer)
73 lexer = property(get_lexer, set_lexer)
74
74
@@ -39,15 +39,15 b' class CompletionWidget(QtGui.QListWidget):'
39
39
40 if etype == QtCore.QEvent.KeyPress:
40 if etype == QtCore.QEvent.KeyPress:
41 key, text = event.key(), event.text()
41 key, text = event.key(), event.text()
42 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
42 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
43 QtCore.Qt.Key_Tab):
43 QtCore.Qt.Key_Tab):
44 self._complete_current()
44 self._complete_current()
45 return True
45 return True
46 elif key == QtCore.Qt.Key_Escape:
46 elif key == QtCore.Qt.Key_Escape:
47 self.hide()
47 self.hide()
48 return True
48 return True
49 elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
49 elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
50 QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
50 QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
51 QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
51 QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
52 self.keyPressEvent(event)
52 self.keyPressEvent(event)
53 return True
53 return True
@@ -114,16 +114,16 b' class CompletionWidget(QtGui.QListWidget):'
114 """
114 """
115 cursor = self._text_edit.textCursor()
115 cursor = self._text_edit.textCursor()
116 if cursor.position() >= self._start_position:
116 if cursor.position() >= self._start_position:
117 cursor.setPosition(self._start_position,
117 cursor.setPosition(self._start_position,
118 QtGui.QTextCursor.KeepAnchor)
118 QtGui.QTextCursor.KeepAnchor)
119 return cursor
119 return cursor
120
120
121 def _update_current(self):
121 def _update_current(self):
122 """ Updates the current item based on the current text.
122 """ Updates the current item based on the current text.
123 """
123 """
124 prefix = self._current_text_cursor().selection().toPlainText()
124 prefix = self._current_text_cursor().selection().toPlainText()
125 if prefix:
125 if prefix:
126 items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith |
126 items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith |
127 QtCore.Qt.MatchCaseSensitive))
127 QtCore.Qt.MatchCaseSensitive))
128 if items:
128 if items:
129 self.setCurrentItem(items[0])
129 self.setCurrentItem(items[0])
@@ -40,11 +40,11 b' def is_letter_or_number(char):'
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
42 class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
43 """ An abstract base class for console-type widgets. This class has
43 """ An abstract base class for console-type widgets. This class has
44 functionality for:
44 functionality for:
45
45
46 * Maintaining a prompt and editing region
46 * Maintaining a prompt and editing region
47 * Providing the traditional Unix-style console keyboard shortcuts
47 * Providing the traditional Unix-style console keyboard shortcuts
48 * Performing tab completion
48 * Performing tab completion
49 * Paging text
49 * Paging text
50 * Handling ANSI escape codes
50 * Handling ANSI escape codes
@@ -79,11 +79,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
79 """
79 """
80 )
80 )
81 # NOTE: this value can only be specified during initialization.
81 # NOTE: this value can only be specified during initialization.
82 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
82 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
83 default_value='inside', config=True,
83 default_value='inside', config=True,
84 help="""
84 help="""
85 The type of paging to use. Valid values are:
85 The type of paging to use. Valid values are:
86
86
87 'inside' : The widget pages like a traditional terminal.
87 'inside' : The widget pages like a traditional terminal.
88 'hsplit' : When paging is requested, the widget is split
88 'hsplit' : When paging is requested, the widget is split
89 horizontally. The top pane contains the console, and the
89 horizontally. The top pane contains the console, and the
@@ -264,7 +264,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
264 key = event.key()
264 key = event.key()
265 if self._control_key_down(event.modifiers()) and \
265 if self._control_key_down(event.modifiers()) and \
266 key in self._ctrl_down_remap:
266 key in self._ctrl_down_remap:
267 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
267 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
268 self._ctrl_down_remap[key],
268 self._ctrl_down_remap[key],
269 QtCore.Qt.NoModifier)
269 QtCore.Qt.NoModifier)
270 QtGui.qApp.sendEvent(obj, new_event)
270 QtGui.qApp.sendEvent(obj, new_event)
@@ -378,10 +378,10 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
378 """ Returns whether text can be cut to the clipboard.
378 """ Returns whether text can be cut to the clipboard.
379 """
379 """
380 cursor = self._control.textCursor()
380 cursor = self._control.textCursor()
381 return (cursor.hasSelection() and
381 return (cursor.hasSelection() and
382 self._in_buffer(cursor.anchor()) and
382 self._in_buffer(cursor.anchor()) and
383 self._in_buffer(cursor.position()))
383 self._in_buffer(cursor.position()))
384
384
385 def can_paste(self):
385 def can_paste(self):
386 """ Returns whether text can be pasted from the clipboard.
386 """ Returns whether text can be pasted from the clipboard.
387 """
387 """
@@ -390,7 +390,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
390 return False
390 return False
391
391
392 def clear(self, keep_input=True):
392 def clear(self, keep_input=True):
393 """ Clear the console.
393 """ Clear the console.
394
394
395 Parameters:
395 Parameters:
396 -----------
396 -----------
@@ -425,7 +425,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
425 input.
425 input.
426
426
427 Parameters:
427 Parameters:
428 -----------
428 -----------
429 source : str, optional
429 source : str, optional
430
430
431 The source to execute. If not specified, the input buffer will be
431 The source to execute. If not specified, the input buffer will be
@@ -467,7 +467,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
467 source += '\n'
467 source += '\n'
468 elif not hidden:
468 elif not hidden:
469 self.input_buffer = source
469 self.input_buffer = source
470
470
471 # Execute the source or show a continuation prompt if it is incomplete.
471 # Execute the source or show a continuation prompt if it is incomplete.
472 complete = self._is_complete(source, interactive)
472 complete = self._is_complete(source, interactive)
473 if hidden:
473 if hidden:
@@ -475,7 +475,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
475 self._execute(source, hidden)
475 self._execute(source, hidden)
476 else:
476 else:
477 error = 'Incomplete noninteractive input: "%s"'
477 error = 'Incomplete noninteractive input: "%s"'
478 raise RuntimeError(error % source)
478 raise RuntimeError(error % source)
479 else:
479 else:
480 if complete:
480 if complete:
481 self._append_plain_text('\n')
481 self._append_plain_text('\n')
@@ -494,7 +494,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
494
494
495 # Perform actual execution.
495 # Perform actual execution.
496 self._execute(source, hidden)
496 self._execute(source, hidden)
497
497
498 else:
498 else:
499 # Do this inside an edit block so continuation prompts are
499 # Do this inside an edit block so continuation prompts are
500 # removed seamlessly via undo/redo.
500 # removed seamlessly via undo/redo.
@@ -505,7 +505,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
505 cursor.endEditBlock()
505 cursor.endEditBlock()
506
506
507 # Do not do this inside the edit block. It works as expected
507 # Do not do this inside the edit block. It works as expected
508 # when using a QPlainTextEdit control, but does not have an
508 # when using a QPlainTextEdit control, but does not have an
509 # effect when using a QTextEdit. I believe this is a Qt bug.
509 # effect when using a QTextEdit. I believe this is a Qt bug.
510 self._control.moveCursor(QtGui.QTextCursor.End)
510 self._control.moveCursor(QtGui.QTextCursor.End)
511
511
@@ -617,7 +617,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
617 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
617 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
618 self._set_cursor(prompt_cursor)
618 self._set_cursor(prompt_cursor)
619 self._set_top_cursor(prompt_cursor)
619 self._set_top_cursor(prompt_cursor)
620
620
621 def redo(self):
621 def redo(self):
622 """ Redo the last operation. If there is no operation to redo, nothing
622 """ Redo the last operation. If there is no operation to redo, nothing
623 happens.
623 happens.
@@ -729,7 +729,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
729
729
730 def _append_custom(self, insert, input, before_prompt=False):
730 def _append_custom(self, insert, input, before_prompt=False):
731 """ A low-level method for appending content to the end of the buffer.
731 """ A low-level method for appending content to the end of the buffer.
732
732
733 If 'before_prompt' is enabled, the content will be inserted before the
733 If 'before_prompt' is enabled, the content will be inserted before the
734 current prompt, if there is one.
734 current prompt, if there is one.
735 """
735 """
@@ -750,7 +750,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
750 diff = cursor.position() - start_pos
750 diff = cursor.position() - start_pos
751 self._append_before_prompt_pos += diff
751 self._append_before_prompt_pos += diff
752 self._prompt_pos += diff
752 self._prompt_pos += diff
753
753
754 return result
754 return result
755
755
756 def _append_html(self, html, before_prompt=False):
756 def _append_html(self, html, before_prompt=False):
@@ -811,7 +811,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
811 self._cancel_text_completion()
811 self._cancel_text_completion()
812
812
813 if len(items) == 1:
813 if len(items) == 1:
814 cursor.setPosition(self._control.textCursor().position(),
814 cursor.setPosition(self._control.textCursor().position(),
815 QtGui.QTextCursor.KeepAnchor)
815 QtGui.QTextCursor.KeepAnchor)
816 cursor.insertText(items[0])
816 cursor.insertText(items[0])
817
817
@@ -825,7 +825,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
825
825
826 if self.gui_completion:
826 if self.gui_completion:
827 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
827 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
828 self._completion_widget.show_items(cursor, items)
828 self._completion_widget.show_items(cursor, items)
829 else:
829 else:
830 cursor.beginEditBlock()
830 cursor.beginEditBlock()
831 self._append_plain_text('\n')
831 self._append_plain_text('\n')
@@ -963,7 +963,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
963
963
964 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
964 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
965 intercepted = True
965 intercepted = True
966
966
967 # Special handling when tab completing in text mode.
967 # Special handling when tab completing in text mode.
968 self._cancel_text_completion()
968 self._cancel_text_completion()
969
969
@@ -1076,7 +1076,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1076 if key == QtCore.Qt.Key_B:
1076 if key == QtCore.Qt.Key_B:
1077 self._set_cursor(self._get_word_start_cursor(position))
1077 self._set_cursor(self._get_word_start_cursor(position))
1078 intercepted = True
1078 intercepted = True
1079
1079
1080 elif key == QtCore.Qt.Key_F:
1080 elif key == QtCore.Qt.Key_F:
1081 self._set_cursor(self._get_word_end_cursor(position))
1081 self._set_cursor(self._get_word_end_cursor(position))
1082 intercepted = True
1082 intercepted = True
@@ -1103,7 +1103,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1103 elif key == QtCore.Qt.Key_Greater:
1103 elif key == QtCore.Qt.Key_Greater:
1104 self._control.moveCursor(QtGui.QTextCursor.End)
1104 self._control.moveCursor(QtGui.QTextCursor.End)
1105 intercepted = True
1105 intercepted = True
1106
1106
1107 elif key == QtCore.Qt.Key_Less:
1107 elif key == QtCore.Qt.Key_Less:
1108 self._control.setTextCursor(self._get_prompt_cursor())
1108 self._control.setTextCursor(self._get_prompt_cursor())
1109 intercepted = True
1109 intercepted = True
@@ -1115,7 +1115,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1115 anchormode = QtGui.QTextCursor.KeepAnchor
1115 anchormode = QtGui.QTextCursor.KeepAnchor
1116 else:
1116 else:
1117 anchormode = QtGui.QTextCursor.MoveAnchor
1117 anchormode = QtGui.QTextCursor.MoveAnchor
1118
1118
1119 if key == QtCore.Qt.Key_Escape:
1119 if key == QtCore.Qt.Key_Escape:
1120 self._keyboard_quit()
1120 self._keyboard_quit()
1121 intercepted = True
1121 intercepted = True
@@ -1144,19 +1144,19 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1144 line, col = cursor.blockNumber(), cursor.columnNumber()
1144 line, col = cursor.blockNumber(), cursor.columnNumber()
1145 if line > self._get_prompt_cursor().blockNumber() and \
1145 if line > self._get_prompt_cursor().blockNumber() and \
1146 col == len(self._continuation_prompt):
1146 col == len(self._continuation_prompt):
1147 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1147 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1148 mode=anchormode)
1148 mode=anchormode)
1149 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1149 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1150 mode=anchormode)
1150 mode=anchormode)
1151 intercepted = True
1151 intercepted = True
1152
1152
1153 # Regular left movement
1153 # Regular left movement
1154 else:
1154 else:
1155 intercepted = not self._in_buffer(position - 1)
1155 intercepted = not self._in_buffer(position - 1)
1156
1156
1157 elif key == QtCore.Qt.Key_Right:
1157 elif key == QtCore.Qt.Key_Right:
1158 original_block_number = cursor.blockNumber()
1158 original_block_number = cursor.blockNumber()
1159 cursor.movePosition(QtGui.QTextCursor.Right,
1159 cursor.movePosition(QtGui.QTextCursor.Right,
1160 mode=anchormode)
1160 mode=anchormode)
1161 if cursor.blockNumber() != original_block_number:
1161 if cursor.blockNumber() != original_block_number:
1162 cursor.movePosition(QtGui.QTextCursor.Right,
1162 cursor.movePosition(QtGui.QTextCursor.Right,
@@ -1237,7 +1237,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1237 return intercepted
1237 return intercepted
1238
1238
1239 def _event_filter_page_keypress(self, event):
1239 def _event_filter_page_keypress(self, event):
1240 """ Filter key events for the paging widget to create console-like
1240 """ Filter key events for the paging widget to create console-like
1241 interface.
1241 interface.
1242 """
1242 """
1243 key = event.key()
1243 key = event.key()
@@ -1253,7 +1253,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1253 if key == QtCore.Qt.Key_Greater:
1253 if key == QtCore.Qt.Key_Greater:
1254 self._page_control.moveCursor(QtGui.QTextCursor.End)
1254 self._page_control.moveCursor(QtGui.QTextCursor.End)
1255 intercepted = True
1255 intercepted = True
1256
1256
1257 elif key == QtCore.Qt.Key_Less:
1257 elif key == QtCore.Qt.Key_Less:
1258 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1258 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1259 intercepted = True
1259 intercepted = True
@@ -1266,15 +1266,15 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1266 return True
1266 return True
1267
1267
1268 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
1268 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
1269 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1269 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1270 QtCore.Qt.Key_PageDown,
1270 QtCore.Qt.Key_PageDown,
1271 QtCore.Qt.NoModifier)
1271 QtCore.Qt.NoModifier)
1272 QtGui.qApp.sendEvent(self._page_control, new_event)
1272 QtGui.qApp.sendEvent(self._page_control, new_event)
1273 return True
1273 return True
1274
1274
1275 elif key == QtCore.Qt.Key_Backspace:
1275 elif key == QtCore.Qt.Key_Backspace:
1276 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1276 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1277 QtCore.Qt.Key_PageUp,
1277 QtCore.Qt.Key_PageUp,
1278 QtCore.Qt.NoModifier)
1278 QtCore.Qt.NoModifier)
1279 QtGui.qApp.sendEvent(self._page_control, new_event)
1279 QtGui.qApp.sendEvent(self._page_control, new_event)
1280 return True
1280 return True
@@ -1300,7 +1300,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1300 width = self._control.viewport().width()
1300 width = self._control.viewport().width()
1301 char_width = QtGui.QFontMetrics(self.font).width(' ')
1301 char_width = QtGui.QFontMetrics(self.font).width(' ')
1302 displaywidth = max(10, (width / char_width) - 1)
1302 displaywidth = max(10, (width / char_width) - 1)
1303
1303
1304 return columnize(items, separator, displaywidth)
1304 return columnize(items, separator, displaywidth)
1305
1305
1306 def _get_block_plain_text(self, block):
1306 def _get_block_plain_text(self, block):
@@ -1308,7 +1308,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1308 """
1308 """
1309 cursor = QtGui.QTextCursor(block)
1309 cursor = QtGui.QTextCursor(block)
1310 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1310 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1311 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1311 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1312 QtGui.QTextCursor.KeepAnchor)
1312 QtGui.QTextCursor.KeepAnchor)
1313 return cursor.selection().toPlainText()
1313 return cursor.selection().toPlainText()
1314
1314
@@ -1316,7 +1316,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1316 """ Convenience method that returns a cursor for the current position.
1316 """ Convenience method that returns a cursor for the current position.
1317 """
1317 """
1318 return self._control.textCursor()
1318 return self._control.textCursor()
1319
1319
1320 def _get_end_cursor(self):
1320 def _get_end_cursor(self):
1321 """ Convenience method that returns a cursor for the last character.
1321 """ Convenience method that returns a cursor for the last character.
1322 """
1322 """
@@ -1482,7 +1482,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1482 self._set_top_cursor(cursor)
1482 self._set_top_cursor(cursor)
1483 cursor.joinPreviousEditBlock()
1483 cursor.joinPreviousEditBlock()
1484 cursor.deletePreviousChar()
1484 cursor.deletePreviousChar()
1485
1485
1486 format = self._ansi_processor.get_format()
1486 format = self._ansi_processor.get_format()
1487 cursor.insertText(substring, format)
1487 cursor.insertText(substring, format)
1488 else:
1488 else:
@@ -1545,7 +1545,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1545 self._cancel_text_completion()
1545 self._cancel_text_completion()
1546 else:
1546 else:
1547 self.input_buffer = ''
1547 self.input_buffer = ''
1548
1548
1549 def _page(self, text, html=False):
1549 def _page(self, text, html=False):
1550 """ Displays text using the pager if it exceeds the height of the
1550 """ Displays text using the pager if it exceeds the height of the
1551 viewport.
1551 viewport.
@@ -1591,7 +1591,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1591 def _prompt_started(self):
1591 def _prompt_started(self):
1592 """ Called immediately after a new prompt is displayed.
1592 """ Called immediately after a new prompt is displayed.
1593 """
1593 """
1594 # Temporarily disable the maximum block count to permit undo/redo and
1594 # Temporarily disable the maximum block count to permit undo/redo and
1595 # to ensure that the prompt position does not change due to truncation.
1595 # to ensure that the prompt position does not change due to truncation.
1596 self._control.document().setMaximumBlockCount(0)
1596 self._control.document().setMaximumBlockCount(0)
1597 self._control.setUndoRedoEnabled(True)
1597 self._control.setUndoRedoEnabled(True)
@@ -1613,7 +1613,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1613 self._control.moveCursor(QtGui.QTextCursor.End)
1613 self._control.moveCursor(QtGui.QTextCursor.End)
1614
1614
1615 def _readline(self, prompt='', callback=None):
1615 def _readline(self, prompt='', callback=None):
1616 """ Reads one line of input from the user.
1616 """ Reads one line of input from the user.
1617
1617
1618 Parameters
1618 Parameters
1619 ----------
1619 ----------
@@ -1669,7 +1669,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1669 else:
1669 else:
1670 self._continuation_prompt = prompt
1670 self._continuation_prompt = prompt
1671 self._continuation_prompt_html = None
1671 self._continuation_prompt_html = None
1672
1672
1673 def _set_cursor(self, cursor):
1673 def _set_cursor(self, cursor):
1674 """ Convenience method to set the current cursor.
1674 """ Convenience method to set the current cursor.
1675 """
1675 """
@@ -1699,16 +1699,16 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1699 as plain text, though ANSI color codes will be handled.
1699 as plain text, though ANSI color codes will be handled.
1700
1700
1701 newline : bool, optional (default True)
1701 newline : bool, optional (default True)
1702 If set, a new line will be written before showing the prompt if
1702 If set, a new line will be written before showing the prompt if
1703 there is not already a newline at the end of the buffer.
1703 there is not already a newline at the end of the buffer.
1704 """
1704 """
1705 # Save the current end position to support _append*(before_prompt=True).
1705 # Save the current end position to support _append*(before_prompt=True).
1706 cursor = self._get_end_cursor()
1706 cursor = self._get_end_cursor()
1707 self._append_before_prompt_pos = cursor.position()
1707 self._append_before_prompt_pos = cursor.position()
1708
1708
1709 # Insert a preliminary newline, if necessary.
1709 # Insert a preliminary newline, if necessary.
1710 if newline and cursor.position() > 0:
1710 if newline and cursor.position() > 0:
1711 cursor.movePosition(QtGui.QTextCursor.Left,
1711 cursor.movePosition(QtGui.QTextCursor.Left,
1712 QtGui.QTextCursor.KeepAnchor)
1712 QtGui.QTextCursor.KeepAnchor)
1713 if cursor.selection().toPlainText() != '\n':
1713 if cursor.selection().toPlainText() != '\n':
1714 self._append_plain_text('\n')
1714 self._append_plain_text('\n')
@@ -103,7 +103,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
103
103
104 # Emitted when an exit request has been received from the kernel.
104 # Emitted when an exit request has been received from the kernel.
105 exit_requested = QtCore.Signal()
105 exit_requested = QtCore.Signal()
106
106
107 # Protected class variables.
107 # Protected class variables.
108 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
108 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
109 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
109 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
@@ -115,7 +115,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
115 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
116 # 'object' interface
116 # 'object' interface
117 #---------------------------------------------------------------------------
117 #---------------------------------------------------------------------------
118
118
119 def __init__(self, *args, **kw):
119 def __init__(self, *args, **kw):
120 super(FrontendWidget, self).__init__(*args, **kw)
120 super(FrontendWidget, self).__init__(*args, **kw)
121
121
@@ -151,9 +151,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
151 # Connect signal handlers.
151 # Connect signal handlers.
152 document = self._control.document()
152 document = self._control.document()
153 document.contentsChange.connect(self._document_contents_change)
153 document.contentsChange.connect(self._document_contents_change)
154
154
155 # Set flag for whether we are connected via localhost.
155 # Set flag for whether we are connected via localhost.
156 self._local_kernel = kw.get('local_kernel',
156 self._local_kernel = kw.get('local_kernel',
157 FrontendWidget._local_kernel)
157 FrontendWidget._local_kernel)
158
158
159 #---------------------------------------------------------------------------
159 #---------------------------------------------------------------------------
@@ -193,7 +193,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
193 self._hidden = hidden
193 self._hidden = hidden
194 if not hidden:
194 if not hidden:
195 self.executing.emit(source)
195 self.executing.emit(source)
196
196
197 def _prompt_started_hook(self):
197 def _prompt_started_hook(self):
198 """ Called immediately after a new prompt is displayed.
198 """ Called immediately after a new prompt is displayed.
199 """
199 """
@@ -394,7 +394,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
394 # to spaces so that output looks as expected regardless of this
394 # to spaces so that output looks as expected regardless of this
395 # widget's tab width.
395 # widget's tab width.
396 text = msg['content']['data'].expandtabs(8)
396 text = msg['content']['data'].expandtabs(8)
397
397
398 self._append_plain_text(text, before_prompt=True)
398 self._append_plain_text(text, before_prompt=True)
399 self._control.moveCursor(QtGui.QTextCursor.End)
399 self._control.moveCursor(QtGui.QTextCursor.End)
400
400
@@ -432,7 +432,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
432 self.reset()
432 self.reset()
433
433
434 def _started_channels(self):
434 def _started_channels(self):
435 """ Called when the KernelManager channels have started listening or
435 """ Called when the KernelManager channels have started listening or
436 when the frontend is assigned an already listening KernelManager.
436 when the frontend is assigned an already listening KernelManager.
437 """
437 """
438 self.reset()
438 self.reset()
@@ -467,7 +467,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
467 def reset(self):
467 def reset(self):
468 """ Resets the widget to its initial state. Similar to ``clear``, but
468 """ Resets the widget to its initial state. Similar to ``clear``, but
469 also re-writes the banner and aborts execution if necessary.
469 also re-writes the banner and aborts execution if necessary.
470 """
470 """
471 if self._executing:
471 if self._executing:
472 self._executing = False
472 self._executing = False
473 self._request_info['execute'] = None
473 self._request_info['execute'] = None
@@ -550,7 +550,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
550 '.'.join(context), # text
550 '.'.join(context), # text
551 self._get_input_buffer_cursor_line(), # line
551 self._get_input_buffer_cursor_line(), # line
552 self._get_input_buffer_cursor_column(), # cursor_pos
552 self._get_input_buffer_cursor_column(), # cursor_pos
553 self.input_buffer) # block
553 self.input_buffer) # block
554 pos = self._get_cursor().position()
554 pos = self._get_cursor().position()
555 info = self._CompletionRequest(msg_id, pos)
555 info = self._CompletionRequest(msg_id, pos)
556 self._request_info['complete'] = info
556 self._request_info['complete'] = info
@@ -561,7 +561,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
561 """
561 """
562 if cursor is None:
562 if cursor is None:
563 cursor = self._get_cursor()
563 cursor = self._get_cursor()
564 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
564 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
565 QtGui.QTextCursor.KeepAnchor)
565 QtGui.QTextCursor.KeepAnchor)
566 text = cursor.selection().toPlainText()
566 text = cursor.selection().toPlainText()
567 return self._completion_lexer.get_context(text)
567 return self._completion_lexer.get_context(text)
@@ -17,7 +17,7 b' class HistoryConsoleWidget(ConsoleWidget):'
17 # an edit is made to a multi-line input buffer. To override the lock, use
17 # an edit is made to a multi-line input buffer. To override the lock, use
18 # Shift in conjunction with the standard history cycling keys.
18 # Shift in conjunction with the standard history cycling keys.
19 history_lock = Bool(False, config=True)
19 history_lock = Bool(False, config=True)
20
20
21 #---------------------------------------------------------------------------
21 #---------------------------------------------------------------------------
22 # 'object' interface
22 # 'object' interface
23 #---------------------------------------------------------------------------
23 #---------------------------------------------------------------------------
@@ -45,7 +45,7 b' class HistoryConsoleWidget(ConsoleWidget):'
45 source, hidden, interactive)
45 source, hidden, interactive)
46
46
47 if executed and not hidden:
47 if executed and not hidden:
48 # Save the command unless it was an empty string or was identical
48 # Save the command unless it was an empty string or was identical
49 # to the previous command.
49 # to the previous command.
50 history = history.rstrip()
50 history = history.rstrip()
51 if history and (not self._history or self._history[-1] != history):
51 if history and (not self._history or self._history[-1] != history):
@@ -90,7 +90,7 b' class HistoryConsoleWidget(ConsoleWidget):'
90 # search.
90 # search.
91 cursor = self._get_prompt_cursor()
91 cursor = self._get_prompt_cursor()
92 if self._history_prefix:
92 if self._history_prefix:
93 cursor.movePosition(QtGui.QTextCursor.Right,
93 cursor.movePosition(QtGui.QTextCursor.Right,
94 n=len(self._history_prefix))
94 n=len(self._history_prefix))
95 else:
95 else:
96 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
96 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
@@ -111,7 +111,7 b' class HistoryConsoleWidget(ConsoleWidget):'
111 return False
111 return False
112
112
113 # Perform the search.
113 # Perform the search.
114 replaced = self.history_next(self._history_prefix,
114 replaced = self.history_next(self._history_prefix,
115 as_prefix=not shift_modifier)
115 as_prefix=not shift_modifier)
116
116
117 # Emulate readline: keep the cursor position fixed for a prefix
117 # Emulate readline: keep the cursor position fixed for a prefix
@@ -120,7 +120,7 b' class HistoryConsoleWidget(ConsoleWidget):'
120 # input buffer is set.)
120 # input buffer is set.)
121 if self._history_prefix and replaced:
121 if self._history_prefix and replaced:
122 cursor = self._get_prompt_cursor()
122 cursor = self._get_prompt_cursor()
123 cursor.movePosition(QtGui.QTextCursor.Right,
123 cursor.movePosition(QtGui.QTextCursor.Right,
124 n=len(self._history_prefix))
124 n=len(self._history_prefix))
125 self._set_cursor(cursor)
125 self._set_cursor(cursor)
126
126
@@ -155,7 +155,7 b' class HistoryConsoleWidget(ConsoleWidget):'
155 or (not as_prefix and substring in history):
155 or (not as_prefix and substring in history):
156 replace = True
156 replace = True
157 break
157 break
158
158
159 if replace:
159 if replace:
160 self._store_edits()
160 self._store_edits()
161 self._history_index = index
161 self._history_index = index
@@ -186,7 +186,7 b' class HistoryConsoleWidget(ConsoleWidget):'
186 or (not as_prefix and substring in history):
186 or (not as_prefix and substring in history):
187 replace = True
187 replace = True
188 break
188 break
189
189
190 if replace:
190 if replace:
191 self._store_edits()
191 self._store_edits()
192 self._history_index = index
192 self._history_index = index
@@ -203,7 +203,7 b' class HistoryConsoleWidget(ConsoleWidget):'
203 The (maximum) number of history items to get.
203 The (maximum) number of history items to get.
204 """
204 """
205 return self._history[-n:]
205 return self._history[-n:]
206
206
207 #---------------------------------------------------------------------------
207 #---------------------------------------------------------------------------
208 # 'HistoryConsoleWidget' protected interface
208 # 'HistoryConsoleWidget' protected interface
209 #---------------------------------------------------------------------------
209 #---------------------------------------------------------------------------
@@ -211,8 +211,8 b' class HistoryConsoleWidget(ConsoleWidget):'
211 def _history_locked(self):
211 def _history_locked(self):
212 """ Returns whether history movement is locked.
212 """ Returns whether history movement is locked.
213 """
213 """
214 return (self.history_lock and
214 return (self.history_lock and
215 (self._get_edited_history(self._history_index) !=
215 (self._get_edited_history(self._history_index) !=
216 self.input_buffer) and
216 self.input_buffer) and
217 (self._get_prompt_cursor().blockNumber() !=
217 (self._get_prompt_cursor().blockNumber() !=
218 self._get_end_cursor().blockNumber()))
218 self._get_end_cursor().blockNumber()))
@@ -81,7 +81,7 b' class IPythonWidget(FrontendWidget):'
81 2. Pygments: .c, .k, .o, etc. (see PygmentsHighlighter)
81 2. Pygments: .c, .k, .o, etc. (see PygmentsHighlighter)
82 3. IPython: .error, .in-prompt, .out-prompt, etc
82 3. IPython: .error, .in-prompt, .out-prompt, etc
83 """)
83 """)
84
84
85 syntax_style = Unicode(config=True,
85 syntax_style = Unicode(config=True,
86 help="""
86 help="""
87 If not empty, use this Pygments style for syntax highlighting.
87 If not empty, use this Pygments style for syntax highlighting.
@@ -110,12 +110,12 b' class IPythonWidget(FrontendWidget):'
110 #---------------------------------------------------------------------------
110 #---------------------------------------------------------------------------
111 # 'object' interface
111 # 'object' interface
112 #---------------------------------------------------------------------------
112 #---------------------------------------------------------------------------
113
113
114 def __init__(self, *args, **kw):
114 def __init__(self, *args, **kw):
115 super(IPythonWidget, self).__init__(*args, **kw)
115 super(IPythonWidget, self).__init__(*args, **kw)
116
116
117 # IPythonWidget protected variables.
117 # IPythonWidget protected variables.
118 self._payload_handlers = {
118 self._payload_handlers = {
119 self._payload_source_edit : self._handle_payload_edit,
119 self._payload_source_edit : self._handle_payload_edit,
120 self._payload_source_exit : self._handle_payload_exit,
120 self._payload_source_exit : self._handle_payload_exit,
121 self._payload_source_page : self._handle_payload_page,
121 self._payload_source_page : self._handle_payload_page,
@@ -151,7 +151,7 b' class IPythonWidget(FrontendWidget):'
151 # but the last component and then suitably decreasing the offset
151 # but the last component and then suitably decreasing the offset
152 # between the current cursor position and the start of completion.
152 # between the current cursor position and the start of completion.
153 if len(matches) > 1 and matches[0][:offset] == text:
153 if len(matches) > 1 and matches[0][:offset] == text:
154 parts = re.split(r'[./\\]', text)
154 parts = re.split(r'[./\\]', text)
155 sep_count = len(parts) - 1
155 sep_count = len(parts) - 1
156 if sep_count:
156 if sep_count:
157 chop_length = sum(map(len, parts[:sep_count])) + sep_count
157 chop_length = sum(map(len, parts[:sep_count])) + sep_count
@@ -228,7 +228,7 b' class IPythonWidget(FrontendWidget):'
228 """ The base handler for the ``display_data`` message.
228 """ The base handler for the ``display_data`` message.
229 """
229 """
230 self.log.debug("display: %s", msg.get('content', ''))
230 self.log.debug("display: %s", msg.get('content', ''))
231 # For now, we don't display data from other frontends, but we
231 # For now, we don't display data from other frontends, but we
232 # eventually will as this allows all frontends to monitor the display
232 # eventually will as this allows all frontends to monitor the display
233 # data. But we need to figure out how to handle this in the GUI.
233 # data. But we need to figure out how to handle this in the GUI.
234 if not self._hidden and self._is_from_this_session(msg):
234 if not self._hidden and self._is_from_this_session(msg):
@@ -302,13 +302,13 b' class IPythonWidget(FrontendWidget):'
302 # text field. Readline-based frontends do get a real text field which
302 # text field. Readline-based frontends do get a real text field which
303 # they can use.
303 # they can use.
304 text = ''
304 text = ''
305
305
306 # Send the completion request to the kernel
306 # Send the completion request to the kernel
307 msg_id = self.kernel_manager.shell_channel.complete(
307 msg_id = self.kernel_manager.shell_channel.complete(
308 text, # text
308 text, # text
309 self._get_input_buffer_cursor_line(), # line
309 self._get_input_buffer_cursor_line(), # line
310 self._get_input_buffer_cursor_column(), # cursor_pos
310 self._get_input_buffer_cursor_column(), # cursor_pos
311 self.input_buffer) # block
311 self.input_buffer) # block
312 pos = self._get_cursor().position()
312 pos = self._get_cursor().position()
313 info = self._CompletionRequest(msg_id, pos)
313 info = self._CompletionRequest(msg_id, pos)
314 self._request_info['complete'] = info
314 self._request_info['complete'] = info
@@ -332,7 +332,7 b' class IPythonWidget(FrontendWidget):'
332 self._append_html(traceback)
332 self._append_html(traceback)
333 else:
333 else:
334 # This is the fallback for now, using plain text with ansi escapes
334 # This is the fallback for now, using plain text with ansi escapes
335 self._append_plain_text(traceback)
335 self._append_plain_text(traceback)
336
336
337 def _process_execute_payload(self, item):
337 def _process_execute_payload(self, item):
338 """ Reimplemented to dispatch payloads to handler methods.
338 """ Reimplemented to dispatch payloads to handler methods.
@@ -344,7 +344,7 b' class IPythonWidget(FrontendWidget):'
344 else:
344 else:
345 handler(item)
345 handler(item)
346 return True
346 return True
347
347
348 def _show_interpreter_prompt(self, number=None):
348 def _show_interpreter_prompt(self, number=None):
349 """ Reimplemented for IPython-style prompts.
349 """ Reimplemented for IPython-style prompts.
350 """
350 """
@@ -383,7 +383,7 b' class IPythonWidget(FrontendWidget):'
383 # Remove the old prompt and insert a new prompt.
383 # Remove the old prompt and insert a new prompt.
384 cursor = QtGui.QTextCursor(block)
384 cursor = QtGui.QTextCursor(block)
385 cursor.movePosition(QtGui.QTextCursor.Right,
385 cursor.movePosition(QtGui.QTextCursor.Right,
386 QtGui.QTextCursor.KeepAnchor,
386 QtGui.QTextCursor.KeepAnchor,
387 self._previous_prompt_obj.length)
387 self._previous_prompt_obj.length)
388 prompt = self._make_in_prompt(previous_prompt_number)
388 prompt = self._make_in_prompt(previous_prompt_number)
389 self._prompt = self._insert_html_fetching_plain_text(
389 self._prompt = self._insert_html_fetching_plain_text(
@@ -485,7 +485,7 b' class IPythonWidget(FrontendWidget):'
485 space_count = len(prompt.lstrip('\n')) - len(end_chars)
485 space_count = len(prompt.lstrip('\n')) - len(end_chars)
486 body = '&nbsp;' * space_count + end_chars
486 body = '&nbsp;' * space_count + end_chars
487 return '<span class="in-prompt">%s</span>' % body
487 return '<span class="in-prompt">%s</span>' % body
488
488
489 def _make_out_prompt(self, number):
489 def _make_out_prompt(self, number):
490 """ Given a prompt number, returns an HTML Out prompt.
490 """ Given a prompt number, returns an HTML Out prompt.
491 """
491 """
@@ -14,7 +14,7 b' from IPython.external.qt import QtCore, QtGui'
14 class KillRing(object):
14 class KillRing(object):
15 """ A generic Emacs-style kill ring.
15 """ A generic Emacs-style kill ring.
16 """
16 """
17
17
18 def __init__(self):
18 def __init__(self):
19 self.clear()
19 self.clear()
20
20
@@ -41,7 +41,7 b' class KillRing(object):'
41
41
42 def rotate(self):
42 def rotate(self):
43 """ Rotate the kill ring, then yank back the new top.
43 """ Rotate the kill ring, then yank back the new top.
44
44
45 Returns:
45 Returns:
46 --------
46 --------
47 A text string or None.
47 A text string or None.
@@ -50,7 +50,7 b' class KillRing(object):'
50 if self._index >= 0:
50 if self._index >= 0:
51 return self._ring[self._index]
51 return self._ring[self._index]
52 return None
52 return None
53
53
54 class QtKillRing(QtCore.QObject):
54 class QtKillRing(QtCore.QObject):
55 """ A kill ring attached to Q[Plain]TextEdit.
55 """ A kill ring attached to Q[Plain]TextEdit.
56 """
56 """
@@ -109,12 +109,12 b' class QtKillRing(QtCore.QObject):'
109 if text:
109 if text:
110 self._skip_cursor = True
110 self._skip_cursor = True
111 cursor = self._text_edit.textCursor()
111 cursor = self._text_edit.textCursor()
112 cursor.movePosition(QtGui.QTextCursor.Left,
112 cursor.movePosition(QtGui.QTextCursor.Left,
113 QtGui.QTextCursor.KeepAnchor,
113 QtGui.QTextCursor.KeepAnchor,
114 n = len(self._prev_yank))
114 n = len(self._prev_yank))
115 cursor.insertText(text)
115 cursor.insertText(text)
116 self._prev_yank = text
116 self._prev_yank = text
117
117
118 #--------------------------------------------------------------------------
118 #--------------------------------------------------------------------------
119 # Protected interface
119 # Protected interface
120 #--------------------------------------------------------------------------
120 #--------------------------------------------------------------------------
@@ -79,7 +79,7 b' class PygmentsBlockUserData(QtGui.QTextBlockUserData):'
79
79
80 def __repr__(self):
80 def __repr__(self):
81 attrs = ['syntax_stack']
81 attrs = ['syntax_stack']
82 kwds = ', '.join([ '%s=%r' % (attr, getattr(self, attr))
82 kwds = ', '.join([ '%s=%r' % (attr, getattr(self, attr))
83 for attr in attrs ])
83 for attr in attrs ])
84 return 'PygmentsBlockUserData(%s)' % kwds
84 return 'PygmentsBlockUserData(%s)' % kwds
85
85
@@ -172,7 +172,7 b' class PygmentsHighlighter(QtGui.QSyntaxHighlighter):'
172 return result
172 return result
173
173
174 def _get_format_from_document(self, token, document):
174 def _get_format_from_document(self, token, document):
175 """ Returns a QTextCharFormat for token by
175 """ Returns a QTextCharFormat for token by
176 """
176 """
177 code, html = self._formatter._format_lines([(token, 'dummy')]).next()
177 code, html = self._formatter._format_lines([(token, 'dummy')]).next()
178 self._document.setHtml(html)
178 self._document.setHtml(html)
@@ -56,7 +56,7 b' class RichIPythonWidget(IPythonWidget):'
56 if svg is not None:
56 if svg is not None:
57 menu.addSeparator()
57 menu.addSeparator()
58 menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg))
58 menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg))
59 menu.addAction('Save SVG As...',
59 menu.addAction('Save SVG As...',
60 lambda: save_svg(svg, self._control))
60 lambda: save_svg(svg, self._control))
61 else:
61 else:
62 menu = super(RichIPythonWidget, self)._context_menu_make(pos)
62 menu = super(RichIPythonWidget, self)._context_menu_make(pos)
@@ -209,7 +209,7 b' class RichIPythonWidget(IPythonWidget):'
209 # Chop stand-alone header from matplotlib SVG
209 # Chop stand-alone header from matplotlib SVG
210 offset = svg.find("<svg")
210 offset = svg.find("<svg")
211 assert(offset > -1)
211 assert(offset > -1)
212
212
213 return svg[offset:]
213 return svg[offset:]
214
214
215 else:
215 else:
@@ -51,20 +51,20 b' class QtShellSocketChannel(SocketChannelQObject, ShellSocketChannel):'
51 # Emitted when the first reply comes back.
51 # Emitted when the first reply comes back.
52 first_reply = QtCore.Signal()
52 first_reply = QtCore.Signal()
53
53
54 # Used by the first_reply signal logic to determine if a reply is the
54 # Used by the first_reply signal logic to determine if a reply is the
55 # first.
55 # first.
56 _handlers_called = False
56 _handlers_called = False
57
57
58 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
59 # 'ShellSocketChannel' interface
59 # 'ShellSocketChannel' interface
60 #---------------------------------------------------------------------------
60 #---------------------------------------------------------------------------
61
61
62 def call_handlers(self, msg):
62 def call_handlers(self, msg):
63 """ Reimplemented to emit signals instead of making callbacks.
63 """ Reimplemented to emit signals instead of making callbacks.
64 """
64 """
65 # Emit the generic signal.
65 # Emit the generic signal.
66 self.message_received.emit(msg)
66 self.message_received.emit(msg)
67
67
68 # Emit signals for specialized message types.
68 # Emit signals for specialized message types.
69 msg_type = msg['header']['msg_type']
69 msg_type = msg['header']['msg_type']
70 signal = getattr(self, msg_type, None)
70 signal = getattr(self, msg_type, None)
@@ -115,7 +115,7 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):'
115 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
116 # 'SubSocketChannel' interface
116 # 'SubSocketChannel' interface
117 #---------------------------------------------------------------------------
117 #---------------------------------------------------------------------------
118
118
119 def call_handlers(self, msg):
119 def call_handlers(self, msg):
120 """ Reimplemented to emit signals instead of making callbacks.
120 """ Reimplemented to emit signals instead of making callbacks.
121 """
121 """
@@ -153,7 +153,7 b' class QtStdInSocketChannel(SocketChannelQObject, StdInSocketChannel):'
153 """
153 """
154 # Emit the generic signal.
154 # Emit the generic signal.
155 self.message_received.emit(msg)
155 self.message_received.emit(msg)
156
156
157 # Emit signals for specialized message types.
157 # Emit signals for specialized message types.
158 msg_type = msg['header']['msg_type']
158 msg_type = msg['header']['msg_type']
159 if msg_type == 'input_request':
159 if msg_type == 'input_request':
@@ -208,7 +208,7 b' class QtKernelManager(KernelManager, SuperQObject):'
208 super(QtKernelManager, self).start_kernel(*args, **kw)
208 super(QtKernelManager, self).start_kernel(*args, **kw)
209
209
210 #------ Channel management -------------------------------------------------
210 #------ Channel management -------------------------------------------------
211
211
212 def start_channels(self, *args, **kw):
212 def start_channels(self, *args, **kw):
213 """ Reimplemented to emit signal.
213 """ Reimplemented to emit signal.
214 """
214 """
@@ -217,7 +217,7 b' class QtKernelManager(KernelManager, SuperQObject):'
217
217
218 def stop_channels(self):
218 def stop_channels(self):
219 """ Reimplemented to emit signal.
219 """ Reimplemented to emit signal.
220 """
220 """
221 super(QtKernelManager, self).stop_channels()
221 super(QtKernelManager, self).stop_channels()
222 self.stopped_channels.emit()
222 self.stopped_channels.emit()
223
223
@@ -233,7 +233,7 b' class QtKernelManager(KernelManager, SuperQObject):'
233 #---------------------------------------------------------------------------
233 #---------------------------------------------------------------------------
234 # Protected interface
234 # Protected interface
235 #---------------------------------------------------------------------------
235 #---------------------------------------------------------------------------
236
236
237 def _first_reply(self):
237 def _first_reply(self):
238 """ Unpauses the heartbeat channel when the first reply is received on
238 """ Unpauses the heartbeat channel when the first reply is received on
239 the execute channel. Note that this will *not* start the heartbeat
239 the execute channel. Note that this will *not* start the heartbeat
@@ -87,7 +87,7 b' class HtmlExporter(object):'
87 ib.setShortcut('I')
87 ib.setShortcut('I')
88 eb = QtGui.QPushButton("&External")
88 eb = QtGui.QPushButton("&External")
89 eb.setShortcut('E')
89 eb.setShortcut('E')
90 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
90 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
91 dialog.windowTitle(), msg)
91 dialog.windowTitle(), msg)
92 box.setInformativeText(info)
92 box.setInformativeText(info)
93 box.addButton(ib, QtGui.QMessageBox.NoRole)
93 box.addButton(ib, QtGui.QMessageBox.NoRole)
@@ -190,12 +190,12 b' def default_image_tag(match, path = None, format = "png"):'
190 """ Return (X)HTML mark-up for the image-tag given by match.
190 """ Return (X)HTML mark-up for the image-tag given by match.
191
191
192 This default implementation merely removes the image, and exists mostly
192 This default implementation merely removes the image, and exists mostly
193 for documentation purposes. More information than is present in the Qt
193 for documentation purposes. More information than is present in the Qt
194 HTML is required to supply the images.
194 HTML is required to supply the images.
195
195
196 Parameters
196 Parameters
197 ----------
197 ----------
198 match : re.SRE_Match
198 match : re.SRE_Match
199 A match to an HTML image tag as exported by Qt, with match.group("Name")
199 A match to an HTML image tag as exported by Qt, with match.group("Name")
200 containing the matched image ID.
200 containing the matched image ID.
201
201
@@ -212,7 +212,7 b' def default_image_tag(match, path = None, format = "png"):'
212
212
213 def fix_html(html):
213 def fix_html(html):
214 """ Transforms a Qt-generated HTML string into a standards-compliant one.
214 """ Transforms a Qt-generated HTML string into a standards-compliant one.
215
215
216 Parameters:
216 Parameters:
217 -----------
217 -----------
218 html : str,
218 html : str,
@@ -53,7 +53,7 b" def kill_embedded(self,parameter_s=''):"
53 figured out what you needed from it, you may then kill it and the program
53 figured out what you needed from it, you may then kill it and the program
54 will then continue to run without the interactive shell interfering again.
54 will then continue to run without the interactive shell interfering again.
55 """
55 """
56
56
57 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
57 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
58 "(y/n)? [y/N] ",'n')
58 "(y/n)? [y/N] ",'n')
59 if kill:
59 if kill:
@@ -206,7 +206,7 b' class InteractiveShellEmbed(TerminalInteractiveShell):'
206
206
207 with nested(self.builtin_trap, self.display_trap):
207 with nested(self.builtin_trap, self.display_trap):
208 self.interact(display_banner=display_banner)
208 self.interact(display_banner=display_banner)
209
209
210 # now, purge out the user namespace from anything we might have added
210 # now, purge out the user namespace from anything we might have added
211 # from the caller's local namespace
211 # from the caller's local namespace
212 delvar = self.user_ns.pop
212 delvar = self.user_ns.pop
@@ -234,7 +234,7 b' def embed(**kwargs):'
234 d = 40
234 d = 40
235 embed
235 embed
236
236
237 Full customization can be done by passing a :class:`Struct` in as the
237 Full customization can be done by passing a :class:`Struct` in as the
238 config argument.
238 config argument.
239 """
239 """
240 config = kwargs.get('config')
240 config = kwargs.get('config')
@@ -117,7 +117,7 b' class TerminalInteractiveShell(InteractiveShell):'
117 self.system = self.system_piped
117 self.system = self.system_piped
118 else:
118 else:
119 self.system = self.system_raw
119 self.system = self.system_raw
120
120
121 self.init_term_title()
121 self.init_term_title()
122 self.init_usage(usage)
122 self.init_usage(usage)
123 self.init_banner(banner1, banner2, display_banner)
123 self.init_banner(banner1, banner2, display_banner)
@@ -214,13 +214,13 b' class TerminalInteractiveShell(InteractiveShell):'
214 If an optional banner argument is given, it will override the
214 If an optional banner argument is given, it will override the
215 internally created default banner.
215 internally created default banner.
216 """
216 """
217
217
218 with nested(self.builtin_trap, self.display_trap):
218 with nested(self.builtin_trap, self.display_trap):
219
219
220 while 1:
220 while 1:
221 try:
221 try:
222 self.interact(display_banner=display_banner)
222 self.interact(display_banner=display_banner)
223 #self.interact_with_readline()
223 #self.interact_with_readline()
224 # XXX for testing of a readline-decoupled repl loop, call
224 # XXX for testing of a readline-decoupled repl loop, call
225 # interact_with_readline above
225 # interact_with_readline above
226 break
226 break
@@ -232,23 +232,23 b' class TerminalInteractiveShell(InteractiveShell):'
232 def interact(self, display_banner=None):
232 def interact(self, display_banner=None):
233 """Closely emulate the interactive Python console."""
233 """Closely emulate the interactive Python console."""
234
234
235 # batch run -> do not interact
235 # batch run -> do not interact
236 if self.exit_now:
236 if self.exit_now:
237 return
237 return
238
238
239 if display_banner is None:
239 if display_banner is None:
240 display_banner = self.display_banner
240 display_banner = self.display_banner
241
241
242 if isinstance(display_banner, basestring):
242 if isinstance(display_banner, basestring):
243 self.show_banner(display_banner)
243 self.show_banner(display_banner)
244 elif display_banner:
244 elif display_banner:
245 self.show_banner()
245 self.show_banner()
246
246
247 more = False
247 more = False
248
248
249 # Mark activity in the builtins
249 # Mark activity in the builtins
250 __builtin__.__dict__['__IPYTHON__active'] += 1
250 __builtin__.__dict__['__IPYTHON__active'] += 1
251
251
252 if self.has_readline:
252 if self.has_readline:
253 self.readline_startup_hook(self.pre_readline)
253 self.readline_startup_hook(self.pre_readline)
254 # exit_now is set by a call to %Exit or %Quit, through the
254 # exit_now is set by a call to %Exit or %Quit, through the
@@ -263,7 +263,7 b' class TerminalInteractiveShell(InteractiveShell):'
263 self.showtraceback()
263 self.showtraceback()
264 if self.autoindent:
264 if self.autoindent:
265 self.rl_do_indent = True
265 self.rl_do_indent = True
266
266
267 else:
267 else:
268 try:
268 try:
269 prompt = self.hooks.generate_prompt(False)
269 prompt = self.hooks.generate_prompt(False)
@@ -276,7 +276,7 b' class TerminalInteractiveShell(InteractiveShell):'
276 break
276 break
277 if self.autoindent:
277 if self.autoindent:
278 self.rl_do_indent = False
278 self.rl_do_indent = False
279
279
280 except KeyboardInterrupt:
280 except KeyboardInterrupt:
281 #double-guard against keyboardinterrupts during kbdint handling
281 #double-guard against keyboardinterrupts during kbdint handling
282 try:
282 try:
@@ -310,7 +310,7 b' class TerminalInteractiveShell(InteractiveShell):'
310 if not more:
310 if not more:
311 source_raw = self.input_splitter.source_raw_reset()[1]
311 source_raw = self.input_splitter.source_raw_reset()[1]
312 self.run_cell(source_raw)
312 self.run_cell(source_raw)
313
313
314 # We are off again...
314 # We are off again...
315 __builtin__.__dict__['__IPYTHON__active'] -= 1
315 __builtin__.__dict__['__IPYTHON__active'] -= 1
316
316
@@ -335,7 +335,7 b' class TerminalInteractiveShell(InteractiveShell):'
335
335
336 if self.has_readline:
336 if self.has_readline:
337 self.set_readline_completer()
337 self.set_readline_completer()
338
338
339 try:
339 try:
340 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
340 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
341 except ValueError:
341 except ValueError:
@@ -351,7 +351,7 b' class TerminalInteractiveShell(InteractiveShell):'
351 if num_ini_spaces(line) > self.indent_current_nsp:
351 if num_ini_spaces(line) > self.indent_current_nsp:
352 line = line[self.indent_current_nsp:]
352 line = line[self.indent_current_nsp:]
353 self.indent_current_nsp = 0
353 self.indent_current_nsp = 0
354
354
355 return line
355 return line
356
356
357 #-------------------------------------------------------------------------
357 #-------------------------------------------------------------------------
@@ -378,7 +378,7 b' class TerminalInteractiveShell(InteractiveShell):'
378 try:
378 try:
379 f = file(err.filename)
379 f = file(err.filename)
380 try:
380 try:
381 # This should be inside a display_trap block and I
381 # This should be inside a display_trap block and I
382 # think it is.
382 # think it is.
383 sys.displayhook(f.read())
383 sys.displayhook(f.read())
384 finally:
384 finally:
@@ -392,10 +392,10 b' class TerminalInteractiveShell(InteractiveShell):'
392 if e.filename in ('<ipython console>','<input>','<string>',
392 if e.filename in ('<ipython console>','<input>','<string>',
393 '<console>','<BackgroundJob compilation>',
393 '<console>','<BackgroundJob compilation>',
394 None):
394 None):
395
395
396 return False
396 return False
397 try:
397 try:
398 if (self.autoedit_syntax and
398 if (self.autoedit_syntax and
399 not self.ask_yes_no('Return to editor to correct syntax error? '
399 not self.ask_yes_no('Return to editor to correct syntax error? '
400 '[Y/n] ','y')):
400 '[Y/n] ','y')):
401 return False
401 return False
@@ -468,7 +468,7 b' class TerminalInteractiveShell(InteractiveShell):'
468 self.ask_exit()
468 self.ask_exit()
469 else:
469 else:
470 self.ask_exit()
470 self.ask_exit()
471
471
472 #------------------------------------------------------------------------
472 #------------------------------------------------------------------------
473 # Magic overrides
473 # Magic overrides
474 #------------------------------------------------------------------------
474 #------------------------------------------------------------------------
@@ -486,38 +486,38 b' class TerminalInteractiveShell(InteractiveShell):'
486 @skip_doctest
486 @skip_doctest
487 def magic_cpaste(self, parameter_s=''):
487 def magic_cpaste(self, parameter_s=''):
488 """Paste & execute a pre-formatted code block from clipboard.
488 """Paste & execute a pre-formatted code block from clipboard.
489
489
490 You must terminate the block with '--' (two minus-signs) alone on the
490 You must terminate the block with '--' (two minus-signs) alone on the
491 line. You can also provide your own sentinel with '%paste -s %%' ('%%'
491 line. You can also provide your own sentinel with '%paste -s %%' ('%%'
492 is the new sentinel for this operation)
492 is the new sentinel for this operation)
493
493
494 The block is dedented prior to execution to enable execution of method
494 The block is dedented prior to execution to enable execution of method
495 definitions. '>' and '+' characters at the beginning of a line are
495 definitions. '>' and '+' characters at the beginning of a line are
496 ignored, to allow pasting directly from e-mails, diff files and
496 ignored, to allow pasting directly from e-mails, diff files and
497 doctests (the '...' continuation prompt is also stripped). The
497 doctests (the '...' continuation prompt is also stripped). The
498 executed block is also assigned to variable named 'pasted_block' for
498 executed block is also assigned to variable named 'pasted_block' for
499 later editing with '%edit pasted_block'.
499 later editing with '%edit pasted_block'.
500
500
501 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
501 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
502 This assigns the pasted block to variable 'foo' as string, without
502 This assigns the pasted block to variable 'foo' as string, without
503 dedenting or executing it (preceding >>> and + is still stripped)
503 dedenting or executing it (preceding >>> and + is still stripped)
504
504
505 '%cpaste -r' re-executes the block previously entered by cpaste.
505 '%cpaste -r' re-executes the block previously entered by cpaste.
506
506
507 Do not be alarmed by garbled output on Windows (it's a readline bug).
507 Do not be alarmed by garbled output on Windows (it's a readline bug).
508 Just press enter and type -- (and press enter again) and the block
508 Just press enter and type -- (and press enter again) and the block
509 will be what was just pasted.
509 will be what was just pasted.
510
510
511 IPython statements (magics, shell escapes) are not supported (yet).
511 IPython statements (magics, shell escapes) are not supported (yet).
512
512
513 See also
513 See also
514 --------
514 --------
515 paste: automatically pull code from clipboard.
515 paste: automatically pull code from clipboard.
516
516
517 Examples
517 Examples
518 --------
518 --------
519 ::
519 ::
520
520
521 In [8]: %cpaste
521 In [8]: %cpaste
522 Pasting code; enter '--' alone on the line to stop.
522 Pasting code; enter '--' alone on the line to stop.
523 :>>> a = ["world!", "Hello"]
523 :>>> a = ["world!", "Hello"]
@@ -525,13 +525,13 b' class TerminalInteractiveShell(InteractiveShell):'
525 :--
525 :--
526 Hello world!
526 Hello world!
527 """
527 """
528
528
529 opts,args = self.parse_options(parameter_s,'rs:',mode='string')
529 opts,args = self.parse_options(parameter_s,'rs:',mode='string')
530 par = args.strip()
530 par = args.strip()
531 if opts.has_key('r'):
531 if opts.has_key('r'):
532 self._rerun_pasted()
532 self._rerun_pasted()
533 return
533 return
534
534
535 sentinel = opts.get('s','--')
535 sentinel = opts.get('s','--')
536
536
537 block = self._strip_pasted_lines_for_code(
537 block = self._strip_pasted_lines_for_code(
@@ -541,7 +541,7 b' class TerminalInteractiveShell(InteractiveShell):'
541
541
542 def magic_paste(self, parameter_s=''):
542 def magic_paste(self, parameter_s=''):
543 """Paste & execute a pre-formatted code block from clipboard.
543 """Paste & execute a pre-formatted code block from clipboard.
544
544
545 The text is pulled directly from the clipboard without user
545 The text is pulled directly from the clipboard without user
546 intervention and printed back on the screen before execution (unless
546 intervention and printed back on the screen before execution (unless
547 the -q flag is given to force quiet mode).
547 the -q flag is given to force quiet mode).
@@ -552,18 +552,18 b' class TerminalInteractiveShell(InteractiveShell):'
552 doctests (the '...' continuation prompt is also stripped). The
552 doctests (the '...' continuation prompt is also stripped). The
553 executed block is also assigned to variable named 'pasted_block' for
553 executed block is also assigned to variable named 'pasted_block' for
554 later editing with '%edit pasted_block'.
554 later editing with '%edit pasted_block'.
555
555
556 You can also pass a variable name as an argument, e.g. '%paste foo'.
556 You can also pass a variable name as an argument, e.g. '%paste foo'.
557 This assigns the pasted block to variable 'foo' as string, without
557 This assigns the pasted block to variable 'foo' as string, without
558 dedenting or executing it (preceding >>> and + is still stripped)
558 dedenting or executing it (preceding >>> and + is still stripped)
559
559
560 Options
560 Options
561 -------
561 -------
562
562
563 -r: re-executes the block previously entered by cpaste.
563 -r: re-executes the block previously entered by cpaste.
564
564
565 -q: quiet mode: do not echo the pasted text back to the terminal.
565 -q: quiet mode: do not echo the pasted text back to the terminal.
566
566
567 IPython statements (magics, shell escapes) are not supported (yet).
567 IPython statements (magics, shell escapes) are not supported (yet).
568
568
569 See also
569 See also
@@ -586,9 +586,9 b' class TerminalInteractiveShell(InteractiveShell):'
586 if not block.endswith('\n'):
586 if not block.endswith('\n'):
587 write('\n')
587 write('\n')
588 write("## -- End pasted text --\n")
588 write("## -- End pasted text --\n")
589
589
590 self._execute_block(block, par)
590 self._execute_block(block, par)
591
591
592 def showindentationerror(self):
592 def showindentationerror(self):
593 super(TerminalInteractiveShell, self).showindentationerror()
593 super(TerminalInteractiveShell, self).showindentationerror()
594 print("If you want to paste code into IPython, try the %paste magic function.")
594 print("If you want to paste code into IPython, try the %paste magic function.")
@@ -201,7 +201,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
201 PlainTextFormatter,
201 PlainTextFormatter,
202 Completer,
202 Completer,
203 ]
203 ]
204
204
205 subcommands = Dict(dict(
205 subcommands = Dict(dict(
206 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
206 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
207 """Launch the IPython Qt Console."""
207 """Launch the IPython Qt Console."""
@@ -216,7 +216,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
216 "Start a kernel without an attached frontend."
216 "Start a kernel without an attached frontend."
217 ),
217 ),
218 ))
218 ))
219
219
220 # *do* autocreate requested profile, but don't create the config file.
220 # *do* autocreate requested profile, but don't create the config file.
221 auto_create=Bool(True)
221 auto_create=Bool(True)
222 # configurables
222 # configurables
@@ -253,7 +253,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
253 def _force_interact_changed(self, name, old, new):
253 def _force_interact_changed(self, name, old, new):
254 if new:
254 if new:
255 self.interact = True
255 self.interact = True
256
256
257 def _file_to_run_changed(self, name, old, new):
257 def _file_to_run_changed(self, name, old, new):
258 if new and not self.force_interact:
258 if new and not self.force_interact:
259 self.interact = False
259 self.interact = False
@@ -283,7 +283,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
283 sub = '--pylab='+gui
283 sub = '--pylab='+gui
284 argv.pop(idx+1)
284 argv.pop(idx+1)
285 argv[idx] = sub
285 argv[idx] = sub
286
286
287 return super(TerminalIPythonApp, self).parse_command_line(argv)
287 return super(TerminalIPythonApp, self).parse_command_line(argv)
288
288
289 def initialize(self, argv=None):
289 def initialize(self, argv=None):
@@ -313,7 +313,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
313 sys.path.insert(0, '')
313 sys.path.insert(0, '')
314
314
315 # Create an InteractiveShell instance.
315 # Create an InteractiveShell instance.
316 # shell.display_banner should always be False for the terminal
316 # shell.display_banner should always be False for the terminal
317 # based app, because we call shell.show_banner() by hand below
317 # based app, because we call shell.show_banner() by hand below
318 # so the banner shows *before* all extension loading stuff.
318 # so the banner shows *before* all extension loading stuff.
319 self.shell = TerminalInteractiveShell.instance(config=self.config,
319 self.shell = TerminalInteractiveShell.instance(config=self.config,
@@ -18,10 +18,10 b' def win32_clipboard_get():'
18 message = ("Getting text from the clipboard requires the pywin32 "
18 message = ("Getting text from the clipboard requires the pywin32 "
19 "extensions: http://sourceforge.net/projects/pywin32/")
19 "extensions: http://sourceforge.net/projects/pywin32/")
20 raise TryNext(message)
20 raise TryNext(message)
21 win32clipboard.OpenClipboard()
21 win32clipboard.OpenClipboard()
22 text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)
22 text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)
23 # FIXME: convert \r\n to \n?
23 # FIXME: convert \r\n to \n?
24 win32clipboard.CloseClipboard()
24 win32clipboard.CloseClipboard()
25 return text
25 return text
26
26
27 def osx_clipboard_get():
27 def osx_clipboard_get():
@@ -9,11 +9,11 b' To enable it type::'
9 You can then disable it with::
9 You can then disable it with::
10
10
11 __builtin__.reload = deepreload.original_reload
11 __builtin__.reload = deepreload.original_reload
12
12
13 Alternatively, you can add a dreload builtin alongside normal reload with::
13 Alternatively, you can add a dreload builtin alongside normal reload with::
14
14
15 __builtin__.dreload = deepreload.reload
15 __builtin__.dreload = deepreload.reload
16
16
17 This code is almost entirely based on knee.py from the standard library.
17 This code is almost entirely based on knee.py from the standard library.
18 """
18 """
19
19
@@ -30,7 +30,7 b' import sys'
30
30
31 # Replacement for __import__()
31 # Replacement for __import__()
32 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
32 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
33 # For now level is ignored, it's just there to prevent crash
33 # For now level is ignored, it's just there to prevent crash
34 # with from __future__ import absolute_import
34 # with from __future__ import absolute_import
35 parent = determine_parent(globals)
35 parent = determine_parent(globals)
36 q, tail = find_head_package(parent, name)
36 q, tail = find_head_package(parent, name)
@@ -94,7 +94,7 b' def load_tail(q, tail):'
94 # to:
94 # to:
95 mname = m.__name__
95 mname = m.__name__
96 # This needs more testing!!! (I don't understand this module too well)
96 # This needs more testing!!! (I don't understand this module too well)
97
97
98 #print '** head,tail=|%s|->|%s|, mname=|%s|' % (head,tail,mname) # dbg
98 #print '** head,tail=|%s|->|%s|, mname=|%s|' % (head,tail,mname) # dbg
99 m = import_module(head, mname, m)
99 m = import_module(head, mname, m)
100 if not m:
100 if not m:
@@ -125,25 +125,25 b' def import_module(partname, fqname, parent):'
125 global found_now
125 global found_now
126 if found_now.has_key(fqname):
126 if found_now.has_key(fqname):
127 try:
127 try:
128 return sys.modules[fqname]
128 return sys.modules[fqname]
129 except KeyError:
129 except KeyError:
130 pass
130 pass
131
131
132 print 'Reloading', fqname #, sys.excepthook is sys.__excepthook__, \
132 print 'Reloading', fqname #, sys.excepthook is sys.__excepthook__, \
133 #sys.displayhook is sys.__displayhook__
133 #sys.displayhook is sys.__displayhook__
134
134
135 found_now[fqname] = 1
135 found_now[fqname] = 1
136 try:
136 try:
137 fp, pathname, stuff = imp.find_module(partname,
137 fp, pathname, stuff = imp.find_module(partname,
138 parent and parent.__path__)
138 parent and parent.__path__)
139 except ImportError:
139 except ImportError:
140 return None
140 return None
141
141
142 try:
142 try:
143 m = imp.load_module(fqname, fp, pathname, stuff)
143 m = imp.load_module(fqname, fp, pathname, stuff)
144 finally:
144 finally:
145 if fp: fp.close()
145 if fp: fp.close()
146
146
147 if parent:
147 if parent:
148 setattr(parent, partname, m)
148 setattr(parent, partname, m)
149
149
@@ -168,14 +168,14 b' except AttributeError:'
168 def reload(module, exclude=['sys', '__builtin__', '__main__']):
168 def reload(module, exclude=['sys', '__builtin__', '__main__']):
169 """Recursively reload all modules used in the given module. Optionally
169 """Recursively reload all modules used in the given module. Optionally
170 takes a list of modules to exclude from reloading. The default exclude
170 takes a list of modules to exclude from reloading. The default exclude
171 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
171 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
172 display, exception, and io hooks.
172 display, exception, and io hooks.
173 """
173 """
174 global found_now
174 global found_now
175 for i in exclude:
175 for i in exclude:
176 found_now[i] = 1
176 found_now[i] = 1
177 original_import = __builtin__.__import__
177 original_import = __builtin__.__import__
178 __builtin__.__import__ = deep_import_hook
178 __builtin__.__import__ = deep_import_hook
179 try:
179 try:
180 ret = deep_reload_hook(module)
180 ret = deep_reload_hook(module)
181 finally:
181 finally:
@@ -44,7 +44,7 b' subclassing more convenient. Their docstrings below have some more details:'
44
44
45 - post_cmd(): run right after the execution of each block. If the block
45 - post_cmd(): run right after the execution of each block. If the block
46 raises an exception, this is NOT called.
46 raises an exception, this is NOT called.
47
47
48
48
49 Operation
49 Operation
50 =========
50 =========
@@ -72,14 +72,14 b' The supported tags are:'
72 word 'stop', to help visually distinguish the blocks in a text editor:
72 word 'stop', to help visually distinguish the blocks in a text editor:
73
73
74 # <demo> --- stop ---
74 # <demo> --- stop ---
75
75
76
76
77 # <demo> silent
77 # <demo> silent
78
78
79 Make a block execute silently (and hence automatically). Typically used in
79 Make a block execute silently (and hence automatically). Typically used in
80 cases where you have some boilerplate or initialization code which you need
80 cases where you have some boilerplate or initialization code which you need
81 executed but do not want to be seen in the demo.
81 executed but do not want to be seen in the demo.
82
82
83 # <demo> auto
83 # <demo> auto
84
84
85 Make a block execute automatically, but still being printed. Useful for
85 Make a block execute automatically, but still being printed. Useful for
@@ -116,7 +116,7 b' been added to the "docs/examples/core" directory. Just cd to this directory in'
116 an IPython session, and type::
116 an IPython session, and type::
117
117
118 %run demo-exercizer.py
118 %run demo-exercizer.py
119
119
120 and then follow the directions.
120 and then follow the directions.
121
121
122 Example
122 Example
@@ -200,14 +200,14 b' class Demo(object):'
200 IPython.Demo? in IPython to see it).
200 IPython.Demo? in IPython to see it).
201
201
202 Inputs:
202 Inputs:
203
203
204 - src is either a file, or file-like object, or a
204 - src is either a file, or file-like object, or a
205 string that can be resolved to a filename.
205 string that can be resolved to a filename.
206
206
207 Optional inputs:
207 Optional inputs:
208
208
209 - title: a string to use as the demo name. Of most use when the demo
209 - title: a string to use as the demo name. Of most use when the demo
210 you are making comes from an object that has no filename, or if you
210 you are making comes from an object that has no filename, or if you
211 want an alternate denotation distinct from the filename.
211 want an alternate denotation distinct from the filename.
212
212
213 - arg_str(''): a string of arguments, internally converted to a list
213 - arg_str(''): a string of arguments, internally converted to a list
@@ -238,7 +238,7 b' class Demo(object):'
238 self.sys_argv = [src] + shlex.split(arg_str)
238 self.sys_argv = [src] + shlex.split(arg_str)
239 self.auto_all = auto_all
239 self.auto_all = auto_all
240 self.src = src
240 self.src = src
241
241
242 # get a few things from ipython. While it's a bit ugly design-wise,
242 # get a few things from ipython. While it's a bit ugly design-wise,
243 # it ensures that things like color scheme and the like are always in
243 # it ensures that things like color scheme and the like are always in
244 # sync with the ipython mode being used. This class is only meant to
244 # sync with the ipython mode being used. This class is only meant to
@@ -268,7 +268,7 b' class Demo(object):'
268 def reload(self):
268 def reload(self):
269 """Reload source from disk and initialize state."""
269 """Reload source from disk and initialize state."""
270 self.fload()
270 self.fload()
271
271
272 self.src = self.fobj.read()
272 self.src = self.fobj.read()
273 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
273 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
274 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
274 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
@@ -315,7 +315,7 b' class Demo(object):'
315 """Get the current block index, validating and checking status.
315 """Get the current block index, validating and checking status.
316
316
317 Returns None if the demo is finished"""
317 Returns None if the demo is finished"""
318
318
319 if index is None:
319 if index is None:
320 if self.finished:
320 if self.finished:
321 print >>io.stdout, 'Demo finished. Use <demo_name>.reset() if you want to rerun it.'
321 print >>io.stdout, 'Demo finished. Use <demo_name>.reset() if you want to rerun it.'
@@ -369,7 +369,7 b' class Demo(object):'
369 # that the default demo.edit() call opens up the sblock we've last run
369 # that the default demo.edit() call opens up the sblock we've last run
370 if index>0:
370 if index>0:
371 index -= 1
371 index -= 1
372
372
373 filename = self.shell.mktempfile(self.src_blocks[index])
373 filename = self.shell.mktempfile(self.src_blocks[index])
374 self.shell.hooks.editor(filename,1)
374 self.shell.hooks.editor(filename,1)
375 new_block = file_read(filename)
375 new_block = file_read(filename)
@@ -379,7 +379,7 b' class Demo(object):'
379 self.block_index = index
379 self.block_index = index
380 # call to run with the newly edited index
380 # call to run with the newly edited index
381 self()
381 self()
382
382
383 def show(self,index=None):
383 def show(self,index=None):
384 """Show a single block on screen"""
384 """Show a single block on screen"""
385
385
@@ -414,7 +414,7 b' class Demo(object):'
414 """Execute a string with one or more lines of code"""
414 """Execute a string with one or more lines of code"""
415
415
416 exec source in self.user_ns
416 exec source in self.user_ns
417
417
418 def __call__(self,index=None):
418 def __call__(self,index=None):
419 """run a block of the demo.
419 """run a block of the demo.
420
420
@@ -452,7 +452,7 b' class Demo(object):'
452 self.post_cmd()
452 self.post_cmd()
453 finally:
453 finally:
454 sys.argv = save_argv
454 sys.argv = save_argv
455
455
456 except:
456 except:
457 self.ip_showtb(filename=self.fname)
457 self.ip_showtb(filename=self.fname)
458 else:
458 else:
@@ -499,7 +499,7 b' class IPythonDemo(Demo):'
499 """Execute a string with one or more lines of code"""
499 """Execute a string with one or more lines of code"""
500
500
501 self.shell.run_cell(source)
501 self.shell.run_cell(source)
502
502
503 class LineDemo(Demo):
503 class LineDemo(Demo):
504 """Demo where each line is executed as a separate block.
504 """Demo where each line is executed as a separate block.
505
505
@@ -513,7 +513,7 b' class LineDemo(Demo):'
513 Note: the input can not have *any* indentation, which means that only
513 Note: the input can not have *any* indentation, which means that only
514 single-lines of input are accepted, not even function definitions are
514 single-lines of input are accepted, not even function definitions are
515 valid."""
515 valid."""
516
516
517 def reload(self):
517 def reload(self):
518 """Reload source from disk and initialize state."""
518 """Reload source from disk and initialize state."""
519 # read data and parse into blocks
519 # read data and parse into blocks
@@ -542,26 +542,26 b' class IPythonLineDemo(IPythonDemo,LineDemo):'
542
542
543 class ClearMixin(object):
543 class ClearMixin(object):
544 """Use this mixin to make Demo classes with less visual clutter.
544 """Use this mixin to make Demo classes with less visual clutter.
545
545
546 Demos using this mixin will clear the screen before every block and use
546 Demos using this mixin will clear the screen before every block and use
547 blank marquees.
547 blank marquees.
548
548
549 Note that in order for the methods defined here to actually override those
549 Note that in order for the methods defined here to actually override those
550 of the classes it's mixed with, it must go /first/ in the inheritance
550 of the classes it's mixed with, it must go /first/ in the inheritance
551 tree. For example:
551 tree. For example:
552
552
553 class ClearIPDemo(ClearMixin,IPythonDemo): pass
553 class ClearIPDemo(ClearMixin,IPythonDemo): pass
554
554
555 will provide an IPythonDemo class with the mixin's features.
555 will provide an IPythonDemo class with the mixin's features.
556 """
556 """
557
557
558 def marquee(self,txt='',width=78,mark='*'):
558 def marquee(self,txt='',width=78,mark='*'):
559 """Blank marquee that returns '' no matter what the input."""
559 """Blank marquee that returns '' no matter what the input."""
560 return ''
560 return ''
561
561
562 def pre_cmd(self):
562 def pre_cmd(self):
563 """Method called before executing each block.
563 """Method called before executing each block.
564
564
565 This one simply clears the screen."""
565 This one simply clears the screen."""
566 from IPython.utils.terminal import term_clear
566 from IPython.utils.terminal import term_clear
567 term_clear()
567 term_clear()
@@ -5,30 +5,30 b' Authors : MinRK'
5
5
6 class YouTubeVideo(object):
6 class YouTubeVideo(object):
7 """Class for embedding a YouTube Video in an IPython session, based on its video id.
7 """Class for embedding a YouTube Video in an IPython session, based on its video id.
8
8
9 e.g. to embed the video on this page:
9 e.g. to embed the video on this page:
10
10
11 http://www.youtube.com/watch?v=foo
11 http://www.youtube.com/watch?v=foo
12
12
13 you would do:
13 you would do:
14
14
15 vid = YouTubeVideo("foo")
15 vid = YouTubeVideo("foo")
16 display(vid)
16 display(vid)
17 """
17 """
18
18
19 def __init__(self, id, width=400, height=300):
19 def __init__(self, id, width=400, height=300):
20 self.id = id
20 self.id = id
21 self.width = width
21 self.width = width
22 self.height = height
22 self.height = height
23
23
24 def _repr_html_(self):
24 def _repr_html_(self):
25 """return YouTube embed iframe for this video id"""
25 """return YouTube embed iframe for this video id"""
26 return """
26 return """
27 <iframe
27 <iframe
28 width="%i"
28 width="%i"
29 height="%i"
29 height="%i"
30 src="http://www.youtube.com/embed/%s"
30 src="http://www.youtube.com/embed/%s"
31 frameborder="0"
31 frameborder="0"
32 allowfullscreen
32 allowfullscreen
33 ></iframe>
33 ></iframe>
34 """%(self.width, self.height, self.id)
34 """%(self.width, self.height, self.id)
@@ -8,11 +8,11 b' session. IPython has two different types of GUI integration:'
8 1. The terminal based IPython supports GUI event loops through Python's
8 1. The terminal based IPython supports GUI event loops through Python's
9 PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
9 PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
10 whenever raw_input is waiting for a user to type code. We implement GUI
10 whenever raw_input is waiting for a user to type code. We implement GUI
11 support in the terminal by setting PyOS_InputHook to a function that
11 support in the terminal by setting PyOS_InputHook to a function that
12 iterates the event loop for a short while. It is important to note that
12 iterates the event loop for a short while. It is important to note that
13 in this situation, the real GUI event loop is NOT run in the normal
13 in this situation, the real GUI event loop is NOT run in the normal
14 manner, so you can't use the normal means to detect that it is running.
14 manner, so you can't use the normal means to detect that it is running.
15 2. In the two process IPython kernel/frontend, the GUI event loop is run in
15 2. In the two process IPython kernel/frontend, the GUI event loop is run in
16 the kernel. In this case, the event loop is run in the normal manner by
16 the kernel. In this case, the event loop is run in the normal manner by
17 calling the function or method of the GUI toolkit that starts the event
17 calling the function or method of the GUI toolkit that starts the event
18 loop.
18 loop.
@@ -47,7 +47,7 b' we proposed the following informal protocol:'
47 *must* use its value. If it has not been set, you can query the toolkit
47 *must* use its value. If it has not been set, you can query the toolkit
48 in the normal manner.
48 in the normal manner.
49 * If you want GUI support and no one else has created an application or
49 * If you want GUI support and no one else has created an application or
50 started the event loop you *must* do this. We don't want projects to
50 started the event loop you *must* do this. We don't want projects to
51 attempt to defer these things to someone else if they themselves need it.
51 attempt to defer these things to someone else if they themselves need it.
52
52
53 The functions below implement this logic for each GUI toolkit. If you need
53 The functions below implement this logic for each GUI toolkit. If you need
@@ -45,7 +45,7 b' from timeit import default_timer as clock'
45 # Frame per second : 60
45 # Frame per second : 60
46 # Should probably be an IPython option
46 # Should probably be an IPython option
47 glut_fps = 60
47 glut_fps = 60
48
48
49
49
50 # Display mode : double buffeed + rgba + depth
50 # Display mode : double buffeed + rgba + depth
51 # Should probably be an IPython option
51 # Should probably be an IPython option
@@ -56,10 +56,10 b' glut_display_mode = (glut.GLUT_DOUBLE |'
56 glutMainLoopEvent = None
56 glutMainLoopEvent = None
57 if sys.platform == 'darwin':
57 if sys.platform == 'darwin':
58 try:
58 try:
59 glutCheckLoop = platform.createBaseFunction(
59 glutCheckLoop = platform.createBaseFunction(
60 'glutCheckLoop', dll=platform.GLUT, resultType=None,
60 'glutCheckLoop', dll=platform.GLUT, resultType=None,
61 argTypes=[],
61 argTypes=[],
62 doc='glutCheckLoop( ) -> None',
62 doc='glutCheckLoop( ) -> None',
63 argNames=(),
63 argNames=(),
64 )
64 )
65 except AttributeError:
65 except AttributeError:
@@ -125,7 +125,7 b' def glut_int_handler(signum, frame):'
125 #-----------------------------------------------------------------------------
125 #-----------------------------------------------------------------------------
126 def inputhook_glut():
126 def inputhook_glut():
127 """Run the pyglet event loop by processing pending events only.
127 """Run the pyglet event loop by processing pending events only.
128
128
129 This keeps processing pending events until stdin is ready. After
129 This keeps processing pending events until stdin is ready. After
130 processing all pending events, a call to time.sleep is inserted. This is
130 processing all pending events, a call to time.sleep is inserted. This is
131 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
131 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
@@ -148,7 +148,7 b' def inputhook_glut():'
148 while not stdin_ready():
148 while not stdin_ready():
149 glutMainLoopEvent()
149 glutMainLoopEvent()
150 # We need to sleep at this point to keep the idle CPU load
150 # We need to sleep at this point to keep the idle CPU load
151 # low. However, if sleep to long, GUI response is poor. As
151 # low. However, if sleep to long, GUI response is poor. As
152 # a compromise, we watch how often GUI events are being processed
152 # a compromise, we watch how often GUI events are being processed
153 # and switch between a short and long sleep time. Here are some
153 # and switch between a short and long sleep time. Here are some
154 # stats useful in helping to tune this.
154 # stats useful in helping to tune this.
@@ -68,7 +68,7 b' else:'
68
68
69 def inputhook_pyglet():
69 def inputhook_pyglet():
70 """Run the pyglet event loop by processing pending events only.
70 """Run the pyglet event loop by processing pending events only.
71
71
72 This keeps processing pending events until stdin is ready. After
72 This keeps processing pending events until stdin is ready. After
73 processing all pending events, a call to time.sleep is inserted. This is
73 processing all pending events, a call to time.sleep is inserted. This is
74 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
74 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
@@ -87,7 +87,7 b' def inputhook_pyglet():'
87 flip(window)
87 flip(window)
88
88
89 # We need to sleep at this point to keep the idle CPU load
89 # We need to sleep at this point to keep the idle CPU load
90 # low. However, if sleep to long, GUI response is poor. As
90 # low. However, if sleep to long, GUI response is poor. As
91 # a compromise, we watch how often GUI events are being processed
91 # a compromise, we watch how often GUI events are being processed
92 # and switch between a short and long sleep time. Here are some
92 # and switch between a short and long sleep time. Here are some
93 # stats useful in helping to tune this.
93 # stats useful in helping to tune this.
@@ -46,8 +46,8 b' def stdin_ready():'
46
46
47 def inputhook_wx1():
47 def inputhook_wx1():
48 """Run the wx event loop by processing pending events only.
48 """Run the wx event loop by processing pending events only.
49
49
50 This approach seems to work, but its performance is not great as it
50 This approach seems to work, but its performance is not great as it
51 relies on having PyOS_InputHook called regularly.
51 relies on having PyOS_InputHook called regularly.
52 """
52 """
53 try:
53 try:
@@ -92,16 +92,16 b' class EventLoopRunner(object):'
92
92
93 def inputhook_wx2():
93 def inputhook_wx2():
94 """Run the wx event loop, polling for stdin.
94 """Run the wx event loop, polling for stdin.
95
95
96 This version runs the wx eventloop for an undetermined amount of time,
96 This version runs the wx eventloop for an undetermined amount of time,
97 during which it periodically checks to see if anything is ready on
97 during which it periodically checks to see if anything is ready on
98 stdin. If anything is ready on stdin, the event loop exits.
98 stdin. If anything is ready on stdin, the event loop exits.
99
99
100 The argument to elr.Run controls how often the event loop looks at stdin.
100 The argument to elr.Run controls how often the event loop looks at stdin.
101 This determines the responsiveness at the keyboard. A setting of 1000
101 This determines the responsiveness at the keyboard. A setting of 1000
102 enables a user to type at most 1 char per second. I have found that a
102 enables a user to type at most 1 char per second. I have found that a
103 setting of 10 gives good keyboard response. We can shorten it further,
103 setting of 10 gives good keyboard response. We can shorten it further,
104 but eventually performance would suffer from calling select/kbhit too
104 but eventually performance would suffer from calling select/kbhit too
105 often.
105 often.
106 """
106 """
107 try:
107 try:
@@ -118,9 +118,9 b' def inputhook_wx2():'
118
118
119 def inputhook_wx3():
119 def inputhook_wx3():
120 """Run the wx event loop by processing pending events only.
120 """Run the wx event loop by processing pending events only.
121
121
122 This is like inputhook_wx1, but it keeps processing pending events
122 This is like inputhook_wx1, but it keeps processing pending events
123 until stdin is ready. After processing all pending events, a call to
123 until stdin is ready. After processing all pending events, a call to
124 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
124 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
125 This sleep time should be tuned though for best performance.
125 This sleep time should be tuned though for best performance.
126 """
126 """
@@ -146,7 +146,7 b' def inputhook_wx3():'
146 evtloop.Dispatch()
146 evtloop.Dispatch()
147 app.ProcessIdle()
147 app.ProcessIdle()
148 # We need to sleep at this point to keep the idle CPU load
148 # We need to sleep at this point to keep the idle CPU load
149 # low. However, if sleep to long, GUI response is poor. As
149 # low. However, if sleep to long, GUI response is poor. As
150 # a compromise, we watch how often GUI events are being processed
150 # a compromise, we watch how often GUI events are being processed
151 # and switch between a short and long sleep time. Here are some
151 # and switch between a short and long sleep time. Here are some
152 # stats useful in helping to tune this.
152 # stats useful in helping to tune this.
@@ -59,11 +59,11 b' def pexpect_monkeypatch():'
59 self.close() will trigger an exception because it tries to call os.close(),
59 self.close() will trigger an exception because it tries to call os.close(),
60 and os is now None.
60 and os is now None.
61 """
61 """
62
62
63 if pexpect.__version__[:3] >= '2.2':
63 if pexpect.__version__[:3] >= '2.2':
64 # No need to patch, fix is already the upstream version.
64 # No need to patch, fix is already the upstream version.
65 return
65 return
66
66
67 def __del__(self):
67 def __del__(self):
68 """This makes sure that no system resources are left open.
68 """This makes sure that no system resources are left open.
69 Python only garbage collects Python objects. OS file descriptors
69 Python only garbage collects Python objects. OS file descriptors
@@ -84,7 +84,7 b' pexpect_monkeypatch()'
84 # The generic runner class
84 # The generic runner class
85 class InteractiveRunner(object):
85 class InteractiveRunner(object):
86 """Class to run a sequence of commands through an interactive program."""
86 """Class to run a sequence of commands through an interactive program."""
87
87
88 def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True):
88 def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True):
89 """Construct a runner.
89 """Construct a runner.
90
90
@@ -121,7 +121,7 b' class InteractiveRunner(object):'
121 increase this value (it is measured in seconds). Note that this
121 increase this value (it is measured in seconds). Note that this
122 variable is not honored at all by older versions of pexpect.
122 variable is not honored at all by older versions of pexpect.
123 """
123 """
124
124
125 self.program = program
125 self.program = program
126 self.prompts = prompts
126 self.prompts = prompts
127 if args is None: args = []
127 if args is None: args = []
@@ -201,7 +201,7 b' class InteractiveRunner(object):'
201 else:
201 else:
202 # Quiet mode, all writes are no-ops
202 # Quiet mode, all writes are no-ops
203 write = lambda s: None
203 write = lambda s: None
204
204
205 c = self.child
205 c = self.child
206 prompts = c.compile_pattern_list(self.prompts)
206 prompts = c.compile_pattern_list(self.prompts)
207 prompt_idx = c.expect_list(prompts)
207 prompt_idx = c.expect_list(prompts)
@@ -214,7 +214,7 b' class InteractiveRunner(object):'
214 if get_output:
214 if get_output:
215 output = []
215 output = []
216 store_output = output.append
216 store_output = output.append
217
217
218 for cmd in source:
218 for cmd in source:
219 # skip blank lines for all matches to the 'main' prompt, while the
219 # skip blank lines for all matches to the 'main' prompt, while the
220 # secondary prompts do not
220 # secondary prompts do not
@@ -233,7 +233,7 b' class InteractiveRunner(object):'
233 write(c.before)
233 write(c.before)
234 end_normal = False
234 end_normal = False
235 break
235 break
236
236
237 write(c.before)
237 write(c.before)
238
238
239 # With an echoing process, the output we get in c.before contains
239 # With an echoing process, the output we get in c.before contains
@@ -266,11 +266,11 b' class InteractiveRunner(object):'
266 # hangs on the second invocation.
266 # hangs on the second invocation.
267 if c.isalive():
267 if c.isalive():
268 c.send('\n')
268 c.send('\n')
269
269
270 # Return any requested output
270 # Return any requested output
271 if get_output:
271 if get_output:
272 return ''.join(output)
272 return ''.join(output)
273
273
274 def main(self,argv=None):
274 def main(self,argv=None):
275 """Run as a command-line script."""
275 """Run as a command-line script."""
276
276
@@ -301,7 +301,7 b' class IPythonRunner(InteractiveRunner):'
301 pexpect need to be matched to the actual prompts, so user-customized
301 pexpect need to be matched to the actual prompts, so user-customized
302 prompts would break this.
302 prompts would break this.
303 """
303 """
304
304
305 def __init__(self,program = 'ipython',args=None,out=sys.stdout,echo=True):
305 def __init__(self,program = 'ipython',args=None,out=sys.stdout,echo=True):
306 """New runner, optionally passing the ipython command to use."""
306 """New runner, optionally passing the ipython command to use."""
307 args0 = ['--colors=NoColor',
307 args0 = ['--colors=NoColor',
@@ -327,10 +327,10 b' class PythonRunner(InteractiveRunner):'
327
327
328 class SAGERunner(InteractiveRunner):
328 class SAGERunner(InteractiveRunner):
329 """Interactive SAGE runner.
329 """Interactive SAGE runner.
330
330
331 WARNING: this runner only works if you manually adjust your SAGE
331 WARNING: this runner only works if you manually adjust your SAGE
332 configuration so that the 'color' option in the configuration file is set to
332 configuration so that the 'color' option in the configuration file is set to
333 'NoColor', because currently the prompt matching regexp does not identify
333 'NoColor', because currently the prompt matching regexp does not identify
334 color sequences."""
334 color sequences."""
335
335
336 def __init__(self,program='sage',args=None,out=sys.stdout,echo=True):
336 def __init__(self,program='sage',args=None,out=sys.stdout,echo=True):
@@ -354,7 +354,7 b' class RunnerFactory(object):'
354
354
355 def __init__(self,out=sys.stdout):
355 def __init__(self,out=sys.stdout):
356 """Instantiate a code runner."""
356 """Instantiate a code runner."""
357
357
358 self.out = out
358 self.out = out
359 self.runner = None
359 self.runner = None
360 self.runnerClass = None
360 self.runnerClass = None
@@ -363,7 +363,7 b' class RunnerFactory(object):'
363 self.runnerClass = runnerClass
363 self.runnerClass = runnerClass
364 self.runner = runnerClass(out=self.out)
364 self.runner = runnerClass(out=self.out)
365 return self.runner
365 return self.runner
366
366
367 def __call__(self,fname):
367 def __call__(self,fname):
368 """Return a runner for the given filename."""
368 """Return a runner for the given filename."""
369
369
@@ -1,5 +1,5 b''
1 """Test suite for pylab_import_all magic
1 """Test suite for pylab_import_all magic
2 Modified from the irunner module but using regex.
2 Modified from the irunner module but using regex.
3 """
3 """
4
4
5 # Global to make tests extra verbose and help debugging
5 # Global to make tests extra verbose and help debugging
@@ -24,7 +24,7 b' class RunnerTestCase(unittest.TestCase):'
24
24
25 def _test_runner(self,runner,source,output):
25 def _test_runner(self,runner,source,output):
26 """Test that a given runner's input/output match."""
26 """Test that a given runner's input/output match."""
27
27
28 runner.run_source(source)
28 runner.run_source(source)
29 out = self.out.getvalue()
29 out = self.out.getvalue()
30 #out = ''
30 #out = ''
@@ -47,7 +47,7 b' def parse_filename(fname):'
47 format (json/py) and the notebook name. This logic can be
47 format (json/py) and the notebook name. This logic can be
48 summarized as follows:
48 summarized as follows:
49
49
50 * notebook.ipynb -> (notebook.ipynb, notebook, json)
50 * notebook.ipynb -> (notebook.ipynb, notebook, json)
51 * notebook.json -> (notebook.json, notebook, json)
51 * notebook.json -> (notebook.json, notebook, json)
52 * notebook.py -> (notebook.py, notebook, py)
52 * notebook.py -> (notebook.py, notebook, py)
53 * notebook -> (notebook.ipynb, notebook, json)
53 * notebook -> (notebook.ipynb, notebook, json)
@@ -61,8 +61,8 b' def parse_filename(fname):'
61
61
62 Returns
62 Returns
63 -------
63 -------
64 (fname, name, format) : (unicode, unicode, unicode)
64 (fname, name, format) : (unicode, unicode, unicode)
65 The filename, notebook name and format.
65 The filename, notebook name and format.
66 """
66 """
67 if fname.endswith(u'.ipynb'):
67 if fname.endswith(u'.ipynb'):
68 format = u'json'
68 format = u'json'
@@ -1,6 +1,6 b''
1 """The basic dict based notebook format.
1 """The basic dict based notebook format.
2
2
3 The Python representation of a notebook is a nested structure of
3 The Python representation of a notebook is a nested structure of
4 dictionary subclasses that support attribute access
4 dictionary subclasses that support attribute access
5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
6 helpers to build the structs in the right form.
6 helpers to build the structs in the right form.
@@ -47,7 +47,7 b' def from_dict(d):'
47
47
48
48
49 def new_output(output_type=None, output_text=None, output_png=None,
49 def new_output(output_type=None, output_text=None, output_png=None,
50 output_html=None, output_svg=None, output_latex=None, output_json=None,
50 output_html=None, output_svg=None, output_latex=None, output_json=None,
51 output_javascript=None, output_jpeg=None, prompt_number=None,
51 output_javascript=None, output_jpeg=None, prompt_number=None,
52 etype=None, evalue=None, traceback=None):
52 etype=None, evalue=None, traceback=None):
53 """Create a new code cell with input and output"""
53 """Create a new code cell with input and output"""
@@ -46,7 +46,7 b' class PyReader(NotebookReader):'
46 cells.append(cell)
46 cells.append(cell)
47 state = u'codecell'
47 state = u'codecell'
48 cell_lines = []
48 cell_lines = []
49 elif line.startswith(u'# <htmlcell>'):
49 elif line.startswith(u'# <htmlcell>'):
50 cell = self.new_cell(state, cell_lines)
50 cell = self.new_cell(state, cell_lines)
51 if cell is not None:
51 if cell is not None:
52 cells.append(cell)
52 cells.append(cell)
@@ -38,7 +38,7 b' from IPython.core.profiledir import ProfileDir'
38 from IPython.utils.daemonize import daemonize
38 from IPython.utils.daemonize import daemonize
39 from IPython.utils.importstring import import_item
39 from IPython.utils.importstring import import_item
40 from IPython.utils.sysinfo import num_cpus
40 from IPython.utils.sysinfo import num_cpus
41 from IPython.utils.traitlets import (Int, Unicode, Bool, CFloat, Dict, List,
41 from IPython.utils.traitlets import (Int, Unicode, Bool, CFloat, Dict, List,
42 DottedObjectName)
42 DottedObjectName)
43
43
44 from IPython.parallel.apps.baseapp import (
44 from IPython.parallel.apps.baseapp import (
@@ -114,7 +114,7 b' Start an ipython cluster by its profile name or cluster'
114 directory. Cluster directories contain configuration, log and
114 directory. Cluster directories contain configuration, log and
115 security related files and are named using the convention
115 security related files and are named using the convention
116 'profile_<name>' and should be creating using the 'start'
116 'profile_<name>' and should be creating using the 'start'
117 subcommand of 'ipcluster'. If your cluster directory is in
117 subcommand of 'ipcluster'. If your cluster directory is in
118 the cwd or the ipython directory, you can simply refer to it
118 the cwd or the ipython directory, you can simply refer to it
119 using its profile name, 'ipcluster start --n=4 --profile=<profile>`,
119 using its profile name, 'ipcluster start --n=4 --profile=<profile>`,
120 otherwise use the 'profile-dir' option.
120 otherwise use the 'profile-dir' option.
@@ -123,7 +123,7 b' stop_help = """Stop a running IPython cluster'
123
123
124 Stop a running ipython cluster by its profile name or cluster
124 Stop a running ipython cluster by its profile name or cluster
125 directory. Cluster directories are named using the convention
125 directory. Cluster directories are named using the convention
126 'profile_<name>'. If your cluster directory is in
126 'profile_<name>'. If your cluster directory is in
127 the cwd or the ipython directory, you can simply refer to it
127 the cwd or the ipython directory, you can simply refer to it
128 using its profile name, 'ipcluster stop --profile=<profile>`, otherwise
128 using its profile name, 'ipcluster stop --profile=<profile>`, otherwise
129 use the '--profile-dir' option.
129 use the '--profile-dir' option.
@@ -135,7 +135,7 b' by profile name or cluster directory.'
135 Cluster directories contain configuration, log and
135 Cluster directories contain configuration, log and
136 security related files and are named using the convention
136 security related files and are named using the convention
137 'profile_<name>' and should be creating using the 'start'
137 'profile_<name>' and should be creating using the 'start'
138 subcommand of 'ipcluster'. If your cluster directory is in
138 subcommand of 'ipcluster'. If your cluster directory is in
139 the cwd or the ipython directory, you can simply refer to it
139 the cwd or the ipython directory, you can simply refer to it
140 using its profile name, 'ipcluster engines --n=4 --profile=<profile>`,
140 using its profile name, 'ipcluster engines --n=4 --profile=<profile>`,
141 otherwise use the 'profile-dir' option.
141 otherwise use the 'profile-dir' option.
@@ -150,12 +150,12 b' class IPClusterStop(BaseParallelApplication):'
150 description = stop_help
150 description = stop_help
151 examples = _stop_examples
151 examples = _stop_examples
152 config_file_name = Unicode(default_config_file_name)
152 config_file_name = Unicode(default_config_file_name)
153
153
154 signal = Int(signal.SIGINT, config=True,
154 signal = Int(signal.SIGINT, config=True,
155 help="signal to use for stopping processes.")
155 help="signal to use for stopping processes.")
156
156
157 aliases = Dict(stop_aliases)
157 aliases = Dict(stop_aliases)
158
158
159 def start(self):
159 def start(self):
160 """Start the app for the stop subcommand."""
160 """Start the app for the stop subcommand."""
161 try:
161 try:
@@ -168,7 +168,7 b' class IPClusterStop(BaseParallelApplication):'
168 # can watch for to learn how I existed.
168 # can watch for to learn how I existed.
169 self.remove_pid_file()
169 self.remove_pid_file()
170 self.exit(ALREADY_STOPPED)
170 self.exit(ALREADY_STOPPED)
171
171
172 if not self.check_pid(pid):
172 if not self.check_pid(pid):
173 self.log.critical(
173 self.log.critical(
174 'Cluster [pid=%r] is not running.' % pid
174 'Cluster [pid=%r] is not running.' % pid
@@ -177,7 +177,7 b' class IPClusterStop(BaseParallelApplication):'
177 # Here I exit with a unusual exit status that other processes
177 # Here I exit with a unusual exit status that other processes
178 # can watch for to learn how I existed.
178 # can watch for to learn how I existed.
179 self.exit(ALREADY_STOPPED)
179 self.exit(ALREADY_STOPPED)
180
180
181 elif os.name=='posix':
181 elif os.name=='posix':
182 sig = self.signal
182 sig = self.signal
183 self.log.info(
183 self.log.info(
@@ -197,7 +197,7 b' class IPClusterStop(BaseParallelApplication):'
197 self.log.error("Stopping cluster failed, assuming already dead.",
197 self.log.error("Stopping cluster failed, assuming already dead.",
198 exc_info=True)
198 exc_info=True)
199 self.remove_pid_file()
199 self.remove_pid_file()
200
200
201 engine_aliases = {}
201 engine_aliases = {}
202 engine_aliases.update(base_aliases)
202 engine_aliases.update(base_aliases)
203 engine_aliases.update(dict(
203 engine_aliases.update(dict(
@@ -228,7 +228,7 b' class IPClusterEngines(BaseParallelApplication):'
228 launchers = launcher.all_launchers
228 launchers = launcher.all_launchers
229 eslaunchers = [ l for l in launchers if 'EngineSet' in l.__name__]
229 eslaunchers = [ l for l in launchers if 'EngineSet' in l.__name__]
230 return [ProfileDir]+eslaunchers
230 return [ProfileDir]+eslaunchers
231
231
232 n = Int(num_cpus(), config=True,
232 n = Int(num_cpus(), config=True,
233 help="""The number of engines to start. The default is to use one for each
233 help="""The number of engines to start. The default is to use one for each
234 CPU on your machine""")
234 CPU on your machine""")
@@ -239,12 +239,12 b' class IPClusterEngines(BaseParallelApplication):'
239 to use various batch systems to launch your engines, such as PBS,SGE,MPIExec,etc.
239 to use various batch systems to launch your engines, such as PBS,SGE,MPIExec,etc.
240 Each launcher class has its own set of configuration options, for making sure
240 Each launcher class has its own set of configuration options, for making sure
241 it will work in your environment.
241 it will work in your environment.
242
242
243 You can also write your own launcher, and specify it's absolute import path,
243 You can also write your own launcher, and specify it's absolute import path,
244 as in 'mymodule.launcher.FTLEnginesLauncher`.
244 as in 'mymodule.launcher.FTLEnginesLauncher`.
245
245
246 Examples include:
246 Examples include:
247
247
248 LocalEngineSetLauncher : start engines locally as subprocesses [default]
248 LocalEngineSetLauncher : start engines locally as subprocesses [default]
249 MPIExecEngineSetLauncher : use mpiexec to launch in an MPI environment
249 MPIExecEngineSetLauncher : use mpiexec to launch in an MPI environment
250 PBSEngineSetLauncher : use PBS (qsub) to submit engines to a batch queue
250 PBSEngineSetLauncher : use PBS (qsub) to submit engines to a batch queue
@@ -273,15 +273,15 b' class IPClusterEngines(BaseParallelApplication):'
273 super(IPClusterEngines, self).initialize(argv)
273 super(IPClusterEngines, self).initialize(argv)
274 self.init_signal()
274 self.init_signal()
275 self.init_launchers()
275 self.init_launchers()
276
276
277 def init_launchers(self):
277 def init_launchers(self):
278 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
278 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
279 self.engine_launcher.on_stop(lambda r: self.loop.stop())
279 self.engine_launcher.on_stop(lambda r: self.loop.stop())
280
280
281 def init_signal(self):
281 def init_signal(self):
282 # Setup signals
282 # Setup signals
283 signal.signal(signal.SIGINT, self.sigint_handler)
283 signal.signal(signal.SIGINT, self.sigint_handler)
284
284
285 def build_launcher(self, clsname, kind=None):
285 def build_launcher(self, clsname, kind=None):
286 """import and instantiate a Launcher based on importstring"""
286 """import and instantiate a Launcher based on importstring"""
287 if '.' not in clsname:
287 if '.' not in clsname:
@@ -302,7 +302,7 b' class IPClusterEngines(BaseParallelApplication):'
302 profile_dir=self.profile_dir.location, cluster_id=self.cluster_id,
302 profile_dir=self.profile_dir.location, cluster_id=self.cluster_id,
303 )
303 )
304 return launcher
304 return launcher
305
305
306 def start_engines(self):
306 def start_engines(self):
307 self.log.info("Starting %i engines"%self.n)
307 self.log.info("Starting %i engines"%self.n)
308 self.engine_launcher.start(self.n)
308 self.engine_launcher.start(self.n)
@@ -327,7 +327,7 b' class IPClusterEngines(BaseParallelApplication):'
327 def sigint_handler(self, signum, frame):
327 def sigint_handler(self, signum, frame):
328 self.log.debug("SIGINT received, stopping launchers...")
328 self.log.debug("SIGINT received, stopping launchers...")
329 self.stop_launchers()
329 self.stop_launchers()
330
330
331 def start_logging(self):
331 def start_logging(self):
332 # Remove old log files of the controller and engine
332 # Remove old log files of the controller and engine
333 if self.clean_logs:
333 if self.clean_logs:
@@ -342,7 +342,7 b' class IPClusterEngines(BaseParallelApplication):'
342 """Start the app for the engines subcommand."""
342 """Start the app for the engines subcommand."""
343 self.log.info("IPython cluster: started")
343 self.log.info("IPython cluster: started")
344 # First see if the cluster is already running
344 # First see if the cluster is already running
345
345
346 # Now log and daemonize
346 # Now log and daemonize
347 self.log.info(
347 self.log.info(
348 'Starting engines with [daemon=%r]' % self.daemonize
348 'Starting engines with [daemon=%r]' % self.daemonize
@@ -392,8 +392,8 b' class IPClusterStart(IPClusterEngines):'
392 def _classes_default(self,):
392 def _classes_default(self,):
393 from IPython.parallel.apps import launcher
393 from IPython.parallel.apps import launcher
394 return [ProfileDir] + [IPClusterEngines] + launcher.all_launchers
394 return [ProfileDir] + [IPClusterEngines] + launcher.all_launchers
395
395
396 clean_logs = Bool(True, config=True,
396 clean_logs = Bool(True, config=True,
397 help="whether to cleanup old logs before starting")
397 help="whether to cleanup old logs before starting")
398
398
399 delay = CFloat(1., config=True,
399 delay = CFloat(1., config=True,
@@ -403,12 +403,12 b' class IPClusterStart(IPClusterEngines):'
403 config=True,
403 config=True,
404 helep="""The class for launching a Controller. Change this value if you want
404 helep="""The class for launching a Controller. Change this value if you want
405 your controller to also be launched by a batch system, such as PBS,SGE,MPIExec,etc.
405 your controller to also be launched by a batch system, such as PBS,SGE,MPIExec,etc.
406
406
407 Each launcher class has its own set of configuration options, for making sure
407 Each launcher class has its own set of configuration options, for making sure
408 it will work in your environment.
408 it will work in your environment.
409
409
410 Examples include:
410 Examples include:
411
411
412 LocalControllerLauncher : start engines locally as subprocesses
412 LocalControllerLauncher : start engines locally as subprocesses
413 MPIExecControllerLauncher : use mpiexec to launch engines in an MPI universe
413 MPIExecControllerLauncher : use mpiexec to launch engines in an MPI universe
414 PBSControllerLauncher : use PBS (qsub) to submit engines to a batch queue
414 PBSControllerLauncher : use PBS (qsub) to submit engines to a batch queue
@@ -420,7 +420,7 b' class IPClusterStart(IPClusterEngines):'
420 reset = Bool(False, config=True,
420 reset = Bool(False, config=True,
421 help="Whether to reset config files as part of '--create'."
421 help="Whether to reset config files as part of '--create'."
422 )
422 )
423
423
424 # flags = Dict(flags)
424 # flags = Dict(flags)
425 aliases = Dict(start_aliases)
425 aliases = Dict(start_aliases)
426
426
@@ -428,10 +428,10 b' class IPClusterStart(IPClusterEngines):'
428 self.controller_launcher = self.build_launcher(self.controller_launcher_class, 'Controller')
428 self.controller_launcher = self.build_launcher(self.controller_launcher_class, 'Controller')
429 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
429 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
430 self.controller_launcher.on_stop(self.stop_launchers)
430 self.controller_launcher.on_stop(self.stop_launchers)
431
431
432 def start_controller(self):
432 def start_controller(self):
433 self.controller_launcher.start()
433 self.controller_launcher.start()
434
434
435 def stop_controller(self):
435 def stop_controller(self):
436 # self.log.info("In stop_controller")
436 # self.log.info("In stop_controller")
437 if self.controller_launcher and self.controller_launcher.running:
437 if self.controller_launcher and self.controller_launcher.running:
@@ -460,7 +460,7 b' class IPClusterStart(IPClusterEngines):'
460 self.exit(ALREADY_STARTED)
460 self.exit(ALREADY_STARTED)
461 else:
461 else:
462 self.remove_pid_file()
462 self.remove_pid_file()
463
463
464
464
465 # Now log and daemonize
465 # Now log and daemonize
466 self.log.info(
466 self.log.info(
@@ -501,11 +501,11 b' class IPClusterApp(Application):'
501 'stop' : (base+'Stop', stop_help),
501 'stop' : (base+'Stop', stop_help),
502 'engines' : (base+'Engines', engines_help),
502 'engines' : (base+'Engines', engines_help),
503 }
503 }
504
504
505 # no aliases or flags for parent App
505 # no aliases or flags for parent App
506 aliases = Dict()
506 aliases = Dict()
507 flags = Dict()
507 flags = Dict()
508
508
509 def start(self):
509 def start(self):
510 if self.subapp is None:
510 if self.subapp is None:
511 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
511 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
@@ -115,10 +115,10 b' class BaseLauncher(LoggingConfigurable):'
115 # the work_dir option.
115 # the work_dir option.
116 work_dir = Unicode(u'.')
116 work_dir = Unicode(u'.')
117 loop = Instance('zmq.eventloop.ioloop.IOLoop')
117 loop = Instance('zmq.eventloop.ioloop.IOLoop')
118
118
119 start_data = Any()
119 start_data = Any()
120 stop_data = Any()
120 stop_data = Any()
121
121
122 def _loop_default(self):
122 def _loop_default(self):
123 return ioloop.IOLoop.instance()
123 return ioloop.IOLoop.instance()
124
124
@@ -255,7 +255,7 b' class LocalProcessLauncher(BaseLauncher):'
255 ``self.work_dir``.
255 ``self.work_dir``.
256 """
256 """
257
257
258 # This is used to to construct self.args, which is passed to
258 # This is used to to construct self.args, which is passed to
259 # spawnProcess.
259 # spawnProcess.
260 cmd_and_args = List([])
260 cmd_and_args = List([])
261 poll_frequency = Int(100) # in ms
261 poll_frequency = Int(100) # in ms
@@ -314,7 +314,7 b' class LocalProcessLauncher(BaseLauncher):'
314 self.killer.start()
314 self.killer.start()
315
315
316 # callbacks, etc:
316 # callbacks, etc:
317
317
318 def handle_stdout(self, fd, events):
318 def handle_stdout(self, fd, events):
319 if WINDOWS:
319 if WINDOWS:
320 line = self.stdout.recv()
320 line = self.stdout.recv()
@@ -325,7 +325,7 b' class LocalProcessLauncher(BaseLauncher):'
325 self.log.info(line[:-1])
325 self.log.info(line[:-1])
326 else:
326 else:
327 self.poll()
327 self.poll()
328
328
329 def handle_stderr(self, fd, events):
329 def handle_stderr(self, fd, events):
330 if WINDOWS:
330 if WINDOWS:
331 line = self.stderr.recv()
331 line = self.stderr.recv()
@@ -336,7 +336,7 b' class LocalProcessLauncher(BaseLauncher):'
336 self.log.error(line[:-1])
336 self.log.error(line[:-1])
337 else:
337 else:
338 self.poll()
338 self.poll()
339
339
340 def poll(self):
340 def poll(self):
341 status = self.process.poll()
341 status = self.process.poll()
342 if status is not None:
342 if status is not None:
@@ -373,13 +373,13 b' class LocalEngineSetLauncher(LocalEngineLauncher):'
373 This can help force the engines to get their ids in order, or limit
373 This can help force the engines to get their ids in order, or limit
374 process flood when starting many engines."""
374 process flood when starting many engines."""
375 )
375 )
376
376
377 # launcher class
377 # launcher class
378 launcher_class = LocalEngineLauncher
378 launcher_class = LocalEngineLauncher
379
379
380 launchers = Dict()
380 launchers = Dict()
381 stop_data = Dict()
381 stop_data = Dict()
382
382
383 def __init__(self, work_dir=u'.', config=None, **kwargs):
383 def __init__(self, work_dir=u'.', config=None, **kwargs):
384 super(LocalEngineSetLauncher, self).__init__(
384 super(LocalEngineSetLauncher, self).__init__(
385 work_dir=work_dir, config=config, **kwargs
385 work_dir=work_dir, config=config, **kwargs
@@ -395,7 +395,7 b' class LocalEngineSetLauncher(LocalEngineLauncher):'
395 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
395 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
396 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
396 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
397 )
397 )
398
398
399 # Copy the engine args over to each engine launcher.
399 # Copy the engine args over to each engine launcher.
400 el.engine_cmd = copy.deepcopy(self.engine_cmd)
400 el.engine_cmd = copy.deepcopy(self.engine_cmd)
401 el.engine_args = copy.deepcopy(self.engine_args)
401 el.engine_args = copy.deepcopy(self.engine_args)
@@ -427,7 +427,7 b' class LocalEngineSetLauncher(LocalEngineLauncher):'
427
427
428 def stop(self):
428 def stop(self):
429 return self.interrupt_then_kill()
429 return self.interrupt_then_kill()
430
430
431 def _notice_engine_stopped(self, data):
431 def _notice_engine_stopped(self, data):
432 pid = data['pid']
432 pid = data['pid']
433 for idx,el in self.launchers.iteritems():
433 for idx,el in self.launchers.iteritems():
@@ -480,7 +480,7 b' class MPIExecControllerLauncher(MPIExecLauncher, ControllerMixin):'
480 @property
480 @property
481 def program(self):
481 def program(self):
482 return self.controller_cmd
482 return self.controller_cmd
483
483
484 @property
484 @property
485 def program_args(self):
485 def program_args(self):
486 return self.cluster_args + self.controller_args
486 return self.cluster_args + self.controller_args
@@ -500,7 +500,7 b' class MPIExecEngineSetLauncher(MPIExecLauncher, EngineMixin):'
500 @property
500 @property
501 def program(self):
501 def program(self):
502 return self.engine_cmd
502 return self.engine_cmd
503
503
504 @property
504 @property
505 def program_args(self):
505 def program_args(self):
506 return self.cluster_args + self.engine_args
506 return self.cluster_args + self.engine_args
@@ -558,15 +558,15 b' class SSHLauncher(LocalProcessLauncher):'
558 self.hostname = hostname
558 self.hostname = hostname
559 if user is not None:
559 if user is not None:
560 self.user = user
560 self.user = user
561
561
562 return super(SSHLauncher, self).start()
562 return super(SSHLauncher, self).start()
563
563
564 def signal(self, sig):
564 def signal(self, sig):
565 if self.state == 'running':
565 if self.state == 'running':
566 # send escaped ssh connection-closer
566 # send escaped ssh connection-closer
567 self.process.stdin.write('~.')
567 self.process.stdin.write('~.')
568 self.process.stdin.flush()
568 self.process.stdin.flush()
569
569
570
570
571
571
572 class SSHControllerLauncher(SSHLauncher, ControllerMixin):
572 class SSHControllerLauncher(SSHLauncher, ControllerMixin):
@@ -577,7 +577,7 b' class SSHControllerLauncher(SSHLauncher, ControllerMixin):'
577 @property
577 @property
578 def program(self):
578 def program(self):
579 return self.controller_cmd
579 return self.controller_cmd
580
580
581 @property
581 @property
582 def program_args(self):
582 def program_args(self):
583 return self.cluster_args + self.controller_args
583 return self.cluster_args + self.controller_args
@@ -591,30 +591,30 b' class SSHEngineLauncher(SSHLauncher, EngineMixin):'
591 @property
591 @property
592 def program(self):
592 def program(self):
593 return self.engine_cmd
593 return self.engine_cmd
594
594
595 @property
595 @property
596 def program_args(self):
596 def program_args(self):
597 return self.cluster_args + self.engine_args
597 return self.cluster_args + self.engine_args
598
598
599
599
600 class SSHEngineSetLauncher(LocalEngineSetLauncher):
600 class SSHEngineSetLauncher(LocalEngineSetLauncher):
601 launcher_class = SSHEngineLauncher
601 launcher_class = SSHEngineLauncher
602 engines = Dict(config=True,
602 engines = Dict(config=True,
603 help="""dict of engines to launch. This is a dict by hostname of ints,
603 help="""dict of engines to launch. This is a dict by hostname of ints,
604 corresponding to the number of engines to start on that host.""")
604 corresponding to the number of engines to start on that host.""")
605
605
606 def start(self, n):
606 def start(self, n):
607 """Start engines by profile or profile_dir.
607 """Start engines by profile or profile_dir.
608 `n` is ignored, and the `engines` config property is used instead.
608 `n` is ignored, and the `engines` config property is used instead.
609 """
609 """
610
610
611 dlist = []
611 dlist = []
612 for host, n in self.engines.iteritems():
612 for host, n in self.engines.iteritems():
613 if isinstance(n, (tuple, list)):
613 if isinstance(n, (tuple, list)):
614 n, args = n
614 n, args = n
615 else:
615 else:
616 args = copy.deepcopy(self.engine_args)
616 args = copy.deepcopy(self.engine_args)
617
617
618 if '@' in host:
618 if '@' in host:
619 user,host = host.split('@',1)
619 user,host = host.split('@',1)
620 else:
620 else:
@@ -625,7 +625,7 b' class SSHEngineSetLauncher(LocalEngineSetLauncher):'
625 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
625 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
626 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
626 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
627 )
627 )
628
628
629 # Copy the engine args over to each engine launcher.
629 # Copy the engine args over to each engine launcher.
630 el.engine_cmd = self.engine_cmd
630 el.engine_cmd = self.engine_cmd
631 el.engine_args = args
631 el.engine_args = args
@@ -637,7 +637,7 b' class SSHEngineSetLauncher(LocalEngineSetLauncher):'
637 dlist.append(d)
637 dlist.append(d)
638 self.notify_start(dlist)
638 self.notify_start(dlist)
639 return dlist
639 return dlist
640
640
641
641
642
642
643 #-----------------------------------------------------------------------------
643 #-----------------------------------------------------------------------------
@@ -687,7 +687,7 b' class WindowsHPCLauncher(BaseLauncher):'
687
687
688 def find_args(self):
688 def find_args(self):
689 return [u'job.exe']
689 return [u'job.exe']
690
690
691 def parse_job_id(self, output):
691 def parse_job_id(self, output):
692 """Take the output of the submit command and return the job id."""
692 """Take the output of the submit command and return the job id."""
693 m = re.search(self.job_id_regexp, output)
693 m = re.search(self.job_id_regexp, output)
@@ -748,7 +748,7 b' class WindowsHPCControllerLauncher(WindowsHPCLauncher, ClusterAppMixin):'
748 job = IPControllerJob(config=self.config)
748 job = IPControllerJob(config=self.config)
749
749
750 t = IPControllerTask(config=self.config)
750 t = IPControllerTask(config=self.config)
751 # The tasks work directory is *not* the actual work directory of
751 # The tasks work directory is *not* the actual work directory of
752 # the controller. It is used as the base path for the stdout/stderr
752 # the controller. It is used as the base path for the stdout/stderr
753 # files that the scheduler redirects to.
753 # files that the scheduler redirects to.
754 t.work_directory = self.profile_dir
754 t.work_directory = self.profile_dir
@@ -781,7 +781,7 b' class WindowsHPCEngineSetLauncher(WindowsHPCLauncher, ClusterAppMixin):'
781
781
782 for i in range(n):
782 for i in range(n):
783 t = IPEngineTask(config=self.config)
783 t = IPEngineTask(config=self.config)
784 # The tasks work directory is *not* the actual work directory of
784 # The tasks work directory is *not* the actual work directory of
785 # the engine. It is used as the base path for the stdout/stderr
785 # the engine. It is used as the base path for the stdout/stderr
786 # files that the scheduler redirects to.
786 # files that the scheduler redirects to.
787 t.work_directory = self.profile_dir
787 t.work_directory = self.profile_dir
@@ -850,13 +850,13 b' class BatchSystemLauncher(BaseLauncher):'
850 help="The filename of the instantiated batch script.")
850 help="The filename of the instantiated batch script.")
851 queue = Unicode(u'', config=True,
851 queue = Unicode(u'', config=True,
852 help="The PBS Queue.")
852 help="The PBS Queue.")
853
853
854 def _queue_changed(self, name, old, new):
854 def _queue_changed(self, name, old, new):
855 self.context[name] = new
855 self.context[name] = new
856
856
857 n = Int(1)
857 n = Int(1)
858 _n_changed = _queue_changed
858 _n_changed = _queue_changed
859
859
860 # not configurable, override in subclasses
860 # not configurable, override in subclasses
861 # PBS Job Array regex
861 # PBS Job Array regex
862 job_array_regexp = Unicode('')
862 job_array_regexp = Unicode('')
@@ -873,10 +873,10 b' class BatchSystemLauncher(BaseLauncher):'
873 # the Formatter instance for rendering the templates:
873 # the Formatter instance for rendering the templates:
874 formatter = Instance(EvalFormatter, (), {})
874 formatter = Instance(EvalFormatter, (), {})
875
875
876
876
877 def find_args(self):
877 def find_args(self):
878 return self.submit_command + [self.batch_file]
878 return self.submit_command + [self.batch_file]
879
879
880 def __init__(self, work_dir=u'.', config=None, **kwargs):
880 def __init__(self, work_dir=u'.', config=None, **kwargs):
881 super(BatchSystemLauncher, self).__init__(
881 super(BatchSystemLauncher, self).__init__(
882 work_dir=work_dir, config=config, **kwargs
882 work_dir=work_dir, config=config, **kwargs
@@ -905,7 +905,7 b' class BatchSystemLauncher(BaseLauncher):'
905 if not self.batch_template:
905 if not self.batch_template:
906 # third (last) priority is default_template
906 # third (last) priority is default_template
907 self.batch_template = self.default_template
907 self.batch_template = self.default_template
908
908
909 # add jobarray or queue lines to user-specified template
909 # add jobarray or queue lines to user-specified template
910 # note that this is *only* when user did not specify a template.
910 # note that this is *only* when user did not specify a template.
911 regex = re.compile(self.job_array_regexp)
911 regex = re.compile(self.job_array_regexp)
@@ -914,17 +914,17 b' class BatchSystemLauncher(BaseLauncher):'
914 self.log.info("adding job array settings to batch script")
914 self.log.info("adding job array settings to batch script")
915 firstline, rest = self.batch_template.split('\n',1)
915 firstline, rest = self.batch_template.split('\n',1)
916 self.batch_template = u'\n'.join([firstline, self.job_array_template, rest])
916 self.batch_template = u'\n'.join([firstline, self.job_array_template, rest])
917
917
918 regex = re.compile(self.queue_regexp)
918 regex = re.compile(self.queue_regexp)
919 # print regex.search(self.batch_template)
919 # print regex.search(self.batch_template)
920 if self.queue and not regex.search(self.batch_template):
920 if self.queue and not regex.search(self.batch_template):
921 self.log.info("adding PBS queue settings to batch script")
921 self.log.info("adding PBS queue settings to batch script")
922 firstline, rest = self.batch_template.split('\n',1)
922 firstline, rest = self.batch_template.split('\n',1)
923 self.batch_template = u'\n'.join([firstline, self.queue_template, rest])
923 self.batch_template = u'\n'.join([firstline, self.queue_template, rest])
924
924
925 script_as_string = self.formatter.format(self.batch_template, **self.context)
925 script_as_string = self.formatter.format(self.batch_template, **self.context)
926 self.log.info('Writing instantiated batch script: %s' % self.batch_file)
926 self.log.info('Writing instantiated batch script: %s' % self.batch_file)
927
927
928 with open(self.batch_file, 'w') as f:
928 with open(self.batch_file, 'w') as f:
929 f.write(script_as_string)
929 f.write(script_as_string)
930 os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
930 os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
@@ -935,7 +935,7 b' class BatchSystemLauncher(BaseLauncher):'
935 # can be used in the batch script template as {profile_dir}
935 # can be used in the batch script template as {profile_dir}
936 self.write_batch_script(n)
936 self.write_batch_script(n)
937 output = check_output(self.args, env=os.environ)
937 output = check_output(self.args, env=os.environ)
938
938
939 job_id = self.parse_job_id(output)
939 job_id = self.parse_job_id(output)
940 self.notify_start(job_id)
940 self.notify_start(job_id)
941 return job_id
941 return job_id
@@ -955,7 +955,7 b' class PBSLauncher(BatchSystemLauncher):'
955 help="The PBS delete command ['qsub']")
955 help="The PBS delete command ['qsub']")
956 job_id_regexp = Unicode(r'\d+', config=True,
956 job_id_regexp = Unicode(r'\d+', config=True,
957 help="Regular expresion for identifying the job ID [r'\d+']")
957 help="Regular expresion for identifying the job ID [r'\d+']")
958
958
959 batch_file = Unicode(u'')
959 batch_file = Unicode(u'')
960 job_array_regexp = Unicode('#PBS\W+-t\W+[\w\d\-\$]+')
960 job_array_regexp = Unicode('#PBS\W+-t\W+[\w\d\-\$]+')
961 job_array_template = Unicode('#PBS -t 1-{n}')
961 job_array_template = Unicode('#PBS -t 1-{n}')
@@ -974,7 +974,7 b' class PBSControllerLauncher(PBSLauncher, BatchClusterAppMixin):'
974 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
974 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
975 """%(' '.join(ipcontroller_cmd_argv)))
975 """%(' '.join(ipcontroller_cmd_argv)))
976
976
977
977
978 def start(self):
978 def start(self):
979 """Start the controller by profile or profile_dir."""
979 """Start the controller by profile or profile_dir."""
980 self.log.info("Starting PBSControllerLauncher: %r" % self.args)
980 self.log.info("Starting PBSControllerLauncher: %r" % self.args)
@@ -1041,20 +1041,20 b' class SGEEngineSetLauncher(SGELauncher, BatchClusterAppMixin):'
1041
1041
1042 class LSFLauncher(BatchSystemLauncher):
1042 class LSFLauncher(BatchSystemLauncher):
1043 """A BatchSystemLauncher subclass for LSF."""
1043 """A BatchSystemLauncher subclass for LSF."""
1044
1044
1045 submit_command = List(['bsub'], config=True,
1045 submit_command = List(['bsub'], config=True,
1046 help="The PBS submit command ['bsub']")
1046 help="The PBS submit command ['bsub']")
1047 delete_command = List(['bkill'], config=True,
1047 delete_command = List(['bkill'], config=True,
1048 help="The PBS delete command ['bkill']")
1048 help="The PBS delete command ['bkill']")
1049 job_id_regexp = Unicode(r'\d+', config=True,
1049 job_id_regexp = Unicode(r'\d+', config=True,
1050 help="Regular expresion for identifying the job ID [r'\d+']")
1050 help="Regular expresion for identifying the job ID [r'\d+']")
1051
1051
1052 batch_file = Unicode(u'')
1052 batch_file = Unicode(u'')
1053 job_array_regexp = Unicode('#BSUB[ \t]-J+\w+\[\d+-\d+\]')
1053 job_array_regexp = Unicode('#BSUB[ \t]-J+\w+\[\d+-\d+\]')
1054 job_array_template = Unicode('#BSUB -J ipengine[1-{n}]')
1054 job_array_template = Unicode('#BSUB -J ipengine[1-{n}]')
1055 queue_regexp = Unicode('#BSUB[ \t]+-q[ \t]+\w+')
1055 queue_regexp = Unicode('#BSUB[ \t]+-q[ \t]+\w+')
1056 queue_template = Unicode('#BSUB -q {queue}')
1056 queue_template = Unicode('#BSUB -q {queue}')
1057
1057
1058 def start(self, n):
1058 def start(self, n):
1059 """Start n copies of the process using LSF batch system.
1059 """Start n copies of the process using LSF batch system.
1060 This cant inherit from the base class because bsub expects
1060 This cant inherit from the base class because bsub expects
@@ -1075,16 +1075,16 b' class LSFLauncher(BatchSystemLauncher):'
1075
1075
1076 class LSFControllerLauncher(LSFLauncher, BatchClusterAppMixin):
1076 class LSFControllerLauncher(LSFLauncher, BatchClusterAppMixin):
1077 """Launch a controller using LSF."""
1077 """Launch a controller using LSF."""
1078
1078
1079 batch_file_name = Unicode(u'lsf_controller', config=True,
1079 batch_file_name = Unicode(u'lsf_controller', config=True,
1080 help="batch file name for the controller job.")
1080 help="batch file name for the controller job.")
1081 default_template= Unicode("""#!/bin/sh
1081 default_template= Unicode("""#!/bin/sh
1082 #BSUB -J ipcontroller
1082 #BSUB -J ipcontroller
1083 #BSUB -oo ipcontroller.o.%%J
1083 #BSUB -oo ipcontroller.o.%%J
1084 #BSUB -eo ipcontroller.e.%%J
1084 #BSUB -eo ipcontroller.e.%%J
1085 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1085 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1086 """%(' '.join(ipcontroller_cmd_argv)))
1086 """%(' '.join(ipcontroller_cmd_argv)))
1087
1087
1088 def start(self):
1088 def start(self):
1089 """Start the controller by profile or profile_dir."""
1089 """Start the controller by profile or profile_dir."""
1090 self.log.info("Starting LSFControllerLauncher: %r" % self.args)
1090 self.log.info("Starting LSFControllerLauncher: %r" % self.args)
@@ -1096,11 +1096,11 b' class LSFEngineSetLauncher(LSFLauncher, BatchClusterAppMixin):'
1096 batch_file_name = Unicode(u'lsf_engines', config=True,
1096 batch_file_name = Unicode(u'lsf_engines', config=True,
1097 help="batch file name for the engine(s) job.")
1097 help="batch file name for the engine(s) job.")
1098 default_template= Unicode(u"""#!/bin/sh
1098 default_template= Unicode(u"""#!/bin/sh
1099 #BSUB -oo ipengine.o.%%J
1099 #BSUB -oo ipengine.o.%%J
1100 #BSUB -eo ipengine.e.%%J
1100 #BSUB -eo ipengine.e.%%J
1101 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1101 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1102 """%(' '.join(ipengine_cmd_argv)))
1102 """%(' '.join(ipengine_cmd_argv)))
1103
1103
1104 def start(self, n):
1104 def start(self, n):
1105 """Start n engines by profile or profile_dir."""
1105 """Start n engines by profile or profile_dir."""
1106 self.log.info('Starting %i engines with LSFEngineSetLauncher: %r' % (n, self.args))
1106 self.log.info('Starting %i engines with LSFEngineSetLauncher: %r' % (n, self.args))
@@ -1,6 +1,6 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Job and task components for writing .xml files that the Windows HPC Server
3 Job and task components for writing .xml files that the Windows HPC Server
4 2008 can use to start jobs.
4 2008 can use to start jobs.
5
5
6 Authors:
6 Authors:
@@ -297,7 +297,7 b' class IPEngineTask(WinHPCTask):'
297 the_uuid = uuid.uuid1()
297 the_uuid = uuid.uuid1()
298 self.std_out_file_path = os.path.join('log','ipengine-%s.out' % the_uuid)
298 self.std_out_file_path = os.path.join('log','ipengine-%s.out' % the_uuid)
299 self.std_err_file_path = os.path.join('log','ipengine-%s.err' % the_uuid)
299 self.std_err_file_path = os.path.join('log','ipengine-%s.err' % the_uuid)
300
300
301 @property
301 @property
302 def command_line(self):
302 def command_line(self):
303 return ' '.join(self.engine_cmd + self.engine_args)
303 return ' '.join(self.engine_cmd + self.engine_args)
@@ -307,7 +307,7 b' class IPEngineTask(WinHPCTask):'
307 # j.job_name = 'IPCluster'
307 # j.job_name = 'IPCluster'
308 # j.username = 'GNET\\bgranger'
308 # j.username = 'GNET\\bgranger'
309 # j.requested_nodes = 'GREEN'
309 # j.requested_nodes = 'GREEN'
310 #
310 #
311 # t = WinHPCTask(None)
311 # t = WinHPCTask(None)
312 # t.task_name = 'Controller'
312 # t.task_name = 'Controller'
313 # t.command_line = r"\\blue\domainusers$\bgranger\Python\Python25\Scripts\ipcontroller.exe --log-to-file -p default --log-level 10"
313 # t.command_line = r"\\blue\domainusers$\bgranger\Python\Python25\Scripts\ipcontroller.exe --log-to-file -p default --log-level 10"
@@ -39,15 +39,15 b' def check_ready(f, self, *args, **kwargs):'
39
39
40 class AsyncResult(object):
40 class AsyncResult(object):
41 """Class for representing results of non-blocking calls.
41 """Class for representing results of non-blocking calls.
42
42
43 Provides the same interface as :py:class:`multiprocessing.pool.AsyncResult`.
43 Provides the same interface as :py:class:`multiprocessing.pool.AsyncResult`.
44 """
44 """
45
45
46 msg_ids = None
46 msg_ids = None
47 _targets = None
47 _targets = None
48 _tracker = None
48 _tracker = None
49 _single_result = False
49 _single_result = False
50
50
51 def __init__(self, client, msg_ids, fname='unknown', targets=None, tracker=None):
51 def __init__(self, client, msg_ids, fname='unknown', targets=None, tracker=None):
52 if isinstance(msg_ids, basestring):
52 if isinstance(msg_ids, basestring):
53 # always a list
53 # always a list
@@ -66,17 +66,17 b' class AsyncResult(object):'
66 self._single_result = not isinstance(targets, (list, tuple))
66 self._single_result = not isinstance(targets, (list, tuple))
67 else:
67 else:
68 self._single_result = False
68 self._single_result = False
69
69
70 def __repr__(self):
70 def __repr__(self):
71 if self._ready:
71 if self._ready:
72 return "<%s: finished>"%(self.__class__.__name__)
72 return "<%s: finished>"%(self.__class__.__name__)
73 else:
73 else:
74 return "<%s: %s>"%(self.__class__.__name__,self._fname)
74 return "<%s: %s>"%(self.__class__.__name__,self._fname)
75
75
76
76
77 def _reconstruct_result(self, res):
77 def _reconstruct_result(self, res):
78 """Reconstruct our result from actual result list (always a list)
78 """Reconstruct our result from actual result list (always a list)
79
79
80 Override me in subclasses for turning a list of results
80 Override me in subclasses for turning a list of results
81 into the expected form.
81 into the expected form.
82 """
82 """
@@ -84,10 +84,10 b' class AsyncResult(object):'
84 return res[0]
84 return res[0]
85 else:
85 else:
86 return res
86 return res
87
87
88 def get(self, timeout=-1):
88 def get(self, timeout=-1):
89 """Return the result when it arrives.
89 """Return the result when it arrives.
90
90
91 If `timeout` is not ``None`` and the result does not arrive within
91 If `timeout` is not ``None`` and the result does not arrive within
92 `timeout` seconds then ``TimeoutError`` is raised. If the
92 `timeout` seconds then ``TimeoutError`` is raised. If the
93 remote call raised an exception then that exception will be reraised
93 remote call raised an exception then that exception will be reraised
@@ -95,7 +95,7 b' class AsyncResult(object):'
95 """
95 """
96 if not self.ready():
96 if not self.ready():
97 self.wait(timeout)
97 self.wait(timeout)
98
98
99 if self._ready:
99 if self._ready:
100 if self._success:
100 if self._success:
101 return self._result
101 return self._result
@@ -103,16 +103,16 b' class AsyncResult(object):'
103 raise self._exception
103 raise self._exception
104 else:
104 else:
105 raise error.TimeoutError("Result not ready.")
105 raise error.TimeoutError("Result not ready.")
106
106
107 def ready(self):
107 def ready(self):
108 """Return whether the call has completed."""
108 """Return whether the call has completed."""
109 if not self._ready:
109 if not self._ready:
110 self.wait(0)
110 self.wait(0)
111 return self._ready
111 return self._ready
112
112
113 def wait(self, timeout=-1):
113 def wait(self, timeout=-1):
114 """Wait until the result is available or until `timeout` seconds pass.
114 """Wait until the result is available or until `timeout` seconds pass.
115
115
116 This method always returns None.
116 This method always returns None.
117 """
117 """
118 if self._ready:
118 if self._ready:
@@ -136,26 +136,26 b' class AsyncResult(object):'
136 self._success = True
136 self._success = True
137 finally:
137 finally:
138 self._metadata = map(self._client.metadata.get, self.msg_ids)
138 self._metadata = map(self._client.metadata.get, self.msg_ids)
139
139
140
140
141 def successful(self):
141 def successful(self):
142 """Return whether the call completed without raising an exception.
142 """Return whether the call completed without raising an exception.
143
143
144 Will raise ``AssertionError`` if the result is not ready.
144 Will raise ``AssertionError`` if the result is not ready.
145 """
145 """
146 assert self.ready()
146 assert self.ready()
147 return self._success
147 return self._success
148
148
149 #----------------------------------------------------------------
149 #----------------------------------------------------------------
150 # Extra methods not in mp.pool.AsyncResult
150 # Extra methods not in mp.pool.AsyncResult
151 #----------------------------------------------------------------
151 #----------------------------------------------------------------
152
152
153 def get_dict(self, timeout=-1):
153 def get_dict(self, timeout=-1):
154 """Get the results as a dict, keyed by engine_id.
154 """Get the results as a dict, keyed by engine_id.
155
155
156 timeout behavior is described in `get()`.
156 timeout behavior is described in `get()`.
157 """
157 """
158
158
159 results = self.get(timeout)
159 results = self.get(timeout)
160 engine_ids = [ md['engine_id'] for md in self._metadata ]
160 engine_ids = [ md['engine_id'] for md in self._metadata ]
161 bycount = sorted(engine_ids, key=lambda k: engine_ids.count(k))
161 bycount = sorted(engine_ids, key=lambda k: engine_ids.count(k))
@@ -163,17 +163,17 b' class AsyncResult(object):'
163 if maxcount > 1:
163 if maxcount > 1:
164 raise ValueError("Cannot build dict, %i jobs ran on engine #%i"%(
164 raise ValueError("Cannot build dict, %i jobs ran on engine #%i"%(
165 maxcount, bycount[-1]))
165 maxcount, bycount[-1]))
166
166
167 return dict(zip(engine_ids,results))
167 return dict(zip(engine_ids,results))
168
168
169 @property
169 @property
170 def result(self):
170 def result(self):
171 """result property wrapper for `get(timeout=0)`."""
171 """result property wrapper for `get(timeout=0)`."""
172 return self.get()
172 return self.get()
173
173
174 # abbreviated alias:
174 # abbreviated alias:
175 r = result
175 r = result
176
176
177 @property
177 @property
178 @check_ready
178 @check_ready
179 def metadata(self):
179 def metadata(self):
@@ -182,15 +182,15 b' class AsyncResult(object):'
182 return self._metadata[0]
182 return self._metadata[0]
183 else:
183 else:
184 return self._metadata
184 return self._metadata
185
185
186 @property
186 @property
187 def result_dict(self):
187 def result_dict(self):
188 """result property as a dict."""
188 """result property as a dict."""
189 return self.get_dict()
189 return self.get_dict()
190
190
191 def __dict__(self):
191 def __dict__(self):
192 return self.get_dict(0)
192 return self.get_dict(0)
193
193
194 def abort(self):
194 def abort(self):
195 """abort my tasks."""
195 """abort my tasks."""
196 assert not self.ready(), "Can't abort, I am already done!"
196 assert not self.ready(), "Can't abort, I am already done!"
@@ -200,10 +200,10 b' class AsyncResult(object):'
200 def sent(self):
200 def sent(self):
201 """check whether my messages have been sent."""
201 """check whether my messages have been sent."""
202 return self._tracker.done
202 return self._tracker.done
203
203
204 def wait_for_send(self, timeout=-1):
204 def wait_for_send(self, timeout=-1):
205 """wait for pyzmq send to complete.
205 """wait for pyzmq send to complete.
206
206
207 This is necessary when sending arrays that you intend to edit in-place.
207 This is necessary when sending arrays that you intend to edit in-place.
208 `timeout` is in seconds, and will raise TimeoutError if it is reached
208 `timeout` is in seconds, and will raise TimeoutError if it is reached
209 before the send completes.
209 before the send completes.
@@ -213,7 +213,7 b' class AsyncResult(object):'
213 #-------------------------------------
213 #-------------------------------------
214 # dict-access
214 # dict-access
215 #-------------------------------------
215 #-------------------------------------
216
216
217 @check_ready
217 @check_ready
218 def __getitem__(self, key):
218 def __getitem__(self, key):
219 """getitem returns result value(s) if keyed by int/slice, or metadata if key is str.
219 """getitem returns result value(s) if keyed by int/slice, or metadata if key is str.
@@ -230,7 +230,7 b' class AsyncResult(object):'
230 return values
230 return values
231 else:
231 else:
232 raise TypeError("Invalid key type %r, must be 'int','slice', or 'str'"%type(key))
232 raise TypeError("Invalid key type %r, must be 'int','slice', or 'str'"%type(key))
233
233
234 @check_ready
234 @check_ready
235 def __getattr__(self, key):
235 def __getattr__(self, key):
236 """getattr maps to getitem for convenient attr access to metadata."""
236 """getattr maps to getitem for convenient attr access to metadata."""
@@ -238,7 +238,7 b' class AsyncResult(object):'
238 raise AttributeError("%r object has no attribute %r"%(
238 raise AttributeError("%r object has no attribute %r"%(
239 self.__class__.__name__, key))
239 self.__class__.__name__, key))
240 return self.__getitem__(key)
240 return self.__getitem__(key)
241
241
242 # asynchronous iterator:
242 # asynchronous iterator:
243 def __iter__(self):
243 def __iter__(self):
244 if self._single_result:
244 if self._single_result:
@@ -256,22 +256,22 b' class AsyncResult(object):'
256 yield r
256 yield r
257
257
258
258
259
259
260 class AsyncMapResult(AsyncResult):
260 class AsyncMapResult(AsyncResult):
261 """Class for representing results of non-blocking gathers.
261 """Class for representing results of non-blocking gathers.
262
262
263 This will properly reconstruct the gather.
263 This will properly reconstruct the gather.
264 """
264 """
265
265
266 def __init__(self, client, msg_ids, mapObject, fname=''):
266 def __init__(self, client, msg_ids, mapObject, fname=''):
267 AsyncResult.__init__(self, client, msg_ids, fname=fname)
267 AsyncResult.__init__(self, client, msg_ids, fname=fname)
268 self._mapObject = mapObject
268 self._mapObject = mapObject
269 self._single_result = False
269 self._single_result = False
270
270
271 def _reconstruct_result(self, res):
271 def _reconstruct_result(self, res):
272 """Perform the gather on the actual results."""
272 """Perform the gather on the actual results."""
273 return self._mapObject.joinPartitions(res)
273 return self._mapObject.joinPartitions(res)
274
274
275 # asynchronous iterator:
275 # asynchronous iterator:
276 def __iter__(self):
276 def __iter__(self):
277 try:
277 try:
@@ -297,11 +297,11 b' class AsyncMapResult(AsyncResult):'
297
297
298 class AsyncHubResult(AsyncResult):
298 class AsyncHubResult(AsyncResult):
299 """Class to wrap pending results that must be requested from the Hub.
299 """Class to wrap pending results that must be requested from the Hub.
300
300
301 Note that waiting/polling on these objects requires polling the Hubover the network,
301 Note that waiting/polling on these objects requires polling the Hubover the network,
302 so use `AsyncHubResult.wait()` sparingly.
302 so use `AsyncHubResult.wait()` sparingly.
303 """
303 """
304
304
305 def wait(self, timeout=-1):
305 def wait(self, timeout=-1):
306 """wait for result to complete."""
306 """wait for result to complete."""
307 start = time.time()
307 start = time.time()
@@ -341,5 +341,5 b' class AsyncHubResult(AsyncResult):'
341 self._success = True
341 self._success = True
342 finally:
342 finally:
343 self._metadata = map(self._client.metadata.get, self.msg_ids)
343 self._metadata = map(self._client.metadata.get, self.msg_ids)
344
344
345 __all__ = ['AsyncResult', 'AsyncMapResult', 'AsyncHubResult'] No newline at end of file
345 __all__ = ['AsyncResult', 'AsyncMapResult', 'AsyncHubResult']
@@ -71,9 +71,9 b' def spin_first(f, self, *args, **kwargs):'
71
71
72 class Metadata(dict):
72 class Metadata(dict):
73 """Subclass of dict for initializing metadata values.
73 """Subclass of dict for initializing metadata values.
74
74
75 Attribute access works on keys.
75 Attribute access works on keys.
76
76
77 These objects have a strict set of keys - errors will raise if you try
77 These objects have a strict set of keys - errors will raise if you try
78 to add new keys.
78 to add new keys.
79 """
79 """
@@ -98,7 +98,7 b' class Metadata(dict):'
98 }
98 }
99 self.update(md)
99 self.update(md)
100 self.update(dict(*args, **kwargs))
100 self.update(dict(*args, **kwargs))
101
101
102 def __getattr__(self, key):
102 def __getattr__(self, key):
103 """getattr aliased to getitem"""
103 """getattr aliased to getitem"""
104 if key in self.iterkeys():
104 if key in self.iterkeys():
@@ -112,21 +112,21 b' class Metadata(dict):'
112 self[key] = value
112 self[key] = value
113 else:
113 else:
114 raise AttributeError(key)
114 raise AttributeError(key)
115
115
116 def __setitem__(self, key, value):
116 def __setitem__(self, key, value):
117 """strict static key enforcement"""
117 """strict static key enforcement"""
118 if key in self.iterkeys():
118 if key in self.iterkeys():
119 dict.__setitem__(self, key, value)
119 dict.__setitem__(self, key, value)
120 else:
120 else:
121 raise KeyError(key)
121 raise KeyError(key)
122
122
123
123
124 class Client(HasTraits):
124 class Client(HasTraits):
125 """A semi-synchronous client to the IPython ZMQ cluster
125 """A semi-synchronous client to the IPython ZMQ cluster
126
126
127 Parameters
127 Parameters
128 ----------
128 ----------
129
129
130 url_or_file : bytes or unicode; zmq url or path to ipcontroller-client.json
130 url_or_file : bytes or unicode; zmq url or path to ipcontroller-client.json
131 Connection information for the Hub's registration. If a json connector
131 Connection information for the Hub's registration. If a json connector
132 file is given, then likely no further configuration is necessary.
132 file is given, then likely no further configuration is necessary.
@@ -139,12 +139,12 b' class Client(HasTraits):'
139 Pass an existing zmq.Context instance, otherwise the client will create its own.
139 Pass an existing zmq.Context instance, otherwise the client will create its own.
140 debug : bool
140 debug : bool
141 flag for lots of message printing for debug purposes
141 flag for lots of message printing for debug purposes
142 timeout : int/float
142 timeout : int/float
143 time (in seconds) to wait for connection replies from the Hub
143 time (in seconds) to wait for connection replies from the Hub
144 [Default: 10]
144 [Default: 10]
145
145
146 #-------------- session related args ----------------
146 #-------------- session related args ----------------
147
147
148 config : Config object
148 config : Config object
149 If specified, this will be relayed to the Session for configuration
149 If specified, this will be relayed to the Session for configuration
150 username : str
150 username : str
@@ -157,16 +157,16 b' class Client(HasTraits):'
157 unpacker : str (import_string) or callable
157 unpacker : str (import_string) or callable
158 The inverse of packer. Only necessary if packer is specified as *not* one
158 The inverse of packer. Only necessary if packer is specified as *not* one
159 of 'json' or 'pickle'.
159 of 'json' or 'pickle'.
160
160
161 #-------------- ssh related args ----------------
161 #-------------- ssh related args ----------------
162 # These are args for configuring the ssh tunnel to be used
162 # These are args for configuring the ssh tunnel to be used
163 # credentials are used to forward connections over ssh to the Controller
163 # credentials are used to forward connections over ssh to the Controller
164 # Note that the ip given in `addr` needs to be relative to sshserver
164 # Note that the ip given in `addr` needs to be relative to sshserver
165 # The most basic case is to leave addr as pointing to localhost (127.0.0.1),
165 # The most basic case is to leave addr as pointing to localhost (127.0.0.1),
166 # and set sshserver as the same machine the Controller is on. However,
166 # and set sshserver as the same machine the Controller is on. However,
167 # the only requirement is that sshserver is able to see the Controller
167 # the only requirement is that sshserver is able to see the Controller
168 # (i.e. is within the same trusted network).
168 # (i.e. is within the same trusted network).
169
169
170 sshserver : str
170 sshserver : str
171 A string of the form passed to ssh, i.e. 'server.tld' or 'user@server.tld:port'
171 A string of the form passed to ssh, i.e. 'server.tld' or 'user@server.tld:port'
172 If keyfile or password is specified, and this is not, it will default to
172 If keyfile or password is specified, and this is not, it will default to
@@ -174,81 +174,81 b' class Client(HasTraits):'
174 sshkey : str; path to ssh private key file
174 sshkey : str; path to ssh private key file
175 This specifies a key to be used in ssh login, default None.
175 This specifies a key to be used in ssh login, default None.
176 Regular default ssh keys will be used without specifying this argument.
176 Regular default ssh keys will be used without specifying this argument.
177 password : str
177 password : str
178 Your ssh password to sshserver. Note that if this is left None,
178 Your ssh password to sshserver. Note that if this is left None,
179 you will be prompted for it if passwordless key based login is unavailable.
179 you will be prompted for it if passwordless key based login is unavailable.
180 paramiko : bool
180 paramiko : bool
181 flag for whether to use paramiko instead of shell ssh for tunneling.
181 flag for whether to use paramiko instead of shell ssh for tunneling.
182 [default: True on win32, False else]
182 [default: True on win32, False else]
183
183
184 ------- exec authentication args -------
184 ------- exec authentication args -------
185 If even localhost is untrusted, you can have some protection against
185 If even localhost is untrusted, you can have some protection against
186 unauthorized execution by signing messages with HMAC digests.
186 unauthorized execution by signing messages with HMAC digests.
187 Messages are still sent as cleartext, so if someone can snoop your
187 Messages are still sent as cleartext, so if someone can snoop your
188 loopback traffic this will not protect your privacy, but will prevent
188 loopback traffic this will not protect your privacy, but will prevent
189 unauthorized execution.
189 unauthorized execution.
190
190
191 exec_key : str
191 exec_key : str
192 an authentication key or file containing a key
192 an authentication key or file containing a key
193 default: None
193 default: None
194
194
195
195
196 Attributes
196 Attributes
197 ----------
197 ----------
198
198
199 ids : list of int engine IDs
199 ids : list of int engine IDs
200 requesting the ids attribute always synchronizes
200 requesting the ids attribute always synchronizes
201 the registration state. To request ids without synchronization,
201 the registration state. To request ids without synchronization,
202 use semi-private _ids attributes.
202 use semi-private _ids attributes.
203
203
204 history : list of msg_ids
204 history : list of msg_ids
205 a list of msg_ids, keeping track of all the execution
205 a list of msg_ids, keeping track of all the execution
206 messages you have submitted in order.
206 messages you have submitted in order.
207
207
208 outstanding : set of msg_ids
208 outstanding : set of msg_ids
209 a set of msg_ids that have been submitted, but whose
209 a set of msg_ids that have been submitted, but whose
210 results have not yet been received.
210 results have not yet been received.
211
211
212 results : dict
212 results : dict
213 a dict of all our results, keyed by msg_id
213 a dict of all our results, keyed by msg_id
214
214
215 block : bool
215 block : bool
216 determines default behavior when block not specified
216 determines default behavior when block not specified
217 in execution methods
217 in execution methods
218
218
219 Methods
219 Methods
220 -------
220 -------
221
221
222 spin
222 spin
223 flushes incoming results and registration state changes
223 flushes incoming results and registration state changes
224 control methods spin, and requesting `ids` also ensures up to date
224 control methods spin, and requesting `ids` also ensures up to date
225
225
226 wait
226 wait
227 wait on one or more msg_ids
227 wait on one or more msg_ids
228
228
229 execution methods
229 execution methods
230 apply
230 apply
231 legacy: execute, run
231 legacy: execute, run
232
232
233 data movement
233 data movement
234 push, pull, scatter, gather
234 push, pull, scatter, gather
235
235
236 query methods
236 query methods
237 queue_status, get_result, purge, result_status
237 queue_status, get_result, purge, result_status
238
238
239 control methods
239 control methods
240 abort, shutdown
240 abort, shutdown
241
241
242 """
242 """
243
243
244
244
245 block = Bool(False)
245 block = Bool(False)
246 outstanding = Set()
246 outstanding = Set()
247 results = Instance('collections.defaultdict', (dict,))
247 results = Instance('collections.defaultdict', (dict,))
248 metadata = Instance('collections.defaultdict', (Metadata,))
248 metadata = Instance('collections.defaultdict', (Metadata,))
249 history = List()
249 history = List()
250 debug = Bool(False)
250 debug = Bool(False)
251
251
252 profile=Unicode()
252 profile=Unicode()
253 def _profile_default(self):
253 def _profile_default(self):
254 if BaseIPythonApplication.initialized():
254 if BaseIPythonApplication.initialized():
@@ -261,8 +261,8 b' class Client(HasTraits):'
261 return u'default'
261 return u'default'
262 else:
262 else:
263 return u'default'
263 return u'default'
264
264
265
265
266 _outstanding_dict = Instance('collections.defaultdict', (set,))
266 _outstanding_dict = Instance('collections.defaultdict', (set,))
267 _ids = List()
267 _ids = List()
268 _connected=Bool(False)
268 _connected=Bool(False)
@@ -281,11 +281,11 b' class Client(HasTraits):'
281 _closed = False
281 _closed = False
282 _ignored_control_replies=Int(0)
282 _ignored_control_replies=Int(0)
283 _ignored_hub_replies=Int(0)
283 _ignored_hub_replies=Int(0)
284
284
285 def __new__(self, *args, **kw):
285 def __new__(self, *args, **kw):
286 # don't raise on positional args
286 # don't raise on positional args
287 return HasTraits.__new__(self, **kw)
287 return HasTraits.__new__(self, **kw)
288
288
289 def __init__(self, url_or_file=None, profile=None, profile_dir=None, ipython_dir=None,
289 def __init__(self, url_or_file=None, profile=None, profile_dir=None, ipython_dir=None,
290 context=None, debug=False, exec_key=None,
290 context=None, debug=False, exec_key=None,
291 sshserver=None, sshkey=None, password=None, paramiko=None,
291 sshserver=None, sshkey=None, password=None, paramiko=None,
@@ -298,14 +298,14 b' class Client(HasTraits):'
298 if context is None:
298 if context is None:
299 context = zmq.Context.instance()
299 context = zmq.Context.instance()
300 self._context = context
300 self._context = context
301
301
302 self._setup_profile_dir(self.profile, profile_dir, ipython_dir)
302 self._setup_profile_dir(self.profile, profile_dir, ipython_dir)
303 if self._cd is not None:
303 if self._cd is not None:
304 if url_or_file is None:
304 if url_or_file is None:
305 url_or_file = pjoin(self._cd.security_dir, 'ipcontroller-client.json')
305 url_or_file = pjoin(self._cd.security_dir, 'ipcontroller-client.json')
306 assert url_or_file is not None, "I can't find enough information to connect to a hub!"\
306 assert url_or_file is not None, "I can't find enough information to connect to a hub!"\
307 " Please specify at least one of url_or_file or profile."
307 " Please specify at least one of url_or_file or profile."
308
308
309 try:
309 try:
310 util.validate_url(url_or_file)
310 util.validate_url(url_or_file)
311 except AssertionError:
311 except AssertionError:
@@ -317,7 +317,7 b' class Client(HasTraits):'
317 cfg = json.loads(f.read())
317 cfg = json.loads(f.read())
318 else:
318 else:
319 cfg = {'url':url_or_file}
319 cfg = {'url':url_or_file}
320
320
321 # sync defaults from args, json:
321 # sync defaults from args, json:
322 if sshserver:
322 if sshserver:
323 cfg['ssh'] = sshserver
323 cfg['ssh'] = sshserver
@@ -346,9 +346,9 b' class Client(HasTraits):'
346 elif not sshserver:
346 elif not sshserver:
347 # otherwise sync with cfg
347 # otherwise sync with cfg
348 sshserver = cfg['ssh']
348 sshserver = cfg['ssh']
349
349
350 self._config = cfg
350 self._config = cfg
351
351
352 self._ssh = bool(sshserver or sshkey or password)
352 self._ssh = bool(sshserver or sshkey or password)
353 if self._ssh and sshserver is None:
353 if self._ssh and sshserver is None:
354 # default to ssh via localhost
354 # default to ssh via localhost
@@ -359,7 +359,7 b' class Client(HasTraits):'
359 else:
359 else:
360 password = getpass("SSH Password for %s: "%sshserver)
360 password = getpass("SSH Password for %s: "%sshserver)
361 ssh_kwargs = dict(keyfile=sshkey, password=password, paramiko=paramiko)
361 ssh_kwargs = dict(keyfile=sshkey, password=password, paramiko=paramiko)
362
362
363 # configure and construct the session
363 # configure and construct the session
364 if exec_key is not None:
364 if exec_key is not None:
365 if os.path.isfile(exec_key):
365 if os.path.isfile(exec_key):
@@ -368,16 +368,16 b' class Client(HasTraits):'
368 exec_key = util.asbytes(exec_key)
368 exec_key = util.asbytes(exec_key)
369 extra_args['key'] = exec_key
369 extra_args['key'] = exec_key
370 self.session = Session(**extra_args)
370 self.session = Session(**extra_args)
371
371
372 self._query_socket = self._context.socket(zmq.DEALER)
372 self._query_socket = self._context.socket(zmq.DEALER)
373 self._query_socket.setsockopt(zmq.IDENTITY, self.session.bsession)
373 self._query_socket.setsockopt(zmq.IDENTITY, self.session.bsession)
374 if self._ssh:
374 if self._ssh:
375 tunnel.tunnel_connection(self._query_socket, url, sshserver, **ssh_kwargs)
375 tunnel.tunnel_connection(self._query_socket, url, sshserver, **ssh_kwargs)
376 else:
376 else:
377 self._query_socket.connect(url)
377 self._query_socket.connect(url)
378
378
379 self.session.debug = self.debug
379 self.session.debug = self.debug
380
380
381 self._notification_handlers = {'registration_notification' : self._register_engine,
381 self._notification_handlers = {'registration_notification' : self._register_engine,
382 'unregistration_notification' : self._unregister_engine,
382 'unregistration_notification' : self._unregister_engine,
383 'shutdown_notification' : lambda msg: self.close(),
383 'shutdown_notification' : lambda msg: self.close(),
@@ -385,11 +385,11 b' class Client(HasTraits):'
385 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
385 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
386 'apply_reply' : self._handle_apply_reply}
386 'apply_reply' : self._handle_apply_reply}
387 self._connect(sshserver, ssh_kwargs, timeout)
387 self._connect(sshserver, ssh_kwargs, timeout)
388
388
389 def __del__(self):
389 def __del__(self):
390 """cleanup sockets, but _not_ context."""
390 """cleanup sockets, but _not_ context."""
391 self.close()
391 self.close()
392
392
393 def _setup_profile_dir(self, profile, profile_dir, ipython_dir):
393 def _setup_profile_dir(self, profile, profile_dir, ipython_dir):
394 if ipython_dir is None:
394 if ipython_dir is None:
395 ipython_dir = get_ipython_dir()
395 ipython_dir = get_ipython_dir()
@@ -407,7 +407,7 b' class Client(HasTraits):'
407 except ProfileDirError:
407 except ProfileDirError:
408 pass
408 pass
409 self._cd = None
409 self._cd = None
410
410
411 def _update_engines(self, engines):
411 def _update_engines(self, engines):
412 """Update our engines dict and _ids from a dict of the form: {id:uuid}."""
412 """Update our engines dict and _ids from a dict of the form: {id:uuid}."""
413 for k,v in engines.iteritems():
413 for k,v in engines.iteritems():
@@ -418,7 +418,7 b' class Client(HasTraits):'
418 if sorted(self._engines.keys()) != range(len(self._engines)) and \
418 if sorted(self._engines.keys()) != range(len(self._engines)) and \
419 self._task_scheme == 'pure' and self._task_socket:
419 self._task_scheme == 'pure' and self._task_socket:
420 self._stop_scheduling_tasks()
420 self._stop_scheduling_tasks()
421
421
422 def _stop_scheduling_tasks(self):
422 def _stop_scheduling_tasks(self):
423 """Stop scheduling tasks because an engine has been unregistered
423 """Stop scheduling tasks because an engine has been unregistered
424 from a pure ZMQ scheduler.
424 from a pure ZMQ scheduler.
@@ -431,7 +431,7 b' class Client(HasTraits):'
431 msg += " If you were running tasks when this happened, " +\
431 msg += " If you were running tasks when this happened, " +\
432 "some `outstanding` msg_ids may never resolve."
432 "some `outstanding` msg_ids may never resolve."
433 warnings.warn(msg, RuntimeWarning)
433 warnings.warn(msg, RuntimeWarning)
434
434
435 def _build_targets(self, targets):
435 def _build_targets(self, targets):
436 """Turn valid target IDs or 'all' into two lists:
436 """Turn valid target IDs or 'all' into two lists:
437 (int_ids, uuids).
437 (int_ids, uuids).
@@ -440,7 +440,7 b' class Client(HasTraits):'
440 # flush notification socket if no engines yet, just in case
440 # flush notification socket if no engines yet, just in case
441 if not self.ids:
441 if not self.ids:
442 raise error.NoEnginesRegistered("Can't build targets without any engines")
442 raise error.NoEnginesRegistered("Can't build targets without any engines")
443
443
444 if targets is None:
444 if targets is None:
445 targets = self._ids
445 targets = self._ids
446 elif isinstance(targets, basestring):
446 elif isinstance(targets, basestring):
@@ -454,21 +454,21 b' class Client(HasTraits):'
454 if targets not in self._ids:
454 if targets not in self._ids:
455 raise IndexError("No such engine: %i"%targets)
455 raise IndexError("No such engine: %i"%targets)
456 targets = [targets]
456 targets = [targets]
457
457
458 if isinstance(targets, slice):
458 if isinstance(targets, slice):
459 indices = range(len(self._ids))[targets]
459 indices = range(len(self._ids))[targets]
460 ids = self.ids
460 ids = self.ids
461 targets = [ ids[i] for i in indices ]
461 targets = [ ids[i] for i in indices ]
462
462
463 if not isinstance(targets, (tuple, list, xrange)):
463 if not isinstance(targets, (tuple, list, xrange)):
464 raise TypeError("targets by int/slice/collection of ints only, not %s"%(type(targets)))
464 raise TypeError("targets by int/slice/collection of ints only, not %s"%(type(targets)))
465
465
466 return [util.asbytes(self._engines[t]) for t in targets], list(targets)
466 return [util.asbytes(self._engines[t]) for t in targets], list(targets)
467
467
468 def _connect(self, sshserver, ssh_kwargs, timeout):
468 def _connect(self, sshserver, ssh_kwargs, timeout):
469 """setup all our socket connections to the cluster. This is called from
469 """setup all our socket connections to the cluster. This is called from
470 __init__."""
470 __init__."""
471
471
472 # Maybe allow reconnecting?
472 # Maybe allow reconnecting?
473 if self._connected:
473 if self._connected:
474 return
474 return
@@ -480,7 +480,7 b' class Client(HasTraits):'
480 return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs)
480 return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs)
481 else:
481 else:
482 return s.connect(url)
482 return s.connect(url)
483
483
484 self.session.send(self._query_socket, 'connection_request')
484 self.session.send(self._query_socket, 'connection_request')
485 # use Poller because zmq.select has wrong units in pyzmq 2.1.7
485 # use Poller because zmq.select has wrong units in pyzmq 2.1.7
486 poller = zmq.Poller()
486 poller = zmq.Poller()
@@ -527,11 +527,11 b' class Client(HasTraits):'
527 else:
527 else:
528 self._connected = False
528 self._connected = False
529 raise Exception("Failed to connect!")
529 raise Exception("Failed to connect!")
530
530
531 #--------------------------------------------------------------------------
531 #--------------------------------------------------------------------------
532 # handlers and callbacks for incoming messages
532 # handlers and callbacks for incoming messages
533 #--------------------------------------------------------------------------
533 #--------------------------------------------------------------------------
534
534
535 def _unwrap_exception(self, content):
535 def _unwrap_exception(self, content):
536 """unwrap exception, and remap engine_id to int."""
536 """unwrap exception, and remap engine_id to int."""
537 e = error.unwrap_exception(content)
537 e = error.unwrap_exception(content)
@@ -541,7 +541,7 b' class Client(HasTraits):'
541 eid = self._engines[e_uuid]
541 eid = self._engines[e_uuid]
542 e.engine_info['engine_id'] = eid
542 e.engine_info['engine_id'] = eid
543 return e
543 return e
544
544
545 def _extract_metadata(self, header, parent, content):
545 def _extract_metadata(self, header, parent, content):
546 md = {'msg_id' : parent['msg_id'],
546 md = {'msg_id' : parent['msg_id'],
547 'received' : datetime.now(),
547 'received' : datetime.now(),
@@ -550,10 +550,10 b' class Client(HasTraits):'
550 'after' : parent.get('after', []),
550 'after' : parent.get('after', []),
551 'status' : content['status'],
551 'status' : content['status'],
552 }
552 }
553
553
554 if md['engine_uuid'] is not None:
554 if md['engine_uuid'] is not None:
555 md['engine_id'] = self._engines.get(md['engine_uuid'], None)
555 md['engine_id'] = self._engines.get(md['engine_uuid'], None)
556
556
557 if 'date' in parent:
557 if 'date' in parent:
558 md['submitted'] = parent['date']
558 md['submitted'] = parent['date']
559 if 'started' in header:
559 if 'started' in header:
@@ -561,7 +561,7 b' class Client(HasTraits):'
561 if 'date' in header:
561 if 'date' in header:
562 md['completed'] = header['date']
562 md['completed'] = header['date']
563 return md
563 return md
564
564
565 def _register_engine(self, msg):
565 def _register_engine(self, msg):
566 """Register a new engine, and update our connection info."""
566 """Register a new engine, and update our connection info."""
567 content = msg['content']
567 content = msg['content']
@@ -576,25 +576,25 b' class Client(HasTraits):'
576 if eid in self._ids:
576 if eid in self._ids:
577 self._ids.remove(eid)
577 self._ids.remove(eid)
578 uuid = self._engines.pop(eid)
578 uuid = self._engines.pop(eid)
579
579
580 self._handle_stranded_msgs(eid, uuid)
580 self._handle_stranded_msgs(eid, uuid)
581
581
582 if self._task_socket and self._task_scheme == 'pure':
582 if self._task_socket and self._task_scheme == 'pure':
583 self._stop_scheduling_tasks()
583 self._stop_scheduling_tasks()
584
584
585 def _handle_stranded_msgs(self, eid, uuid):
585 def _handle_stranded_msgs(self, eid, uuid):
586 """Handle messages known to be on an engine when the engine unregisters.
586 """Handle messages known to be on an engine when the engine unregisters.
587
587
588 It is possible that this will fire prematurely - that is, an engine will
588 It is possible that this will fire prematurely - that is, an engine will
589 go down after completing a result, and the client will be notified
589 go down after completing a result, and the client will be notified
590 of the unregistration and later receive the successful result.
590 of the unregistration and later receive the successful result.
591 """
591 """
592
592
593 outstanding = self._outstanding_dict[uuid]
593 outstanding = self._outstanding_dict[uuid]
594
594
595 for msg_id in list(outstanding):
595 for msg_id in list(outstanding):
596 if msg_id in self.results:
596 if msg_id in self.results:
597 # we already
597 # we already
598 continue
598 continue
599 try:
599 try:
600 raise error.EngineError("Engine %r died while running task %r"%(eid, msg_id))
600 raise error.EngineError("Engine %r died while running task %r"%(eid, msg_id))
@@ -608,13 +608,13 b' class Client(HasTraits):'
608 header['date'] = datetime.now()
608 header['date'] = datetime.now()
609 msg = dict(parent_header=parent, header=header, content=content)
609 msg = dict(parent_header=parent, header=header, content=content)
610 self._handle_apply_reply(msg)
610 self._handle_apply_reply(msg)
611
611
612 def _handle_execute_reply(self, msg):
612 def _handle_execute_reply(self, msg):
613 """Save the reply to an execute_request into our results.
613 """Save the reply to an execute_request into our results.
614
614
615 execute messages are never actually used. apply is used instead.
615 execute messages are never actually used. apply is used instead.
616 """
616 """
617
617
618 parent = msg['parent_header']
618 parent = msg['parent_header']
619 msg_id = parent['msg_id']
619 msg_id = parent['msg_id']
620 if msg_id not in self.outstanding:
620 if msg_id not in self.outstanding:
@@ -625,7 +625,7 b' class Client(HasTraits):'
625 else:
625 else:
626 self.outstanding.remove(msg_id)
626 self.outstanding.remove(msg_id)
627 self.results[msg_id] = self._unwrap_exception(msg['content'])
627 self.results[msg_id] = self._unwrap_exception(msg['content'])
628
628
629 def _handle_apply_reply(self, msg):
629 def _handle_apply_reply(self, msg):
630 """Save the reply to an apply_request into our results."""
630 """Save the reply to an apply_request into our results."""
631 parent = msg['parent_header']
631 parent = msg['parent_header']
@@ -641,17 +641,17 b' class Client(HasTraits):'
641 self.outstanding.remove(msg_id)
641 self.outstanding.remove(msg_id)
642 content = msg['content']
642 content = msg['content']
643 header = msg['header']
643 header = msg['header']
644
644
645 # construct metadata:
645 # construct metadata:
646 md = self.metadata[msg_id]
646 md = self.metadata[msg_id]
647 md.update(self._extract_metadata(header, parent, content))
647 md.update(self._extract_metadata(header, parent, content))
648 # is this redundant?
648 # is this redundant?
649 self.metadata[msg_id] = md
649 self.metadata[msg_id] = md
650
650
651 e_outstanding = self._outstanding_dict[md['engine_uuid']]
651 e_outstanding = self._outstanding_dict[md['engine_uuid']]
652 if msg_id in e_outstanding:
652 if msg_id in e_outstanding:
653 e_outstanding.remove(msg_id)
653 e_outstanding.remove(msg_id)
654
654
655 # construct result:
655 # construct result:
656 if content['status'] == 'ok':
656 if content['status'] == 'ok':
657 self.results[msg_id] = util.unserialize_object(msg['buffers'])[0]
657 self.results[msg_id] = util.unserialize_object(msg['buffers'])[0]
@@ -662,7 +662,7 b' class Client(HasTraits):'
662 pass
662 pass
663 else:
663 else:
664 self.results[msg_id] = self._unwrap_exception(content)
664 self.results[msg_id] = self._unwrap_exception(content)
665
665
666 def _flush_notifications(self):
666 def _flush_notifications(self):
667 """Flush notifications of engine registrations waiting
667 """Flush notifications of engine registrations waiting
668 in ZMQ queue."""
668 in ZMQ queue."""
@@ -677,7 +677,7 b' class Client(HasTraits):'
677 else:
677 else:
678 handler(msg)
678 handler(msg)
679 idents,msg = self.session.recv(self._notification_socket, mode=zmq.NOBLOCK)
679 idents,msg = self.session.recv(self._notification_socket, mode=zmq.NOBLOCK)
680
680
681 def _flush_results(self, sock):
681 def _flush_results(self, sock):
682 """Flush task or queue results waiting in ZMQ queue."""
682 """Flush task or queue results waiting in ZMQ queue."""
683 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
683 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
@@ -691,11 +691,11 b' class Client(HasTraits):'
691 else:
691 else:
692 handler(msg)
692 handler(msg)
693 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
693 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
694
694
695 def _flush_control(self, sock):
695 def _flush_control(self, sock):
696 """Flush replies from the control channel waiting
696 """Flush replies from the control channel waiting
697 in the ZMQ queue.
697 in the ZMQ queue.
698
698
699 Currently: ignore them."""
699 Currently: ignore them."""
700 if self._ignored_control_replies <= 0:
700 if self._ignored_control_replies <= 0:
701 return
701 return
@@ -705,18 +705,18 b' class Client(HasTraits):'
705 if self.debug:
705 if self.debug:
706 pprint(msg)
706 pprint(msg)
707 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
707 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
708
708
709 def _flush_ignored_control(self):
709 def _flush_ignored_control(self):
710 """flush ignored control replies"""
710 """flush ignored control replies"""
711 while self._ignored_control_replies > 0:
711 while self._ignored_control_replies > 0:
712 self.session.recv(self._control_socket)
712 self.session.recv(self._control_socket)
713 self._ignored_control_replies -= 1
713 self._ignored_control_replies -= 1
714
714
715 def _flush_ignored_hub_replies(self):
715 def _flush_ignored_hub_replies(self):
716 ident,msg = self.session.recv(self._query_socket, mode=zmq.NOBLOCK)
716 ident,msg = self.session.recv(self._query_socket, mode=zmq.NOBLOCK)
717 while msg is not None:
717 while msg is not None:
718 ident,msg = self.session.recv(self._query_socket, mode=zmq.NOBLOCK)
718 ident,msg = self.session.recv(self._query_socket, mode=zmq.NOBLOCK)
719
719
720 def _flush_iopub(self, sock):
720 def _flush_iopub(self, sock):
721 """Flush replies from the iopub channel waiting
721 """Flush replies from the iopub channel waiting
722 in the ZMQ queue.
722 in the ZMQ queue.
@@ -730,10 +730,10 b' class Client(HasTraits):'
730 content = msg['content']
730 content = msg['content']
731 header = msg['header']
731 header = msg['header']
732 msg_type = msg['header']['msg_type']
732 msg_type = msg['header']['msg_type']
733
733
734 # init metadata:
734 # init metadata:
735 md = self.metadata[msg_id]
735 md = self.metadata[msg_id]
736
736
737 if msg_type == 'stream':
737 if msg_type == 'stream':
738 name = content['name']
738 name = content['name']
739 s = md[name] or ''
739 s = md[name] or ''
@@ -744,40 +744,40 b' class Client(HasTraits):'
744 md.update({'pyin' : content['code']})
744 md.update({'pyin' : content['code']})
745 else:
745 else:
746 md.update({msg_type : content.get('data', '')})
746 md.update({msg_type : content.get('data', '')})
747
747
748 # reduntant?
748 # reduntant?
749 self.metadata[msg_id] = md
749 self.metadata[msg_id] = md
750
750
751 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
751 idents,msg = self.session.recv(sock, mode=zmq.NOBLOCK)
752
752
753 #--------------------------------------------------------------------------
753 #--------------------------------------------------------------------------
754 # len, getitem
754 # len, getitem
755 #--------------------------------------------------------------------------
755 #--------------------------------------------------------------------------
756
756
757 def __len__(self):
757 def __len__(self):
758 """len(client) returns # of engines."""
758 """len(client) returns # of engines."""
759 return len(self.ids)
759 return len(self.ids)
760
760
761 def __getitem__(self, key):
761 def __getitem__(self, key):
762 """index access returns DirectView multiplexer objects
762 """index access returns DirectView multiplexer objects
763
763
764 Must be int, slice, or list/tuple/xrange of ints"""
764 Must be int, slice, or list/tuple/xrange of ints"""
765 if not isinstance(key, (int, slice, tuple, list, xrange)):
765 if not isinstance(key, (int, slice, tuple, list, xrange)):
766 raise TypeError("key by int/slice/iterable of ints only, not %s"%(type(key)))
766 raise TypeError("key by int/slice/iterable of ints only, not %s"%(type(key)))
767 else:
767 else:
768 return self.direct_view(key)
768 return self.direct_view(key)
769
769
770 #--------------------------------------------------------------------------
770 #--------------------------------------------------------------------------
771 # Begin public methods
771 # Begin public methods
772 #--------------------------------------------------------------------------
772 #--------------------------------------------------------------------------
773
773
774 @property
774 @property
775 def ids(self):
775 def ids(self):
776 """Always up-to-date ids property."""
776 """Always up-to-date ids property."""
777 self._flush_notifications()
777 self._flush_notifications()
778 # always copy:
778 # always copy:
779 return list(self._ids)
779 return list(self._ids)
780
780
781 def close(self):
781 def close(self):
782 if self._closed:
782 if self._closed:
783 return
783 return
@@ -786,7 +786,7 b' class Client(HasTraits):'
786 if isinstance(socket, zmq.Socket) and not socket.closed:
786 if isinstance(socket, zmq.Socket) and not socket.closed:
787 socket.close()
787 socket.close()
788 self._closed = True
788 self._closed = True
789
789
790 def spin(self):
790 def spin(self):
791 """Flush any registration notifications and execution results
791 """Flush any registration notifications and execution results
792 waiting in the ZMQ queue.
792 waiting in the ZMQ queue.
@@ -803,13 +803,13 b' class Client(HasTraits):'
803 self._flush_iopub(self._iopub_socket)
803 self._flush_iopub(self._iopub_socket)
804 if self._query_socket:
804 if self._query_socket:
805 self._flush_ignored_hub_replies()
805 self._flush_ignored_hub_replies()
806
806
807 def wait(self, jobs=None, timeout=-1):
807 def wait(self, jobs=None, timeout=-1):
808 """waits on one or more `jobs`, for up to `timeout` seconds.
808 """waits on one or more `jobs`, for up to `timeout` seconds.
809
809
810 Parameters
810 Parameters
811 ----------
811 ----------
812
812
813 jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects
813 jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects
814 ints are indices to self.history
814 ints are indices to self.history
815 strs are msg_ids
815 strs are msg_ids
@@ -817,10 +817,10 b' class Client(HasTraits):'
817 timeout : float
817 timeout : float
818 a time in seconds, after which to give up.
818 a time in seconds, after which to give up.
819 default is -1, which means no timeout
819 default is -1, which means no timeout
820
820
821 Returns
821 Returns
822 -------
822 -------
823
823
824 True : when all msg_ids are done
824 True : when all msg_ids are done
825 False : timeout reached, some msg_ids still outstanding
825 False : timeout reached, some msg_ids still outstanding
826 """
826 """
@@ -848,11 +848,11 b' class Client(HasTraits):'
848 time.sleep(1e-3)
848 time.sleep(1e-3)
849 self.spin()
849 self.spin()
850 return len(theids.intersection(self.outstanding)) == 0
850 return len(theids.intersection(self.outstanding)) == 0
851
851
852 #--------------------------------------------------------------------------
852 #--------------------------------------------------------------------------
853 # Control methods
853 # Control methods
854 #--------------------------------------------------------------------------
854 #--------------------------------------------------------------------------
855
855
856 @spin_first
856 @spin_first
857 def clear(self, targets=None, block=None):
857 def clear(self, targets=None, block=None):
858 """Clear the namespace in target(s)."""
858 """Clear the namespace in target(s)."""
@@ -873,22 +873,22 b' class Client(HasTraits):'
873 self._ignored_control_replies += len(targets)
873 self._ignored_control_replies += len(targets)
874 if error:
874 if error:
875 raise error
875 raise error
876
876
877
877
878 @spin_first
878 @spin_first
879 def abort(self, jobs=None, targets=None, block=None):
879 def abort(self, jobs=None, targets=None, block=None):
880 """Abort specific jobs from the execution queues of target(s).
880 """Abort specific jobs from the execution queues of target(s).
881
881
882 This is a mechanism to prevent jobs that have already been submitted
882 This is a mechanism to prevent jobs that have already been submitted
883 from executing.
883 from executing.
884
884
885 Parameters
885 Parameters
886 ----------
886 ----------
887
887
888 jobs : msg_id, list of msg_ids, or AsyncResult
888 jobs : msg_id, list of msg_ids, or AsyncResult
889 The jobs to be aborted
889 The jobs to be aborted
890
890
891
891
892 """
892 """
893 block = self.block if block is None else block
893 block = self.block if block is None else block
894 targets = self._build_targets(targets)[0]
894 targets = self._build_targets(targets)[0]
@@ -905,7 +905,7 b' class Client(HasTraits):'
905 msg_ids.append(j)
905 msg_ids.append(j)
906 content = dict(msg_ids=msg_ids)
906 content = dict(msg_ids=msg_ids)
907 for t in targets:
907 for t in targets:
908 self.session.send(self._control_socket, 'abort_request',
908 self.session.send(self._control_socket, 'abort_request',
909 content=content, ident=t)
909 content=content, ident=t)
910 error = False
910 error = False
911 if block:
911 if block:
@@ -920,7 +920,7 b' class Client(HasTraits):'
920 self._ignored_control_replies += len(targets)
920 self._ignored_control_replies += len(targets)
921 if error:
921 if error:
922 raise error
922 raise error
923
923
924 @spin_first
924 @spin_first
925 def shutdown(self, targets=None, restart=False, hub=False, block=None):
925 def shutdown(self, targets=None, restart=False, hub=False, block=None):
926 """Terminates one or more engine processes, optionally including the hub."""
926 """Terminates one or more engine processes, optionally including the hub."""
@@ -929,7 +929,7 b' class Client(HasTraits):'
929 targets = 'all'
929 targets = 'all'
930 targets = self._build_targets(targets)[0]
930 targets = self._build_targets(targets)[0]
931 for t in targets:
931 for t in targets:
932 self.session.send(self._control_socket, 'shutdown_request',
932 self.session.send(self._control_socket, 'shutdown_request',
933 content={'restart':restart},ident=t)
933 content={'restart':restart},ident=t)
934 error = False
934 error = False
935 if block or hub:
935 if block or hub:
@@ -942,7 +942,7 b' class Client(HasTraits):'
942 error = self._unwrap_exception(msg['content'])
942 error = self._unwrap_exception(msg['content'])
943 else:
943 else:
944 self._ignored_control_replies += len(targets)
944 self._ignored_control_replies += len(targets)
945
945
946 if hub:
946 if hub:
947 time.sleep(0.25)
947 time.sleep(0.25)
948 self.session.send(self._query_socket, 'shutdown_request')
948 self.session.send(self._query_socket, 'shutdown_request')
@@ -951,34 +951,34 b' class Client(HasTraits):'
951 pprint(msg)
951 pprint(msg)
952 if msg['content']['status'] != 'ok':
952 if msg['content']['status'] != 'ok':
953 error = self._unwrap_exception(msg['content'])
953 error = self._unwrap_exception(msg['content'])
954
954
955 if error:
955 if error:
956 raise error
956 raise error
957
957
958 #--------------------------------------------------------------------------
958 #--------------------------------------------------------------------------
959 # Execution related methods
959 # Execution related methods
960 #--------------------------------------------------------------------------
960 #--------------------------------------------------------------------------
961
961
962 def _maybe_raise(self, result):
962 def _maybe_raise(self, result):
963 """wrapper for maybe raising an exception if apply failed."""
963 """wrapper for maybe raising an exception if apply failed."""
964 if isinstance(result, error.RemoteError):
964 if isinstance(result, error.RemoteError):
965 raise result
965 raise result
966
966
967 return result
967 return result
968
968
969 def send_apply_message(self, socket, f, args=None, kwargs=None, subheader=None, track=False,
969 def send_apply_message(self, socket, f, args=None, kwargs=None, subheader=None, track=False,
970 ident=None):
970 ident=None):
971 """construct and send an apply message via a socket.
971 """construct and send an apply message via a socket.
972
972
973 This is the principal method with which all engine execution is performed by views.
973 This is the principal method with which all engine execution is performed by views.
974 """
974 """
975
975
976 assert not self._closed, "cannot use me anymore, I'm closed!"
976 assert not self._closed, "cannot use me anymore, I'm closed!"
977 # defaults:
977 # defaults:
978 args = args if args is not None else []
978 args = args if args is not None else []
979 kwargs = kwargs if kwargs is not None else {}
979 kwargs = kwargs if kwargs is not None else {}
980 subheader = subheader if subheader is not None else {}
980 subheader = subheader if subheader is not None else {}
981
981
982 # validate arguments
982 # validate arguments
983 if not callable(f):
983 if not callable(f):
984 raise TypeError("f must be callable, not %s"%type(f))
984 raise TypeError("f must be callable, not %s"%type(f))
@@ -988,12 +988,12 b' class Client(HasTraits):'
988 raise TypeError("kwargs must be dict, not %s"%type(kwargs))
988 raise TypeError("kwargs must be dict, not %s"%type(kwargs))
989 if not isinstance(subheader, dict):
989 if not isinstance(subheader, dict):
990 raise TypeError("subheader must be dict, not %s"%type(subheader))
990 raise TypeError("subheader must be dict, not %s"%type(subheader))
991
991
992 bufs = util.pack_apply_message(f,args,kwargs)
992 bufs = util.pack_apply_message(f,args,kwargs)
993
993
994 msg = self.session.send(socket, "apply_request", buffers=bufs, ident=ident,
994 msg = self.session.send(socket, "apply_request", buffers=bufs, ident=ident,
995 subheader=subheader, track=track)
995 subheader=subheader, track=track)
996
996
997 msg_id = msg['header']['msg_id']
997 msg_id = msg['header']['msg_id']
998 self.outstanding.add(msg_id)
998 self.outstanding.add(msg_id)
999 if ident:
999 if ident:
@@ -1005,22 +1005,22 b' class Client(HasTraits):'
1005 self._outstanding_dict[ident].add(msg_id)
1005 self._outstanding_dict[ident].add(msg_id)
1006 self.history.append(msg_id)
1006 self.history.append(msg_id)
1007 self.metadata[msg_id]['submitted'] = datetime.now()
1007 self.metadata[msg_id]['submitted'] = datetime.now()
1008
1008
1009 return msg
1009 return msg
1010
1010
1011 #--------------------------------------------------------------------------
1011 #--------------------------------------------------------------------------
1012 # construct a View object
1012 # construct a View object
1013 #--------------------------------------------------------------------------
1013 #--------------------------------------------------------------------------
1014
1014
1015 def load_balanced_view(self, targets=None):
1015 def load_balanced_view(self, targets=None):
1016 """construct a DirectView object.
1016 """construct a DirectView object.
1017
1017
1018 If no arguments are specified, create a LoadBalancedView
1018 If no arguments are specified, create a LoadBalancedView
1019 using all engines.
1019 using all engines.
1020
1020
1021 Parameters
1021 Parameters
1022 ----------
1022 ----------
1023
1023
1024 targets: list,slice,int,etc. [default: use all engines]
1024 targets: list,slice,int,etc. [default: use all engines]
1025 The subset of engines across which to load-balance
1025 The subset of engines across which to load-balance
1026 """
1026 """
@@ -1029,16 +1029,16 b' class Client(HasTraits):'
1029 if targets is not None:
1029 if targets is not None:
1030 targets = self._build_targets(targets)[1]
1030 targets = self._build_targets(targets)[1]
1031 return LoadBalancedView(client=self, socket=self._task_socket, targets=targets)
1031 return LoadBalancedView(client=self, socket=self._task_socket, targets=targets)
1032
1032
1033 def direct_view(self, targets='all'):
1033 def direct_view(self, targets='all'):
1034 """construct a DirectView object.
1034 """construct a DirectView object.
1035
1035
1036 If no targets are specified, create a DirectView
1036 If no targets are specified, create a DirectView
1037 using all engines.
1037 using all engines.
1038
1038
1039 Parameters
1039 Parameters
1040 ----------
1040 ----------
1041
1041
1042 targets: list,slice,int,etc. [default: use all engines]
1042 targets: list,slice,int,etc. [default: use all engines]
1043 The engines to use for the View
1043 The engines to use for the View
1044 """
1044 """
@@ -1049,56 +1049,56 b' class Client(HasTraits):'
1049 if single:
1049 if single:
1050 targets = targets[0]
1050 targets = targets[0]
1051 return DirectView(client=self, socket=self._mux_socket, targets=targets)
1051 return DirectView(client=self, socket=self._mux_socket, targets=targets)
1052
1052
1053 #--------------------------------------------------------------------------
1053 #--------------------------------------------------------------------------
1054 # Query methods
1054 # Query methods
1055 #--------------------------------------------------------------------------
1055 #--------------------------------------------------------------------------
1056
1056
1057 @spin_first
1057 @spin_first
1058 def get_result(self, indices_or_msg_ids=None, block=None):
1058 def get_result(self, indices_or_msg_ids=None, block=None):
1059 """Retrieve a result by msg_id or history index, wrapped in an AsyncResult object.
1059 """Retrieve a result by msg_id or history index, wrapped in an AsyncResult object.
1060
1060
1061 If the client already has the results, no request to the Hub will be made.
1061 If the client already has the results, no request to the Hub will be made.
1062
1062
1063 This is a convenient way to construct AsyncResult objects, which are wrappers
1063 This is a convenient way to construct AsyncResult objects, which are wrappers
1064 that include metadata about execution, and allow for awaiting results that
1064 that include metadata about execution, and allow for awaiting results that
1065 were not submitted by this Client.
1065 were not submitted by this Client.
1066
1066
1067 It can also be a convenient way to retrieve the metadata associated with
1067 It can also be a convenient way to retrieve the metadata associated with
1068 blocking execution, since it always retrieves
1068 blocking execution, since it always retrieves
1069
1069
1070 Examples
1070 Examples
1071 --------
1071 --------
1072 ::
1072 ::
1073
1073
1074 In [10]: r = client.apply()
1074 In [10]: r = client.apply()
1075
1075
1076 Parameters
1076 Parameters
1077 ----------
1077 ----------
1078
1078
1079 indices_or_msg_ids : integer history index, str msg_id, or list of either
1079 indices_or_msg_ids : integer history index, str msg_id, or list of either
1080 The indices or msg_ids of indices to be retrieved
1080 The indices or msg_ids of indices to be retrieved
1081
1081
1082 block : bool
1082 block : bool
1083 Whether to wait for the result to be done
1083 Whether to wait for the result to be done
1084
1084
1085 Returns
1085 Returns
1086 -------
1086 -------
1087
1087
1088 AsyncResult
1088 AsyncResult
1089 A single AsyncResult object will always be returned.
1089 A single AsyncResult object will always be returned.
1090
1090
1091 AsyncHubResult
1091 AsyncHubResult
1092 A subclass of AsyncResult that retrieves results from the Hub
1092 A subclass of AsyncResult that retrieves results from the Hub
1093
1093
1094 """
1094 """
1095 block = self.block if block is None else block
1095 block = self.block if block is None else block
1096 if indices_or_msg_ids is None:
1096 if indices_or_msg_ids is None:
1097 indices_or_msg_ids = -1
1097 indices_or_msg_ids = -1
1098
1098
1099 if not isinstance(indices_or_msg_ids, (list,tuple)):
1099 if not isinstance(indices_or_msg_ids, (list,tuple)):
1100 indices_or_msg_ids = [indices_or_msg_ids]
1100 indices_or_msg_ids = [indices_or_msg_ids]
1101
1101
1102 theids = []
1102 theids = []
1103 for id in indices_or_msg_ids:
1103 for id in indices_or_msg_ids:
1104 if isinstance(id, int):
1104 if isinstance(id, int):
@@ -1106,18 +1106,18 b' class Client(HasTraits):'
1106 if not isinstance(id, basestring):
1106 if not isinstance(id, basestring):
1107 raise TypeError("indices must be str or int, not %r"%id)
1107 raise TypeError("indices must be str or int, not %r"%id)
1108 theids.append(id)
1108 theids.append(id)
1109
1109
1110 local_ids = filter(lambda msg_id: msg_id in self.history or msg_id in self.results, theids)
1110 local_ids = filter(lambda msg_id: msg_id in self.history or msg_id in self.results, theids)
1111 remote_ids = filter(lambda msg_id: msg_id not in local_ids, theids)
1111 remote_ids = filter(lambda msg_id: msg_id not in local_ids, theids)
1112
1112
1113 if remote_ids:
1113 if remote_ids:
1114 ar = AsyncHubResult(self, msg_ids=theids)
1114 ar = AsyncHubResult(self, msg_ids=theids)
1115 else:
1115 else:
1116 ar = AsyncResult(self, msg_ids=theids)
1116 ar = AsyncResult(self, msg_ids=theids)
1117
1117
1118 if block:
1118 if block:
1119 ar.wait()
1119 ar.wait()
1120
1120
1121 return ar
1121 return ar
1122
1122
1123 @spin_first
1123 @spin_first
@@ -1181,27 +1181,27 b' class Client(HasTraits):'
1181 ar.wait()
1181 ar.wait()
1182
1182
1183 return ar
1183 return ar
1184
1184
1185 @spin_first
1185 @spin_first
1186 def result_status(self, msg_ids, status_only=True):
1186 def result_status(self, msg_ids, status_only=True):
1187 """Check on the status of the result(s) of the apply request with `msg_ids`.
1187 """Check on the status of the result(s) of the apply request with `msg_ids`.
1188
1188
1189 If status_only is False, then the actual results will be retrieved, else
1189 If status_only is False, then the actual results will be retrieved, else
1190 only the status of the results will be checked.
1190 only the status of the results will be checked.
1191
1191
1192 Parameters
1192 Parameters
1193 ----------
1193 ----------
1194
1194
1195 msg_ids : list of msg_ids
1195 msg_ids : list of msg_ids
1196 if int:
1196 if int:
1197 Passed as index to self.history for convenience.
1197 Passed as index to self.history for convenience.
1198 status_only : bool (default: True)
1198 status_only : bool (default: True)
1199 if False:
1199 if False:
1200 Retrieve the actual results of completed tasks.
1200 Retrieve the actual results of completed tasks.
1201
1201
1202 Returns
1202 Returns
1203 -------
1203 -------
1204
1204
1205 results : dict
1205 results : dict
1206 There will always be the keys 'pending' and 'completed', which will
1206 There will always be the keys 'pending' and 'completed', which will
1207 be lists of msg_ids that are incomplete or complete. If `status_only`
1207 be lists of msg_ids that are incomplete or complete. If `status_only`
@@ -1209,7 +1209,7 b' class Client(HasTraits):'
1209 """
1209 """
1210 if not isinstance(msg_ids, (list,tuple)):
1210 if not isinstance(msg_ids, (list,tuple)):
1211 msg_ids = [msg_ids]
1211 msg_ids = [msg_ids]
1212
1212
1213 theids = []
1213 theids = []
1214 for msg_id in msg_ids:
1214 for msg_id in msg_ids:
1215 if isinstance(msg_id, int):
1215 if isinstance(msg_id, int):
@@ -1217,17 +1217,17 b' class Client(HasTraits):'
1217 if not isinstance(msg_id, basestring):
1217 if not isinstance(msg_id, basestring):
1218 raise TypeError("msg_ids must be str, not %r"%msg_id)
1218 raise TypeError("msg_ids must be str, not %r"%msg_id)
1219 theids.append(msg_id)
1219 theids.append(msg_id)
1220
1220
1221 completed = []
1221 completed = []
1222 local_results = {}
1222 local_results = {}
1223
1223
1224 # comment this block out to temporarily disable local shortcut:
1224 # comment this block out to temporarily disable local shortcut:
1225 for msg_id in theids:
1225 for msg_id in theids:
1226 if msg_id in self.results:
1226 if msg_id in self.results:
1227 completed.append(msg_id)
1227 completed.append(msg_id)
1228 local_results[msg_id] = self.results[msg_id]
1228 local_results[msg_id] = self.results[msg_id]
1229 theids.remove(msg_id)
1229 theids.remove(msg_id)
1230
1230
1231 if theids: # some not locally cached
1231 if theids: # some not locally cached
1232 content = dict(msg_ids=theids, status_only=status_only)
1232 content = dict(msg_ids=theids, status_only=status_only)
1233 msg = self.session.send(self._query_socket, "result_request", content=content)
1233 msg = self.session.send(self._query_socket, "result_request", content=content)
@@ -1241,16 +1241,16 b' class Client(HasTraits):'
1241 buffers = msg['buffers']
1241 buffers = msg['buffers']
1242 else:
1242 else:
1243 content = dict(completed=[],pending=[])
1243 content = dict(completed=[],pending=[])
1244
1244
1245 content['completed'].extend(completed)
1245 content['completed'].extend(completed)
1246
1246
1247 if status_only:
1247 if status_only:
1248 return content
1248 return content
1249
1249
1250 failures = []
1250 failures = []
1251 # load cached results into result:
1251 # load cached results into result:
1252 content.update(local_results)
1252 content.update(local_results)
1253
1253
1254 # update cache with results:
1254 # update cache with results:
1255 for msg_id in sorted(theids):
1255 for msg_id in sorted(theids):
1256 if msg_id in content['completed']:
1256 if msg_id in content['completed']:
@@ -1261,34 +1261,34 b' class Client(HasTraits):'
1261 iodict = rec['io']
1261 iodict = rec['io']
1262 if isinstance(rcontent, str):
1262 if isinstance(rcontent, str):
1263 rcontent = self.session.unpack(rcontent)
1263 rcontent = self.session.unpack(rcontent)
1264
1264
1265 md = self.metadata[msg_id]
1265 md = self.metadata[msg_id]
1266 md.update(self._extract_metadata(header, parent, rcontent))
1266 md.update(self._extract_metadata(header, parent, rcontent))
1267 md.update(iodict)
1267 md.update(iodict)
1268
1268
1269 if rcontent['status'] == 'ok':
1269 if rcontent['status'] == 'ok':
1270 res,buffers = util.unserialize_object(buffers)
1270 res,buffers = util.unserialize_object(buffers)
1271 else:
1271 else:
1272 print rcontent
1272 print rcontent
1273 res = self._unwrap_exception(rcontent)
1273 res = self._unwrap_exception(rcontent)
1274 failures.append(res)
1274 failures.append(res)
1275
1275
1276 self.results[msg_id] = res
1276 self.results[msg_id] = res
1277 content[msg_id] = res
1277 content[msg_id] = res
1278
1278
1279 if len(theids) == 1 and failures:
1279 if len(theids) == 1 and failures:
1280 raise failures[0]
1280 raise failures[0]
1281
1281
1282 error.collect_exceptions(failures, "result_status")
1282 error.collect_exceptions(failures, "result_status")
1283 return content
1283 return content
1284
1284
1285 @spin_first
1285 @spin_first
1286 def queue_status(self, targets='all', verbose=False):
1286 def queue_status(self, targets='all', verbose=False):
1287 """Fetch the status of engine queues.
1287 """Fetch the status of engine queues.
1288
1288
1289 Parameters
1289 Parameters
1290 ----------
1290 ----------
1291
1291
1292 targets : int/str/list of ints/strs
1292 targets : int/str/list of ints/strs
1293 the engines whose states are to be queried.
1293 the engines whose states are to be queried.
1294 default : all
1294 default : all
@@ -1310,31 +1310,31 b' class Client(HasTraits):'
1310 return content[targets]
1310 return content[targets]
1311 else:
1311 else:
1312 return content
1312 return content
1313
1313
1314 @spin_first
1314 @spin_first
1315 def purge_results(self, jobs=[], targets=[]):
1315 def purge_results(self, jobs=[], targets=[]):
1316 """Tell the Hub to forget results.
1316 """Tell the Hub to forget results.
1317
1317
1318 Individual results can be purged by msg_id, or the entire
1318 Individual results can be purged by msg_id, or the entire
1319 history of specific targets can be purged.
1319 history of specific targets can be purged.
1320
1320
1321 Use `purge_results('all')` to scrub everything from the Hub's db.
1321 Use `purge_results('all')` to scrub everything from the Hub's db.
1322
1322
1323 Parameters
1323 Parameters
1324 ----------
1324 ----------
1325
1325
1326 jobs : str or list of str or AsyncResult objects
1326 jobs : str or list of str or AsyncResult objects
1327 the msg_ids whose results should be forgotten.
1327 the msg_ids whose results should be forgotten.
1328 targets : int/str/list of ints/strs
1328 targets : int/str/list of ints/strs
1329 The targets, by int_id, whose entire history is to be purged.
1329 The targets, by int_id, whose entire history is to be purged.
1330
1330
1331 default : None
1331 default : None
1332 """
1332 """
1333 if not targets and not jobs:
1333 if not targets and not jobs:
1334 raise ValueError("Must specify at least one of `targets` and `jobs`")
1334 raise ValueError("Must specify at least one of `targets` and `jobs`")
1335 if targets:
1335 if targets:
1336 targets = self._build_targets(targets)[1]
1336 targets = self._build_targets(targets)[1]
1337
1337
1338 # construct msg_ids from jobs
1338 # construct msg_ids from jobs
1339 if jobs == 'all':
1339 if jobs == 'all':
1340 msg_ids = jobs
1340 msg_ids = jobs
@@ -1363,23 +1363,23 b' class Client(HasTraits):'
1363 @spin_first
1363 @spin_first
1364 def hub_history(self):
1364 def hub_history(self):
1365 """Get the Hub's history
1365 """Get the Hub's history
1366
1366
1367 Just like the Client, the Hub has a history, which is a list of msg_ids.
1367 Just like the Client, the Hub has a history, which is a list of msg_ids.
1368 This will contain the history of all clients, and, depending on configuration,
1368 This will contain the history of all clients, and, depending on configuration,
1369 may contain history across multiple cluster sessions.
1369 may contain history across multiple cluster sessions.
1370
1370
1371 Any msg_id returned here is a valid argument to `get_result`.
1371 Any msg_id returned here is a valid argument to `get_result`.
1372
1372
1373 Returns
1373 Returns
1374 -------
1374 -------
1375
1375
1376 msg_ids : list of strs
1376 msg_ids : list of strs
1377 list of all msg_ids, ordered by task submission time.
1377 list of all msg_ids, ordered by task submission time.
1378 """
1378 """
1379
1379
1380 self.session.send(self._query_socket, "history_request", content={})
1380 self.session.send(self._query_socket, "history_request", content={})
1381 idents, msg = self.session.recv(self._query_socket, 0)
1381 idents, msg = self.session.recv(self._query_socket, 0)
1382
1382
1383 if self.debug:
1383 if self.debug:
1384 pprint(msg)
1384 pprint(msg)
1385 content = msg['content']
1385 content = msg['content']
@@ -1391,12 +1391,12 b' class Client(HasTraits):'
1391 @spin_first
1391 @spin_first
1392 def db_query(self, query, keys=None):
1392 def db_query(self, query, keys=None):
1393 """Query the Hub's TaskRecord database
1393 """Query the Hub's TaskRecord database
1394
1394
1395 This will return a list of task record dicts that match `query`
1395 This will return a list of task record dicts that match `query`
1396
1396
1397 Parameters
1397 Parameters
1398 ----------
1398 ----------
1399
1399
1400 query : mongodb query dict
1400 query : mongodb query dict
1401 The search dict. See mongodb query docs for details.
1401 The search dict. See mongodb query docs for details.
1402 keys : list of strs [optional]
1402 keys : list of strs [optional]
@@ -1413,9 +1413,9 b' class Client(HasTraits):'
1413 content = msg['content']
1413 content = msg['content']
1414 if content['status'] != 'ok':
1414 if content['status'] != 'ok':
1415 raise self._unwrap_exception(content)
1415 raise self._unwrap_exception(content)
1416
1416
1417 records = content['records']
1417 records = content['records']
1418
1418
1419 buffer_lens = content['buffer_lens']
1419 buffer_lens = content['buffer_lens']
1420 result_buffer_lens = content['result_buffer_lens']
1420 result_buffer_lens = content['result_buffer_lens']
1421 buffers = msg['buffers']
1421 buffers = msg['buffers']
@@ -1429,7 +1429,7 b' class Client(HasTraits):'
1429 if has_rbufs:
1429 if has_rbufs:
1430 blen = result_buffer_lens[i]
1430 blen = result_buffer_lens[i]
1431 rec['result_buffers'], buffers = buffers[:blen],buffers[blen:]
1431 rec['result_buffers'], buffers = buffers[:blen],buffers[blen:]
1432
1432
1433 return records
1433 return records
1434
1434
1435 __all__ = [ 'Client' ]
1435 __all__ = [ 'Client' ]
@@ -33,14 +33,14 b' from .asyncresult import AsyncMapResult'
33 @skip_doctest
33 @skip_doctest
34 def remote(view, block=None, **flags):
34 def remote(view, block=None, **flags):
35 """Turn a function into a remote function.
35 """Turn a function into a remote function.
36
36
37 This method can be used for map:
37 This method can be used for map:
38
38
39 In [1]: @remote(view,block=True)
39 In [1]: @remote(view,block=True)
40 ...: def func(a):
40 ...: def func(a):
41 ...: pass
41 ...: pass
42 """
42 """
43
43
44 def remote_function(f):
44 def remote_function(f):
45 return RemoteFunction(view, f, block=block, **flags)
45 return RemoteFunction(view, f, block=block, **flags)
46 return remote_function
46 return remote_function
@@ -48,14 +48,14 b' def remote(view, block=None, **flags):'
48 @skip_doctest
48 @skip_doctest
49 def parallel(view, dist='b', block=None, **flags):
49 def parallel(view, dist='b', block=None, **flags):
50 """Turn a function into a parallel remote function.
50 """Turn a function into a parallel remote function.
51
51
52 This method can be used for map:
52 This method can be used for map:
53
53
54 In [1]: @parallel(view, block=True)
54 In [1]: @parallel(view, block=True)
55 ...: def func(a):
55 ...: def func(a):
56 ...: pass
56 ...: pass
57 """
57 """
58
58
59 def parallel_function(f):
59 def parallel_function(f):
60 return ParallelFunction(view, f, dist=dist, block=block, **flags)
60 return ParallelFunction(view, f, dist=dist, block=block, **flags)
61 return parallel_function
61 return parallel_function
@@ -66,10 +66,10 b" def parallel(view, dist='b', block=None, **flags):"
66
66
67 class RemoteFunction(object):
67 class RemoteFunction(object):
68 """Turn an existing function into a remote function.
68 """Turn an existing function into a remote function.
69
69
70 Parameters
70 Parameters
71 ----------
71 ----------
72
72
73 view : View instance
73 view : View instance
74 The view to be used for execution
74 The view to be used for execution
75 f : callable
75 f : callable
@@ -77,37 +77,37 b' class RemoteFunction(object):'
77 block : bool [default: None]
77 block : bool [default: None]
78 Whether to wait for results or not. The default behavior is
78 Whether to wait for results or not. The default behavior is
79 to use the current `block` attribute of `view`
79 to use the current `block` attribute of `view`
80
80
81 **flags : remaining kwargs are passed to View.temp_flags
81 **flags : remaining kwargs are passed to View.temp_flags
82 """
82 """
83
83
84 view = None # the remote connection
84 view = None # the remote connection
85 func = None # the wrapped function
85 func = None # the wrapped function
86 block = None # whether to block
86 block = None # whether to block
87 flags = None # dict of extra kwargs for temp_flags
87 flags = None # dict of extra kwargs for temp_flags
88
88
89 def __init__(self, view, f, block=None, **flags):
89 def __init__(self, view, f, block=None, **flags):
90 self.view = view
90 self.view = view
91 self.func = f
91 self.func = f
92 self.block=block
92 self.block=block
93 self.flags=flags
93 self.flags=flags
94
94
95 def __call__(self, *args, **kwargs):
95 def __call__(self, *args, **kwargs):
96 block = self.view.block if self.block is None else self.block
96 block = self.view.block if self.block is None else self.block
97 with self.view.temp_flags(block=block, **self.flags):
97 with self.view.temp_flags(block=block, **self.flags):
98 return self.view.apply(self.func, *args, **kwargs)
98 return self.view.apply(self.func, *args, **kwargs)
99
99
100
100
101 class ParallelFunction(RemoteFunction):
101 class ParallelFunction(RemoteFunction):
102 """Class for mapping a function to sequences.
102 """Class for mapping a function to sequences.
103
103
104 This will distribute the sequences according the a mapper, and call
104 This will distribute the sequences according the a mapper, and call
105 the function on each sub-sequence. If called via map, then the function
105 the function on each sub-sequence. If called via map, then the function
106 will be called once on each element, rather that each sub-sequence.
106 will be called once on each element, rather that each sub-sequence.
107
107
108 Parameters
108 Parameters
109 ----------
109 ----------
110
110
111 view : View instance
111 view : View instance
112 The view to be used for execution
112 The view to be used for execution
113 f : callable
113 f : callable
@@ -124,17 +124,17 b' class ParallelFunction(RemoteFunction):'
124 The size of chunk to use when breaking up sequences in a load-balanced manner
124 The size of chunk to use when breaking up sequences in a load-balanced manner
125 **flags : remaining kwargs are passed to View.temp_flags
125 **flags : remaining kwargs are passed to View.temp_flags
126 """
126 """
127
127
128 chunksize=None
128 chunksize=None
129 mapObject=None
129 mapObject=None
130
130
131 def __init__(self, view, f, dist='b', block=None, chunksize=None, **flags):
131 def __init__(self, view, f, dist='b', block=None, chunksize=None, **flags):
132 super(ParallelFunction, self).__init__(view, f, block=block, **flags)
132 super(ParallelFunction, self).__init__(view, f, block=block, **flags)
133 self.chunksize = chunksize
133 self.chunksize = chunksize
134
134
135 mapClass = Map.dists[dist]
135 mapClass = Map.dists[dist]
136 self.mapObject = mapClass()
136 self.mapObject = mapClass()
137
137
138 def __call__(self, *sequences):
138 def __call__(self, *sequences):
139 # check that the length of sequences match
139 # check that the length of sequences match
140 len_0 = len(sequences[0])
140 len_0 = len(sequences[0])
@@ -155,7 +155,7 b' class ParallelFunction(RemoteFunction):'
155 # multiplexed:
155 # multiplexed:
156 targets = self.view.targets
156 targets = self.view.targets
157 nparts = len(targets)
157 nparts = len(targets)
158
158
159 msg_ids = []
159 msg_ids = []
160 # my_f = lambda *a: map(self.func, *a)
160 # my_f = lambda *a: map(self.func, *a)
161 client = self.view.client
161 client = self.view.client
@@ -169,7 +169,7 b' class ParallelFunction(RemoteFunction):'
169 args.append(part)
169 args.append(part)
170 if not args:
170 if not args:
171 continue
171 continue
172
172
173 # print (args)
173 # print (args)
174 if hasattr(self, '_map'):
174 if hasattr(self, '_map'):
175 if sys.version_info[0] >= 3:
175 if sys.version_info[0] >= 3:
@@ -179,15 +179,15 b' class ParallelFunction(RemoteFunction):'
179 args = [self.func]+args
179 args = [self.func]+args
180 else:
180 else:
181 f=self.func
181 f=self.func
182
182
183 view = self.view if balanced else client[t]
183 view = self.view if balanced else client[t]
184 with view.temp_flags(block=False, **self.flags):
184 with view.temp_flags(block=False, **self.flags):
185 ar = view.apply(f, *args)
185 ar = view.apply(f, *args)
186
186
187 msg_ids.append(ar.msg_ids[0])
187 msg_ids.append(ar.msg_ids[0])
188
188
189 r = AsyncMapResult(self.view.client, msg_ids, self.mapObject, fname=self.func.__name__)
189 r = AsyncMapResult(self.view.client, msg_ids, self.mapObject, fname=self.func.__name__)
190
190
191 if self.block:
191 if self.block:
192 try:
192 try:
193 return r.get()
193 return r.get()
@@ -195,9 +195,9 b' class ParallelFunction(RemoteFunction):'
195 return r
195 return r
196 else:
196 else:
197 return r
197 return r
198
198
199 def map(self, *sequences):
199 def map(self, *sequences):
200 """call a function on each element of a sequence remotely.
200 """call a function on each element of a sequence remotely.
201 This should behave very much like the builtin map, but return an AsyncMapResult
201 This should behave very much like the builtin map, but return an AsyncMapResult
202 if self.block is False.
202 if self.block is False.
203 """
203 """
@@ -76,56 +76,56 b' def spin_after(f, self, *args, **kwargs):'
76 @skip_doctest
76 @skip_doctest
77 class View(HasTraits):
77 class View(HasTraits):
78 """Base View class for more convenint apply(f,*args,**kwargs) syntax via attributes.
78 """Base View class for more convenint apply(f,*args,**kwargs) syntax via attributes.
79
79
80 Don't use this class, use subclasses.
80 Don't use this class, use subclasses.
81
81
82 Methods
82 Methods
83 -------
83 -------
84
84
85 spin
85 spin
86 flushes incoming results and registration state changes
86 flushes incoming results and registration state changes
87 control methods spin, and requesting `ids` also ensures up to date
87 control methods spin, and requesting `ids` also ensures up to date
88
88
89 wait
89 wait
90 wait on one or more msg_ids
90 wait on one or more msg_ids
91
91
92 execution methods
92 execution methods
93 apply
93 apply
94 legacy: execute, run
94 legacy: execute, run
95
95
96 data movement
96 data movement
97 push, pull, scatter, gather
97 push, pull, scatter, gather
98
98
99 query methods
99 query methods
100 get_result, queue_status, purge_results, result_status
100 get_result, queue_status, purge_results, result_status
101
101
102 control methods
102 control methods
103 abort, shutdown
103 abort, shutdown
104
104
105 """
105 """
106 # flags
106 # flags
107 block=Bool(False)
107 block=Bool(False)
108 track=Bool(True)
108 track=Bool(True)
109 targets = Any()
109 targets = Any()
110
110
111 history=List()
111 history=List()
112 outstanding = Set()
112 outstanding = Set()
113 results = Dict()
113 results = Dict()
114 client = Instance('IPython.parallel.Client')
114 client = Instance('IPython.parallel.Client')
115
115
116 _socket = Instance('zmq.Socket')
116 _socket = Instance('zmq.Socket')
117 _flag_names = List(['targets', 'block', 'track'])
117 _flag_names = List(['targets', 'block', 'track'])
118 _targets = Any()
118 _targets = Any()
119 _idents = Any()
119 _idents = Any()
120
120
121 def __init__(self, client=None, socket=None, **flags):
121 def __init__(self, client=None, socket=None, **flags):
122 super(View, self).__init__(client=client, _socket=socket)
122 super(View, self).__init__(client=client, _socket=socket)
123 self.block = client.block
123 self.block = client.block
124
124
125 self.set_flags(**flags)
125 self.set_flags(**flags)
126
126
127 assert not self.__class__ is View, "Don't use base View objects, use subclasses"
127 assert not self.__class__ is View, "Don't use base View objects, use subclasses"
128
128
129
129
130 def __repr__(self):
130 def __repr__(self):
131 strtargets = str(self.targets)
131 strtargets = str(self.targets)
@@ -135,17 +135,17 b' class View(HasTraits):'
135
135
136 def set_flags(self, **kwargs):
136 def set_flags(self, **kwargs):
137 """set my attribute flags by keyword.
137 """set my attribute flags by keyword.
138
138
139 Views determine behavior with a few attributes (`block`, `track`, etc.).
139 Views determine behavior with a few attributes (`block`, `track`, etc.).
140 These attributes can be set all at once by name with this method.
140 These attributes can be set all at once by name with this method.
141
141
142 Parameters
142 Parameters
143 ----------
143 ----------
144
144
145 block : bool
145 block : bool
146 whether to wait for results
146 whether to wait for results
147 track : bool
147 track : bool
148 whether to create a MessageTracker to allow the user to
148 whether to create a MessageTracker to allow the user to
149 safely edit after arrays and buffers during non-copying
149 safely edit after arrays and buffers during non-copying
150 sends.
150 sends.
151 """
151 """
@@ -154,16 +154,16 b' class View(HasTraits):'
154 raise KeyError("Invalid name: %r"%name)
154 raise KeyError("Invalid name: %r"%name)
155 else:
155 else:
156 setattr(self, name, value)
156 setattr(self, name, value)
157
157
158 @contextmanager
158 @contextmanager
159 def temp_flags(self, **kwargs):
159 def temp_flags(self, **kwargs):
160 """temporarily set flags, for use in `with` statements.
160 """temporarily set flags, for use in `with` statements.
161
161
162 See set_flags for permanent setting of flags
162 See set_flags for permanent setting of flags
163
163
164 Examples
164 Examples
165 --------
165 --------
166
166
167 >>> view.track=False
167 >>> view.track=False
168 ...
168 ...
169 >>> with view.temp_flags(track=True):
169 >>> with view.temp_flags(track=True):
@@ -171,7 +171,7 b' class View(HasTraits):'
171 ... ar.tracker.wait() # wait for send to finish
171 ... ar.tracker.wait() # wait for send to finish
172 >>> view.track
172 >>> view.track
173 False
173 False
174
174
175 """
175 """
176 # preflight: save flags, and set temporaries
176 # preflight: save flags, and set temporaries
177 saved_flags = {}
177 saved_flags = {}
@@ -184,23 +184,23 b' class View(HasTraits):'
184 finally:
184 finally:
185 # postflight: restore saved flags
185 # postflight: restore saved flags
186 self.set_flags(**saved_flags)
186 self.set_flags(**saved_flags)
187
187
188
188
189 #----------------------------------------------------------------
189 #----------------------------------------------------------------
190 # apply
190 # apply
191 #----------------------------------------------------------------
191 #----------------------------------------------------------------
192
192
193 @sync_results
193 @sync_results
194 @save_ids
194 @save_ids
195 def _really_apply(self, f, args, kwargs, block=None, **options):
195 def _really_apply(self, f, args, kwargs, block=None, **options):
196 """wrapper for client.send_apply_message"""
196 """wrapper for client.send_apply_message"""
197 raise NotImplementedError("Implement in subclasses")
197 raise NotImplementedError("Implement in subclasses")
198
198
199 def apply(self, f, *args, **kwargs):
199 def apply(self, f, *args, **kwargs):
200 """calls f(*args, **kwargs) on remote engines, returning the result.
200 """calls f(*args, **kwargs) on remote engines, returning the result.
201
201
202 This method sets all apply flags via this View's attributes.
202 This method sets all apply flags via this View's attributes.
203
203
204 if self.block is False:
204 if self.block is False:
205 returns AsyncResult
205 returns AsyncResult
206 else:
206 else:
@@ -210,7 +210,7 b' class View(HasTraits):'
210
210
211 def apply_async(self, f, *args, **kwargs):
211 def apply_async(self, f, *args, **kwargs):
212 """calls f(*args, **kwargs) on remote engines in a nonblocking manner.
212 """calls f(*args, **kwargs) on remote engines in a nonblocking manner.
213
213
214 returns AsyncResult
214 returns AsyncResult
215 """
215 """
216 return self._really_apply(f, args, kwargs, block=False)
216 return self._really_apply(f, args, kwargs, block=False)
@@ -219,7 +219,7 b' class View(HasTraits):'
219 def apply_sync(self, f, *args, **kwargs):
219 def apply_sync(self, f, *args, **kwargs):
220 """calls f(*args, **kwargs) on remote engines in a blocking manner,
220 """calls f(*args, **kwargs) on remote engines in a blocking manner,
221 returning the result.
221 returning the result.
222
222
223 returns: actual result of f(*args, **kwargs)
223 returns: actual result of f(*args, **kwargs)
224 """
224 """
225 return self._really_apply(f, args, kwargs, block=True)
225 return self._really_apply(f, args, kwargs, block=True)
@@ -231,14 +231,14 b' class View(HasTraits):'
231 def spin(self):
231 def spin(self):
232 """spin the client, and sync"""
232 """spin the client, and sync"""
233 self.client.spin()
233 self.client.spin()
234
234
235 @sync_results
235 @sync_results
236 def wait(self, jobs=None, timeout=-1):
236 def wait(self, jobs=None, timeout=-1):
237 """waits on one or more `jobs`, for up to `timeout` seconds.
237 """waits on one or more `jobs`, for up to `timeout` seconds.
238
238
239 Parameters
239 Parameters
240 ----------
240 ----------
241
241
242 jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects
242 jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects
243 ints are indices to self.history
243 ints are indices to self.history
244 strs are msg_ids
244 strs are msg_ids
@@ -246,23 +246,23 b' class View(HasTraits):'
246 timeout : float
246 timeout : float
247 a time in seconds, after which to give up.
247 a time in seconds, after which to give up.
248 default is -1, which means no timeout
248 default is -1, which means no timeout
249
249
250 Returns
250 Returns
251 -------
251 -------
252
252
253 True : when all msg_ids are done
253 True : when all msg_ids are done
254 False : timeout reached, some msg_ids still outstanding
254 False : timeout reached, some msg_ids still outstanding
255 """
255 """
256 if jobs is None:
256 if jobs is None:
257 jobs = self.history
257 jobs = self.history
258 return self.client.wait(jobs, timeout)
258 return self.client.wait(jobs, timeout)
259
259
260 def abort(self, jobs=None, targets=None, block=None):
260 def abort(self, jobs=None, targets=None, block=None):
261 """Abort jobs on my engines.
261 """Abort jobs on my engines.
262
262
263 Parameters
263 Parameters
264 ----------
264 ----------
265
265
266 jobs : None, str, list of strs, optional
266 jobs : None, str, list of strs, optional
267 if None: abort all jobs.
267 if None: abort all jobs.
268 else: abort specific msg_id(s).
268 else: abort specific msg_id(s).
@@ -275,13 +275,13 b' class View(HasTraits):'
275 """Fetch the Queue status of my engines"""
275 """Fetch the Queue status of my engines"""
276 targets = targets if targets is not None else self.targets
276 targets = targets if targets is not None else self.targets
277 return self.client.queue_status(targets=targets, verbose=verbose)
277 return self.client.queue_status(targets=targets, verbose=verbose)
278
278
279 def purge_results(self, jobs=[], targets=[]):
279 def purge_results(self, jobs=[], targets=[]):
280 """Instruct the controller to forget specific results."""
280 """Instruct the controller to forget specific results."""
281 if targets is None or targets == 'all':
281 if targets is None or targets == 'all':
282 targets = self.targets
282 targets = self.targets
283 return self.client.purge_results(jobs=jobs, targets=targets)
283 return self.client.purge_results(jobs=jobs, targets=targets)
284
284
285 def shutdown(self, targets=None, restart=False, hub=False, block=None):
285 def shutdown(self, targets=None, restart=False, hub=False, block=None):
286 """Terminates one or more engine processes, optionally including the hub.
286 """Terminates one or more engine processes, optionally including the hub.
287 """
287 """
@@ -289,15 +289,15 b' class View(HasTraits):'
289 if targets is None or targets == 'all':
289 if targets is None or targets == 'all':
290 targets = self.targets
290 targets = self.targets
291 return self.client.shutdown(targets=targets, restart=restart, hub=hub, block=block)
291 return self.client.shutdown(targets=targets, restart=restart, hub=hub, block=block)
292
292
293 @spin_after
293 @spin_after
294 def get_result(self, indices_or_msg_ids=None):
294 def get_result(self, indices_or_msg_ids=None):
295 """return one or more results, specified by history index or msg_id.
295 """return one or more results, specified by history index or msg_id.
296
296
297 See client.get_result for details.
297 See client.get_result for details.
298
298
299 """
299 """
300
300
301 if indices_or_msg_ids is None:
301 if indices_or_msg_ids is None:
302 indices_or_msg_ids = -1
302 indices_or_msg_ids = -1
303 if isinstance(indices_or_msg_ids, int):
303 if isinstance(indices_or_msg_ids, int):
@@ -308,57 +308,57 b' class View(HasTraits):'
308 if isinstance(index, int):
308 if isinstance(index, int):
309 indices_or_msg_ids[i] = self.history[index]
309 indices_or_msg_ids[i] = self.history[index]
310 return self.client.get_result(indices_or_msg_ids)
310 return self.client.get_result(indices_or_msg_ids)
311
311
312 #-------------------------------------------------------------------
312 #-------------------------------------------------------------------
313 # Map
313 # Map
314 #-------------------------------------------------------------------
314 #-------------------------------------------------------------------
315
315
316 def map(self, f, *sequences, **kwargs):
316 def map(self, f, *sequences, **kwargs):
317 """override in subclasses"""
317 """override in subclasses"""
318 raise NotImplementedError
318 raise NotImplementedError
319
319
320 def map_async(self, f, *sequences, **kwargs):
320 def map_async(self, f, *sequences, **kwargs):
321 """Parallel version of builtin `map`, using this view's engines.
321 """Parallel version of builtin `map`, using this view's engines.
322
322
323 This is equivalent to map(...block=False)
323 This is equivalent to map(...block=False)
324
324
325 See `self.map` for details.
325 See `self.map` for details.
326 """
326 """
327 if 'block' in kwargs:
327 if 'block' in kwargs:
328 raise TypeError("map_async doesn't take a `block` keyword argument.")
328 raise TypeError("map_async doesn't take a `block` keyword argument.")
329 kwargs['block'] = False
329 kwargs['block'] = False
330 return self.map(f,*sequences,**kwargs)
330 return self.map(f,*sequences,**kwargs)
331
331
332 def map_sync(self, f, *sequences, **kwargs):
332 def map_sync(self, f, *sequences, **kwargs):
333 """Parallel version of builtin `map`, using this view's engines.
333 """Parallel version of builtin `map`, using this view's engines.
334
334
335 This is equivalent to map(...block=True)
335 This is equivalent to map(...block=True)
336
336
337 See `self.map` for details.
337 See `self.map` for details.
338 """
338 """
339 if 'block' in kwargs:
339 if 'block' in kwargs:
340 raise TypeError("map_sync doesn't take a `block` keyword argument.")
340 raise TypeError("map_sync doesn't take a `block` keyword argument.")
341 kwargs['block'] = True
341 kwargs['block'] = True
342 return self.map(f,*sequences,**kwargs)
342 return self.map(f,*sequences,**kwargs)
343
343
344 def imap(self, f, *sequences, **kwargs):
344 def imap(self, f, *sequences, **kwargs):
345 """Parallel version of `itertools.imap`.
345 """Parallel version of `itertools.imap`.
346
346
347 See `self.map` for details.
347 See `self.map` for details.
348
348
349 """
349 """
350
350
351 return iter(self.map_async(f,*sequences, **kwargs))
351 return iter(self.map_async(f,*sequences, **kwargs))
352
352
353 #-------------------------------------------------------------------
353 #-------------------------------------------------------------------
354 # Decorators
354 # Decorators
355 #-------------------------------------------------------------------
355 #-------------------------------------------------------------------
356
356
357 def remote(self, block=True, **flags):
357 def remote(self, block=True, **flags):
358 """Decorator for making a RemoteFunction"""
358 """Decorator for making a RemoteFunction"""
359 block = self.block if block is None else block
359 block = self.block if block is None else block
360 return remote(self, block=block, **flags)
360 return remote(self, block=block, **flags)
361
361
362 def parallel(self, dist='b', block=None, **flags):
362 def parallel(self, dist='b', block=None, **flags):
363 """Decorator for making a ParallelFunction"""
363 """Decorator for making a ParallelFunction"""
364 block = self.block if block is None else block
364 block = self.block if block is None else block
@@ -367,45 +367,45 b' class View(HasTraits):'
367 @skip_doctest
367 @skip_doctest
368 class DirectView(View):
368 class DirectView(View):
369 """Direct Multiplexer View of one or more engines.
369 """Direct Multiplexer View of one or more engines.
370
370
371 These are created via indexed access to a client:
371 These are created via indexed access to a client:
372
372
373 >>> dv_1 = client[1]
373 >>> dv_1 = client[1]
374 >>> dv_all = client[:]
374 >>> dv_all = client[:]
375 >>> dv_even = client[::2]
375 >>> dv_even = client[::2]
376 >>> dv_some = client[1:3]
376 >>> dv_some = client[1:3]
377
377
378 This object provides dictionary access to engine namespaces:
378 This object provides dictionary access to engine namespaces:
379
379
380 # push a=5:
380 # push a=5:
381 >>> dv['a'] = 5
381 >>> dv['a'] = 5
382 # pull 'foo':
382 # pull 'foo':
383 >>> db['foo']
383 >>> db['foo']
384
384
385 """
385 """
386
386
387 def __init__(self, client=None, socket=None, targets=None):
387 def __init__(self, client=None, socket=None, targets=None):
388 super(DirectView, self).__init__(client=client, socket=socket, targets=targets)
388 super(DirectView, self).__init__(client=client, socket=socket, targets=targets)
389
389
390 @property
390 @property
391 def importer(self):
391 def importer(self):
392 """sync_imports(local=True) as a property.
392 """sync_imports(local=True) as a property.
393
393
394 See sync_imports for details.
394 See sync_imports for details.
395
395
396 """
396 """
397 return self.sync_imports(True)
397 return self.sync_imports(True)
398
398
399 @contextmanager
399 @contextmanager
400 def sync_imports(self, local=True):
400 def sync_imports(self, local=True):
401 """Context Manager for performing simultaneous local and remote imports.
401 """Context Manager for performing simultaneous local and remote imports.
402
402
403 'import x as y' will *not* work. The 'as y' part will simply be ignored.
403 'import x as y' will *not* work. The 'as y' part will simply be ignored.
404
404
405 >>> with view.sync_imports():
405 >>> with view.sync_imports():
406 ... from numpy import recarray
406 ... from numpy import recarray
407 importing recarray from numpy on engine(s)
407 importing recarray from numpy on engine(s)
408
408
409 """
409 """
410 import __builtin__
410 import __builtin__
411 local_import = __builtin__.__import__
411 local_import = __builtin__.__import__
@@ -424,7 +424,7 b' class DirectView(View):'
424 user_ns[key] = getattr(mod, key)
424 user_ns[key] = getattr(mod, key)
425 else:
425 else:
426 user_ns[name] = sys.modules[name]
426 user_ns[name] = sys.modules[name]
427
427
428 def view_import(name, globals={}, locals={}, fromlist=[], level=-1):
428 def view_import(name, globals={}, locals={}, fromlist=[], level=-1):
429 """the drop-in replacement for __import__, that optionally imports
429 """the drop-in replacement for __import__, that optionally imports
430 locally as well.
430 locally as well.
@@ -432,19 +432,19 b' class DirectView(View):'
432 # don't override nested imports
432 # don't override nested imports
433 save_import = __builtin__.__import__
433 save_import = __builtin__.__import__
434 __builtin__.__import__ = local_import
434 __builtin__.__import__ = local_import
435
435
436 if imp.lock_held():
436 if imp.lock_held():
437 # this is a side-effect import, don't do it remotely, or even
437 # this is a side-effect import, don't do it remotely, or even
438 # ignore the local effects
438 # ignore the local effects
439 return local_import(name, globals, locals, fromlist, level)
439 return local_import(name, globals, locals, fromlist, level)
440
440
441 imp.acquire_lock()
441 imp.acquire_lock()
442 if local:
442 if local:
443 mod = local_import(name, globals, locals, fromlist, level)
443 mod = local_import(name, globals, locals, fromlist, level)
444 else:
444 else:
445 raise NotImplementedError("remote-only imports not yet implemented")
445 raise NotImplementedError("remote-only imports not yet implemented")
446 imp.release_lock()
446 imp.release_lock()
447
447
448 key = name+':'+','.join(fromlist or [])
448 key = name+':'+','.join(fromlist or [])
449 if level == -1 and key not in modules:
449 if level == -1 and key not in modules:
450 modules.add(key)
450 modules.add(key)
@@ -455,9 +455,9 b' class DirectView(View):'
455 results.append(self.apply_async(remote_import, name, fromlist, level))
455 results.append(self.apply_async(remote_import, name, fromlist, level))
456 # restore override
456 # restore override
457 __builtin__.__import__ = save_import
457 __builtin__.__import__ = save_import
458
458
459 return mod
459 return mod
460
460
461 # override __import__
461 # override __import__
462 __builtin__.__import__ = view_import
462 __builtin__.__import__ = view_import
463 try:
463 try:
@@ -470,38 +470,38 b' class DirectView(View):'
470 finally:
470 finally:
471 # always restore __import__
471 # always restore __import__
472 __builtin__.__import__ = local_import
472 __builtin__.__import__ = local_import
473
473
474 for r in results:
474 for r in results:
475 # raise possible remote ImportErrors here
475 # raise possible remote ImportErrors here
476 r.get()
476 r.get()
477
477
478
478
479 @sync_results
479 @sync_results
480 @save_ids
480 @save_ids
481 def _really_apply(self, f, args=None, kwargs=None, targets=None, block=None, track=None):
481 def _really_apply(self, f, args=None, kwargs=None, targets=None, block=None, track=None):
482 """calls f(*args, **kwargs) on remote engines, returning the result.
482 """calls f(*args, **kwargs) on remote engines, returning the result.
483
483
484 This method sets all of `apply`'s flags via this View's attributes.
484 This method sets all of `apply`'s flags via this View's attributes.
485
485
486 Parameters
486 Parameters
487 ----------
487 ----------
488
488
489 f : callable
489 f : callable
490
490
491 args : list [default: empty]
491 args : list [default: empty]
492
492
493 kwargs : dict [default: empty]
493 kwargs : dict [default: empty]
494
494
495 targets : target list [default: self.targets]
495 targets : target list [default: self.targets]
496 where to run
496 where to run
497 block : bool [default: self.block]
497 block : bool [default: self.block]
498 whether to block
498 whether to block
499 track : bool [default: self.track]
499 track : bool [default: self.track]
500 whether to ask zmq to track the message, for safe non-copying sends
500 whether to ask zmq to track the message, for safe non-copying sends
501
501
502 Returns
502 Returns
503 -------
503 -------
504
504
505 if self.block is False:
505 if self.block is False:
506 returns AsyncResult
506 returns AsyncResult
507 else:
507 else:
@@ -514,7 +514,7 b' class DirectView(View):'
514 block = self.block if block is None else block
514 block = self.block if block is None else block
515 track = self.track if track is None else track
515 track = self.track if track is None else track
516 targets = self.targets if targets is None else targets
516 targets = self.targets if targets is None else targets
517
517
518 _idents = self.client._build_targets(targets)[0]
518 _idents = self.client._build_targets(targets)[0]
519 msg_ids = []
519 msg_ids = []
520 trackers = []
520 trackers = []
@@ -532,31 +532,31 b' class DirectView(View):'
532 except KeyboardInterrupt:
532 except KeyboardInterrupt:
533 pass
533 pass
534 return ar
534 return ar
535
535
536 @spin_after
536 @spin_after
537 def map(self, f, *sequences, **kwargs):
537 def map(self, f, *sequences, **kwargs):
538 """view.map(f, *sequences, block=self.block) => list|AsyncMapResult
538 """view.map(f, *sequences, block=self.block) => list|AsyncMapResult
539
539
540 Parallel version of builtin `map`, using this View's `targets`.
540 Parallel version of builtin `map`, using this View's `targets`.
541
541
542 There will be one task per target, so work will be chunked
542 There will be one task per target, so work will be chunked
543 if the sequences are longer than `targets`.
543 if the sequences are longer than `targets`.
544
544
545 Results can be iterated as they are ready, but will become available in chunks.
545 Results can be iterated as they are ready, but will become available in chunks.
546
546
547 Parameters
547 Parameters
548 ----------
548 ----------
549
549
550 f : callable
550 f : callable
551 function to be mapped
551 function to be mapped
552 *sequences: one or more sequences of matching length
552 *sequences: one or more sequences of matching length
553 the sequences to be distributed and passed to `f`
553 the sequences to be distributed and passed to `f`
554 block : bool
554 block : bool
555 whether to wait for the result or not [default self.block]
555 whether to wait for the result or not [default self.block]
556
556
557 Returns
557 Returns
558 -------
558 -------
559
559
560 if block=False:
560 if block=False:
561 AsyncMapResult
561 AsyncMapResult
562 An object like AsyncResult, but which reassembles the sequence of results
562 An object like AsyncResult, but which reassembles the sequence of results
@@ -566,24 +566,24 b' class DirectView(View):'
566 list
566 list
567 the result of map(f,*sequences)
567 the result of map(f,*sequences)
568 """
568 """
569
569
570 block = kwargs.pop('block', self.block)
570 block = kwargs.pop('block', self.block)
571 for k in kwargs.keys():
571 for k in kwargs.keys():
572 if k not in ['block', 'track']:
572 if k not in ['block', 'track']:
573 raise TypeError("invalid keyword arg, %r"%k)
573 raise TypeError("invalid keyword arg, %r"%k)
574
574
575 assert len(sequences) > 0, "must have some sequences to map onto!"
575 assert len(sequences) > 0, "must have some sequences to map onto!"
576 pf = ParallelFunction(self, f, block=block, **kwargs)
576 pf = ParallelFunction(self, f, block=block, **kwargs)
577 return pf.map(*sequences)
577 return pf.map(*sequences)
578
578
579 def execute(self, code, targets=None, block=None):
579 def execute(self, code, targets=None, block=None):
580 """Executes `code` on `targets` in blocking or nonblocking manner.
580 """Executes `code` on `targets` in blocking or nonblocking manner.
581
581
582 ``execute`` is always `bound` (affects engine namespace)
582 ``execute`` is always `bound` (affects engine namespace)
583
583
584 Parameters
584 Parameters
585 ----------
585 ----------
586
586
587 code : str
587 code : str
588 the code string to be executed
588 the code string to be executed
589 block : bool
589 block : bool
@@ -591,15 +591,15 b' class DirectView(View):'
591 default: self.block
591 default: self.block
592 """
592 """
593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
594
594
595 def run(self, filename, targets=None, block=None):
595 def run(self, filename, targets=None, block=None):
596 """Execute contents of `filename` on my engine(s).
596 """Execute contents of `filename` on my engine(s).
597
597
598 This simply reads the contents of the file and calls `execute`.
598 This simply reads the contents of the file and calls `execute`.
599
599
600 Parameters
600 Parameters
601 ----------
601 ----------
602
602
603 filename : str
603 filename : str
604 The path to the file
604 The path to the file
605 targets : int/str/list of ints/strs
605 targets : int/str/list of ints/strs
@@ -608,34 +608,34 b' class DirectView(View):'
608 block : bool
608 block : bool
609 whether or not to wait until done
609 whether or not to wait until done
610 default: self.block
610 default: self.block
611
611
612 """
612 """
613 with open(filename, 'r') as f:
613 with open(filename, 'r') as f:
614 # add newline in case of trailing indented whitespace
614 # add newline in case of trailing indented whitespace
615 # which will cause SyntaxError
615 # which will cause SyntaxError
616 code = f.read()+'\n'
616 code = f.read()+'\n'
617 return self.execute(code, block=block, targets=targets)
617 return self.execute(code, block=block, targets=targets)
618
618
619 def update(self, ns):
619 def update(self, ns):
620 """update remote namespace with dict `ns`
620 """update remote namespace with dict `ns`
621
621
622 See `push` for details.
622 See `push` for details.
623 """
623 """
624 return self.push(ns, block=self.block, track=self.track)
624 return self.push(ns, block=self.block, track=self.track)
625
625
626 def push(self, ns, targets=None, block=None, track=None):
626 def push(self, ns, targets=None, block=None, track=None):
627 """update remote namespace with dict `ns`
627 """update remote namespace with dict `ns`
628
628
629 Parameters
629 Parameters
630 ----------
630 ----------
631
631
632 ns : dict
632 ns : dict
633 dict of keys with which to update engine namespace(s)
633 dict of keys with which to update engine namespace(s)
634 block : bool [default : self.block]
634 block : bool [default : self.block]
635 whether to wait to be notified of engine receipt
635 whether to wait to be notified of engine receipt
636
636
637 """
637 """
638
638
639 block = block if block is not None else self.block
639 block = block if block is not None else self.block
640 track = track if track is not None else self.track
640 track = track if track is not None else self.track
641 targets = targets if targets is not None else self.targets
641 targets = targets if targets is not None else self.targets
@@ -646,15 +646,15 b' class DirectView(View):'
646
646
647 def get(self, key_s):
647 def get(self, key_s):
648 """get object(s) by `key_s` from remote namespace
648 """get object(s) by `key_s` from remote namespace
649
649
650 see `pull` for details.
650 see `pull` for details.
651 """
651 """
652 # block = block if block is not None else self.block
652 # block = block if block is not None else self.block
653 return self.pull(key_s, block=True)
653 return self.pull(key_s, block=True)
654
654
655 def pull(self, names, targets=None, block=None):
655 def pull(self, names, targets=None, block=None):
656 """get object(s) by `name` from remote namespace
656 """get object(s) by `name` from remote namespace
657
657
658 will return one object if it is a key.
658 will return one object if it is a key.
659 can also take a list of keys, in which case it will return a list of objects.
659 can also take a list of keys, in which case it will return a list of objects.
660 """
660 """
@@ -670,7 +670,7 b' class DirectView(View):'
670 else:
670 else:
671 raise TypeError("names must be strs, not %r"%names)
671 raise TypeError("names must be strs, not %r"%names)
672 return self._really_apply(util._pull, (names,), block=block, targets=targets)
672 return self._really_apply(util._pull, (names,), block=block, targets=targets)
673
673
674 def scatter(self, key, seq, dist='b', flatten=False, targets=None, block=None, track=None):
674 def scatter(self, key, seq, dist='b', flatten=False, targets=None, block=None, track=None):
675 """
675 """
676 Partition a Python sequence and send the partitions to a set of engines.
676 Partition a Python sequence and send the partitions to a set of engines.
@@ -678,7 +678,7 b' class DirectView(View):'
678 block = block if block is not None else self.block
678 block = block if block is not None else self.block
679 track = track if track is not None else self.track
679 track = track if track is not None else self.track
680 targets = targets if targets is not None else self.targets
680 targets = targets if targets is not None else self.targets
681
681
682 mapObject = Map.dists[dist]()
682 mapObject = Map.dists[dist]()
683 nparts = len(targets)
683 nparts = len(targets)
684 msg_ids = []
684 msg_ids = []
@@ -693,18 +693,18 b' class DirectView(View):'
693 msg_ids.extend(r.msg_ids)
693 msg_ids.extend(r.msg_ids)
694 if track:
694 if track:
695 trackers.append(r._tracker)
695 trackers.append(r._tracker)
696
696
697 if track:
697 if track:
698 tracker = zmq.MessageTracker(*trackers)
698 tracker = zmq.MessageTracker(*trackers)
699 else:
699 else:
700 tracker = None
700 tracker = None
701
701
702 r = AsyncResult(self.client, msg_ids, fname='scatter', targets=targets, tracker=tracker)
702 r = AsyncResult(self.client, msg_ids, fname='scatter', targets=targets, tracker=tracker)
703 if block:
703 if block:
704 r.wait()
704 r.wait()
705 else:
705 else:
706 return r
706 return r
707
707
708 @sync_results
708 @sync_results
709 @save_ids
709 @save_ids
710 def gather(self, key, dist='b', targets=None, block=None):
710 def gather(self, key, dist='b', targets=None, block=None):
@@ -715,53 +715,53 b' class DirectView(View):'
715 targets = targets if targets is not None else self.targets
715 targets = targets if targets is not None else self.targets
716 mapObject = Map.dists[dist]()
716 mapObject = Map.dists[dist]()
717 msg_ids = []
717 msg_ids = []
718
718
719 for index, engineid in enumerate(targets):
719 for index, engineid in enumerate(targets):
720 msg_ids.extend(self.pull(key, block=False, targets=engineid).msg_ids)
720 msg_ids.extend(self.pull(key, block=False, targets=engineid).msg_ids)
721
721
722 r = AsyncMapResult(self.client, msg_ids, mapObject, fname='gather')
722 r = AsyncMapResult(self.client, msg_ids, mapObject, fname='gather')
723
723
724 if block:
724 if block:
725 try:
725 try:
726 return r.get()
726 return r.get()
727 except KeyboardInterrupt:
727 except KeyboardInterrupt:
728 pass
728 pass
729 return r
729 return r
730
730
731 def __getitem__(self, key):
731 def __getitem__(self, key):
732 return self.get(key)
732 return self.get(key)
733
733
734 def __setitem__(self,key, value):
734 def __setitem__(self,key, value):
735 self.update({key:value})
735 self.update({key:value})
736
736
737 def clear(self, targets=None, block=False):
737 def clear(self, targets=None, block=False):
738 """Clear the remote namespaces on my engines."""
738 """Clear the remote namespaces on my engines."""
739 block = block if block is not None else self.block
739 block = block if block is not None else self.block
740 targets = targets if targets is not None else self.targets
740 targets = targets if targets is not None else self.targets
741 return self.client.clear(targets=targets, block=block)
741 return self.client.clear(targets=targets, block=block)
742
742
743 def kill(self, targets=None, block=True):
743 def kill(self, targets=None, block=True):
744 """Kill my engines."""
744 """Kill my engines."""
745 block = block if block is not None else self.block
745 block = block if block is not None else self.block
746 targets = targets if targets is not None else self.targets
746 targets = targets if targets is not None else self.targets
747 return self.client.kill(targets=targets, block=block)
747 return self.client.kill(targets=targets, block=block)
748
748
749 #----------------------------------------
749 #----------------------------------------
750 # activate for %px,%autopx magics
750 # activate for %px,%autopx magics
751 #----------------------------------------
751 #----------------------------------------
752 def activate(self):
752 def activate(self):
753 """Make this `View` active for parallel magic commands.
753 """Make this `View` active for parallel magic commands.
754
754
755 IPython has a magic command syntax to work with `MultiEngineClient` objects.
755 IPython has a magic command syntax to work with `MultiEngineClient` objects.
756 In a given IPython session there is a single active one. While
756 In a given IPython session there is a single active one. While
757 there can be many `Views` created and used by the user,
757 there can be many `Views` created and used by the user,
758 there is only one active one. The active `View` is used whenever
758 there is only one active one. The active `View` is used whenever
759 the magic commands %px and %autopx are used.
759 the magic commands %px and %autopx are used.
760
760
761 The activate() method is called on a given `View` to make it
761 The activate() method is called on a given `View` to make it
762 active. Once this has been done, the magic commands can be used.
762 active. Once this has been done, the magic commands can be used.
763 """
763 """
764
764
765 try:
765 try:
766 # This is injected into __builtins__.
766 # This is injected into __builtins__.
767 ip = get_ipython()
767 ip = get_ipython()
@@ -779,34 +779,34 b' class DirectView(View):'
779 @skip_doctest
779 @skip_doctest
780 class LoadBalancedView(View):
780 class LoadBalancedView(View):
781 """An load-balancing View that only executes via the Task scheduler.
781 """An load-balancing View that only executes via the Task scheduler.
782
782
783 Load-balanced views can be created with the client's `view` method:
783 Load-balanced views can be created with the client's `view` method:
784
784
785 >>> v = client.load_balanced_view()
785 >>> v = client.load_balanced_view()
786
786
787 or targets can be specified, to restrict the potential destinations:
787 or targets can be specified, to restrict the potential destinations:
788
788
789 >>> v = client.client.load_balanced_view(([1,3])
789 >>> v = client.client.load_balanced_view(([1,3])
790
790
791 which would restrict loadbalancing to between engines 1 and 3.
791 which would restrict loadbalancing to between engines 1 and 3.
792
792
793 """
793 """
794
794
795 follow=Any()
795 follow=Any()
796 after=Any()
796 after=Any()
797 timeout=CFloat()
797 timeout=CFloat()
798 retries = CInt(0)
798 retries = CInt(0)
799
799
800 _task_scheme = Any()
800 _task_scheme = Any()
801 _flag_names = List(['targets', 'block', 'track', 'follow', 'after', 'timeout', 'retries'])
801 _flag_names = List(['targets', 'block', 'track', 'follow', 'after', 'timeout', 'retries'])
802
802
803 def __init__(self, client=None, socket=None, **flags):
803 def __init__(self, client=None, socket=None, **flags):
804 super(LoadBalancedView, self).__init__(client=client, socket=socket, **flags)
804 super(LoadBalancedView, self).__init__(client=client, socket=socket, **flags)
805 self._task_scheme=client._task_scheme
805 self._task_scheme=client._task_scheme
806
806
807 def _validate_dependency(self, dep):
807 def _validate_dependency(self, dep):
808 """validate a dependency.
808 """validate a dependency.
809
809
810 For use in `set_flags`.
810 For use in `set_flags`.
811 """
811 """
812 if dep is None or isinstance(dep, (basestring, AsyncResult, Dependency)):
812 if dep is None or isinstance(dep, (basestring, AsyncResult, Dependency)):
@@ -825,9 +825,9 b' class LoadBalancedView(View):'
825 return False
825 return False
826 else:
826 else:
827 return False
827 return False
828
828
829 return True
829 return True
830
830
831 def _render_dependency(self, dep):
831 def _render_dependency(self, dep):
832 """helper for building jsonable dependencies from various input forms."""
832 """helper for building jsonable dependencies from various input forms."""
833 if isinstance(dep, Dependency):
833 if isinstance(dep, Dependency):
@@ -842,18 +842,18 b' class LoadBalancedView(View):'
842
842
843 def set_flags(self, **kwargs):
843 def set_flags(self, **kwargs):
844 """set my attribute flags by keyword.
844 """set my attribute flags by keyword.
845
845
846 A View is a wrapper for the Client's apply method, but with attributes
846 A View is a wrapper for the Client's apply method, but with attributes
847 that specify keyword arguments, those attributes can be set by keyword
847 that specify keyword arguments, those attributes can be set by keyword
848 argument with this method.
848 argument with this method.
849
849
850 Parameters
850 Parameters
851 ----------
851 ----------
852
852
853 block : bool
853 block : bool
854 whether to wait for results
854 whether to wait for results
855 track : bool
855 track : bool
856 whether to create a MessageTracker to allow the user to
856 whether to create a MessageTracker to allow the user to
857 safely edit after arrays and buffers during non-copying
857 safely edit after arrays and buffers during non-copying
858 sends.
858 sends.
859
859
@@ -878,7 +878,7 b' class LoadBalancedView(View):'
878 retries : int
878 retries : int
879 Number of times a task will be retried on failure.
879 Number of times a task will be retried on failure.
880 """
880 """
881
881
882 super(LoadBalancedView, self).set_flags(**kwargs)
882 super(LoadBalancedView, self).set_flags(**kwargs)
883 for name in ('follow', 'after'):
883 for name in ('follow', 'after'):
884 if name in kwargs:
884 if name in kwargs:
@@ -895,35 +895,35 b' class LoadBalancedView(View):'
895 if t < 0:
895 if t < 0:
896 raise ValueError("Invalid timeout: %s"%t)
896 raise ValueError("Invalid timeout: %s"%t)
897 self.timeout = t
897 self.timeout = t
898
898
899 @sync_results
899 @sync_results
900 @save_ids
900 @save_ids
901 def _really_apply(self, f, args=None, kwargs=None, block=None, track=None,
901 def _really_apply(self, f, args=None, kwargs=None, block=None, track=None,
902 after=None, follow=None, timeout=None,
902 after=None, follow=None, timeout=None,
903 targets=None, retries=None):
903 targets=None, retries=None):
904 """calls f(*args, **kwargs) on a remote engine, returning the result.
904 """calls f(*args, **kwargs) on a remote engine, returning the result.
905
905
906 This method temporarily sets all of `apply`'s flags for a single call.
906 This method temporarily sets all of `apply`'s flags for a single call.
907
907
908 Parameters
908 Parameters
909 ----------
909 ----------
910
910
911 f : callable
911 f : callable
912
912
913 args : list [default: empty]
913 args : list [default: empty]
914
914
915 kwargs : dict [default: empty]
915 kwargs : dict [default: empty]
916
916
917 block : bool [default: self.block]
917 block : bool [default: self.block]
918 whether to block
918 whether to block
919 track : bool [default: self.track]
919 track : bool [default: self.track]
920 whether to ask zmq to track the message, for safe non-copying sends
920 whether to ask zmq to track the message, for safe non-copying sends
921
921
922 !!!!!! TODO: THE REST HERE !!!!
922 !!!!!! TODO: THE REST HERE !!!!
923
923
924 Returns
924 Returns
925 -------
925 -------
926
926
927 if self.block is False:
927 if self.block is False:
928 returns AsyncResult
928 returns AsyncResult
929 else:
929 else:
@@ -939,7 +939,7 b' class LoadBalancedView(View):'
939 msg += " because the pure ZMQ scheduler cannot handle"
939 msg += " because the pure ZMQ scheduler cannot handle"
940 msg += " disappearing engines."
940 msg += " disappearing engines."
941 raise RuntimeError(msg)
941 raise RuntimeError(msg)
942
942
943 if self._task_scheme == 'pure':
943 if self._task_scheme == 'pure':
944 # pure zmq scheme doesn't support extra features
944 # pure zmq scheme doesn't support extra features
945 msg = "Pure ZMQ scheduler doesn't support the following flags:"
945 msg = "Pure ZMQ scheduler doesn't support the following flags:"
@@ -950,7 +950,7 b' class LoadBalancedView(View):'
950 if isinstance(f, dependent):
950 if isinstance(f, dependent):
951 # soft warn on functional dependencies
951 # soft warn on functional dependencies
952 warnings.warn(msg, RuntimeWarning)
952 warnings.warn(msg, RuntimeWarning)
953
953
954 # build args
954 # build args
955 args = [] if args is None else args
955 args = [] if args is None else args
956 kwargs = {} if kwargs is None else kwargs
956 kwargs = {} if kwargs is None else kwargs
@@ -961,7 +961,7 b' class LoadBalancedView(View):'
961 follow = self.follow if follow is None else follow
961 follow = self.follow if follow is None else follow
962 timeout = self.timeout if timeout is None else timeout
962 timeout = self.timeout if timeout is None else timeout
963 targets = self.targets if targets is None else targets
963 targets = self.targets if targets is None else targets
964
964
965 if not isinstance(retries, int):
965 if not isinstance(retries, int):
966 raise TypeError('retries must be int, not %r'%type(retries))
966 raise TypeError('retries must be int, not %r'%type(retries))
967
967
@@ -971,40 +971,40 b' class LoadBalancedView(View):'
971 idents = self.client._build_targets(targets)[0]
971 idents = self.client._build_targets(targets)[0]
972 # ensure *not* bytes
972 # ensure *not* bytes
973 idents = [ ident.decode() for ident in idents ]
973 idents = [ ident.decode() for ident in idents ]
974
974
975 after = self._render_dependency(after)
975 after = self._render_dependency(after)
976 follow = self._render_dependency(follow)
976 follow = self._render_dependency(follow)
977 subheader = dict(after=after, follow=follow, timeout=timeout, targets=idents, retries=retries)
977 subheader = dict(after=after, follow=follow, timeout=timeout, targets=idents, retries=retries)
978
978
979 msg = self.client.send_apply_message(self._socket, f, args, kwargs, track=track,
979 msg = self.client.send_apply_message(self._socket, f, args, kwargs, track=track,
980 subheader=subheader)
980 subheader=subheader)
981 tracker = None if track is False else msg['tracker']
981 tracker = None if track is False else msg['tracker']
982
982
983 ar = AsyncResult(self.client, msg['header']['msg_id'], fname=f.__name__, targets=None, tracker=tracker)
983 ar = AsyncResult(self.client, msg['header']['msg_id'], fname=f.__name__, targets=None, tracker=tracker)
984
984
985 if block:
985 if block:
986 try:
986 try:
987 return ar.get()
987 return ar.get()
988 except KeyboardInterrupt:
988 except KeyboardInterrupt:
989 pass
989 pass
990 return ar
990 return ar
991
991
992 @spin_after
992 @spin_after
993 @save_ids
993 @save_ids
994 def map(self, f, *sequences, **kwargs):
994 def map(self, f, *sequences, **kwargs):
995 """view.map(f, *sequences, block=self.block, chunksize=1) => list|AsyncMapResult
995 """view.map(f, *sequences, block=self.block, chunksize=1) => list|AsyncMapResult
996
996
997 Parallel version of builtin `map`, load-balanced by this View.
997 Parallel version of builtin `map`, load-balanced by this View.
998
998
999 `block`, and `chunksize` can be specified by keyword only.
999 `block`, and `chunksize` can be specified by keyword only.
1000
1000
1001 Each `chunksize` elements will be a separate task, and will be
1001 Each `chunksize` elements will be a separate task, and will be
1002 load-balanced. This lets individual elements be available for iteration
1002 load-balanced. This lets individual elements be available for iteration
1003 as soon as they arrive.
1003 as soon as they arrive.
1004
1004
1005 Parameters
1005 Parameters
1006 ----------
1006 ----------
1007
1007
1008 f : callable
1008 f : callable
1009 function to be mapped
1009 function to be mapped
1010 *sequences: one or more sequences of matching length
1010 *sequences: one or more sequences of matching length
@@ -1012,15 +1012,15 b' class LoadBalancedView(View):'
1012 block : bool
1012 block : bool
1013 whether to wait for the result or not [default self.block]
1013 whether to wait for the result or not [default self.block]
1014 track : bool
1014 track : bool
1015 whether to create a MessageTracker to allow the user to
1015 whether to create a MessageTracker to allow the user to
1016 safely edit after arrays and buffers during non-copying
1016 safely edit after arrays and buffers during non-copying
1017 sends.
1017 sends.
1018 chunksize : int
1018 chunksize : int
1019 how many elements should be in each task [default 1]
1019 how many elements should be in each task [default 1]
1020
1020
1021 Returns
1021 Returns
1022 -------
1022 -------
1023
1023
1024 if block=False:
1024 if block=False:
1025 AsyncMapResult
1025 AsyncMapResult
1026 An object like AsyncResult, but which reassembles the sequence of results
1026 An object like AsyncResult, but which reassembles the sequence of results
@@ -1028,20 +1028,20 b' class LoadBalancedView(View):'
1028 results are complete.
1028 results are complete.
1029 else:
1029 else:
1030 the result of map(f,*sequences)
1030 the result of map(f,*sequences)
1031
1031
1032 """
1032 """
1033
1033
1034 # default
1034 # default
1035 block = kwargs.get('block', self.block)
1035 block = kwargs.get('block', self.block)
1036 chunksize = kwargs.get('chunksize', 1)
1036 chunksize = kwargs.get('chunksize', 1)
1037
1037
1038 keyset = set(kwargs.keys())
1038 keyset = set(kwargs.keys())
1039 extra_keys = keyset.difference_update(set(['block', 'chunksize']))
1039 extra_keys = keyset.difference_update(set(['block', 'chunksize']))
1040 if extra_keys:
1040 if extra_keys:
1041 raise TypeError("Invalid kwargs: %s"%list(extra_keys))
1041 raise TypeError("Invalid kwargs: %s"%list(extra_keys))
1042
1042
1043 assert len(sequences) > 0, "must have some sequences to map onto!"
1043 assert len(sequences) > 0, "must have some sequences to map onto!"
1044
1044
1045 pf = ParallelFunction(self, f, block=block, chunksize=chunksize)
1045 pf = ParallelFunction(self, f, block=block, chunksize=chunksize)
1046 return pf.map(*sequences)
1046 return pf.map(*sequences)
1047
1047
@@ -1,4 +1,4 b''
1 """A Task logger that presents our DB interface,
1 """A Task logger that presents our DB interface,
2 but exists entirely in memory and implemented with dicts.
2 but exists entirely in memory and implemented with dicts.
3
3
4 Authors:
4 Authors:
@@ -70,7 +70,7 b' filters = {'
70
70
71 class CompositeFilter(object):
71 class CompositeFilter(object):
72 """Composite filter for matching multiple properties."""
72 """Composite filter for matching multiple properties."""
73
73
74 def __init__(self, dikt):
74 def __init__(self, dikt):
75 self.tests = []
75 self.tests = []
76 self.values = []
76 self.values = []
@@ -91,23 +91,23 b' class BaseDB(LoggingConfigurable):'
91
91
92 class DictDB(BaseDB):
92 class DictDB(BaseDB):
93 """Basic in-memory dict-based object for saving Task Records.
93 """Basic in-memory dict-based object for saving Task Records.
94
94
95 This is the first object to present the DB interface
95 This is the first object to present the DB interface
96 for logging tasks out of memory.
96 for logging tasks out of memory.
97
97
98 The interface is based on MongoDB, so adding a MongoDB
98 The interface is based on MongoDB, so adding a MongoDB
99 backend should be straightforward.
99 backend should be straightforward.
100 """
100 """
101
101
102 _records = Dict()
102 _records = Dict()
103
103
104 def _match_one(self, rec, tests):
104 def _match_one(self, rec, tests):
105 """Check if a specific record matches tests."""
105 """Check if a specific record matches tests."""
106 for key,test in tests.iteritems():
106 for key,test in tests.iteritems():
107 if not test(rec.get(key, None)):
107 if not test(rec.get(key, None)):
108 return False
108 return False
109 return True
109 return True
110
110
111 def _match(self, check):
111 def _match(self, check):
112 """Find all the matches for a check dict."""
112 """Find all the matches for a check dict."""
113 matches = []
113 matches = []
@@ -117,12 +117,12 b' class DictDB(BaseDB):'
117 tests[k] = CompositeFilter(v)
117 tests[k] = CompositeFilter(v)
118 else:
118 else:
119 tests[k] = lambda o: o==v
119 tests[k] = lambda o: o==v
120
120
121 for rec in self._records.itervalues():
121 for rec in self._records.itervalues():
122 if self._match_one(rec, tests):
122 if self._match_one(rec, tests):
123 matches.append(rec)
123 matches.append(rec)
124 return matches
124 return matches
125
125
126 def _extract_subdict(self, rec, keys):
126 def _extract_subdict(self, rec, keys):
127 """extract subdict of keys"""
127 """extract subdict of keys"""
128 d = {}
128 d = {}
@@ -130,42 +130,42 b' class DictDB(BaseDB):'
130 for key in keys:
130 for key in keys:
131 d[key] = rec[key]
131 d[key] = rec[key]
132 return d
132 return d
133
133
134 def add_record(self, msg_id, rec):
134 def add_record(self, msg_id, rec):
135 """Add a new Task Record, by msg_id."""
135 """Add a new Task Record, by msg_id."""
136 if self._records.has_key(msg_id):
136 if self._records.has_key(msg_id):
137 raise KeyError("Already have msg_id %r"%(msg_id))
137 raise KeyError("Already have msg_id %r"%(msg_id))
138 self._records[msg_id] = rec
138 self._records[msg_id] = rec
139
139
140 def get_record(self, msg_id):
140 def get_record(self, msg_id):
141 """Get a specific Task Record, by msg_id."""
141 """Get a specific Task Record, by msg_id."""
142 if not self._records.has_key(msg_id):
142 if not self._records.has_key(msg_id):
143 raise KeyError("No such msg_id %r"%(msg_id))
143 raise KeyError("No such msg_id %r"%(msg_id))
144 return self._records[msg_id]
144 return self._records[msg_id]
145
145
146 def update_record(self, msg_id, rec):
146 def update_record(self, msg_id, rec):
147 """Update the data in an existing record."""
147 """Update the data in an existing record."""
148 self._records[msg_id].update(rec)
148 self._records[msg_id].update(rec)
149
149
150 def drop_matching_records(self, check):
150 def drop_matching_records(self, check):
151 """Remove a record from the DB."""
151 """Remove a record from the DB."""
152 matches = self._match(check)
152 matches = self._match(check)
153 for m in matches:
153 for m in matches:
154 del self._records[m['msg_id']]
154 del self._records[m['msg_id']]
155
155
156 def drop_record(self, msg_id):
156 def drop_record(self, msg_id):
157 """Remove a record from the DB."""
157 """Remove a record from the DB."""
158 del self._records[msg_id]
158 del self._records[msg_id]
159
159
160
160
161 def find_records(self, check, keys=None):
161 def find_records(self, check, keys=None):
162 """Find records matching a query dict, optionally extracting subset of keys.
162 """Find records matching a query dict, optionally extracting subset of keys.
163
163
164 Returns dict keyed by msg_id of matching records.
164 Returns dict keyed by msg_id of matching records.
165
165
166 Parameters
166 Parameters
167 ----------
167 ----------
168
168
169 check: dict
169 check: dict
170 mongodb-style query argument
170 mongodb-style query argument
171 keys: list of strs [optional]
171 keys: list of strs [optional]
@@ -177,8 +177,8 b' class DictDB(BaseDB):'
177 return [ self._extract_subdict(rec, keys) for rec in matches ]
177 return [ self._extract_subdict(rec, keys) for rec in matches ]
178 else:
178 else:
179 return matches
179 return matches
180
180
181
181
182 def get_history(self):
182 def get_history(self):
183 """get all msg_ids, ordered by time submitted."""
183 """get all msg_ids, ordered by time submitted."""
184 msg_ids = self._records.keys()
184 msg_ids = self._records.keys()
@@ -31,10 +31,10 b' class Heart(object):'
31 """A basic heart object for responding to a HeartMonitor.
31 """A basic heart object for responding to a HeartMonitor.
32 This is a simple wrapper with defaults for the most common
32 This is a simple wrapper with defaults for the most common
33 Device model for responding to heartbeats.
33 Device model for responding to heartbeats.
34
34
35 It simply builds a threadsafe zmq.FORWARDER Device, defaulting to using
35 It simply builds a threadsafe zmq.FORWARDER Device, defaulting to using
36 SUB/XREQ for in/out.
36 SUB/XREQ for in/out.
37
37
38 You can specify the XREQ's IDENTITY via the optional heart_id argument."""
38 You can specify the XREQ's IDENTITY via the optional heart_id argument."""
39 device=None
39 device=None
40 id=None
40 id=None
@@ -49,27 +49,27 b' class Heart(object):'
49 heart_id = uuid.uuid4().bytes
49 heart_id = uuid.uuid4().bytes
50 self.device.setsockopt_out(zmq.IDENTITY, heart_id)
50 self.device.setsockopt_out(zmq.IDENTITY, heart_id)
51 self.id = heart_id
51 self.id = heart_id
52
52
53 def start(self):
53 def start(self):
54 return self.device.start()
54 return self.device.start()
55
55
56 class HeartMonitor(LoggingConfigurable):
56 class HeartMonitor(LoggingConfigurable):
57 """A basic HeartMonitor class
57 """A basic HeartMonitor class
58 pingstream: a PUB stream
58 pingstream: a PUB stream
59 pongstream: an XREP stream
59 pongstream: an XREP stream
60 period: the period of the heartbeat in milliseconds"""
60 period: the period of the heartbeat in milliseconds"""
61
61
62 period=CFloat(1000, config=True,
62 period=CFloat(1000, config=True,
63 help='The frequency at which the Hub pings the engines for heartbeats '
63 help='The frequency at which the Hub pings the engines for heartbeats '
64 ' (in ms) [default: 100]',
64 ' (in ms) [default: 100]',
65 )
65 )
66
66
67 pingstream=Instance('zmq.eventloop.zmqstream.ZMQStream')
67 pingstream=Instance('zmq.eventloop.zmqstream.ZMQStream')
68 pongstream=Instance('zmq.eventloop.zmqstream.ZMQStream')
68 pongstream=Instance('zmq.eventloop.zmqstream.ZMQStream')
69 loop = Instance('zmq.eventloop.ioloop.IOLoop')
69 loop = Instance('zmq.eventloop.ioloop.IOLoop')
70 def _loop_default(self):
70 def _loop_default(self):
71 return ioloop.IOLoop.instance()
71 return ioloop.IOLoop.instance()
72
72
73 # not settable:
73 # not settable:
74 hearts=Set()
74 hearts=Set()
75 responses=Set()
75 responses=Set()
@@ -79,30 +79,30 b' class HeartMonitor(LoggingConfigurable):'
79 _failure_handlers = Set()
79 _failure_handlers = Set()
80 lifetime = CFloat(0)
80 lifetime = CFloat(0)
81 tic = CFloat(0)
81 tic = CFloat(0)
82
82
83 def __init__(self, **kwargs):
83 def __init__(self, **kwargs):
84 super(HeartMonitor, self).__init__(**kwargs)
84 super(HeartMonitor, self).__init__(**kwargs)
85
85
86 self.pongstream.on_recv(self.handle_pong)
86 self.pongstream.on_recv(self.handle_pong)
87
87
88 def start(self):
88 def start(self):
89 self.caller = ioloop.PeriodicCallback(self.beat, self.period, self.loop)
89 self.caller = ioloop.PeriodicCallback(self.beat, self.period, self.loop)
90 self.caller.start()
90 self.caller.start()
91
91
92 def add_new_heart_handler(self, handler):
92 def add_new_heart_handler(self, handler):
93 """add a new handler for new hearts"""
93 """add a new handler for new hearts"""
94 self.log.debug("heartbeat::new_heart_handler: %s"%handler)
94 self.log.debug("heartbeat::new_heart_handler: %s"%handler)
95 self._new_handlers.add(handler)
95 self._new_handlers.add(handler)
96
96
97 def add_heart_failure_handler(self, handler):
97 def add_heart_failure_handler(self, handler):
98 """add a new handler for heart failure"""
98 """add a new handler for heart failure"""
99 self.log.debug("heartbeat::new heart failure handler: %s"%handler)
99 self.log.debug("heartbeat::new heart failure handler: %s"%handler)
100 self._failure_handlers.add(handler)
100 self._failure_handlers.add(handler)
101
101
102 def beat(self):
102 def beat(self):
103 self.pongstream.flush()
103 self.pongstream.flush()
104 self.last_ping = self.lifetime
104 self.last_ping = self.lifetime
105
105
106 toc = time.time()
106 toc = time.time()
107 self.lifetime += toc-self.tic
107 self.lifetime += toc-self.tic
108 self.tic = toc
108 self.tic = toc
@@ -118,7 +118,7 b' class HeartMonitor(LoggingConfigurable):'
118 # print self.on_probation, self.hearts
118 # print self.on_probation, self.hearts
119 # self.log.debug("heartbeat::beat %.3f, %i beating hearts"%(self.lifetime, len(self.hearts)))
119 # self.log.debug("heartbeat::beat %.3f, %i beating hearts"%(self.lifetime, len(self.hearts)))
120 self.pingstream.send(asbytes(str(self.lifetime)))
120 self.pingstream.send(asbytes(str(self.lifetime)))
121
121
122 def handle_new_heart(self, heart):
122 def handle_new_heart(self, heart):
123 if self._new_handlers:
123 if self._new_handlers:
124 for handler in self._new_handlers:
124 for handler in self._new_handlers:
@@ -126,7 +126,7 b' class HeartMonitor(LoggingConfigurable):'
126 else:
126 else:
127 self.log.info("heartbeat::yay, got new heart %s!"%heart)
127 self.log.info("heartbeat::yay, got new heart %s!"%heart)
128 self.hearts.add(heart)
128 self.hearts.add(heart)
129
129
130 def handle_heart_failure(self, heart):
130 def handle_heart_failure(self, heart):
131 if self._failure_handlers:
131 if self._failure_handlers:
132 for handler in self._failure_handlers:
132 for handler in self._failure_handlers:
@@ -138,8 +138,8 b' class HeartMonitor(LoggingConfigurable):'
138 else:
138 else:
139 self.log.info("heartbeat::Heart %s failed :("%heart)
139 self.log.info("heartbeat::Heart %s failed :("%heart)
140 self.hearts.remove(heart)
140 self.hearts.remove(heart)
141
141
142
142
143 def handle_pong(self, msg):
143 def handle_pong(self, msg):
144 "a heart just beat"
144 "a heart just beat"
145 current = asbytes(str(self.lifetime))
145 current = asbytes(str(self.lifetime))
@@ -164,10 +164,10 b" if __name__ == '__main__':"
164 pub.bind('tcp://127.0.0.1:5555')
164 pub.bind('tcp://127.0.0.1:5555')
165 xrep = context.socket(zmq.ROUTER)
165 xrep = context.socket(zmq.ROUTER)
166 xrep.bind('tcp://127.0.0.1:5556')
166 xrep.bind('tcp://127.0.0.1:5556')
167
167
168 outstream = zmqstream.ZMQStream(pub, loop)
168 outstream = zmqstream.ZMQStream(pub, loop)
169 instream = zmqstream.ZMQStream(xrep, loop)
169 instream = zmqstream.ZMQStream(xrep, loop)
170
170
171 hb = HeartMonitor(loop, outstream, instream)
171 hb = HeartMonitor(loop, outstream, instream)
172
172
173 loop.start()
173 loop.start()
@@ -73,7 +73,7 b' def empty_record():'
73 'stdout': '',
73 'stdout': '',
74 'stderr': '',
74 'stderr': '',
75 }
75 }
76
76
77 def init_record(msg):
77 def init_record(msg):
78 """Initialize a TaskRecord based on a request."""
78 """Initialize a TaskRecord based on a request."""
79 header = msg['header']
79 header = msg['header']
@@ -118,7 +118,7 b' class EngineConnector(HasTraits):'
118
118
119 class HubFactory(RegistrationFactory):
119 class HubFactory(RegistrationFactory):
120 """The Configurable for setting up a Hub."""
120 """The Configurable for setting up a Hub."""
121
121
122 # port-pairs for monitoredqueues:
122 # port-pairs for monitoredqueues:
123 hb = Tuple(Int,Int,config=True,
123 hb = Tuple(Int,Int,config=True,
124 help="""XREQ/SUB Port pair for Engine heartbeats""")
124 help="""XREQ/SUB Port pair for Engine heartbeats""")
@@ -127,10 +127,10 b' class HubFactory(RegistrationFactory):'
127
127
128 mux = Tuple(Int,Int,config=True,
128 mux = Tuple(Int,Int,config=True,
129 help="""Engine/Client Port pair for MUX queue""")
129 help="""Engine/Client Port pair for MUX queue""")
130
130
131 def _mux_default(self):
131 def _mux_default(self):
132 return tuple(util.select_random_ports(2))
132 return tuple(util.select_random_ports(2))
133
133
134 task = Tuple(Int,Int,config=True,
134 task = Tuple(Int,Int,config=True,
135 help="""Engine/Client Port pair for Task queue""")
135 help="""Engine/Client Port pair for Task queue""")
136 def _task_default(self):
136 def _task_default(self):
@@ -138,88 +138,88 b' class HubFactory(RegistrationFactory):'
138
138
139 control = Tuple(Int,Int,config=True,
139 control = Tuple(Int,Int,config=True,
140 help="""Engine/Client Port pair for Control queue""")
140 help="""Engine/Client Port pair for Control queue""")
141
141
142 def _control_default(self):
142 def _control_default(self):
143 return tuple(util.select_random_ports(2))
143 return tuple(util.select_random_ports(2))
144
144
145 iopub = Tuple(Int,Int,config=True,
145 iopub = Tuple(Int,Int,config=True,
146 help="""Engine/Client Port pair for IOPub relay""")
146 help="""Engine/Client Port pair for IOPub relay""")
147
147
148 def _iopub_default(self):
148 def _iopub_default(self):
149 return tuple(util.select_random_ports(2))
149 return tuple(util.select_random_ports(2))
150
150
151 # single ports:
151 # single ports:
152 mon_port = Int(config=True,
152 mon_port = Int(config=True,
153 help="""Monitor (SUB) port for queue traffic""")
153 help="""Monitor (SUB) port for queue traffic""")
154
154
155 def _mon_port_default(self):
155 def _mon_port_default(self):
156 return util.select_random_ports(1)[0]
156 return util.select_random_ports(1)[0]
157
157
158 notifier_port = Int(config=True,
158 notifier_port = Int(config=True,
159 help="""PUB port for sending engine status notifications""")
159 help="""PUB port for sending engine status notifications""")
160
160
161 def _notifier_port_default(self):
161 def _notifier_port_default(self):
162 return util.select_random_ports(1)[0]
162 return util.select_random_ports(1)[0]
163
163
164 engine_ip = Unicode('127.0.0.1', config=True,
164 engine_ip = Unicode('127.0.0.1', config=True,
165 help="IP on which to listen for engine connections. [default: loopback]")
165 help="IP on which to listen for engine connections. [default: loopback]")
166 engine_transport = Unicode('tcp', config=True,
166 engine_transport = Unicode('tcp', config=True,
167 help="0MQ transport for engine connections. [default: tcp]")
167 help="0MQ transport for engine connections. [default: tcp]")
168
168
169 client_ip = Unicode('127.0.0.1', config=True,
169 client_ip = Unicode('127.0.0.1', config=True,
170 help="IP on which to listen for client connections. [default: loopback]")
170 help="IP on which to listen for client connections. [default: loopback]")
171 client_transport = Unicode('tcp', config=True,
171 client_transport = Unicode('tcp', config=True,
172 help="0MQ transport for client connections. [default : tcp]")
172 help="0MQ transport for client connections. [default : tcp]")
173
173
174 monitor_ip = Unicode('127.0.0.1', config=True,
174 monitor_ip = Unicode('127.0.0.1', config=True,
175 help="IP on which to listen for monitor messages. [default: loopback]")
175 help="IP on which to listen for monitor messages. [default: loopback]")
176 monitor_transport = Unicode('tcp', config=True,
176 monitor_transport = Unicode('tcp', config=True,
177 help="0MQ transport for monitor messages. [default : tcp]")
177 help="0MQ transport for monitor messages. [default : tcp]")
178
178
179 monitor_url = Unicode('')
179 monitor_url = Unicode('')
180
180
181 db_class = DottedObjectName('IPython.parallel.controller.dictdb.DictDB',
181 db_class = DottedObjectName('IPython.parallel.controller.dictdb.DictDB',
182 config=True, help="""The class to use for the DB backend""")
182 config=True, help="""The class to use for the DB backend""")
183
183
184 # not configurable
184 # not configurable
185 db = Instance('IPython.parallel.controller.dictdb.BaseDB')
185 db = Instance('IPython.parallel.controller.dictdb.BaseDB')
186 heartmonitor = Instance('IPython.parallel.controller.heartmonitor.HeartMonitor')
186 heartmonitor = Instance('IPython.parallel.controller.heartmonitor.HeartMonitor')
187
187
188 def _ip_changed(self, name, old, new):
188 def _ip_changed(self, name, old, new):
189 self.engine_ip = new
189 self.engine_ip = new
190 self.client_ip = new
190 self.client_ip = new
191 self.monitor_ip = new
191 self.monitor_ip = new
192 self._update_monitor_url()
192 self._update_monitor_url()
193
193
194 def _update_monitor_url(self):
194 def _update_monitor_url(self):
195 self.monitor_url = "%s://%s:%i"%(self.monitor_transport, self.monitor_ip, self.mon_port)
195 self.monitor_url = "%s://%s:%i"%(self.monitor_transport, self.monitor_ip, self.mon_port)
196
196
197 def _transport_changed(self, name, old, new):
197 def _transport_changed(self, name, old, new):
198 self.engine_transport = new
198 self.engine_transport = new
199 self.client_transport = new
199 self.client_transport = new
200 self.monitor_transport = new
200 self.monitor_transport = new
201 self._update_monitor_url()
201 self._update_monitor_url()
202
202
203 def __init__(self, **kwargs):
203 def __init__(self, **kwargs):
204 super(HubFactory, self).__init__(**kwargs)
204 super(HubFactory, self).__init__(**kwargs)
205 self._update_monitor_url()
205 self._update_monitor_url()
206
206
207
207
208 def construct(self):
208 def construct(self):
209 self.init_hub()
209 self.init_hub()
210
210
211 def start(self):
211 def start(self):
212 self.heartmonitor.start()
212 self.heartmonitor.start()
213 self.log.info("Heartmonitor started")
213 self.log.info("Heartmonitor started")
214
214
215 def init_hub(self):
215 def init_hub(self):
216 """construct"""
216 """construct"""
217 client_iface = "%s://%s:"%(self.client_transport, self.client_ip) + "%i"
217 client_iface = "%s://%s:"%(self.client_transport, self.client_ip) + "%i"
218 engine_iface = "%s://%s:"%(self.engine_transport, self.engine_ip) + "%i"
218 engine_iface = "%s://%s:"%(self.engine_transport, self.engine_ip) + "%i"
219
219
220 ctx = self.context
220 ctx = self.context
221 loop = self.loop
221 loop = self.loop
222
222
223 # Registrar socket
223 # Registrar socket
224 q = ZMQStream(ctx.socket(zmq.ROUTER), loop)
224 q = ZMQStream(ctx.socket(zmq.ROUTER), loop)
225 q.bind(client_iface % self.regport)
225 q.bind(client_iface % self.regport)
@@ -227,7 +227,7 b' class HubFactory(RegistrationFactory):'
227 if self.client_ip != self.engine_ip:
227 if self.client_ip != self.engine_ip:
228 q.bind(engine_iface % self.regport)
228 q.bind(engine_iface % self.regport)
229 self.log.info("Hub listening on %s for registration."%(engine_iface%self.regport))
229 self.log.info("Hub listening on %s for registration."%(engine_iface%self.regport))
230
230
231 ### Engine connections ###
231 ### Engine connections ###
232
232
233 # heartbeat
233 # heartbeat
@@ -253,11 +253,11 b' class HubFactory(RegistrationFactory):'
253 sub.bind(self.monitor_url)
253 sub.bind(self.monitor_url)
254 sub.bind('inproc://monitor')
254 sub.bind('inproc://monitor')
255 sub = ZMQStream(sub, loop)
255 sub = ZMQStream(sub, loop)
256
256
257 # connect the db
257 # connect the db
258 self.log.info('Hub using DB backend: %r'%(self.db_class.split()[-1]))
258 self.log.info('Hub using DB backend: %r'%(self.db_class.split()[-1]))
259 # cdir = self.config.Global.cluster_dir
259 # cdir = self.config.Global.cluster_dir
260 self.db = import_item(str(self.db_class))(session=self.session.session,
260 self.db = import_item(str(self.db_class))(session=self.session.session,
261 config=self.config, log=self.log)
261 config=self.config, log=self.log)
262 time.sleep(.25)
262 time.sleep(.25)
263 try:
263 try:
@@ -295,11 +295,11 b' class HubFactory(RegistrationFactory):'
295 query=q, notifier=n, resubmit=r, db=self.db,
295 query=q, notifier=n, resubmit=r, db=self.db,
296 engine_info=self.engine_info, client_info=self.client_info,
296 engine_info=self.engine_info, client_info=self.client_info,
297 log=self.log)
297 log=self.log)
298
298
299
299
300 class Hub(SessionFactory):
300 class Hub(SessionFactory):
301 """The IPython Controller Hub with 0MQ connections
301 """The IPython Controller Hub with 0MQ connections
302
302
303 Parameters
303 Parameters
304 ==========
304 ==========
305 loop: zmq IOLoop instance
305 loop: zmq IOLoop instance
@@ -333,7 +333,7 b' class Hub(SessionFactory):'
333 incoming_registrations=Dict()
333 incoming_registrations=Dict()
334 registration_timeout=Int()
334 registration_timeout=Int()
335 _idcounter=Int(0)
335 _idcounter=Int(0)
336
336
337 # objects from constructor:
337 # objects from constructor:
338 query=Instance(ZMQStream)
338 query=Instance(ZMQStream)
339 monitor=Instance(ZMQStream)
339 monitor=Instance(ZMQStream)
@@ -343,8 +343,8 b' class Hub(SessionFactory):'
343 db=Instance(object)
343 db=Instance(object)
344 client_info=Dict()
344 client_info=Dict()
345 engine_info=Dict()
345 engine_info=Dict()
346
346
347
347
348 def __init__(self, **kwargs):
348 def __init__(self, **kwargs):
349 """
349 """
350 # universal:
350 # universal:
@@ -359,10 +359,10 b' class Hub(SessionFactory):'
359 engine_info: zmq address/protocol dict for engine connections
359 engine_info: zmq address/protocol dict for engine connections
360 client_info: zmq address/protocol dict for client connections
360 client_info: zmq address/protocol dict for client connections
361 """
361 """
362
362
363 super(Hub, self).__init__(**kwargs)
363 super(Hub, self).__init__(**kwargs)
364 self.registration_timeout = max(5000, 2*self.heartmonitor.period)
364 self.registration_timeout = max(5000, 2*self.heartmonitor.period)
365
365
366 # validate connection dicts:
366 # validate connection dicts:
367 for k,v in self.client_info.iteritems():
367 for k,v in self.client_info.iteritems():
368 if k == 'task':
368 if k == 'task':
@@ -371,14 +371,14 b' class Hub(SessionFactory):'
371 util.validate_url_container(v)
371 util.validate_url_container(v)
372 # util.validate_url_container(self.client_info)
372 # util.validate_url_container(self.client_info)
373 util.validate_url_container(self.engine_info)
373 util.validate_url_container(self.engine_info)
374
374
375 # register our callbacks
375 # register our callbacks
376 self.query.on_recv(self.dispatch_query)
376 self.query.on_recv(self.dispatch_query)
377 self.monitor.on_recv(self.dispatch_monitor_traffic)
377 self.monitor.on_recv(self.dispatch_monitor_traffic)
378
378
379 self.heartmonitor.add_heart_failure_handler(self.handle_heart_failure)
379 self.heartmonitor.add_heart_failure_handler(self.handle_heart_failure)
380 self.heartmonitor.add_new_heart_handler(self.handle_new_heart)
380 self.heartmonitor.add_new_heart_handler(self.handle_new_heart)
381
381
382 self.monitor_handlers = {b'in' : self.save_queue_request,
382 self.monitor_handlers = {b'in' : self.save_queue_request,
383 b'out': self.save_queue_result,
383 b'out': self.save_queue_result,
384 b'intask': self.save_task_request,
384 b'intask': self.save_task_request,
@@ -388,7 +388,7 b' class Hub(SessionFactory):'
388 b'outcontrol': _passer,
388 b'outcontrol': _passer,
389 b'iopub': self.save_iopub_message,
389 b'iopub': self.save_iopub_message,
390 }
390 }
391
391
392 self.query_handlers = {'queue_request': self.queue_status,
392 self.query_handlers = {'queue_request': self.queue_status,
393 'result_request': self.get_results,
393 'result_request': self.get_results,
394 'history_request': self.get_history,
394 'history_request': self.get_history,
@@ -401,16 +401,16 b' class Hub(SessionFactory):'
401 'unregistration_request' : self.unregister_engine,
401 'unregistration_request' : self.unregister_engine,
402 'connection_request': self.connection_request,
402 'connection_request': self.connection_request,
403 }
403 }
404
404
405 # ignore resubmit replies
405 # ignore resubmit replies
406 self.resubmit.on_recv(lambda msg: None, copy=False)
406 self.resubmit.on_recv(lambda msg: None, copy=False)
407
407
408 self.log.info("hub::created hub")
408 self.log.info("hub::created hub")
409
409
410 @property
410 @property
411 def _next_id(self):
411 def _next_id(self):
412 """gemerate a new ID.
412 """gemerate a new ID.
413
413
414 No longer reuse old ids, just count from 0."""
414 No longer reuse old ids, just count from 0."""
415 newid = self._idcounter
415 newid = self._idcounter
416 self._idcounter += 1
416 self._idcounter += 1
@@ -421,17 +421,17 b' class Hub(SessionFactory):'
421 # while newid in self.ids or newid in incoming:
421 # while newid in self.ids or newid in incoming:
422 # newid += 1
422 # newid += 1
423 # return newid
423 # return newid
424
424
425 #-----------------------------------------------------------------------------
425 #-----------------------------------------------------------------------------
426 # message validation
426 # message validation
427 #-----------------------------------------------------------------------------
427 #-----------------------------------------------------------------------------
428
428
429 def _validate_targets(self, targets):
429 def _validate_targets(self, targets):
430 """turn any valid targets argument into a list of integer ids"""
430 """turn any valid targets argument into a list of integer ids"""
431 if targets is None:
431 if targets is None:
432 # default to all
432 # default to all
433 targets = self.ids
433 targets = self.ids
434
434
435 if isinstance(targets, (int,str,unicode)):
435 if isinstance(targets, (int,str,unicode)):
436 # only one target specified
436 # only one target specified
437 targets = [targets]
437 targets = [targets]
@@ -448,12 +448,12 b' class Hub(SessionFactory):'
448 if not targets:
448 if not targets:
449 raise IndexError("No Engines Registered")
449 raise IndexError("No Engines Registered")
450 return targets
450 return targets
451
451
452 #-----------------------------------------------------------------------------
452 #-----------------------------------------------------------------------------
453 # dispatch methods (1 per stream)
453 # dispatch methods (1 per stream)
454 #-----------------------------------------------------------------------------
454 #-----------------------------------------------------------------------------
455
455
456
456
457 def dispatch_monitor_traffic(self, msg):
457 def dispatch_monitor_traffic(self, msg):
458 """all ME and Task queue messages come through here, as well as
458 """all ME and Task queue messages come through here, as well as
459 IOPub traffic."""
459 IOPub traffic."""
@@ -471,8 +471,8 b' class Hub(SessionFactory):'
471 handler(idents, msg)
471 handler(idents, msg)
472 else:
472 else:
473 self.log.error("Invalid monitor topic: %r"%switch)
473 self.log.error("Invalid monitor topic: %r"%switch)
474
474
475
475
476 def dispatch_query(self, msg):
476 def dispatch_query(self, msg):
477 """Route registration requests and queries from clients."""
477 """Route registration requests and queries from clients."""
478 try:
478 try:
@@ -488,7 +488,7 b' class Hub(SessionFactory):'
488 except Exception:
488 except Exception:
489 content = error.wrap_exception()
489 content = error.wrap_exception()
490 self.log.error("Bad Query Message: %r"%msg, exc_info=True)
490 self.log.error("Bad Query Message: %r"%msg, exc_info=True)
491 self.session.send(self.query, "hub_error", ident=client_id,
491 self.session.send(self.query, "hub_error", ident=client_id,
492 content=content)
492 content=content)
493 return
493 return
494 # print client_id, header, parent, content
494 # print client_id, header, parent, content
@@ -501,23 +501,23 b' class Hub(SessionFactory):'
501 except:
501 except:
502 content = error.wrap_exception()
502 content = error.wrap_exception()
503 self.log.error("Bad Message Type: %r"%msg_type, exc_info=True)
503 self.log.error("Bad Message Type: %r"%msg_type, exc_info=True)
504 self.session.send(self.query, "hub_error", ident=client_id,
504 self.session.send(self.query, "hub_error", ident=client_id,
505 content=content)
505 content=content)
506 return
506 return
507
507
508 else:
508 else:
509 handler(idents, msg)
509 handler(idents, msg)
510
510
511 def dispatch_db(self, msg):
511 def dispatch_db(self, msg):
512 """"""
512 """"""
513 raise NotImplementedError
513 raise NotImplementedError
514
514
515 #---------------------------------------------------------------------------
515 #---------------------------------------------------------------------------
516 # handler methods (1 per event)
516 # handler methods (1 per event)
517 #---------------------------------------------------------------------------
517 #---------------------------------------------------------------------------
518
518
519 #----------------------- Heartbeat --------------------------------------
519 #----------------------- Heartbeat --------------------------------------
520
520
521 def handle_new_heart(self, heart):
521 def handle_new_heart(self, heart):
522 """handler to attach to heartbeater.
522 """handler to attach to heartbeater.
523 Called when a new heart starts to beat.
523 Called when a new heart starts to beat.
@@ -527,8 +527,8 b' class Hub(SessionFactory):'
527 self.log.info("heartbeat::ignoring new heart: %r"%heart)
527 self.log.info("heartbeat::ignoring new heart: %r"%heart)
528 else:
528 else:
529 self.finish_registration(heart)
529 self.finish_registration(heart)
530
530
531
531
532 def handle_heart_failure(self, heart):
532 def handle_heart_failure(self, heart):
533 """handler to attach to heartbeater.
533 """handler to attach to heartbeater.
534 called when a previously registered heart fails to respond to beat request.
534 called when a previously registered heart fails to respond to beat request.
@@ -540,9 +540,9 b' class Hub(SessionFactory):'
540 self.log.info("heartbeat::ignoring heart failure %r"%heart)
540 self.log.info("heartbeat::ignoring heart failure %r"%heart)
541 else:
541 else:
542 self.unregister_engine(heart, dict(content=dict(id=eid, queue=queue)))
542 self.unregister_engine(heart, dict(content=dict(id=eid, queue=queue)))
543
543
544 #----------------------- MUX Queue Traffic ------------------------------
544 #----------------------- MUX Queue Traffic ------------------------------
545
545
546 def save_queue_request(self, idents, msg):
546 def save_queue_request(self, idents, msg):
547 if len(idents) < 2:
547 if len(idents) < 2:
548 self.log.error("invalid identity prefix: %r"%idents)
548 self.log.error("invalid identity prefix: %r"%idents)
@@ -553,7 +553,7 b' class Hub(SessionFactory):'
553 except Exception:
553 except Exception:
554 self.log.error("queue::client %r sent invalid message to %r: %r"%(client_id, queue_id, msg), exc_info=True)
554 self.log.error("queue::client %r sent invalid message to %r: %r"%(client_id, queue_id, msg), exc_info=True)
555 return
555 return
556
556
557 eid = self.by_ident.get(queue_id, None)
557 eid = self.by_ident.get(queue_id, None)
558 if eid is None:
558 if eid is None:
559 self.log.error("queue::target %r not registered"%queue_id)
559 self.log.error("queue::target %r not registered"%queue_id)
@@ -584,16 +584,16 b' class Hub(SessionFactory):'
584 self.db.add_record(msg_id, record)
584 self.db.add_record(msg_id, record)
585 except Exception:
585 except Exception:
586 self.log.error("DB Error adding record %r"%msg_id, exc_info=True)
586 self.log.error("DB Error adding record %r"%msg_id, exc_info=True)
587
587
588
588
589 self.pending.add(msg_id)
589 self.pending.add(msg_id)
590 self.queues[eid].append(msg_id)
590 self.queues[eid].append(msg_id)
591
591
592 def save_queue_result(self, idents, msg):
592 def save_queue_result(self, idents, msg):
593 if len(idents) < 2:
593 if len(idents) < 2:
594 self.log.error("invalid identity prefix: %r"%idents)
594 self.log.error("invalid identity prefix: %r"%idents)
595 return
595 return
596
596
597 client_id, queue_id = idents[:2]
597 client_id, queue_id = idents[:2]
598 try:
598 try:
599 msg = self.session.unserialize(msg)
599 msg = self.session.unserialize(msg)
@@ -601,12 +601,12 b' class Hub(SessionFactory):'
601 self.log.error("queue::engine %r sent invalid message to %r: %r"%(
601 self.log.error("queue::engine %r sent invalid message to %r: %r"%(
602 queue_id,client_id, msg), exc_info=True)
602 queue_id,client_id, msg), exc_info=True)
603 return
603 return
604
604
605 eid = self.by_ident.get(queue_id, None)
605 eid = self.by_ident.get(queue_id, None)
606 if eid is None:
606 if eid is None:
607 self.log.error("queue::unknown engine %r is sending a reply: "%queue_id)
607 self.log.error("queue::unknown engine %r is sending a reply: "%queue_id)
608 return
608 return
609
609
610 parent = msg['parent_header']
610 parent = msg['parent_header']
611 if not parent:
611 if not parent:
612 return
612 return
@@ -637,14 +637,14 b' class Hub(SessionFactory):'
637 self.db.update_record(msg_id, result)
637 self.db.update_record(msg_id, result)
638 except Exception:
638 except Exception:
639 self.log.error("DB Error updating record %r"%msg_id, exc_info=True)
639 self.log.error("DB Error updating record %r"%msg_id, exc_info=True)
640
640
641
641
642 #--------------------- Task Queue Traffic ------------------------------
642 #--------------------- Task Queue Traffic ------------------------------
643
643
644 def save_task_request(self, idents, msg):
644 def save_task_request(self, idents, msg):
645 """Save the submission of a task."""
645 """Save the submission of a task."""
646 client_id = idents[0]
646 client_id = idents[0]
647
647
648 try:
648 try:
649 msg = self.session.unserialize(msg)
649 msg = self.session.unserialize(msg)
650 except Exception:
650 except Exception:
@@ -691,7 +691,7 b' class Hub(SessionFactory):'
691 self.log.error("DB Error adding record %r"%msg_id, exc_info=True)
691 self.log.error("DB Error adding record %r"%msg_id, exc_info=True)
692 except Exception:
692 except Exception:
693 self.log.error("DB Error saving task request %r"%msg_id, exc_info=True)
693 self.log.error("DB Error saving task request %r"%msg_id, exc_info=True)
694
694
695 def save_task_result(self, idents, msg):
695 def save_task_result(self, idents, msg):
696 """save the result of a completed task."""
696 """save the result of a completed task."""
697 client_id = idents[0]
697 client_id = idents[0]
@@ -701,7 +701,7 b' class Hub(SessionFactory):'
701 self.log.error("task::invalid task result message send to %r: %r"%(
701 self.log.error("task::invalid task result message send to %r: %r"%(
702 client_id, msg), exc_info=True)
702 client_id, msg), exc_info=True)
703 return
703 return
704
704
705 parent = msg['parent_header']
705 parent = msg['parent_header']
706 if not parent:
706 if not parent:
707 # print msg
707 # print msg
@@ -710,11 +710,11 b' class Hub(SessionFactory):'
710 msg_id = parent['msg_id']
710 msg_id = parent['msg_id']
711 if msg_id in self.unassigned:
711 if msg_id in self.unassigned:
712 self.unassigned.remove(msg_id)
712 self.unassigned.remove(msg_id)
713
713
714 header = msg['header']
714 header = msg['header']
715 engine_uuid = header.get('engine', None)
715 engine_uuid = header.get('engine', None)
716 eid = self.by_ident.get(engine_uuid, None)
716 eid = self.by_ident.get(engine_uuid, None)
717
717
718 if msg_id in self.pending:
718 if msg_id in self.pending:
719 self.pending.remove(msg_id)
719 self.pending.remove(msg_id)
720 self.all_completed.add(msg_id)
720 self.all_completed.add(msg_id)
@@ -737,10 +737,10 b' class Hub(SessionFactory):'
737 self.db.update_record(msg_id, result)
737 self.db.update_record(msg_id, result)
738 except Exception:
738 except Exception:
739 self.log.error("DB Error saving task request %r"%msg_id, exc_info=True)
739 self.log.error("DB Error saving task request %r"%msg_id, exc_info=True)
740
740
741 else:
741 else:
742 self.log.debug("task::unknown task %r finished"%msg_id)
742 self.log.debug("task::unknown task %r finished"%msg_id)
743
743
744 def save_task_destination(self, idents, msg):
744 def save_task_destination(self, idents, msg):
745 try:
745 try:
746 msg = self.session.unserialize(msg, content=True)
746 msg = self.session.unserialize(msg, content=True)
@@ -752,30 +752,30 b' class Hub(SessionFactory):'
752 msg_id = content['msg_id']
752 msg_id = content['msg_id']
753 engine_uuid = content['engine_id']
753 engine_uuid = content['engine_id']
754 eid = self.by_ident[util.asbytes(engine_uuid)]
754 eid = self.by_ident[util.asbytes(engine_uuid)]
755
755
756 self.log.info("task::task %r arrived on %r"%(msg_id, eid))
756 self.log.info("task::task %r arrived on %r"%(msg_id, eid))
757 if msg_id in self.unassigned:
757 if msg_id in self.unassigned:
758 self.unassigned.remove(msg_id)
758 self.unassigned.remove(msg_id)
759 # else:
759 # else:
760 # self.log.debug("task::task %r not listed as MIA?!"%(msg_id))
760 # self.log.debug("task::task %r not listed as MIA?!"%(msg_id))
761
761
762 self.tasks[eid].append(msg_id)
762 self.tasks[eid].append(msg_id)
763 # self.pending[msg_id][1].update(received=datetime.now(),engine=(eid,engine_uuid))
763 # self.pending[msg_id][1].update(received=datetime.now(),engine=(eid,engine_uuid))
764 try:
764 try:
765 self.db.update_record(msg_id, dict(engine_uuid=engine_uuid))
765 self.db.update_record(msg_id, dict(engine_uuid=engine_uuid))
766 except Exception:
766 except Exception:
767 self.log.error("DB Error saving task destination %r"%msg_id, exc_info=True)
767 self.log.error("DB Error saving task destination %r"%msg_id, exc_info=True)
768
768
769
769
770 def mia_task_request(self, idents, msg):
770 def mia_task_request(self, idents, msg):
771 raise NotImplementedError
771 raise NotImplementedError
772 client_id = idents[0]
772 client_id = idents[0]
773 # content = dict(mia=self.mia,status='ok')
773 # content = dict(mia=self.mia,status='ok')
774 # self.session.send('mia_reply', content=content, idents=client_id)
774 # self.session.send('mia_reply', content=content, idents=client_id)
775
775
776
776
777 #--------------------- IOPub Traffic ------------------------------
777 #--------------------- IOPub Traffic ------------------------------
778
778
779 def save_iopub_message(self, topics, msg):
779 def save_iopub_message(self, topics, msg):
780 """save an iopub message into the db"""
780 """save an iopub message into the db"""
781 # print (topics)
781 # print (topics)
@@ -784,7 +784,7 b' class Hub(SessionFactory):'
784 except Exception:
784 except Exception:
785 self.log.error("iopub::invalid IOPub message", exc_info=True)
785 self.log.error("iopub::invalid IOPub message", exc_info=True)
786 return
786 return
787
787
788 parent = msg['parent_header']
788 parent = msg['parent_header']
789 if not parent:
789 if not parent:
790 self.log.error("iopub::invalid IOPub message: %r"%msg)
790 self.log.error("iopub::invalid IOPub message: %r"%msg)
@@ -792,7 +792,7 b' class Hub(SessionFactory):'
792 msg_id = parent['msg_id']
792 msg_id = parent['msg_id']
793 msg_type = msg['header']['msg_type']
793 msg_type = msg['header']['msg_type']
794 content = msg['content']
794 content = msg['content']
795
795
796 # ensure msg_id is in db
796 # ensure msg_id is in db
797 try:
797 try:
798 rec = self.db.get_record(msg_id)
798 rec = self.db.get_record(msg_id)
@@ -806,25 +806,25 b' class Hub(SessionFactory):'
806 name = content['name']
806 name = content['name']
807 s = rec[name] or ''
807 s = rec[name] or ''
808 d[name] = s + content['data']
808 d[name] = s + content['data']
809
809
810 elif msg_type == 'pyerr':
810 elif msg_type == 'pyerr':
811 d['pyerr'] = content
811 d['pyerr'] = content
812 elif msg_type == 'pyin':
812 elif msg_type == 'pyin':
813 d['pyin'] = content['code']
813 d['pyin'] = content['code']
814 else:
814 else:
815 d[msg_type] = content.get('data', '')
815 d[msg_type] = content.get('data', '')
816
816
817 try:
817 try:
818 self.db.update_record(msg_id, d)
818 self.db.update_record(msg_id, d)
819 except Exception:
819 except Exception:
820 self.log.error("DB Error saving iopub message %r"%msg_id, exc_info=True)
820 self.log.error("DB Error saving iopub message %r"%msg_id, exc_info=True)
821
821
822
822
823
823
824 #-------------------------------------------------------------------------
824 #-------------------------------------------------------------------------
825 # Registration requests
825 # Registration requests
826 #-------------------------------------------------------------------------
826 #-------------------------------------------------------------------------
827
827
828 def connection_request(self, client_id, msg):
828 def connection_request(self, client_id, msg):
829 """Reply with connection addresses for clients."""
829 """Reply with connection addresses for clients."""
830 self.log.info("client::client %r connected"%client_id)
830 self.log.info("client::client %r connected"%client_id)
@@ -836,7 +836,7 b' class Hub(SessionFactory):'
836 jsonable[str(k)] = v.decode('ascii')
836 jsonable[str(k)] = v.decode('ascii')
837 content['engines'] = jsonable
837 content['engines'] = jsonable
838 self.session.send(self.query, 'connection_reply', content, parent=msg, ident=client_id)
838 self.session.send(self.query, 'connection_reply', content, parent=msg, ident=client_id)
839
839
840 def register_engine(self, reg, msg):
840 def register_engine(self, reg, msg):
841 """Register a new engine."""
841 """Register a new engine."""
842 content = msg['content']
842 content = msg['content']
@@ -851,9 +851,9 b' class Hub(SessionFactory):'
851 """register a new engine, and create the socket(s) necessary"""
851 """register a new engine, and create the socket(s) necessary"""
852 eid = self._next_id
852 eid = self._next_id
853 # print (eid, queue, reg, heart)
853 # print (eid, queue, reg, heart)
854
854
855 self.log.debug("registration::register_engine(%i, %r, %r, %r)"%(eid, queue, reg, heart))
855 self.log.debug("registration::register_engine(%i, %r, %r, %r)"%(eid, queue, reg, heart))
856
856
857 content = dict(id=eid,status='ok')
857 content = dict(id=eid,status='ok')
858 content.update(self.engine_info)
858 content.update(self.engine_info)
859 # check if requesting available IDs:
859 # check if requesting available IDs:
@@ -885,11 +885,11 b' class Hub(SessionFactory):'
885 self.log.error("queue_id %r in use"%queue, exc_info=True)
885 self.log.error("queue_id %r in use"%queue, exc_info=True)
886 content = error.wrap_exception()
886 content = error.wrap_exception()
887 break
887 break
888
888
889 msg = self.session.send(self.query, "registration_reply",
889 msg = self.session.send(self.query, "registration_reply",
890 content=content,
890 content=content,
891 ident=reg)
891 ident=reg)
892
892
893 if content['status'] == 'ok':
893 if content['status'] == 'ok':
894 if heart in self.heartmonitor.hearts:
894 if heart in self.heartmonitor.hearts:
895 # already beating
895 # already beating
@@ -903,7 +903,7 b' class Hub(SessionFactory):'
903 else:
903 else:
904 self.log.error("registration::registration %i failed: %r"%(eid, content['evalue']))
904 self.log.error("registration::registration %i failed: %r"%(eid, content['evalue']))
905 return eid
905 return eid
906
906
907 def unregister_engine(self, ident, msg):
907 def unregister_engine(self, ident, msg):
908 """Unregister an engine that explicitly requested to leave."""
908 """Unregister an engine that explicitly requested to leave."""
909 try:
909 try:
@@ -918,7 +918,7 b' class Hub(SessionFactory):'
918 self.dead_engines.add(uuid)
918 self.dead_engines.add(uuid)
919 # self.ids.remove(eid)
919 # self.ids.remove(eid)
920 # uuid = self.keytable.pop(eid)
920 # uuid = self.keytable.pop(eid)
921 #
921 #
922 # ec = self.engines.pop(eid)
922 # ec = self.engines.pop(eid)
923 # self.hearts.pop(ec.heartbeat)
923 # self.hearts.pop(ec.heartbeat)
924 # self.by_ident.pop(ec.queue)
924 # self.by_ident.pop(ec.queue)
@@ -927,20 +927,20 b' class Hub(SessionFactory):'
927 dc = ioloop.DelayedCallback(handleit, self.registration_timeout, self.loop)
927 dc = ioloop.DelayedCallback(handleit, self.registration_timeout, self.loop)
928 dc.start()
928 dc.start()
929 ############## TODO: HANDLE IT ################
929 ############## TODO: HANDLE IT ################
930
930
931 if self.notifier:
931 if self.notifier:
932 self.session.send(self.notifier, "unregistration_notification", content=content)
932 self.session.send(self.notifier, "unregistration_notification", content=content)
933
933
934 def _handle_stranded_msgs(self, eid, uuid):
934 def _handle_stranded_msgs(self, eid, uuid):
935 """Handle messages known to be on an engine when the engine unregisters.
935 """Handle messages known to be on an engine when the engine unregisters.
936
936
937 It is possible that this will fire prematurely - that is, an engine will
937 It is possible that this will fire prematurely - that is, an engine will
938 go down after completing a result, and the client will be notified
938 go down after completing a result, and the client will be notified
939 that the result failed and later receive the actual result.
939 that the result failed and later receive the actual result.
940 """
940 """
941
941
942 outstanding = self.queues[eid]
942 outstanding = self.queues[eid]
943
943
944 for msg_id in outstanding:
944 for msg_id in outstanding:
945 self.pending.remove(msg_id)
945 self.pending.remove(msg_id)
946 self.all_completed.add(msg_id)
946 self.all_completed.add(msg_id)
@@ -959,12 +959,12 b' class Hub(SessionFactory):'
959 self.db.update_record(msg_id, rec)
959 self.db.update_record(msg_id, rec)
960 except Exception:
960 except Exception:
961 self.log.error("DB Error handling stranded msg %r"%msg_id, exc_info=True)
961 self.log.error("DB Error handling stranded msg %r"%msg_id, exc_info=True)
962
962
963
963
964 def finish_registration(self, heart):
964 def finish_registration(self, heart):
965 """Second half of engine registration, called after our HeartMonitor
965 """Second half of engine registration, called after our HeartMonitor
966 has received a beat from the Engine's Heart."""
966 has received a beat from the Engine's Heart."""
967 try:
967 try:
968 (eid,queue,reg,purge) = self.incoming_registrations.pop(heart)
968 (eid,queue,reg,purge) = self.incoming_registrations.pop(heart)
969 except KeyError:
969 except KeyError:
970 self.log.error("registration::tried to finish nonexistant registration", exc_info=True)
970 self.log.error("registration::tried to finish nonexistant registration", exc_info=True)
@@ -975,7 +975,7 b' class Hub(SessionFactory):'
975 control = queue
975 control = queue
976 self.ids.add(eid)
976 self.ids.add(eid)
977 self.keytable[eid] = queue
977 self.keytable[eid] = queue
978 self.engines[eid] = EngineConnector(id=eid, queue=queue, registration=reg,
978 self.engines[eid] = EngineConnector(id=eid, queue=queue, registration=reg,
979 control=control, heartbeat=heart)
979 control=control, heartbeat=heart)
980 self.by_ident[queue] = eid
980 self.by_ident[queue] = eid
981 self.queues[eid] = list()
981 self.queues[eid] = list()
@@ -986,18 +986,18 b' class Hub(SessionFactory):'
986 if self.notifier:
986 if self.notifier:
987 self.session.send(self.notifier, "registration_notification", content=content)
987 self.session.send(self.notifier, "registration_notification", content=content)
988 self.log.info("engine::Engine Connected: %i"%eid)
988 self.log.info("engine::Engine Connected: %i"%eid)
989
989
990 def _purge_stalled_registration(self, heart):
990 def _purge_stalled_registration(self, heart):
991 if heart in self.incoming_registrations:
991 if heart in self.incoming_registrations:
992 eid = self.incoming_registrations.pop(heart)[0]
992 eid = self.incoming_registrations.pop(heart)[0]
993 self.log.info("registration::purging stalled registration: %i"%eid)
993 self.log.info("registration::purging stalled registration: %i"%eid)
994 else:
994 else:
995 pass
995 pass
996
996
997 #-------------------------------------------------------------------------
997 #-------------------------------------------------------------------------
998 # Client Requests
998 # Client Requests
999 #-------------------------------------------------------------------------
999 #-------------------------------------------------------------------------
1000
1000
1001 def shutdown_request(self, client_id, msg):
1001 def shutdown_request(self, client_id, msg):
1002 """handle shutdown request."""
1002 """handle shutdown request."""
1003 self.session.send(self.query, 'shutdown_reply', content={'status': 'ok'}, ident=client_id)
1003 self.session.send(self.query, 'shutdown_reply', content={'status': 'ok'}, ident=client_id)
@@ -1005,13 +1005,13 b' class Hub(SessionFactory):'
1005 self.session.send(self.notifier, 'shutdown_notice', content={'status': 'ok'})
1005 self.session.send(self.notifier, 'shutdown_notice', content={'status': 'ok'})
1006 dc = ioloop.DelayedCallback(lambda : self._shutdown(), 1000, self.loop)
1006 dc = ioloop.DelayedCallback(lambda : self._shutdown(), 1000, self.loop)
1007 dc.start()
1007 dc.start()
1008
1008
1009 def _shutdown(self):
1009 def _shutdown(self):
1010 self.log.info("hub::hub shutting down.")
1010 self.log.info("hub::hub shutting down.")
1011 time.sleep(0.1)
1011 time.sleep(0.1)
1012 sys.exit(0)
1012 sys.exit(0)
1013
1013
1014
1014
1015 def check_load(self, client_id, msg):
1015 def check_load(self, client_id, msg):
1016 content = msg['content']
1016 content = msg['content']
1017 try:
1017 try:
@@ -1019,17 +1019,17 b' class Hub(SessionFactory):'
1019 targets = self._validate_targets(targets)
1019 targets = self._validate_targets(targets)
1020 except:
1020 except:
1021 content = error.wrap_exception()
1021 content = error.wrap_exception()
1022 self.session.send(self.query, "hub_error",
1022 self.session.send(self.query, "hub_error",
1023 content=content, ident=client_id)
1023 content=content, ident=client_id)
1024 return
1024 return
1025
1025
1026 content = dict(status='ok')
1026 content = dict(status='ok')
1027 # loads = {}
1027 # loads = {}
1028 for t in targets:
1028 for t in targets:
1029 content[bytes(t)] = len(self.queues[t])+len(self.tasks[t])
1029 content[bytes(t)] = len(self.queues[t])+len(self.tasks[t])
1030 self.session.send(self.query, "load_reply", content=content, ident=client_id)
1030 self.session.send(self.query, "load_reply", content=content, ident=client_id)
1031
1031
1032
1032
1033 def queue_status(self, client_id, msg):
1033 def queue_status(self, client_id, msg):
1034 """Return the Queue status of one or more targets.
1034 """Return the Queue status of one or more targets.
1035 if verbose: return the msg_ids
1035 if verbose: return the msg_ids
@@ -1043,7 +1043,7 b' class Hub(SessionFactory):'
1043 targets = self._validate_targets(targets)
1043 targets = self._validate_targets(targets)
1044 except:
1044 except:
1045 content = error.wrap_exception()
1045 content = error.wrap_exception()
1046 self.session.send(self.query, "hub_error",
1046 self.session.send(self.query, "hub_error",
1047 content=content, ident=client_id)
1047 content=content, ident=client_id)
1048 return
1048 return
1049 verbose = content.get('verbose', False)
1049 verbose = content.get('verbose', False)
@@ -1060,7 +1060,7 b' class Hub(SessionFactory):'
1060 content['unassigned'] = list(self.unassigned) if verbose else len(self.unassigned)
1060 content['unassigned'] = list(self.unassigned) if verbose else len(self.unassigned)
1061 # print (content)
1061 # print (content)
1062 self.session.send(self.query, "queue_reply", content=content, ident=client_id)
1062 self.session.send(self.query, "queue_reply", content=content, ident=client_id)
1063
1063
1064 def purge_results(self, client_id, msg):
1064 def purge_results(self, client_id, msg):
1065 """Purge results from memory. This method is more valuable before we move
1065 """Purge results from memory. This method is more valuable before we move
1066 to a DB based message storage mechanism."""
1066 to a DB based message storage mechanism."""
@@ -1101,9 +1101,9 b' class Hub(SessionFactory):'
1101 except Exception:
1101 except Exception:
1102 reply = error.wrap_exception()
1102 reply = error.wrap_exception()
1103 break
1103 break
1104
1104
1105 self.session.send(self.query, 'purge_reply', content=reply, ident=client_id)
1105 self.session.send(self.query, 'purge_reply', content=reply, ident=client_id)
1106
1106
1107 def resubmit_task(self, client_id, msg):
1107 def resubmit_task(self, client_id, msg):
1108 """Resubmit one or more tasks."""
1108 """Resubmit one or more tasks."""
1109 def finish(reply):
1109 def finish(reply):
@@ -1169,7 +1169,7 b' class Hub(SessionFactory):'
1169
1169
1170 finish(dict(status='ok'))
1170 finish(dict(status='ok'))
1171
1171
1172
1172
1173 def _extract_record(self, rec):
1173 def _extract_record(self, rec):
1174 """decompose a TaskRecord dict into subsection of reply for get_result"""
1174 """decompose a TaskRecord dict into subsection of reply for get_result"""
1175 io_dict = {}
1175 io_dict = {}
@@ -1184,9 +1184,9 b' class Hub(SessionFactory):'
1184 buffers = map(bytes, rec['result_buffers'])
1184 buffers = map(bytes, rec['result_buffers'])
1185 else:
1185 else:
1186 buffers = []
1186 buffers = []
1187
1187
1188 return content, buffers
1188 return content, buffers
1189
1189
1190 def get_results(self, client_id, msg):
1190 def get_results(self, client_id, msg):
1191 """Get the result of 1 or more messages."""
1191 """Get the result of 1 or more messages."""
1192 content = msg['content']
1192 content = msg['content']
@@ -1207,7 +1207,7 b' class Hub(SessionFactory):'
1207 records[rec['msg_id']] = rec
1207 records[rec['msg_id']] = rec
1208 except Exception:
1208 except Exception:
1209 content = error.wrap_exception()
1209 content = error.wrap_exception()
1210 self.session.send(self.query, "result_reply", content=content,
1210 self.session.send(self.query, "result_reply", content=content,
1211 parent=msg, ident=client_id)
1211 parent=msg, ident=client_id)
1212 return
1212 return
1213 else:
1213 else:
@@ -1235,7 +1235,7 b' class Hub(SessionFactory):'
1235 except:
1235 except:
1236 content = error.wrap_exception()
1236 content = error.wrap_exception()
1237 break
1237 break
1238 self.session.send(self.query, "result_reply", content=content,
1238 self.session.send(self.query, "result_reply", content=content,
1239 parent=msg, ident=client_id,
1239 parent=msg, ident=client_id,
1240 buffers=buffers)
1240 buffers=buffers)
1241
1241
@@ -1247,8 +1247,8 b' class Hub(SessionFactory):'
1247 content = error.wrap_exception()
1247 content = error.wrap_exception()
1248 else:
1248 else:
1249 content = dict(status='ok', history=msg_ids)
1249 content = dict(status='ok', history=msg_ids)
1250
1250
1251 self.session.send(self.query, "history_reply", content=content,
1251 self.session.send(self.query, "history_reply", content=content,
1252 parent=msg, ident=client_id)
1252 parent=msg, ident=client_id)
1253
1253
1254 def db_query(self, client_id, msg):
1254 def db_query(self, client_id, msg):
@@ -1270,7 +1270,7 b' class Hub(SessionFactory):'
1270 else:
1270 else:
1271 buffer_lens = []
1271 buffer_lens = []
1272 result_buffer_lens = []
1272 result_buffer_lens = []
1273
1273
1274 for rec in records:
1274 for rec in records:
1275 # buffers may be None, so double check
1275 # buffers may be None, so double check
1276 if buffer_lens is not None:
1276 if buffer_lens is not None:
@@ -1284,7 +1284,7 b' class Hub(SessionFactory):'
1284 content = dict(status='ok', records=records, buffer_lens=buffer_lens,
1284 content = dict(status='ok', records=records, buffer_lens=buffer_lens,
1285 result_buffer_lens=result_buffer_lens)
1285 result_buffer_lens=result_buffer_lens)
1286 # self.log.debug (content)
1286 # self.log.debug (content)
1287 self.session.send(self.query, "db_reply", content=content,
1287 self.session.send(self.query, "db_reply", content=content,
1288 parent=msg, ident=client_id,
1288 parent=msg, ident=client_id,
1289 buffers=buffers)
1289 buffers=buffers)
1290
1290
@@ -66,18 +66,18 b' def plainrandom(loads):'
66
66
67 def lru(loads):
67 def lru(loads):
68 """Always pick the front of the line.
68 """Always pick the front of the line.
69
69
70 The content of `loads` is ignored.
70 The content of `loads` is ignored.
71
71
72 Assumes LRU ordering of loads, with oldest first.
72 Assumes LRU ordering of loads, with oldest first.
73 """
73 """
74 return 0
74 return 0
75
75
76 def twobin(loads):
76 def twobin(loads):
77 """Pick two at random, use the LRU of the two.
77 """Pick two at random, use the LRU of the two.
78
78
79 The content of loads is ignored.
79 The content of loads is ignored.
80
80
81 Assumes LRU ordering of loads, with oldest first.
81 Assumes LRU ordering of loads, with oldest first.
82 """
82 """
83 n = len(loads)
83 n = len(loads)
@@ -87,7 +87,7 b' def twobin(loads):'
87
87
88 def weighted(loads):
88 def weighted(loads):
89 """Pick two at random using inverse load as weight.
89 """Pick two at random using inverse load as weight.
90
90
91 Return the less loaded of the two.
91 Return the less loaded of the two.
92 """
92 """
93 # weight 0 a million times more than 1:
93 # weight 0 a million times more than 1:
@@ -109,7 +109,7 b' def weighted(loads):'
109
109
110 def leastload(loads):
110 def leastload(loads):
111 """Always choose the lowest load.
111 """Always choose the lowest load.
112
112
113 If the lowest load occurs more than once, the first
113 If the lowest load occurs more than once, the first
114 occurance will be used. If loads has LRU ordering, this means
114 occurance will be used. If loads has LRU ordering, this means
115 the LRU of those with the lowest load is chosen.
115 the LRU of those with the lowest load is chosen.
@@ -124,13 +124,13 b' MET = Dependency([])'
124
124
125 class TaskScheduler(SessionFactory):
125 class TaskScheduler(SessionFactory):
126 """Python TaskScheduler object.
126 """Python TaskScheduler object.
127
127
128 This is the simplest object that supports msg_id based
128 This is the simplest object that supports msg_id based
129 DAG dependencies. *Only* task msg_ids are checked, not
129 DAG dependencies. *Only* task msg_ids are checked, not
130 msg_ids of jobs submitted via the MUX queue.
130 msg_ids of jobs submitted via the MUX queue.
131
131
132 """
132 """
133
133
134 hwm = Int(0, config=True, shortname='hwm',
134 hwm = Int(0, config=True, shortname='hwm',
135 help="""specify the High Water Mark (HWM) for the downstream
135 help="""specify the High Water Mark (HWM) for the downstream
136 socket in the Task scheduler. This is the maximum number
136 socket in the Task scheduler. This is the maximum number
@@ -144,7 +144,7 b' class TaskScheduler(SessionFactory):'
144 def _scheme_name_changed(self, old, new):
144 def _scheme_name_changed(self, old, new):
145 self.log.debug("Using scheme %r"%new)
145 self.log.debug("Using scheme %r"%new)
146 self.scheme = globals()[new]
146 self.scheme = globals()[new]
147
147
148 # input arguments:
148 # input arguments:
149 scheme = Instance(FunctionType) # function for determining the destination
149 scheme = Instance(FunctionType) # function for determining the destination
150 def _scheme_default(self):
150 def _scheme_default(self):
@@ -153,7 +153,7 b' class TaskScheduler(SessionFactory):'
153 engine_stream = Instance(zmqstream.ZMQStream) # engine-facing stream
153 engine_stream = Instance(zmqstream.ZMQStream) # engine-facing stream
154 notifier_stream = Instance(zmqstream.ZMQStream) # hub-facing sub stream
154 notifier_stream = Instance(zmqstream.ZMQStream) # hub-facing sub stream
155 mon_stream = Instance(zmqstream.ZMQStream) # hub-facing pub stream
155 mon_stream = Instance(zmqstream.ZMQStream) # hub-facing pub stream
156
156
157 # internals:
157 # internals:
158 graph = Dict() # dict by msg_id of [ msg_ids that depend on key ]
158 graph = Dict() # dict by msg_id of [ msg_ids that depend on key ]
159 retries = Dict() # dict by msg_id of retries remaining (non-neg ints)
159 retries = Dict() # dict by msg_id of retries remaining (non-neg ints)
@@ -173,12 +173,12 b' class TaskScheduler(SessionFactory):'
173 all_ids = Set() # set of all submitted task IDs
173 all_ids = Set() # set of all submitted task IDs
174 blacklist = Dict() # dict by msg_id of locations where a job has encountered UnmetDependency
174 blacklist = Dict() # dict by msg_id of locations where a job has encountered UnmetDependency
175 auditor = Instance('zmq.eventloop.ioloop.PeriodicCallback')
175 auditor = Instance('zmq.eventloop.ioloop.PeriodicCallback')
176
176
177 ident = CBytes() # ZMQ identity. This should just be self.session.session
177 ident = CBytes() # ZMQ identity. This should just be self.session.session
178 # but ensure Bytes
178 # but ensure Bytes
179 def _ident_default(self):
179 def _ident_default(self):
180 return self.session.bsession
180 return self.session.bsession
181
181
182 def start(self):
182 def start(self):
183 self.engine_stream.on_recv(self.dispatch_result, copy=False)
183 self.engine_stream.on_recv(self.dispatch_result, copy=False)
184 self._notification_handlers = dict(
184 self._notification_handlers = dict(
@@ -189,20 +189,20 b' class TaskScheduler(SessionFactory):'
189 self.auditor = ioloop.PeriodicCallback(self.audit_timeouts, 2e3, self.loop) # 1 Hz
189 self.auditor = ioloop.PeriodicCallback(self.audit_timeouts, 2e3, self.loop) # 1 Hz
190 self.auditor.start()
190 self.auditor.start()
191 self.log.info("Scheduler started [%s]"%self.scheme_name)
191 self.log.info("Scheduler started [%s]"%self.scheme_name)
192
192
193 def resume_receiving(self):
193 def resume_receiving(self):
194 """Resume accepting jobs."""
194 """Resume accepting jobs."""
195 self.client_stream.on_recv(self.dispatch_submission, copy=False)
195 self.client_stream.on_recv(self.dispatch_submission, copy=False)
196
196
197 def stop_receiving(self):
197 def stop_receiving(self):
198 """Stop accepting jobs while there are no engines.
198 """Stop accepting jobs while there are no engines.
199 Leave them in the ZMQ queue."""
199 Leave them in the ZMQ queue."""
200 self.client_stream.on_recv(None)
200 self.client_stream.on_recv(None)
201
201
202 #-----------------------------------------------------------------------
202 #-----------------------------------------------------------------------
203 # [Un]Registration Handling
203 # [Un]Registration Handling
204 #-----------------------------------------------------------------------
204 #-----------------------------------------------------------------------
205
205
206 def dispatch_notification(self, msg):
206 def dispatch_notification(self, msg):
207 """dispatch register/unregister events."""
207 """dispatch register/unregister events."""
208 try:
208 try:
@@ -215,9 +215,9 b' class TaskScheduler(SessionFactory):'
215 except ValueError:
215 except ValueError:
216 self.log.warn("task::Unauthorized message from: %r"%idents)
216 self.log.warn("task::Unauthorized message from: %r"%idents)
217 return
217 return
218
218
219 msg_type = msg['header']['msg_type']
219 msg_type = msg['header']['msg_type']
220
220
221 handler = self._notification_handlers.get(msg_type, None)
221 handler = self._notification_handlers.get(msg_type, None)
222 if handler is None:
222 if handler is None:
223 self.log.error("Unhandled message type: %r"%msg_type)
223 self.log.error("Unhandled message type: %r"%msg_type)
@@ -226,7 +226,7 b' class TaskScheduler(SessionFactory):'
226 handler(asbytes(msg['content']['queue']))
226 handler(asbytes(msg['content']['queue']))
227 except Exception:
227 except Exception:
228 self.log.error("task::Invalid notification msg: %r",msg)
228 self.log.error("task::Invalid notification msg: %r",msg)
229
229
230 def _register_engine(self, uid):
230 def _register_engine(self, uid):
231 """New engine with ident `uid` became available."""
231 """New engine with ident `uid` became available."""
232 # head of the line:
232 # head of the line:
@@ -247,10 +247,10 b' class TaskScheduler(SessionFactory):'
247 if len(self.targets) == 1:
247 if len(self.targets) == 1:
248 # this was our only engine
248 # this was our only engine
249 self.stop_receiving()
249 self.stop_receiving()
250
250
251 # handle any potentially finished tasks:
251 # handle any potentially finished tasks:
252 self.engine_stream.flush()
252 self.engine_stream.flush()
253
253
254 # don't pop destinations, because they might be used later
254 # don't pop destinations, because they might be used later
255 # map(self.destinations.pop, self.completed.pop(uid))
255 # map(self.destinations.pop, self.completed.pop(uid))
256 # map(self.destinations.pop, self.failed.pop(uid))
256 # map(self.destinations.pop, self.failed.pop(uid))
@@ -259,7 +259,7 b' class TaskScheduler(SessionFactory):'
259 idx = self.targets.index(uid)
259 idx = self.targets.index(uid)
260 self.targets.pop(idx)
260 self.targets.pop(idx)
261 self.loads.pop(idx)
261 self.loads.pop(idx)
262
262
263 # wait 5 seconds before cleaning up pending jobs, since the results might
263 # wait 5 seconds before cleaning up pending jobs, since the results might
264 # still be incoming
264 # still be incoming
265 if self.pending[uid]:
265 if self.pending[uid]:
@@ -269,7 +269,7 b' class TaskScheduler(SessionFactory):'
269 self.completed.pop(uid)
269 self.completed.pop(uid)
270 self.failed.pop(uid)
270 self.failed.pop(uid)
271
271
272
272
273 def handle_stranded_tasks(self, engine):
273 def handle_stranded_tasks(self, engine):
274 """Deal with jobs resident in an engine that died."""
274 """Deal with jobs resident in an engine that died."""
275 lost = self.pending[engine]
275 lost = self.pending[engine]
@@ -296,8 +296,8 b' class TaskScheduler(SessionFactory):'
296 # finally scrub completed/failed lists
296 # finally scrub completed/failed lists
297 self.completed.pop(engine)
297 self.completed.pop(engine)
298 self.failed.pop(engine)
298 self.failed.pop(engine)
299
299
300
300
301 #-----------------------------------------------------------------------
301 #-----------------------------------------------------------------------
302 # Job Submission
302 # Job Submission
303 #-----------------------------------------------------------------------
303 #-----------------------------------------------------------------------
@@ -311,24 +311,24 b' class TaskScheduler(SessionFactory):'
311 except Exception:
311 except Exception:
312 self.log.error("task::Invaid task msg: %r"%raw_msg, exc_info=True)
312 self.log.error("task::Invaid task msg: %r"%raw_msg, exc_info=True)
313 return
313 return
314
314
315
315
316 # send to monitor
316 # send to monitor
317 self.mon_stream.send_multipart([b'intask']+raw_msg, copy=False)
317 self.mon_stream.send_multipart([b'intask']+raw_msg, copy=False)
318
318
319 header = msg['header']
319 header = msg['header']
320 msg_id = header['msg_id']
320 msg_id = header['msg_id']
321 self.all_ids.add(msg_id)
321 self.all_ids.add(msg_id)
322
322
323 # get targets as a set of bytes objects
323 # get targets as a set of bytes objects
324 # from a list of unicode objects
324 # from a list of unicode objects
325 targets = header.get('targets', [])
325 targets = header.get('targets', [])
326 targets = map(asbytes, targets)
326 targets = map(asbytes, targets)
327 targets = set(targets)
327 targets = set(targets)
328
328
329 retries = header.get('retries', 0)
329 retries = header.get('retries', 0)
330 self.retries[msg_id] = retries
330 self.retries[msg_id] = retries
331
331
332 # time dependencies
332 # time dependencies
333 after = header.get('after', None)
333 after = header.get('after', None)
334 if after:
334 if after:
@@ -352,17 +352,17 b' class TaskScheduler(SessionFactory):'
352 after = MET
352 after = MET
353 else:
353 else:
354 after = MET
354 after = MET
355
355
356 # location dependencies
356 # location dependencies
357 follow = Dependency(header.get('follow', []))
357 follow = Dependency(header.get('follow', []))
358
358
359 # turn timeouts into datetime objects:
359 # turn timeouts into datetime objects:
360 timeout = header.get('timeout', None)
360 timeout = header.get('timeout', None)
361 if timeout:
361 if timeout:
362 timeout = datetime.now() + timedelta(0,timeout,0)
362 timeout = datetime.now() + timedelta(0,timeout,0)
363
363
364 args = [raw_msg, targets, after, follow, timeout]
364 args = [raw_msg, targets, after, follow, timeout]
365
365
366 # validate and reduce dependencies:
366 # validate and reduce dependencies:
367 for dep in after,follow:
367 for dep in after,follow:
368 if not dep: # empty dependency
368 if not dep: # empty dependency
@@ -375,7 +375,7 b' class TaskScheduler(SessionFactory):'
375 if dep.unreachable(self.all_completed, self.all_failed):
375 if dep.unreachable(self.all_completed, self.all_failed):
376 self.depending[msg_id] = args
376 self.depending[msg_id] = args
377 return self.fail_unreachable(msg_id)
377 return self.fail_unreachable(msg_id)
378
378
379 if after.check(self.all_completed, self.all_failed):
379 if after.check(self.all_completed, self.all_failed):
380 # time deps already met, try to run
380 # time deps already met, try to run
381 if not self.maybe_run(msg_id, *args):
381 if not self.maybe_run(msg_id, *args):
@@ -385,7 +385,7 b' class TaskScheduler(SessionFactory):'
385 self.save_unmet(msg_id, *args)
385 self.save_unmet(msg_id, *args)
386 else:
386 else:
387 self.save_unmet(msg_id, *args)
387 self.save_unmet(msg_id, *args)
388
388
389 def audit_timeouts(self):
389 def audit_timeouts(self):
390 """Audit all waiting tasks for expired timeouts."""
390 """Audit all waiting tasks for expired timeouts."""
391 now = datetime.now()
391 now = datetime.now()
@@ -395,7 +395,7 b' class TaskScheduler(SessionFactory):'
395 raw,after,targets,follow,timeout = self.depending[msg_id]
395 raw,after,targets,follow,timeout = self.depending[msg_id]
396 if timeout and timeout < now:
396 if timeout and timeout < now:
397 self.fail_unreachable(msg_id, error.TaskTimeout)
397 self.fail_unreachable(msg_id, error.TaskTimeout)
398
398
399 def fail_unreachable(self, msg_id, why=error.ImpossibleDependency):
399 def fail_unreachable(self, msg_id, why=error.ImpossibleDependency):
400 """a task has become unreachable, send a reply with an ImpossibleDependency
400 """a task has become unreachable, send a reply with an ImpossibleDependency
401 error."""
401 error."""
@@ -406,25 +406,25 b' class TaskScheduler(SessionFactory):'
406 for mid in follow.union(after):
406 for mid in follow.union(after):
407 if mid in self.graph:
407 if mid in self.graph:
408 self.graph[mid].remove(msg_id)
408 self.graph[mid].remove(msg_id)
409
409
410 # FIXME: unpacking a message I've already unpacked, but didn't save:
410 # FIXME: unpacking a message I've already unpacked, but didn't save:
411 idents,msg = self.session.feed_identities(raw_msg, copy=False)
411 idents,msg = self.session.feed_identities(raw_msg, copy=False)
412 header = self.session.unpack(msg[1].bytes)
412 header = self.session.unpack(msg[1].bytes)
413
413
414 try:
414 try:
415 raise why()
415 raise why()
416 except:
416 except:
417 content = error.wrap_exception()
417 content = error.wrap_exception()
418
418
419 self.all_done.add(msg_id)
419 self.all_done.add(msg_id)
420 self.all_failed.add(msg_id)
420 self.all_failed.add(msg_id)
421
421
422 msg = self.session.send(self.client_stream, 'apply_reply', content,
422 msg = self.session.send(self.client_stream, 'apply_reply', content,
423 parent=header, ident=idents)
423 parent=header, ident=idents)
424 self.session.send(self.mon_stream, msg, ident=[b'outtask']+idents)
424 self.session.send(self.mon_stream, msg, ident=[b'outtask']+idents)
425
425
426 self.update_graph(msg_id, success=False)
426 self.update_graph(msg_id, success=False)
427
427
428 def maybe_run(self, msg_id, raw_msg, targets, after, follow, timeout):
428 def maybe_run(self, msg_id, raw_msg, targets, after, follow, timeout):
429 """check location dependencies, and run if they are met."""
429 """check location dependencies, and run if they are met."""
430 blacklist = self.blacklist.setdefault(msg_id, set())
430 blacklist = self.blacklist.setdefault(msg_id, set())
@@ -443,7 +443,7 b' class TaskScheduler(SessionFactory):'
443 return False
443 return False
444 # check follow
444 # check follow
445 return follow.check(self.completed[target], self.failed[target])
445 return follow.check(self.completed[target], self.failed[target])
446
446
447 indices = filter(can_run, range(len(self.targets)))
447 indices = filter(can_run, range(len(self.targets)))
448
448
449 if not indices:
449 if not indices:
@@ -472,10 +472,10 b' class TaskScheduler(SessionFactory):'
472 return False
472 return False
473 else:
473 else:
474 indices = None
474 indices = None
475
475
476 self.submit_task(msg_id, raw_msg, targets, follow, timeout, indices)
476 self.submit_task(msg_id, raw_msg, targets, follow, timeout, indices)
477 return True
477 return True
478
478
479 def save_unmet(self, msg_id, raw_msg, targets, after, follow, timeout):
479 def save_unmet(self, msg_id, raw_msg, targets, after, follow, timeout):
480 """Save a message for later submission when its dependencies are met."""
480 """Save a message for later submission when its dependencies are met."""
481 self.depending[msg_id] = [raw_msg,targets,after,follow,timeout]
481 self.depending[msg_id] = [raw_msg,targets,after,follow,timeout]
@@ -484,7 +484,7 b' class TaskScheduler(SessionFactory):'
484 if dep_id not in self.graph:
484 if dep_id not in self.graph:
485 self.graph[dep_id] = set()
485 self.graph[dep_id] = set()
486 self.graph[dep_id].add(msg_id)
486 self.graph[dep_id].add(msg_id)
487
487
488 def submit_task(self, msg_id, raw_msg, targets, follow, timeout, indices=None):
488 def submit_task(self, msg_id, raw_msg, targets, follow, timeout, indices=None):
489 """Submit a task to any of a subset of our targets."""
489 """Submit a task to any of a subset of our targets."""
490 if indices:
490 if indices:
@@ -504,10 +504,10 b' class TaskScheduler(SessionFactory):'
504 self.pending[target][msg_id] = (raw_msg, targets, MET, follow, timeout)
504 self.pending[target][msg_id] = (raw_msg, targets, MET, follow, timeout)
505 # notify Hub
505 # notify Hub
506 content = dict(msg_id=msg_id, engine_id=target.decode('ascii'))
506 content = dict(msg_id=msg_id, engine_id=target.decode('ascii'))
507 self.session.send(self.mon_stream, 'task_destination', content=content,
507 self.session.send(self.mon_stream, 'task_destination', content=content,
508 ident=[b'tracktask',self.ident])
508 ident=[b'tracktask',self.ident])
509
509
510
510
511 #-----------------------------------------------------------------------
511 #-----------------------------------------------------------------------
512 # Result Handling
512 # Result Handling
513 #-----------------------------------------------------------------------
513 #-----------------------------------------------------------------------
@@ -545,7 +545,7 b' class TaskScheduler(SessionFactory):'
545 self.mon_stream.send_multipart([b'outtask']+raw_msg, copy=False)
545 self.mon_stream.send_multipart([b'outtask']+raw_msg, copy=False)
546 else:
546 else:
547 self.handle_unmet_dependency(idents, parent)
547 self.handle_unmet_dependency(idents, parent)
548
548
549 def handle_result(self, idents, parent, raw_msg, success=True):
549 def handle_result(self, idents, parent, raw_msg, success=True):
550 """handle a real task result, either success or failure"""
550 """handle a real task result, either success or failure"""
551 # first, relay result to client
551 # first, relay result to client
@@ -567,21 +567,21 b' class TaskScheduler(SessionFactory):'
567 self.all_failed.add(msg_id)
567 self.all_failed.add(msg_id)
568 self.all_done.add(msg_id)
568 self.all_done.add(msg_id)
569 self.destinations[msg_id] = engine
569 self.destinations[msg_id] = engine
570
570
571 self.update_graph(msg_id, success)
571 self.update_graph(msg_id, success)
572
572
573 def handle_unmet_dependency(self, idents, parent):
573 def handle_unmet_dependency(self, idents, parent):
574 """handle an unmet dependency"""
574 """handle an unmet dependency"""
575 engine = idents[0]
575 engine = idents[0]
576 msg_id = parent['msg_id']
576 msg_id = parent['msg_id']
577
577
578 if msg_id not in self.blacklist:
578 if msg_id not in self.blacklist:
579 self.blacklist[msg_id] = set()
579 self.blacklist[msg_id] = set()
580 self.blacklist[msg_id].add(engine)
580 self.blacklist[msg_id].add(engine)
581
581
582 args = self.pending[engine].pop(msg_id)
582 args = self.pending[engine].pop(msg_id)
583 raw,targets,after,follow,timeout = args
583 raw,targets,after,follow,timeout = args
584
584
585 if self.blacklist[msg_id] == targets:
585 if self.blacklist[msg_id] == targets:
586 self.depending[msg_id] = args
586 self.depending[msg_id] = args
587 self.fail_unreachable(msg_id)
587 self.fail_unreachable(msg_id)
@@ -590,7 +590,7 b' class TaskScheduler(SessionFactory):'
590 if msg_id not in self.all_failed:
590 if msg_id not in self.all_failed:
591 # put it back in our dependency tree
591 # put it back in our dependency tree
592 self.save_unmet(msg_id, *args)
592 self.save_unmet(msg_id, *args)
593
593
594 if self.hwm:
594 if self.hwm:
595 try:
595 try:
596 idx = self.targets.index(engine)
596 idx = self.targets.index(engine)
@@ -599,13 +599,13 b' class TaskScheduler(SessionFactory):'
599 else:
599 else:
600 if self.loads[idx] == self.hwm-1:
600 if self.loads[idx] == self.hwm-1:
601 self.update_graph(None)
601 self.update_graph(None)
602
602
603
603
604
604
605 def update_graph(self, dep_id=None, success=True):
605 def update_graph(self, dep_id=None, success=True):
606 """dep_id just finished. Update our dependency
606 """dep_id just finished. Update our dependency
607 graph and submit any jobs that just became runable.
607 graph and submit any jobs that just became runable.
608
608
609 Called with dep_id=None to update entire graph for hwm, but without finishing
609 Called with dep_id=None to update entire graph for hwm, but without finishing
610 a task.
610 a task.
611 """
611 """
@@ -624,26 +624,26 b' class TaskScheduler(SessionFactory):'
624 # or b) dep_id was given as None
624 # or b) dep_id was given as None
625 if dep_id is None or self.hwm and any( [ load==self.hwm-1 for load in self.loads ]):
625 if dep_id is None or self.hwm and any( [ load==self.hwm-1 for load in self.loads ]):
626 jobs = self.depending.keys()
626 jobs = self.depending.keys()
627
627
628 for msg_id in jobs:
628 for msg_id in jobs:
629 raw_msg, targets, after, follow, timeout = self.depending[msg_id]
629 raw_msg, targets, after, follow, timeout = self.depending[msg_id]
630
630
631 if after.unreachable(self.all_completed, self.all_failed)\
631 if after.unreachable(self.all_completed, self.all_failed)\
632 or follow.unreachable(self.all_completed, self.all_failed):
632 or follow.unreachable(self.all_completed, self.all_failed):
633 self.fail_unreachable(msg_id)
633 self.fail_unreachable(msg_id)
634
634
635 elif after.check(self.all_completed, self.all_failed): # time deps met, maybe run
635 elif after.check(self.all_completed, self.all_failed): # time deps met, maybe run
636 if self.maybe_run(msg_id, raw_msg, targets, MET, follow, timeout):
636 if self.maybe_run(msg_id, raw_msg, targets, MET, follow, timeout):
637
637
638 self.depending.pop(msg_id)
638 self.depending.pop(msg_id)
639 for mid in follow.union(after):
639 for mid in follow.union(after):
640 if mid in self.graph:
640 if mid in self.graph:
641 self.graph[mid].remove(msg_id)
641 self.graph[mid].remove(msg_id)
642
642
643 #----------------------------------------------------------------------
643 #----------------------------------------------------------------------
644 # methods to be overridden by subclasses
644 # methods to be overridden by subclasses
645 #----------------------------------------------------------------------
645 #----------------------------------------------------------------------
646
646
647 def add_job(self, idx):
647 def add_job(self, idx):
648 """Called after self.targets[idx] just got the job with header.
648 """Called after self.targets[idx] just got the job with header.
649 Override with subclasses. The default ordering is simple LRU.
649 Override with subclasses. The default ordering is simple LRU.
@@ -651,21 +651,21 b' class TaskScheduler(SessionFactory):'
651 self.loads[idx] += 1
651 self.loads[idx] += 1
652 for lis in (self.targets, self.loads):
652 for lis in (self.targets, self.loads):
653 lis.append(lis.pop(idx))
653 lis.append(lis.pop(idx))
654
654
655
655
656 def finish_job(self, idx):
656 def finish_job(self, idx):
657 """Called after self.targets[idx] just finished a job.
657 """Called after self.targets[idx] just finished a job.
658 Override with subclasses."""
658 Override with subclasses."""
659 self.loads[idx] -= 1
659 self.loads[idx] -= 1
660
660
661
661
662
662
663 def launch_scheduler(in_addr, out_addr, mon_addr, not_addr, config=None,
663 def launch_scheduler(in_addr, out_addr, mon_addr, not_addr, config=None,
664 logname='root', log_url=None, loglevel=logging.DEBUG,
664 logname='root', log_url=None, loglevel=logging.DEBUG,
665 identity=b'task', in_thread=False):
665 identity=b'task', in_thread=False):
666
666
667 ZMQStream = zmqstream.ZMQStream
667 ZMQStream = zmqstream.ZMQStream
668
668
669 if config:
669 if config:
670 # unwrap dict back into Config
670 # unwrap dict back into Config
671 config = Config(config)
671 config = Config(config)
@@ -675,14 +675,14 b' def launch_scheduler(in_addr, out_addr, mon_addr, not_addr, config=None,'
675 ctx = zmq.Context.instance()
675 ctx = zmq.Context.instance()
676 loop = ioloop.IOLoop.instance()
676 loop = ioloop.IOLoop.instance()
677 else:
677 else:
678 # in a process, don't use instance()
678 # in a process, don't use instance()
679 # for safety with multiprocessing
679 # for safety with multiprocessing
680 ctx = zmq.Context()
680 ctx = zmq.Context()
681 loop = ioloop.IOLoop()
681 loop = ioloop.IOLoop()
682 ins = ZMQStream(ctx.socket(zmq.ROUTER),loop)
682 ins = ZMQStream(ctx.socket(zmq.ROUTER),loop)
683 ins.setsockopt(zmq.IDENTITY, identity)
683 ins.setsockopt(zmq.IDENTITY, identity)
684 ins.bind(in_addr)
684 ins.bind(in_addr)
685
685
686 outs = ZMQStream(ctx.socket(zmq.ROUTER),loop)
686 outs = ZMQStream(ctx.socket(zmq.ROUTER),loop)
687 outs.setsockopt(zmq.IDENTITY, identity)
687 outs.setsockopt(zmq.IDENTITY, identity)
688 outs.bind(out_addr)
688 outs.bind(out_addr)
@@ -691,7 +691,7 b' def launch_scheduler(in_addr, out_addr, mon_addr, not_addr, config=None,'
691 nots = zmqstream.ZMQStream(ctx.socket(zmq.SUB),loop)
691 nots = zmqstream.ZMQStream(ctx.socket(zmq.SUB),loop)
692 nots.setsockopt(zmq.SUBSCRIBE, b'')
692 nots.setsockopt(zmq.SUBSCRIBE, b'')
693 nots.connect(not_addr)
693 nots.connect(not_addr)
694
694
695 # setup logging.
695 # setup logging.
696 if in_thread:
696 if in_thread:
697 log = Application.instance().log
697 log = Application.instance().log
@@ -700,7 +700,7 b' def launch_scheduler(in_addr, out_addr, mon_addr, not_addr, config=None,'
700 log = connect_logger(logname, ctx, log_url, root="scheduler", loglevel=loglevel)
700 log = connect_logger(logname, ctx, log_url, root="scheduler", loglevel=loglevel)
701 else:
701 else:
702 log = local_logger(logname, loglevel)
702 log = local_logger(logname, loglevel)
703
703
704 scheduler = TaskScheduler(client_stream=ins, engine_stream=outs,
704 scheduler = TaskScheduler(client_stream=ins, engine_stream=outs,
705 mon_stream=mons, notifier_stream=nots,
705 mon_stream=mons, notifier_stream=nots,
706 loop=loop, log=log,
706 loop=loop, log=log,
@@ -87,7 +87,7 b' def _convert_bufs(bs):'
87
87
88 class SQLiteDB(BaseDB):
88 class SQLiteDB(BaseDB):
89 """SQLite3 TaskRecord backend."""
89 """SQLite3 TaskRecord backend."""
90
90
91 filename = Unicode('tasks.db', config=True,
91 filename = Unicode('tasks.db', config=True,
92 help="""The filename of the sqlite task database. [default: 'tasks.db']""")
92 help="""The filename of the sqlite task database. [default: 'tasks.db']""")
93 location = Unicode('', config=True,
93 location = Unicode('', config=True,
@@ -98,7 +98,7 b' class SQLiteDB(BaseDB):'
98 a new table will be created with the Hub's IDENT. Specifying the table will result
98 a new table will be created with the Hub's IDENT. Specifying the table will result
99 in tasks from previous sessions being available via Clients' db_query and
99 in tasks from previous sessions being available via Clients' db_query and
100 get_result methods.""")
100 get_result methods.""")
101
101
102 _db = Instance('sqlite3.Connection')
102 _db = Instance('sqlite3.Connection')
103 # the ordered list of column names
103 # the ordered list of column names
104 _keys = List(['msg_id' ,
104 _keys = List(['msg_id' ,
@@ -142,7 +142,7 b' class SQLiteDB(BaseDB):'
142 'stdout' : 'text',
142 'stdout' : 'text',
143 'stderr' : 'text',
143 'stderr' : 'text',
144 })
144 })
145
145
146 def __init__(self, **kwargs):
146 def __init__(self, **kwargs):
147 super(SQLiteDB, self).__init__(**kwargs)
147 super(SQLiteDB, self).__init__(**kwargs)
148 if not self.table:
148 if not self.table:
@@ -160,14 +160,14 b' class SQLiteDB(BaseDB):'
160 else:
160 else:
161 self.location = u'.'
161 self.location = u'.'
162 self._init_db()
162 self._init_db()
163
163
164 # register db commit as 2s periodic callback
164 # register db commit as 2s periodic callback
165 # to prevent clogging pipes
165 # to prevent clogging pipes
166 # assumes we are being run in a zmq ioloop app
166 # assumes we are being run in a zmq ioloop app
167 loop = ioloop.IOLoop.instance()
167 loop = ioloop.IOLoop.instance()
168 pc = ioloop.PeriodicCallback(self._db.commit, 2000, loop)
168 pc = ioloop.PeriodicCallback(self._db.commit, 2000, loop)
169 pc.start()
169 pc.start()
170
170
171 def _defaults(self, keys=None):
171 def _defaults(self, keys=None):
172 """create an empty record"""
172 """create an empty record"""
173 d = {}
173 d = {}
@@ -175,10 +175,10 b' class SQLiteDB(BaseDB):'
175 for key in keys:
175 for key in keys:
176 d[key] = None
176 d[key] = None
177 return d
177 return d
178
178
179 def _check_table(self):
179 def _check_table(self):
180 """Ensure that an incorrect table doesn't exist
180 """Ensure that an incorrect table doesn't exist
181
181
182 If a bad (old) table does exist, return False
182 If a bad (old) table does exist, return False
183 """
183 """
184 cursor = self._db.execute("PRAGMA table_info(%s)"%self.table)
184 cursor = self._db.execute("PRAGMA table_info(%s)"%self.table)
@@ -202,7 +202,7 b' class SQLiteDB(BaseDB):'
202 )
202 )
203 return False
203 return False
204 return True
204 return True
205
205
206 def _init_db(self):
206 def _init_db(self):
207 """Connect to the database and get new session number."""
207 """Connect to the database and get new session number."""
208 # register adapters
208 # register adapters
@@ -212,7 +212,7 b' class SQLiteDB(BaseDB):'
212 sqlite3.register_converter('bufs', _convert_bufs)
212 sqlite3.register_converter('bufs', _convert_bufs)
213 # connect to the db
213 # connect to the db
214 dbfile = os.path.join(self.location, self.filename)
214 dbfile = os.path.join(self.location, self.filename)
215 self._db = sqlite3.connect(dbfile, detect_types=sqlite3.PARSE_DECLTYPES,
215 self._db = sqlite3.connect(dbfile, detect_types=sqlite3.PARSE_DECLTYPES,
216 # isolation_level = None)#,
216 # isolation_level = None)#,
217 cached_statements=64)
217 cached_statements=64)
218 # print dir(self._db)
218 # print dir(self._db)
@@ -225,8 +225,8 b' class SQLiteDB(BaseDB):'
225 "Table %s exists and doesn't match db format, trying %s"%
225 "Table %s exists and doesn't match db format, trying %s"%
226 (first_table,self.table)
226 (first_table,self.table)
227 )
227 )
228
228
229 self._db.execute("""CREATE TABLE IF NOT EXISTS %s
229 self._db.execute("""CREATE TABLE IF NOT EXISTS %s
230 (msg_id text PRIMARY KEY,
230 (msg_id text PRIMARY KEY,
231 header dict text,
231 header dict text,
232 content dict text,
232 content dict text,
@@ -248,32 +248,32 b' class SQLiteDB(BaseDB):'
248 stderr text)
248 stderr text)
249 """%self.table)
249 """%self.table)
250 self._db.commit()
250 self._db.commit()
251
251
252 def _dict_to_list(self, d):
252 def _dict_to_list(self, d):
253 """turn a mongodb-style record dict into a list."""
253 """turn a mongodb-style record dict into a list."""
254
254
255 return [ d[key] for key in self._keys ]
255 return [ d[key] for key in self._keys ]
256
256
257 def _list_to_dict(self, line, keys=None):
257 def _list_to_dict(self, line, keys=None):
258 """Inverse of dict_to_list"""
258 """Inverse of dict_to_list"""
259 keys = self._keys if keys is None else keys
259 keys = self._keys if keys is None else keys
260 d = self._defaults(keys)
260 d = self._defaults(keys)
261 for key,value in zip(keys, line):
261 for key,value in zip(keys, line):
262 d[key] = value
262 d[key] = value
263
263
264 return d
264 return d
265
265
266 def _render_expression(self, check):
266 def _render_expression(self, check):
267 """Turn a mongodb-style search dict into an SQL query."""
267 """Turn a mongodb-style search dict into an SQL query."""
268 expressions = []
268 expressions = []
269 args = []
269 args = []
270
270
271 skeys = set(check.keys())
271 skeys = set(check.keys())
272 skeys.difference_update(set(self._keys))
272 skeys.difference_update(set(self._keys))
273 skeys.difference_update(set(['buffers', 'result_buffers']))
273 skeys.difference_update(set(['buffers', 'result_buffers']))
274 if skeys:
274 if skeys:
275 raise KeyError("Illegal testing key(s): %s"%skeys)
275 raise KeyError("Illegal testing key(s): %s"%skeys)
276
276
277 for name,sub_check in check.iteritems():
277 for name,sub_check in check.iteritems():
278 if isinstance(sub_check, dict):
278 if isinstance(sub_check, dict):
279 for test,value in sub_check.iteritems():
279 for test,value in sub_check.iteritems():
@@ -283,7 +283,7 b' class SQLiteDB(BaseDB):'
283 raise KeyError("Unsupported operator: %r"%test)
283 raise KeyError("Unsupported operator: %r"%test)
284 if isinstance(op, tuple):
284 if isinstance(op, tuple):
285 op, join = op
285 op, join = op
286
286
287 if value is None and op in null_operators:
287 if value is None and op in null_operators:
288 expr = "%s %s"%null_operators[op]
288 expr = "%s %s"%null_operators[op]
289 else:
289 else:
@@ -304,10 +304,10 b' class SQLiteDB(BaseDB):'
304 else:
304 else:
305 expressions.append("%s = ?"%name)
305 expressions.append("%s = ?"%name)
306 args.append(sub_check)
306 args.append(sub_check)
307
307
308 expr = " AND ".join(expressions)
308 expr = " AND ".join(expressions)
309 return expr, args
309 return expr, args
310
310
311 def add_record(self, msg_id, rec):
311 def add_record(self, msg_id, rec):
312 """Add a new Task Record, by msg_id."""
312 """Add a new Task Record, by msg_id."""
313 d = self._defaults()
313 d = self._defaults()
@@ -317,7 +317,7 b' class SQLiteDB(BaseDB):'
317 tups = '(%s)'%(','.join(['?']*len(line)))
317 tups = '(%s)'%(','.join(['?']*len(line)))
318 self._db.execute("INSERT INTO %s VALUES %s"%(self.table, tups), line)
318 self._db.execute("INSERT INTO %s VALUES %s"%(self.table, tups), line)
319 # self._db.commit()
319 # self._db.commit()
320
320
321 def get_record(self, msg_id):
321 def get_record(self, msg_id):
322 """Get a specific Task Record, by msg_id."""
322 """Get a specific Task Record, by msg_id."""
323 cursor = self._db.execute("""SELECT * FROM %s WHERE msg_id==?"""%self.table, (msg_id,))
323 cursor = self._db.execute("""SELECT * FROM %s WHERE msg_id==?"""%self.table, (msg_id,))
@@ -325,7 +325,7 b' class SQLiteDB(BaseDB):'
325 if line is None:
325 if line is None:
326 raise KeyError("No such msg: %r"%msg_id)
326 raise KeyError("No such msg: %r"%msg_id)
327 return self._list_to_dict(line)
327 return self._list_to_dict(line)
328
328
329 def update_record(self, msg_id, rec):
329 def update_record(self, msg_id, rec):
330 """Update the data in an existing record."""
330 """Update the data in an existing record."""
331 query = "UPDATE %s SET "%self.table
331 query = "UPDATE %s SET "%self.table
@@ -340,27 +340,27 b' class SQLiteDB(BaseDB):'
340 values.append(msg_id)
340 values.append(msg_id)
341 self._db.execute(query, values)
341 self._db.execute(query, values)
342 # self._db.commit()
342 # self._db.commit()
343
343
344 def drop_record(self, msg_id):
344 def drop_record(self, msg_id):
345 """Remove a record from the DB."""
345 """Remove a record from the DB."""
346 self._db.execute("""DELETE FROM %s WHERE msg_id==?"""%self.table, (msg_id,))
346 self._db.execute("""DELETE FROM %s WHERE msg_id==?"""%self.table, (msg_id,))
347 # self._db.commit()
347 # self._db.commit()
348
348
349 def drop_matching_records(self, check):
349 def drop_matching_records(self, check):
350 """Remove a record from the DB."""
350 """Remove a record from the DB."""
351 expr,args = self._render_expression(check)
351 expr,args = self._render_expression(check)
352 query = "DELETE FROM %s WHERE %s"%(self.table, expr)
352 query = "DELETE FROM %s WHERE %s"%(self.table, expr)
353 self._db.execute(query,args)
353 self._db.execute(query,args)
354 # self._db.commit()
354 # self._db.commit()
355
355
356 def find_records(self, check, keys=None):
356 def find_records(self, check, keys=None):
357 """Find records matching a query dict, optionally extracting subset of keys.
357 """Find records matching a query dict, optionally extracting subset of keys.
358
358
359 Returns list of matching records.
359 Returns list of matching records.
360
360
361 Parameters
361 Parameters
362 ----------
362 ----------
363
363
364 check: dict
364 check: dict
365 mongodb-style query argument
365 mongodb-style query argument
366 keys: list of strs [optional]
366 keys: list of strs [optional]
@@ -371,7 +371,7 b' class SQLiteDB(BaseDB):'
371 bad_keys = [ key for key in keys if key not in self._keys ]
371 bad_keys = [ key for key in keys if key not in self._keys ]
372 if bad_keys:
372 if bad_keys:
373 raise KeyError("Bad record key(s): %s"%bad_keys)
373 raise KeyError("Bad record key(s): %s"%bad_keys)
374
374
375 if keys:
375 if keys:
376 # ensure msg_id is present and first:
376 # ensure msg_id is present and first:
377 if 'msg_id' in keys:
377 if 'msg_id' in keys:
@@ -389,7 +389,7 b' class SQLiteDB(BaseDB):'
389 rec = self._list_to_dict(line, keys)
389 rec = self._list_to_dict(line, keys)
390 records.append(rec)
390 records.append(rec)
391 return records
391 return records
392
392
393 def get_history(self):
393 def get_history(self):
394 """get all msg_ids, ordered by time submitted."""
394 """get all msg_ids, ordered by time submitted."""
395 query = """SELECT msg_id FROM %s ORDER by submitted ASC"""%self.table
395 query = """SELECT msg_id FROM %s ORDER by submitted ASC"""%self.table
@@ -27,7 +27,7 b' from IPython.external.ssh import tunnel'
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 Instance, Dict, Int, Type, CFloat, Unicode, CBytes, Bool
28 Instance, Dict, Int, Type, CFloat, Unicode, CBytes, Bool
29 )
29 )
30 # from IPython.utils.localinterfaces import LOCALHOST
30 # from IPython.utils.localinterfaces import LOCALHOST
31
31
32 from IPython.parallel.controller.heartmonitor import Heart
32 from IPython.parallel.controller.heartmonitor import Heart
33 from IPython.parallel.factory import RegistrationFactory
33 from IPython.parallel.factory import RegistrationFactory
@@ -39,7 +39,7 b' from .streamkernel import Kernel'
39
39
40 class EngineFactory(RegistrationFactory):
40 class EngineFactory(RegistrationFactory):
41 """IPython engine"""
41 """IPython engine"""
42
42
43 # configurables:
43 # configurables:
44 out_stream_factory=Type('IPython.zmq.iostream.OutStream', config=True,
44 out_stream_factory=Type('IPython.zmq.iostream.OutStream', config=True,
45 help="""The OutStream for handling stdout/err.
45 help="""The OutStream for handling stdout/err.
@@ -60,32 +60,32 b' class EngineFactory(RegistrationFactory):'
60 help="""The SSH private key file to use when tunneling connections to the Controller.""")
60 help="""The SSH private key file to use when tunneling connections to the Controller.""")
61 paramiko=Bool(sys.platform == 'win32', config=True,
61 paramiko=Bool(sys.platform == 'win32', config=True,
62 help="""Whether to use paramiko instead of openssh for tunnels.""")
62 help="""Whether to use paramiko instead of openssh for tunnels.""")
63
63
64 # not configurable:
64 # not configurable:
65 user_ns=Dict()
65 user_ns=Dict()
66 id=Int(allow_none=True)
66 id=Int(allow_none=True)
67 registrar=Instance('zmq.eventloop.zmqstream.ZMQStream')
67 registrar=Instance('zmq.eventloop.zmqstream.ZMQStream')
68 kernel=Instance(Kernel)
68 kernel=Instance(Kernel)
69
69
70 bident = CBytes()
70 bident = CBytes()
71 ident = Unicode()
71 ident = Unicode()
72 def _ident_changed(self, name, old, new):
72 def _ident_changed(self, name, old, new):
73 self.bident = asbytes(new)
73 self.bident = asbytes(new)
74 using_ssh=Bool(False)
74 using_ssh=Bool(False)
75
75
76
76
77 def __init__(self, **kwargs):
77 def __init__(self, **kwargs):
78 super(EngineFactory, self).__init__(**kwargs)
78 super(EngineFactory, self).__init__(**kwargs)
79 self.ident = self.session.session
79 self.ident = self.session.session
80
80
81 def init_connector(self):
81 def init_connector(self):
82 """construct connection function, which handles tunnels."""
82 """construct connection function, which handles tunnels."""
83 self.using_ssh = bool(self.sshkey or self.sshserver)
83 self.using_ssh = bool(self.sshkey or self.sshserver)
84
84
85 if self.sshkey and not self.sshserver:
85 if self.sshkey and not self.sshserver:
86 # We are using ssh directly to the controller, tunneling localhost to localhost
86 # We are using ssh directly to the controller, tunneling localhost to localhost
87 self.sshserver = self.url.split('://')[1].split(':')[0]
87 self.sshserver = self.url.split('://')[1].split(':')[0]
88
88
89 if self.using_ssh:
89 if self.using_ssh:
90 if tunnel.try_passwordless_ssh(self.sshserver, self.sshkey, self.paramiko):
90 if tunnel.try_passwordless_ssh(self.sshserver, self.sshkey, self.paramiko):
91 password=False
91 password=False
@@ -93,7 +93,7 b' class EngineFactory(RegistrationFactory):'
93 password = getpass("SSH Password for %s: "%self.sshserver)
93 password = getpass("SSH Password for %s: "%self.sshserver)
94 else:
94 else:
95 password = False
95 password = False
96
96
97 def connect(s, url):
97 def connect(s, url):
98 url = disambiguate_url(url, self.location)
98 url = disambiguate_url(url, self.location)
99 if self.using_ssh:
99 if self.using_ssh:
@@ -104,7 +104,7 b' class EngineFactory(RegistrationFactory):'
104 )
104 )
105 else:
105 else:
106 return s.connect(url)
106 return s.connect(url)
107
107
108 def maybe_tunnel(url):
108 def maybe_tunnel(url):
109 """like connect, but don't complete the connection (for use by heartbeat)"""
109 """like connect, but don't complete the connection (for use by heartbeat)"""
110 url = disambiguate_url(url, self.location)
110 url = disambiguate_url(url, self.location)
@@ -116,10 +116,10 b' class EngineFactory(RegistrationFactory):'
116 )
116 )
117 return url
117 return url
118 return connect, maybe_tunnel
118 return connect, maybe_tunnel
119
119
120 def register(self):
120 def register(self):
121 """send the registration_request"""
121 """send the registration_request"""
122
122
123 self.log.info("Registering with controller at %s"%self.url)
123 self.log.info("Registering with controller at %s"%self.url)
124 ctx = self.context
124 ctx = self.context
125 connect,maybe_tunnel = self.init_connector()
125 connect,maybe_tunnel = self.init_connector()
@@ -127,13 +127,13 b' class EngineFactory(RegistrationFactory):'
127 reg.setsockopt(zmq.IDENTITY, self.bident)
127 reg.setsockopt(zmq.IDENTITY, self.bident)
128 connect(reg, self.url)
128 connect(reg, self.url)
129 self.registrar = zmqstream.ZMQStream(reg, self.loop)
129 self.registrar = zmqstream.ZMQStream(reg, self.loop)
130
130
131
131
132 content = dict(queue=self.ident, heartbeat=self.ident, control=self.ident)
132 content = dict(queue=self.ident, heartbeat=self.ident, control=self.ident)
133 self.registrar.on_recv(lambda msg: self.complete_registration(msg, connect, maybe_tunnel))
133 self.registrar.on_recv(lambda msg: self.complete_registration(msg, connect, maybe_tunnel))
134 # print (self.session.key)
134 # print (self.session.key)
135 self.session.send(self.registrar, "registration_request",content=content)
135 self.session.send(self.registrar, "registration_request",content=content)
136
136
137 def complete_registration(self, msg, connect, maybe_tunnel):
137 def complete_registration(self, msg, connect, maybe_tunnel):
138 # print msg
138 # print msg
139 self._abort_dc.stop()
139 self._abort_dc.stop()
@@ -142,25 +142,25 b' class EngineFactory(RegistrationFactory):'
142 identity = self.bident
142 identity = self.bident
143 idents,msg = self.session.feed_identities(msg)
143 idents,msg = self.session.feed_identities(msg)
144 msg = Message(self.session.unserialize(msg))
144 msg = Message(self.session.unserialize(msg))
145
145
146 if msg.content.status == 'ok':
146 if msg.content.status == 'ok':
147 self.id = int(msg.content.id)
147 self.id = int(msg.content.id)
148
148
149 # launch heartbeat
149 # launch heartbeat
150 hb_addrs = msg.content.heartbeat
150 hb_addrs = msg.content.heartbeat
151
151
152 # possibly forward hb ports with tunnels
152 # possibly forward hb ports with tunnels
153 hb_addrs = [ maybe_tunnel(addr) for addr in hb_addrs ]
153 hb_addrs = [ maybe_tunnel(addr) for addr in hb_addrs ]
154 heart = Heart(*map(str, hb_addrs), heart_id=identity)
154 heart = Heart(*map(str, hb_addrs), heart_id=identity)
155 heart.start()
155 heart.start()
156
156
157 # create Shell Streams (MUX, Task, etc.):
157 # create Shell Streams (MUX, Task, etc.):
158 queue_addr = msg.content.mux
158 queue_addr = msg.content.mux
159 shell_addrs = [ str(queue_addr) ]
159 shell_addrs = [ str(queue_addr) ]
160 task_addr = msg.content.task
160 task_addr = msg.content.task
161 if task_addr:
161 if task_addr:
162 shell_addrs.append(str(task_addr))
162 shell_addrs.append(str(task_addr))
163
163
164 # Uncomment this to go back to two-socket model
164 # Uncomment this to go back to two-socket model
165 # shell_streams = []
165 # shell_streams = []
166 # for addr in shell_addrs:
166 # for addr in shell_addrs:
@@ -168,7 +168,7 b' class EngineFactory(RegistrationFactory):'
168 # stream.setsockopt(zmq.IDENTITY, identity)
168 # stream.setsockopt(zmq.IDENTITY, identity)
169 # stream.connect(disambiguate_url(addr, self.location))
169 # stream.connect(disambiguate_url(addr, self.location))
170 # shell_streams.append(stream)
170 # shell_streams.append(stream)
171
171
172 # Now use only one shell stream for mux and tasks
172 # Now use only one shell stream for mux and tasks
173 stream = zmqstream.ZMQStream(ctx.socket(zmq.ROUTER), loop)
173 stream = zmqstream.ZMQStream(ctx.socket(zmq.ROUTER), loop)
174 stream.setsockopt(zmq.IDENTITY, identity)
174 stream.setsockopt(zmq.IDENTITY, identity)
@@ -176,19 +176,19 b' class EngineFactory(RegistrationFactory):'
176 for addr in shell_addrs:
176 for addr in shell_addrs:
177 connect(stream, addr)
177 connect(stream, addr)
178 # end single stream-socket
178 # end single stream-socket
179
179
180 # control stream:
180 # control stream:
181 control_addr = str(msg.content.control)
181 control_addr = str(msg.content.control)
182 control_stream = zmqstream.ZMQStream(ctx.socket(zmq.ROUTER), loop)
182 control_stream = zmqstream.ZMQStream(ctx.socket(zmq.ROUTER), loop)
183 control_stream.setsockopt(zmq.IDENTITY, identity)
183 control_stream.setsockopt(zmq.IDENTITY, identity)
184 connect(control_stream, control_addr)
184 connect(control_stream, control_addr)
185
185
186 # create iopub stream:
186 # create iopub stream:
187 iopub_addr = msg.content.iopub
187 iopub_addr = msg.content.iopub
188 iopub_stream = zmqstream.ZMQStream(ctx.socket(zmq.PUB), loop)
188 iopub_stream = zmqstream.ZMQStream(ctx.socket(zmq.PUB), loop)
189 iopub_stream.setsockopt(zmq.IDENTITY, identity)
189 iopub_stream.setsockopt(zmq.IDENTITY, identity)
190 connect(iopub_stream, iopub_addr)
190 connect(iopub_stream, iopub_addr)
191
191
192 # # Redirect input streams and set a display hook.
192 # # Redirect input streams and set a display hook.
193 if self.out_stream_factory:
193 if self.out_stream_factory:
194 sys.stdout = self.out_stream_factory(self.session, iopub_stream, u'stdout')
194 sys.stdout = self.out_stream_factory(self.session, iopub_stream, u'stdout')
@@ -199,25 +199,25 b' class EngineFactory(RegistrationFactory):'
199 sys.displayhook = self.display_hook_factory(self.session, iopub_stream)
199 sys.displayhook = self.display_hook_factory(self.session, iopub_stream)
200 sys.displayhook.topic = 'engine.%i.pyout'%self.id
200 sys.displayhook.topic = 'engine.%i.pyout'%self.id
201
201
202 self.kernel = Kernel(config=self.config, int_id=self.id, ident=self.ident, session=self.session,
202 self.kernel = Kernel(config=self.config, int_id=self.id, ident=self.ident, session=self.session,
203 control_stream=control_stream, shell_streams=shell_streams, iopub_stream=iopub_stream,
203 control_stream=control_stream, shell_streams=shell_streams, iopub_stream=iopub_stream,
204 loop=loop, user_ns = self.user_ns, log=self.log)
204 loop=loop, user_ns = self.user_ns, log=self.log)
205 self.kernel.start()
205 self.kernel.start()
206
206
207
207
208 else:
208 else:
209 self.log.fatal("Registration Failed: %s"%msg)
209 self.log.fatal("Registration Failed: %s"%msg)
210 raise Exception("Registration Failed: %s"%msg)
210 raise Exception("Registration Failed: %s"%msg)
211
211
212 self.log.info("Completed registration with id %i"%self.id)
212 self.log.info("Completed registration with id %i"%self.id)
213
213
214
214
215 def abort(self):
215 def abort(self):
216 self.log.fatal("Registration timed out after %.1f seconds"%self.timeout)
216 self.log.fatal("Registration timed out after %.1f seconds"%self.timeout)
217 self.session.send(self.registrar, "unregistration_request", content=dict(id=self.id))
217 self.session.send(self.registrar, "unregistration_request", content=dict(id=self.id))
218 time.sleep(1)
218 time.sleep(1)
219 sys.exit(255)
219 sys.exit(255)
220
220
221 def start(self):
221 def start(self):
222 dc = ioloop.DelayedCallback(self.register, 0, self.loop)
222 dc = ioloop.DelayedCallback(self.register, 0, self.loop)
223 dc.start()
223 dc.start()
@@ -47,17 +47,17 b' def printer(*args):'
47
47
48 class _Passer(zmqstream.ZMQStream):
48 class _Passer(zmqstream.ZMQStream):
49 """Empty class that implements `send()` that does nothing.
49 """Empty class that implements `send()` that does nothing.
50
50
51 Subclass ZMQStream for Session typechecking
51 Subclass ZMQStream for Session typechecking
52
52
53 """
53 """
54 def __init__(self, *args, **kwargs):
54 def __init__(self, *args, **kwargs):
55 pass
55 pass
56
56
57 def send(self, *args, **kwargs):
57 def send(self, *args, **kwargs):
58 pass
58 pass
59 send_multipart = send
59 send_multipart = send
60
60
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # Main kernel class
63 # Main kernel class
@@ -68,63 +68,63 b' class Kernel(SessionFactory):'
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69 # Kernel interface
69 # Kernel interface
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71
71
72 # kwargs:
72 # kwargs:
73 exec_lines = List(Unicode, config=True,
73 exec_lines = List(Unicode, config=True,
74 help="List of lines to execute")
74 help="List of lines to execute")
75
75
76 # identities:
76 # identities:
77 int_id = Int(-1)
77 int_id = Int(-1)
78 bident = CBytes()
78 bident = CBytes()
79 ident = Unicode()
79 ident = Unicode()
80 def _ident_changed(self, name, old, new):
80 def _ident_changed(self, name, old, new):
81 self.bident = asbytes(new)
81 self.bident = asbytes(new)
82
82
83 user_ns = Dict(config=True, help="""Set the user's namespace of the Kernel""")
83 user_ns = Dict(config=True, help="""Set the user's namespace of the Kernel""")
84
84
85 control_stream = Instance(zmqstream.ZMQStream)
85 control_stream = Instance(zmqstream.ZMQStream)
86 task_stream = Instance(zmqstream.ZMQStream)
86 task_stream = Instance(zmqstream.ZMQStream)
87 iopub_stream = Instance(zmqstream.ZMQStream)
87 iopub_stream = Instance(zmqstream.ZMQStream)
88 client = Instance('IPython.parallel.Client')
88 client = Instance('IPython.parallel.Client')
89
89
90 # internals
90 # internals
91 shell_streams = List()
91 shell_streams = List()
92 compiler = Instance(CommandCompiler, (), {})
92 compiler = Instance(CommandCompiler, (), {})
93 completer = Instance(KernelCompleter)
93 completer = Instance(KernelCompleter)
94
94
95 aborted = Set()
95 aborted = Set()
96 shell_handlers = Dict()
96 shell_handlers = Dict()
97 control_handlers = Dict()
97 control_handlers = Dict()
98
98
99 def _set_prefix(self):
99 def _set_prefix(self):
100 self.prefix = "engine.%s"%self.int_id
100 self.prefix = "engine.%s"%self.int_id
101
101
102 def _connect_completer(self):
102 def _connect_completer(self):
103 self.completer = KernelCompleter(self.user_ns)
103 self.completer = KernelCompleter(self.user_ns)
104
104
105 def __init__(self, **kwargs):
105 def __init__(self, **kwargs):
106 super(Kernel, self).__init__(**kwargs)
106 super(Kernel, self).__init__(**kwargs)
107 self._set_prefix()
107 self._set_prefix()
108 self._connect_completer()
108 self._connect_completer()
109
109
110 self.on_trait_change(self._set_prefix, 'id')
110 self.on_trait_change(self._set_prefix, 'id')
111 self.on_trait_change(self._connect_completer, 'user_ns')
111 self.on_trait_change(self._connect_completer, 'user_ns')
112
112
113 # Build dict of handlers for message types
113 # Build dict of handlers for message types
114 for msg_type in ['execute_request', 'complete_request', 'apply_request',
114 for msg_type in ['execute_request', 'complete_request', 'apply_request',
115 'clear_request']:
115 'clear_request']:
116 self.shell_handlers[msg_type] = getattr(self, msg_type)
116 self.shell_handlers[msg_type] = getattr(self, msg_type)
117
117
118 for msg_type in ['shutdown_request', 'abort_request']+self.shell_handlers.keys():
118 for msg_type in ['shutdown_request', 'abort_request']+self.shell_handlers.keys():
119 self.control_handlers[msg_type] = getattr(self, msg_type)
119 self.control_handlers[msg_type] = getattr(self, msg_type)
120
120
121 self._initial_exec_lines()
121 self._initial_exec_lines()
122
122
123 def _wrap_exception(self, method=None):
123 def _wrap_exception(self, method=None):
124 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
124 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
125 content=wrap_exception(e_info)
125 content=wrap_exception(e_info)
126 return content
126 return content
127
127
128 def _initial_exec_lines(self):
128 def _initial_exec_lines(self):
129 s = _Passer()
129 s = _Passer()
130 content = dict(silent=True, user_variable=[],user_expressions=[])
130 content = dict(silent=True, user_variable=[],user_expressions=[])
@@ -133,20 +133,20 b' class Kernel(SessionFactory):'
133 content.update({'code':line})
133 content.update({'code':line})
134 msg = self.session.msg('execute_request', content)
134 msg = self.session.msg('execute_request', content)
135 self.execute_request(s, [], msg)
135 self.execute_request(s, [], msg)
136
136
137
137
138 #-------------------- control handlers -----------------------------
138 #-------------------- control handlers -----------------------------
139 def abort_queues(self):
139 def abort_queues(self):
140 for stream in self.shell_streams:
140 for stream in self.shell_streams:
141 if stream:
141 if stream:
142 self.abort_queue(stream)
142 self.abort_queue(stream)
143
143
144 def abort_queue(self, stream):
144 def abort_queue(self, stream):
145 while True:
145 while True:
146 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
146 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
147 if msg is None:
147 if msg is None:
148 return
148 return
149
149
150 self.log.info("Aborting:")
150 self.log.info("Aborting:")
151 self.log.info(str(msg))
151 self.log.info(str(msg))
152 msg_type = msg['header']['msg_type']
152 msg_type = msg['header']['msg_type']
@@ -154,13 +154,13 b' class Kernel(SessionFactory):'
154 # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
154 # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
155 # self.reply_socket.send(ident,zmq.SNDMORE)
155 # self.reply_socket.send(ident,zmq.SNDMORE)
156 # self.reply_socket.send_json(reply_msg)
156 # self.reply_socket.send_json(reply_msg)
157 reply_msg = self.session.send(stream, reply_type,
157 reply_msg = self.session.send(stream, reply_type,
158 content={'status' : 'aborted'}, parent=msg, ident=idents)
158 content={'status' : 'aborted'}, parent=msg, ident=idents)
159 self.log.debug(str(reply_msg))
159 self.log.debug(str(reply_msg))
160 # We need to wait a bit for requests to come in. This can probably
160 # We need to wait a bit for requests to come in. This can probably
161 # be set shorter for true asynchronous clients.
161 # be set shorter for true asynchronous clients.
162 time.sleep(0.05)
162 time.sleep(0.05)
163
163
164 def abort_request(self, stream, ident, parent):
164 def abort_request(self, stream, ident, parent):
165 """abort a specifig msg by id"""
165 """abort a specifig msg by id"""
166 msg_ids = parent['content'].get('msg_ids', None)
166 msg_ids = parent['content'].get('msg_ids', None)
@@ -170,12 +170,12 b' class Kernel(SessionFactory):'
170 self.abort_queues()
170 self.abort_queues()
171 for mid in msg_ids:
171 for mid in msg_ids:
172 self.aborted.add(str(mid))
172 self.aborted.add(str(mid))
173
173
174 content = dict(status='ok')
174 content = dict(status='ok')
175 reply_msg = self.session.send(stream, 'abort_reply', content=content,
175 reply_msg = self.session.send(stream, 'abort_reply', content=content,
176 parent=parent, ident=ident)
176 parent=parent, ident=ident)
177 self.log.debug(str(reply_msg))
177 self.log.debug(str(reply_msg))
178
178
179 def shutdown_request(self, stream, ident, parent):
179 def shutdown_request(self, stream, ident, parent):
180 """kill ourself. This should really be handled in an external process"""
180 """kill ourself. This should really be handled in an external process"""
181 try:
181 try:
@@ -190,7 +190,7 b' class Kernel(SessionFactory):'
190 self.log.debug(str(msg))
190 self.log.debug(str(msg))
191 dc = ioloop.DelayedCallback(lambda : sys.exit(0), 1000, self.loop)
191 dc = ioloop.DelayedCallback(lambda : sys.exit(0), 1000, self.loop)
192 dc.start()
192 dc.start()
193
193
194 def dispatch_control(self, msg):
194 def dispatch_control(self, msg):
195 idents,msg = self.session.feed_identities(msg, copy=False)
195 idents,msg = self.session.feed_identities(msg, copy=False)
196 try:
196 try:
@@ -200,7 +200,7 b' class Kernel(SessionFactory):'
200 return
200 return
201 else:
201 else:
202 self.log.debug("Control received, %s", msg)
202 self.log.debug("Control received, %s", msg)
203
203
204 header = msg['header']
204 header = msg['header']
205 msg_id = header['msg_id']
205 msg_id = header['msg_id']
206 msg_type = header['msg_type']
206 msg_type = header['msg_type']
@@ -210,10 +210,10 b' class Kernel(SessionFactory):'
210 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r"%msg_type)
210 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r"%msg_type)
211 else:
211 else:
212 handler(self.control_stream, idents, msg)
212 handler(self.control_stream, idents, msg)
213
213
214
214
215 #-------------------- queue helpers ------------------------------
215 #-------------------- queue helpers ------------------------------
216
216
217 def check_dependencies(self, dependencies):
217 def check_dependencies(self, dependencies):
218 if not dependencies:
218 if not dependencies:
219 return True
219 return True
@@ -225,28 +225,28 b' class Kernel(SessionFactory):'
225 results = self.client.get_results(dependencies,status_only=True)
225 results = self.client.get_results(dependencies,status_only=True)
226 if results['status'] != 'ok':
226 if results['status'] != 'ok':
227 return False
227 return False
228
228
229 if anyorall == 'any':
229 if anyorall == 'any':
230 if not results['completed']:
230 if not results['completed']:
231 return False
231 return False
232 else:
232 else:
233 if results['pending']:
233 if results['pending']:
234 return False
234 return False
235
235
236 return True
236 return True
237
237
238 def check_aborted(self, msg_id):
238 def check_aborted(self, msg_id):
239 return msg_id in self.aborted
239 return msg_id in self.aborted
240
240
241 #-------------------- queue handlers -----------------------------
241 #-------------------- queue handlers -----------------------------
242
242
243 def clear_request(self, stream, idents, parent):
243 def clear_request(self, stream, idents, parent):
244 """Clear our namespace."""
244 """Clear our namespace."""
245 self.user_ns = {}
245 self.user_ns = {}
246 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
246 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
247 content = dict(status='ok'))
247 content = dict(status='ok'))
248 self._initial_exec_lines()
248 self._initial_exec_lines()
249
249
250 def execute_request(self, stream, ident, parent):
250 def execute_request(self, stream, ident, parent):
251 self.log.debug('execute request %s'%parent)
251 self.log.debug('execute request %s'%parent)
252 try:
252 try:
@@ -273,8 +273,8 b' class Kernel(SessionFactory):'
273 reply_content = exc_content
273 reply_content = exc_content
274 else:
274 else:
275 reply_content = {'status' : 'ok'}
275 reply_content = {'status' : 'ok'}
276
276
277 reply_msg = self.session.send(stream, u'execute_reply', reply_content, parent=parent,
277 reply_msg = self.session.send(stream, u'execute_reply', reply_content, parent=parent,
278 ident=ident, subheader = dict(started=started))
278 ident=ident, subheader = dict(started=started))
279 self.log.debug(str(reply_msg))
279 self.log.debug(str(reply_msg))
280 if reply_msg['content']['status'] == u'error':
280 if reply_msg['content']['status'] == u'error':
@@ -289,7 +289,7 b' class Kernel(SessionFactory):'
289
289
290 def complete(self, msg):
290 def complete(self, msg):
291 return self.completer.complete(msg.content.line, msg.content.text)
291 return self.completer.complete(msg.content.line, msg.content.text)
292
292
293 def apply_request(self, stream, ident, parent):
293 def apply_request(self, stream, ident, parent):
294 # flush previous reply, so this request won't block it
294 # flush previous reply, so this request won't block it
295 stream.flush(zmq.POLLOUT)
295 stream.flush(zmq.POLLOUT)
@@ -314,21 +314,21 b' class Kernel(SessionFactory):'
314 sys.stderr.set_parent(parent)
314 sys.stderr.set_parent(parent)
315 # exec "f(*args,**kwargs)" in self.user_ns, self.user_ns
315 # exec "f(*args,**kwargs)" in self.user_ns, self.user_ns
316 working = self.user_ns
316 working = self.user_ns
317 # suffix =
317 # suffix =
318 prefix = "_"+str(msg_id).replace("-","")+"_"
318 prefix = "_"+str(msg_id).replace("-","")+"_"
319
319
320 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
320 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
321 # if bound:
321 # if bound:
322 # bound_ns = Namespace(working)
322 # bound_ns = Namespace(working)
323 # args = [bound_ns]+list(args)
323 # args = [bound_ns]+list(args)
324
324
325 fname = getattr(f, '__name__', 'f')
325 fname = getattr(f, '__name__', 'f')
326
326
327 fname = prefix+"f"
327 fname = prefix+"f"
328 argname = prefix+"args"
328 argname = prefix+"args"
329 kwargname = prefix+"kwargs"
329 kwargname = prefix+"kwargs"
330 resultname = prefix+"result"
330 resultname = prefix+"result"
331
331
332 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
332 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
333 # print ns
333 # print ns
334 working.update(ns)
334 working.update(ns)
@@ -341,7 +341,7 b' class Kernel(SessionFactory):'
341 working.pop(key)
341 working.pop(key)
342 # if bound:
342 # if bound:
343 # working.update(bound_ns)
343 # working.update(bound_ns)
344
344
345 packed_result,buf = serialize_object(result)
345 packed_result,buf = serialize_object(result)
346 result_buf = [packed_result]+buf
346 result_buf = [packed_result]+buf
347 except:
347 except:
@@ -351,24 +351,24 b' class Kernel(SessionFactory):'
351 ident=asbytes('%s.pyerr'%self.prefix))
351 ident=asbytes('%s.pyerr'%self.prefix))
352 reply_content = exc_content
352 reply_content = exc_content
353 result_buf = []
353 result_buf = []
354
354
355 if exc_content['ename'] == 'UnmetDependency':
355 if exc_content['ename'] == 'UnmetDependency':
356 sub['dependencies_met'] = False
356 sub['dependencies_met'] = False
357 else:
357 else:
358 reply_content = {'status' : 'ok'}
358 reply_content = {'status' : 'ok'}
359
359
360 # put 'ok'/'error' status in header, for scheduler introspection:
360 # put 'ok'/'error' status in header, for scheduler introspection:
361 sub['status'] = reply_content['status']
361 sub['status'] = reply_content['status']
362
362
363 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
363 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
364 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
364 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
365
365
366 # flush i/o
366 # flush i/o
367 # should this be before reply_msg is sent, like in the single-kernel code,
367 # should this be before reply_msg is sent, like in the single-kernel code,
368 # or should nothing get in the way of real results?
368 # or should nothing get in the way of real results?
369 sys.stdout.flush()
369 sys.stdout.flush()
370 sys.stderr.flush()
370 sys.stderr.flush()
371
371
372 def dispatch_queue(self, stream, msg):
372 def dispatch_queue(self, stream, msg):
373 self.control_stream.flush()
373 self.control_stream.flush()
374 idents,msg = self.session.feed_identities(msg, copy=False)
374 idents,msg = self.session.feed_identities(msg, copy=False)
@@ -379,8 +379,8 b' class Kernel(SessionFactory):'
379 return
379 return
380 else:
380 else:
381 self.log.debug("Message received, %s", msg)
381 self.log.debug("Message received, %s", msg)
382
382
383
383
384 header = msg['header']
384 header = msg['header']
385 msg_id = header['msg_id']
385 msg_id = header['msg_id']
386 msg_type = msg['header']['msg_type']
386 msg_type = msg['header']['msg_type']
@@ -397,25 +397,25 b' class Kernel(SessionFactory):'
397 self.log.error("UNKNOWN MESSAGE TYPE: %r"%msg_type)
397 self.log.error("UNKNOWN MESSAGE TYPE: %r"%msg_type)
398 else:
398 else:
399 handler(stream, idents, msg)
399 handler(stream, idents, msg)
400
400
401 def start(self):
401 def start(self):
402 #### stream mode:
402 #### stream mode:
403 if self.control_stream:
403 if self.control_stream:
404 self.control_stream.on_recv(self.dispatch_control, copy=False)
404 self.control_stream.on_recv(self.dispatch_control, copy=False)
405 self.control_stream.on_err(printer)
405 self.control_stream.on_err(printer)
406
406
407 def make_dispatcher(stream):
407 def make_dispatcher(stream):
408 def dispatcher(msg):
408 def dispatcher(msg):
409 return self.dispatch_queue(stream, msg)
409 return self.dispatch_queue(stream, msg)
410 return dispatcher
410 return dispatcher
411
411
412 for s in self.shell_streams:
412 for s in self.shell_streams:
413 s.on_recv(make_dispatcher(s), copy=False)
413 s.on_recv(make_dispatcher(s), copy=False)
414 s.on_err(printer)
414 s.on_err(printer)
415
415
416 if self.iopub_stream:
416 if self.iopub_stream:
417 self.iopub_stream.on_err(printer)
417 self.iopub_stream.on_err(printer)
418
418
419 #### while True mode:
419 #### while True mode:
420 # while True:
420 # while True:
421 # idle = True
421 # idle = True
@@ -428,7 +428,7 b' class Kernel(SessionFactory):'
428 # else:
428 # else:
429 # idle=False
429 # idle=False
430 # self.dispatch_queue(self.shell_stream, msg)
430 # self.dispatch_queue(self.shell_stream, msg)
431 #
431 #
432 # if not self.task_stream.empty():
432 # if not self.task_stream.empty():
433 # idle=False
433 # idle=False
434 # msg = self.task_stream.recv_multipart()
434 # msg = self.task_stream.recv_multipart()
@@ -56,7 +56,7 b' class NotDefined(KernelError):'
56
56
57 def __repr__(self):
57 def __repr__(self):
58 return '<NotDefined: %s>' % self.name
58 return '<NotDefined: %s>' % self.name
59
59
60 __str__ = __repr__
60 __str__ = __repr__
61
61
62
62
@@ -175,35 +175,35 b' class RemoteError(KernelError):'
175 evalue=None
175 evalue=None
176 traceback=None
176 traceback=None
177 engine_info=None
177 engine_info=None
178
178
179 def __init__(self, ename, evalue, traceback, engine_info=None):
179 def __init__(self, ename, evalue, traceback, engine_info=None):
180 self.ename=ename
180 self.ename=ename
181 self.evalue=evalue
181 self.evalue=evalue
182 self.traceback=traceback
182 self.traceback=traceback
183 self.engine_info=engine_info or {}
183 self.engine_info=engine_info or {}
184 self.args=(ename, evalue)
184 self.args=(ename, evalue)
185
185
186 def __repr__(self):
186 def __repr__(self):
187 engineid = self.engine_info.get('engine_id', ' ')
187 engineid = self.engine_info.get('engine_id', ' ')
188 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
188 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
189
189
190 def __str__(self):
190 def __str__(self):
191 sig = "%s(%s)"%(self.ename, self.evalue)
191 sig = "%s(%s)"%(self.ename, self.evalue)
192 if self.traceback:
192 if self.traceback:
193 return sig + '\n' + self.traceback
193 return sig + '\n' + self.traceback
194 else:
194 else:
195 return sig
195 return sig
196
196
197
197
198 class TaskRejectError(KernelError):
198 class TaskRejectError(KernelError):
199 """Exception to raise when a task should be rejected by an engine.
199 """Exception to raise when a task should be rejected by an engine.
200
200
201 This exception can be used to allow a task running on an engine to test
201 This exception can be used to allow a task running on an engine to test
202 if the engine (or the user's namespace on the engine) has the needed
202 if the engine (or the user's namespace on the engine) has the needed
203 task dependencies. If not, the task should raise this exception. For
203 task dependencies. If not, the task should raise this exception. For
204 the task to be retried on another engine, the task should be created
204 the task to be retried on another engine, the task should be created
205 with the `retries` argument > 1.
205 with the `retries` argument > 1.
206
206
207 The advantage of this approach over our older properties system is that
207 The advantage of this approach over our older properties system is that
208 tasks have full access to the user's namespace on the engines and the
208 tasks have full access to the user's namespace on the engines and the
209 properties don't have to be managed or tested by the controller.
209 properties don't have to be managed or tested by the controller.
@@ -240,7 +240,7 b' class CompositeError(RemoteError):'
240 engine_str = self._get_engine_str(ei)
240 engine_str = self._get_engine_str(ei)
241 s = s + '\n' + engine_str + en + ': ' + str(ev)
241 s = s + '\n' + engine_str + en + ': ' + str(ev)
242 return s
242 return s
243
243
244 def __repr__(self):
244 def __repr__(self):
245 return "CompositeError(%i)"%len(self.elist)
245 return "CompositeError(%i)"%len(self.elist)
246
246
@@ -258,7 +258,7 b' class CompositeError(RemoteError):'
258 else:
258 else:
259 print (self._get_engine_str(ei))
259 print (self._get_engine_str(ei))
260 print (etb or 'No traceback available')
260 print (etb or 'No traceback available')
261
261
262 def raise_exception(self, excid=0):
262 def raise_exception(self, excid=0):
263 try:
263 try:
264 en,ev,etb,ei = self.elist[excid]
264 en,ev,etb,ei = self.elist[excid]
@@ -280,7 +280,7 b" def collect_exceptions(rdict_or_list, method='unspecified'):"
280 if isinstance(r, RemoteError):
280 if isinstance(r, RemoteError):
281 en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info
281 en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info
282 # Sometimes we could have CompositeError in our list. Just take
282 # Sometimes we could have CompositeError in our list. Just take
283 # the errors out of them and put them in our new list. This
283 # the errors out of them and put them in our new list. This
284 # has the effect of flattening lists of CompositeErrors into one
284 # has the effect of flattening lists of CompositeErrors into one
285 # CompositeError
285 # CompositeError
286 if en=='CompositeError':
286 if en=='CompositeError':
@@ -312,7 +312,7 b' def wrap_exception(engine_info={}):'
312 return exc_content
312 return exc_content
313
313
314 def unwrap_exception(content):
314 def unwrap_exception(content):
315 err = RemoteError(content['ename'], content['evalue'],
315 err = RemoteError(content['ename'], content['evalue'],
316 ''.join(content['traceback']),
316 ''.join(content['traceback']),
317 content.get('engine_info', {}))
317 content.get('engine_info', {}))
318 return err
318 return err
@@ -61,7 +61,7 b' def pysh():'
61 """Pysh is a set of modules and extensions to IPython which make shell-like
61 """Pysh is a set of modules and extensions to IPython which make shell-like
62 usage with Python syntax more convenient. Keep in mind that pysh is NOT a
62 usage with Python syntax more convenient. Keep in mind that pysh is NOT a
63 full-blown shell, so don't try to make it your /etc/passwd entry!
63 full-blown shell, so don't try to make it your /etc/passwd entry!
64
64
65 In particular, it has no job control, so if you type Ctrl-Z (under Unix),
65 In particular, it has no job control, so if you type Ctrl-Z (under Unix),
66 you'll suspend pysh itself, not the process you just started.
66 you'll suspend pysh itself, not the process you just started.
67
67
@@ -72,7 +72,7 b' def pysh():'
72 ALIASES
72 ALIASES
73 -------
73 -------
74 All of your $PATH has been loaded as IPython aliases, so you should be
74 All of your $PATH has been loaded as IPython aliases, so you should be
75 able to type any normal system command and have it executed. See %alias?
75 able to type any normal system command and have it executed. See %alias?
76 and %unalias? for details on the alias facilities.
76 and %unalias? for details on the alias facilities.
77
77
78 SPECIAL SYNTAX
78 SPECIAL SYNTAX
@@ -139,7 +139,7 b' def pysh():'
139 sys.platform is: linux2
139 sys.platform is: linux2
140
140
141 IPython's input history handling is still active, which allows you to
141 IPython's input history handling is still active, which allows you to
142 rerun a single block of multi-line input by simply using exec:
142 rerun a single block of multi-line input by simply using exec:
143 fperez[~/test]|14> $$alist = ls *.eps
143 fperez[~/test]|14> $$alist = ls *.eps
144 fperez[~/test]|15> exec _i11
144 fperez[~/test]|15> exec _i11
145 file image2.eps 921 image2.eps
145 file image2.eps 921 image2.eps
@@ -155,7 +155,7 b' def pysh():'
155 automatically loaded. Some additional functions, useful for shell usage,
155 automatically loaded. Some additional functions, useful for shell usage,
156 are listed below. You can request more help about them with '?'.
156 are listed below. You can request more help about them with '?'.
157
157
158 shell - execute a command in the underlying system shell
158 shell - execute a command in the underlying system shell
159 system - like shell(), but return the exit status of the command
159 system - like shell(), but return the exit status of the command
160 sout - capture the output of a command as a string
160 sout - capture the output of a command as a string
161 lout - capture the output of a command as a list (split on '\\n')
161 lout - capture the output of a command as a list (split on '\\n')
@@ -180,7 +180,7 b' def pysh():'
180
180
181 PROMPT CUSTOMIZATION
181 PROMPT CUSTOMIZATION
182 --------------------
182 --------------------
183
183
184 The supplied ipythonrc-pysh profile comes with an example of a very
184 The supplied ipythonrc-pysh profile comes with an example of a very
185 colored and detailed prompt, mainly to serve as an illustration. The
185 colored and detailed prompt, mainly to serve as an illustration. The
186 valid escape sequences, besides color names, are:
186 valid escape sequences, besides color names, are:
@@ -7,14 +7,14 b' ip = ipapi.get()'
7
7
8 def clear_f(self,arg):
8 def clear_f(self,arg):
9 """ Clear various data (e.g. stored history data)
9 """ Clear various data (e.g. stored history data)
10
10
11 %clear in - clear input history
11 %clear in - clear input history
12 %clear out - clear output history
12 %clear out - clear output history
13 %clear shadow_compress - Compresses shadow history (to speed up ipython)
13 %clear shadow_compress - Compresses shadow history (to speed up ipython)
14 %clear shadow_nuke - permanently erase all entries in shadow history
14 %clear shadow_nuke - permanently erase all entries in shadow history
15 %clear dhist - clear dir history
15 %clear dhist - clear dir history
16 %clear array - clear only variables that are NumPy arrays
16 %clear array - clear only variables that are NumPy arrays
17
17
18 Examples:
18 Examples:
19
19
20 In [1]: clear in
20 In [1]: clear in
@@ -29,11 +29,11 b' def clear_f(self,arg):'
29 In [4]: clear dhist
29 In [4]: clear dhist
30 Clearing directory history
30 Clearing directory history
31 """
31 """
32
32
33 api = self.getapi()
33 api = self.getapi()
34 user_ns = self.user_ns # local lookup, heavily used
34 user_ns = self.user_ns # local lookup, heavily used
35
35
36
36
37 for target in arg.split():
37 for target in arg.split():
38
38
39 if target == 'out':
39 if target == 'out':
@@ -50,7 +50,7 b' def clear_f(self,arg):'
50 del user_ns[key]
50 del user_ns[key]
51 except: pass
51 except: pass
52 # must be done in-place
52 # must be done in-place
53 self.history_manager.input_hist_parsed[:] = ['\n'] * pc
53 self.history_manager.input_hist_parsed[:] = ['\n'] * pc
54 self.history_manager.input_hist_raw[:] = ['\n'] * pc
54 self.history_manager.input_hist_raw[:] = ['\n'] * pc
55
55
56 elif target == 'array':
56 elif target == 'array':
@@ -68,7 +68,7 b' def clear_f(self,arg):'
68 elif target == 'shadow_compress':
68 elif target == 'shadow_compress':
69 print "Compressing shadow history"
69 print "Compressing shadow history"
70 api.db.hcompress('shadowhist')
70 api.db.hcompress('shadowhist')
71
71
72 elif target == 'shadow_nuke':
72 elif target == 'shadow_nuke':
73 print "Erased all keys from shadow history "
73 print "Erased all keys from shadow history "
74 for k in ip.db.keys('shadowhist/*'):
74 for k in ip.db.keys('shadowhist/*'):
@@ -78,7 +78,7 b' def clear_f(self,arg):'
78 print "Clearing directory history"
78 print "Clearing directory history"
79 del user_ns['_dh'][:]
79 del user_ns['_dh'][:]
80
80
81 gc.collect()
81 gc.collect()
82
82
83 # Activate the extension
83 # Activate the extension
84 ip.define_magic("clear",clear_f)
84 ip.define_magic("clear",clear_f)
@@ -9,7 +9,7 b' ip = ipapi.get()'
9
9
10 import os,sys
10 import os,sys
11
11
12 def restore_env(self):
12 def restore_env(self):
13 ip = self.getapi()
13 ip = self.getapi()
14 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
14 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
15 for k,v in env['set'].items():
15 for k,v in env['set'].items():
@@ -19,51 +19,51 b' def restore_env(self):'
19 for k,v in env['pre']:
19 for k,v in env['pre']:
20 os.environ[k] = v + os.environ.get(k,"")
20 os.environ[k] = v + os.environ.get(k,"")
21 raise TryNext
21 raise TryNext
22
22
23 ip.set_hook('late_startup_hook', restore_env)
23 ip.set_hook('late_startup_hook', restore_env)
24
24
25 def persist_env(self, parameter_s=''):
25 def persist_env(self, parameter_s=''):
26 """ Store environment variables persistently
26 """ Store environment variables persistently
27
27
28 IPython remembers the values across sessions, which is handy to avoid
28 IPython remembers the values across sessions, which is handy to avoid
29 editing startup files.
29 editing startup files.
30
30
31 %env - Show all environment variables
31 %env - Show all environment variables
32 %env VISUAL=jed - set VISUAL to jed
32 %env VISUAL=jed - set VISUAL to jed
33 %env PATH+=;/foo - append ;foo to PATH
33 %env PATH+=;/foo - append ;foo to PATH
34 %env PATH+=;/bar - also append ;bar to PATH
34 %env PATH+=;/bar - also append ;bar to PATH
35 %env PATH-=/wbin; - prepend /wbin; to PATH
35 %env PATH-=/wbin; - prepend /wbin; to PATH
36 %env -d VISUAL - forget VISUAL persistent val
36 %env -d VISUAL - forget VISUAL persistent val
37 %env -p - print all persistent env modifications
37 %env -p - print all persistent env modifications
38 """
38 """
39
39
40 if not parameter_s.strip():
40 if not parameter_s.strip():
41 return os.environ.data
41 return os.environ.data
42
42
43 ip = self.getapi()
43 ip = self.getapi()
44 db = ip.db
44 db = ip.db
45 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
45 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
46
46
47 if parameter_s.startswith('-p'):
47 if parameter_s.startswith('-p'):
48 return env
48 return env
49
49
50 elif parameter_s.startswith('-d'):
50 elif parameter_s.startswith('-d'):
51 parts = (parameter_s.split()[1], '<del>')
51 parts = (parameter_s.split()[1], '<del>')
52
52
53 else:
53 else:
54 parts = parameter_s.strip().split('=')
54 parts = parameter_s.strip().split('=')
55
55
56 if len(parts) == 2:
56 if len(parts) == 2:
57 k,v = [p.strip() for p in parts]
57 k,v = [p.strip() for p in parts]
58
58
59 if v == '<del>':
59 if v == '<del>':
60 if k in env['set']:
60 if k in env['set']:
61 del env['set'][k]
61 del env['set'][k]
62 env['add'] = [el for el in env['add'] if el[0] != k]
62 env['add'] = [el for el in env['add'] if el[0] != k]
63 env['pre'] = [el for el in env['pre'] if el[0] != k]
63 env['pre'] = [el for el in env['pre'] if el[0] != k]
64
64
65 print "Forgot '%s' (for next session)" % k
65 print "Forgot '%s' (for next session)" % k
66
66
67 elif k.endswith('+'):
67 elif k.endswith('+'):
68 k = k[:-1]
68 k = k[:-1]
69 env['add'].append((k,v))
69 env['add'].append((k,v))
@@ -74,13 +74,13 b" def persist_env(self, parameter_s=''):"
74 env['pre'].append((k,v))
74 env['pre'].append((k,v))
75 os.environ[k] = v + os.environ.get(k,"")
75 os.environ[k] = v + os.environ.get(k,"")
76 print k,"after prepend =",os.environ[k]
76 print k,"after prepend =",os.environ[k]
77
77
78
78
79 else:
79 else:
80 env['set'][k] = v
80 env['set'][k] = v
81 print "Setting",k,"to",v
81 print "Setting",k,"to",v
82 os.environ[k] = v
82 os.environ[k] = v
83
83
84 db['stored_env'] = env
84 db['stored_env'] = env
85
85
86 def env_completer(self,event):
86 def env_completer(self,event):
@@ -1,7 +1,7 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """ IPython extension: new prefilters for output grabbing
2 """ IPython extension: new prefilters for output grabbing
3
3
4 Provides
4 Provides
5
5
6 var = %magic blah blah
6 var = %magic blah blah
7
7
@@ -41,7 +41,7 b' def init_handlers():'
41 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)',
41 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)',
42 hnd_magic
42 hnd_magic
43 )
43 )
44
44
45 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)',
45 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)',
46 hnd_syscmd
46 hnd_syscmd
47 )
47 )
@@ -53,7 +53,7 b' def regex_prefilter_f(self,line):'
53 mo = pat.match(line)
53 mo = pat.match(line)
54 if mo:
54 if mo:
55 return handler(line,mo)
55 return handler(line,mo)
56
56
57 raise TryNext
57 raise TryNext
58
58
59 ip.set_hook('input_prefilter', regex_prefilter_f)
59 ip.set_hook('input_prefilter', regex_prefilter_f)
@@ -82,7 +82,7 b' def bzr_commands():'
82 return __bzr_commands
82 return __bzr_commands
83 out = os.popen('bzr help commands')
83 out = os.popen('bzr help commands')
84 __bzr_commands = [l.split()[0] for l in out]
84 __bzr_commands = [l.split()[0] for l in out]
85 return __bzr_commands
85 return __bzr_commands
86
86
87 def bzr_completer(self,event):
87 def bzr_completer(self,event):
88 """ Completer for bazaar commands """
88 """ Completer for bazaar commands """
@@ -113,8 +113,8 b' def apt_get_packages(prefix):'
113 for p in out:
113 for p in out:
114 if p.startswith(prefix):
114 if p.startswith(prefix):
115 yield p.rstrip()
115 yield p.rstrip()
116
116
117
117
118 apt_commands = """\
118 apt_commands = """\
119 update upgrade install remove purge source build-dep dist-upgrade
119 update upgrade install remove purge source build-dep dist-upgrade
120 dselect-upgrade clean autoclean check"""
120 dselect-upgrade clean autoclean check"""
@@ -18,15 +18,15 b" def indent(s, ind= ' '):"
18
18
19 class ExtUtil:
19 class ExtUtil:
20 """ IPython extensios (ipy_* etc.) management utilities """
20 """ IPython extensios (ipy_* etc.) management utilities """
21
21
22 def describe(self):
22 def describe(self):
23 for n,mod in self._active():
23 for n,mod in self._active():
24 doc = inspect.getdoc(mod)
24 doc = inspect.getdoc(mod)
25 if doc:
25 if doc:
26 print '== %s ==' % n
26 print '== %s ==' % n
27 print indent(doc)
27 print indent(doc)
28
28
29
29
30 def ls(self):
30 def ls(self):
31 """ Show list of installed extensions. """
31 """ Show list of installed extensions. """
32 for n,m in self._active():
32 for n,m in self._active():
@@ -37,8 +37,8 b' class ExtUtil:'
37 o = getattr(m, 'ip', None)
37 o = getattr(m, 'ip', None)
38 if isinstance(o, InteractiveShell):
38 if isinstance(o, InteractiveShell):
39 act.append((mname,m))
39 act.append((mname,m))
40 act.sort()
40 act.sort()
41 return act
41 return act
42
42
43 extutil = ExtUtil()
43 extutil = ExtUtil()
44 ip.push('extutil')
44 ip.push('extutil')
@@ -27,30 +27,30 b' import IPython.utils.generics'
27
27
28 def parse_args(args):
28 def parse_args(args):
29 """ Given arg string 'CMD files... target', return ([files], target) """
29 """ Given arg string 'CMD files... target', return ([files], target) """
30
30
31 tup = args.split(None, 1)
31 tup = args.split(None, 1)
32 if len(tup) == 1:
32 if len(tup) == 1:
33 raise UsageError("Expected arguments for " + tup[0])
33 raise UsageError("Expected arguments for " + tup[0])
34
34
35 tup2 = shlex.split(tup[1])
35 tup2 = shlex.split(tup[1])
36
36
37 flist, trg = mglob.expand(tup2[0:-1]), tup2[-1]
37 flist, trg = mglob.expand(tup2[0:-1]), tup2[-1]
38 if not flist:
38 if not flist:
39 raise UsageError("No files found:" + str(tup2[0:-1]))
39 raise UsageError("No files found:" + str(tup2[0:-1]))
40 return flist, trg
40 return flist, trg
41
41
42 def icp(ip,arg):
42 def icp(ip,arg):
43 """ icp files... targetdir
43 """ icp files... targetdir
44
44
45 Copy all files to target, creating dirs for target if necessary
45 Copy all files to target, creating dirs for target if necessary
46
46
47 icp srcdir dstdir
47 icp srcdir dstdir
48
48
49 Copy srcdir to distdir
49 Copy srcdir to distdir
50
50
51 """
51 """
52 import distutils.dir_util
52 import distutils.dir_util
53
53
54 fs, targetdir = parse_args(arg)
54 fs, targetdir = parse_args(arg)
55 if not os.path.isdir(targetdir) and len(fs) > 1:
55 if not os.path.isdir(targetdir) and len(fs) > 1:
56 distutils.dir_util.mkpath(targetdir,verbose =1)
56 distutils.dir_util.mkpath(targetdir,verbose =1)
@@ -64,21 +64,21 b' ip.define_alias("icp",icp)'
64
64
65 def imv(ip,arg):
65 def imv(ip,arg):
66 """ imv src tgt
66 """ imv src tgt
67
67
68 Move source to target.
68 Move source to target.
69 """
69 """
70
70
71 fs, target = parse_args(arg)
71 fs, target = parse_args(arg)
72 if len(fs) > 1:
72 if len(fs) > 1:
73 assert os.path.isdir(target)
73 assert os.path.isdir(target)
74 for f in fs:
74 for f in fs:
75 shutil.move(f, target)
75 shutil.move(f, target)
76 return fs
76 return fs
77 ip.define_alias("imv",imv)
77 ip.define_alias("imv",imv)
78
78
79 def irm(ip,arg):
79 def irm(ip,arg):
80 """ irm path[s]...
80 """ irm path[s]...
81
81
82 Remove file[s] or dir[s] path. Dirs are deleted recursively.
82 Remove file[s] or dir[s] path. Dirs are deleted recursively.
83 """
83 """
84 try:
84 try:
@@ -97,24 +97,24 b' ip.define_alias("irm",irm)'
97
97
98 def imkdir(ip,arg):
98 def imkdir(ip,arg):
99 """ imkdir path
99 """ imkdir path
100
100
101 Creates dir path, and all dirs on the road
101 Creates dir path, and all dirs on the road
102 """
102 """
103 import distutils.dir_util
103 import distutils.dir_util
104 targetdir = arg.split(None,1)[1]
104 targetdir = arg.split(None,1)[1]
105 distutils.dir_util.mkpath(targetdir,verbose =1)
105 distutils.dir_util.mkpath(targetdir,verbose =1)
106
106
107 ip.define_alias("imkdir",imkdir)
107 ip.define_alias("imkdir",imkdir)
108
108
109 def igrep(ip,arg):
109 def igrep(ip,arg):
110 """ igrep PAT files...
110 """ igrep PAT files...
111
111
112 Very dumb file scan, case-insensitive.
112 Very dumb file scan, case-insensitive.
113
113
114 e.g.
114 e.g.
115
115
116 igrep "test this" rec:*.py
116 igrep "test this" rec:*.py
117
117
118 """
118 """
119 elems = shlex.split(arg)
119 elems = shlex.split(arg)
120 dummy, pat, fs = elems[0], elems[1], mglob.expand(elems[2:])
120 dummy, pat, fs = elems[0], elems[1], mglob.expand(elems[2:])
@@ -130,19 +130,19 b' def igrep(ip,arg):'
130 print l.rstrip()
130 print l.rstrip()
131 return res
131 return res
132
132
133 ip.define_alias("igrep",igrep)
133 ip.define_alias("igrep",igrep)
134
134
135 def collect(ip,arg):
135 def collect(ip,arg):
136 """ collect foo/a.txt rec:bar=*.py
136 """ collect foo/a.txt rec:bar=*.py
137
137
138 Copies foo/a.txt to ~/_ipython/collect/foo/a.txt and *.py from bar,
138 Copies foo/a.txt to ~/_ipython/collect/foo/a.txt and *.py from bar,
139 likewise
139 likewise
140
140
141 Without args, try to open ~/_ipython/collect dir (in win32 at least).
141 Without args, try to open ~/_ipython/collect dir (in win32 at least).
142 """
142 """
143 from IPython.external.path import path
143 from IPython.external.path import path
144 basedir = path(ip.ipython_dir + '/collect')
144 basedir = path(ip.ipython_dir + '/collect')
145 try:
145 try:
146 fs = mglob.expand(arg.split(None,1)[1])
146 fs = mglob.expand(arg.split(None,1)[1])
147 except IndexError:
147 except IndexError:
148 os.startfile(basedir)
148 os.startfile(basedir)
@@ -160,23 +160,23 b' def collect(ip,arg):'
160 print f,"=>",trg
160 print f,"=>",trg
161 shutil.copy2(f,trg)
161 shutil.copy2(f,trg)
162
162
163 ip.define_alias("collect",collect)
163 ip.define_alias("collect",collect)
164
164
165 def inote(ip,arg):
165 def inote(ip,arg):
166 """ inote Hello world
166 """ inote Hello world
167
167
168 Adds timestamp and Hello world to ~/_ipython/notes.txt
168 Adds timestamp and Hello world to ~/_ipython/notes.txt
169
169
170 Without args, opens notes.txt for editing.
170 Without args, opens notes.txt for editing.
171 """
171 """
172 import time
172 import time
173 fname = ip.ipython_dir + '/notes.txt'
173 fname = ip.ipython_dir + '/notes.txt'
174
174
175 try:
175 try:
176 entry = " === " + time.asctime() + ': ===\n' + arg.split(None,1)[1] + '\n'
176 entry = " === " + time.asctime() + ': ===\n' + arg.split(None,1)[1] + '\n'
177 f= open(fname, 'a').write(entry)
177 f= open(fname, 'a').write(entry)
178 except IndexError:
178 except IndexError:
179 ip.hooks.editor(fname)
179 ip.hooks.editor(fname)
180
180
181 ip.define_alias("inote",inote)
181 ip.define_alias("inote",inote)
182
182
@@ -186,7 +186,7 b' def pathobj_unmangle(s):'
186 return s.replace('__',' ').replace('DOT','.')
186 return s.replace('__',' ').replace('DOT','.')
187
187
188
188
189
189
190 class PathObj(path):
190 class PathObj(path):
191 def __init__(self,p):
191 def __init__(self,p):
192 self.path = p
192 self.path = p
@@ -205,7 +205,7 b' class PathObj(path):'
205 sep = ''
205 sep = ''
206 else:
206 else:
207 sep = '/'
207 sep = '/'
208
208
209 tgt = self.path + sep + pathobj_unmangle(name)
209 tgt = self.path + sep + pathobj_unmangle(name)
210 #print "tgt",tgt
210 #print "tgt",tgt
211 if os.path.isdir(tgt):
211 if os.path.isdir(tgt):
@@ -216,15 +216,15 b' class PathObj(path):'
216 raise AttributeError, name # <<< DON'T FORGET THIS LINE !!
216 raise AttributeError, name # <<< DON'T FORGET THIS LINE !!
217 def __str__(self):
217 def __str__(self):
218 return self.path
218 return self.path
219
219
220 def __repr__(self):
220 def __repr__(self):
221 return "<PathObj to %s>" % self.path
221 return "<PathObj to %s>" % self.path
222
222
223 def __call__(self):
223 def __call__(self):
224 print "cd:",self.path
224 print "cd:",self.path
225 os.chdir(self.path)
225 os.chdir(self.path)
226
226
227 def complete_pathobj(obj, prev_completions):
227 def complete_pathobj(obj, prev_completions):
228 if hasattr(obj,'__complete__'):
228 if hasattr(obj,'__complete__'):
229 res = obj.__complete__()
229 res = obj.__complete__()
230 if res:
230 if res:
@@ -242,5 +242,5 b' def test_pathobj():'
242 startmenu = PathObj("d:/Documents and Settings/All Users/Start Menu/Programs")
242 startmenu = PathObj("d:/Documents and Settings/All Users/Start Menu/Programs")
243 cwd = PathObj('.')
243 cwd = PathObj('.')
244 ip.push("rootdir startmenu cwd")
244 ip.push("rootdir startmenu cwd")
245
245
246 #test_pathobj() No newline at end of file
246 #test_pathobj()
@@ -7,7 +7,7 b' Stores variables in Struct with some notes in PicleShare database'
7
7
8 """
8 """
9
9
10 from datetime import datetime
10 from datetime import datetime
11 from IPython.core import ipapi
11 from IPython.core import ipapi
12 ip = ipapi.get()
12 ip = ipapi.get()
13
13
@@ -15,7 +15,7 b' import pickleshare'
15
15
16 import inspect,pickle,os,sys,textwrap
16 import inspect,pickle,os,sys,textwrap
17 from IPython.core.fakemodule import FakeModule
17 from IPython.core.fakemodule import FakeModule
18 from IPython.utils.ipstruct import Struct
18 from IPython.utils.ipstruct import Struct
19 from IPython.utils.warn import error
19 from IPython.utils.warn import error
20
20
21
21
@@ -43,7 +43,7 b' def refresh_variables(ip, key=None):'
43 print "Restored", justkey
43 print "Restored", justkey
44 else:
44 else:
45 ip.user_ns[origname] = obj['val']
45 ip.user_ns[origname] = obj['val']
46 print "Restored", origname
46 print "Restored", origname
47
47
48 def read_variables(ip, key=None):
48 def read_variables(ip, key=None):
49 db = ip.db
49 db = ip.db
@@ -54,7 +54,7 b' def read_variables(ip, key=None):'
54 for key in keys:
54 for key in keys:
55 # strip autorestore
55 # strip autorestore
56 justkey = os.path.basename(key)
56 justkey = os.path.basename(key)
57 print "restoring from ", justkey
57 print "restoring from ", justkey
58 try:
58 try:
59 obj = db[key]
59 obj = db[key]
60 except KeyError:
60 except KeyError:
@@ -66,7 +66,7 b' def read_variables(ip, key=None):'
66
66
67 def detail_variables(ip, key=None):
67 def detail_variables(ip, key=None):
68 db, get = ip.db, ip.db.get
68 db, get = ip.db, ip.db.get
69
69
70 if key is None:
70 if key is None:
71 keys = db.keys('jot/*')
71 keys = db.keys('jot/*')
72 else:
72 else:
@@ -82,7 +82,7 b' def detail_variables(ip, key=None):'
82 for key in keys:
82 for key in keys:
83 v = get(key,'<unavailable>')
83 v = get(key,'<unavailable>')
84 justkey = os.path.basename(key)
84 justkey = os.path.basename(key)
85 try:
85 try:
86 print fmthead % (justkey, datetime.ctime(v.get('time','<unavailable>')))
86 print fmthead % (justkey, datetime.ctime(v.get('time','<unavailable>')))
87 print fmtbody % (v.get('comment','<unavailable>'))
87 print fmtbody % (v.get('comment','<unavailable>'))
88 d = v.get('val','unavailable')
88 d = v.get('val','unavailable')
@@ -105,7 +105,7 b" def jot_obj(self, obj, name, comment=''):"
105 write obj data to the note database, with whatever that should be noted.
105 write obj data to the note database, with whatever that should be noted.
106 """
106 """
107 had = self.db.keys('jot/'+name+'*')
107 had = self.db.keys('jot/'+name+'*')
108 # if it the same name but a later version, we stupidly add a number to the
108 # if it the same name but a later version, we stupidly add a number to the
109 # so the name doesn't collide. Any better idea?
109 # so the name doesn't collide. Any better idea?
110 suffix = ''
110 suffix = ''
111 if len(had)>0:
111 if len(had)>0:
@@ -116,47 +116,47 b" def jot_obj(self, obj, name, comment=''):"
116
116
117 uname = 'jot/'+name+suffix
117 uname = 'jot/'+name+suffix
118
118
119 # which one works better?
119 # which one works better?
120 #all = ip.shadowhist.all()
120 #all = ip.shadowhist.all()
121 all = ip.shell.history_manager.input_hist_parsed
121 all = ip.shell.history_manager.input_hist_parsed
122
122
123 # We may actually want to make snapshot of files that are run-ned.
123 # We may actually want to make snapshot of files that are run-ned.
124
124
125 # get the comment
125 # get the comment
126 try:
126 try:
127 comment = ip.magic_edit('-x').strip()
127 comment = ip.magic_edit('-x').strip()
128 except:
128 except:
129 print "No comment is recorded."
129 print "No comment is recorded."
130 comment = ''
130 comment = ''
131
131
132 self.db[uname] = Struct({'val':obj,
132 self.db[uname] = Struct({'val':obj,
133 'time' : datetime.now(),
133 'time' : datetime.now(),
134 'hist' : all,
134 'hist' : all,
135 'name' : name,
135 'name' : name,
136 'comment' : comment,})
136 'comment' : comment,})
137
137
138 print "Jotted down notes for '%s' (%s)" % (uname, obj.__class__.__name__)
138 print "Jotted down notes for '%s' (%s)" % (uname, obj.__class__.__name__)
139
139
140
140
141
141
142 def magic_jot(self, parameter_s=''):
142 def magic_jot(self, parameter_s=''):
143 """Lightweight persistence for python variables.
143 """Lightweight persistence for python variables.
144
144
145 Example:
145 Example:
146
146
147 ville@badger[~]|1> A = ['hello',10,'world']\\
147 ville@badger[~]|1> A = ['hello',10,'world']\\
148 ville@badger[~]|2> %jot A\\
148 ville@badger[~]|2> %jot A\\
149 ville@badger[~]|3> Exit
149 ville@badger[~]|3> Exit
150
150
151 (IPython session is closed and started again...)
151 (IPython session is closed and started again...)
152
152
153 ville@badger:~$ ipython -p pysh\\
153 ville@badger:~$ ipython -p pysh\\
154 ville@badger[~]|1> print A
154 ville@badger[~]|1> print A
155
155
156 ['hello', 10, 'world']
156 ['hello', 10, 'world']
157
157
158 Usage:
158 Usage:
159
159
160 %jot - Show list of all variables and their current values\\
160 %jot - Show list of all variables and their current values\\
161 %jot -l - Show list of all variables and their current values in detail\\
161 %jot -l - Show list of all variables and their current values in detail\\
162 %jot -l <var> - Show one variable and its current values in detail\\
162 %jot -l <var> - Show one variable and its current values in detail\\
@@ -165,16 +165,16 b" def magic_jot(self, parameter_s=''):"
165 %jot -z - Remove all variables from storage (disabled)\\
165 %jot -z - Remove all variables from storage (disabled)\\
166 %jot -r <var> - Refresh/Load variable from jot (delete current vals)\\
166 %jot -r <var> - Refresh/Load variable from jot (delete current vals)\\
167 %jot foo >a.txt - Store value of foo to new file a.txt\\
167 %jot foo >a.txt - Store value of foo to new file a.txt\\
168 %jot foo >>a.txt - Append value of foo to file a.txt\\
168 %jot foo >>a.txt - Append value of foo to file a.txt\\
169
169
170 It should be noted that if you change the value of a variable, you
170 It should be noted that if you change the value of a variable, you
171 need to %note it again if you want to persist the new value.
171 need to %note it again if you want to persist the new value.
172
172
173 Note also that the variables will need to be pickleable; most basic
173 Note also that the variables will need to be pickleable; most basic
174 python types can be safely %stored.
174 python types can be safely %stored.
175
175
176 """
176 """
177
177
178 opts,argsl = self.parse_options(parameter_s,'drzl',mode='string')
178 opts,argsl = self.parse_options(parameter_s,'drzl',mode='string')
179 args = argsl.split(None,1)
179 args = argsl.split(None,1)
180 ip = self.getapi()
180 ip = self.getapi()
@@ -214,30 +214,30 b" def magic_jot(self, parameter_s=''):"
214 else:
214 else:
215 print "Details for", tolist, ":"
215 print "Details for", tolist, ":"
216 detail_variables(ip, tolist)
216 detail_variables(ip, tolist)
217
217
218 # run without arguments -> list noted variables & notes
218 # run without arguments -> list noted variables & notes
219 elif not args:
219 elif not args:
220 vars = self.db.keys('jot/*')
220 vars = self.db.keys('jot/*')
221 vars.sort()
221 vars.sort()
222 if vars:
222 if vars:
223 size = max(map(len,vars)) - 4
223 size = max(map(len,vars)) - 4
224 else:
224 else:
225 size = 0
225 size = 0
226
226
227 print 'Variables and their in-db values:'
227 print 'Variables and their in-db values:'
228 fmt = '%-'+str(size)+'s [%s] -> %s'
228 fmt = '%-'+str(size)+'s [%s] -> %s'
229 get = db.get
229 get = db.get
230 for var in vars:
230 for var in vars:
231 justkey = os.path.basename(var)
231 justkey = os.path.basename(var)
232 v = get(var,'<unavailable>')
232 v = get(var,'<unavailable>')
233 try:
233 try:
234 print fmt % (justkey,\
234 print fmt % (justkey,\
235 datetime.ctime(v.get('time','<unavailable>')),\
235 datetime.ctime(v.get('time','<unavailable>')),\
236 v.get('comment','<unavailable>')[:70].replace('\n',' '),)
236 v.get('comment','<unavailable>')[:70].replace('\n',' '),)
237 except AttributeError:
237 except AttributeError:
238 print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50])
238 print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50])
239
239
240
240
241 # default action - store the variable
241 # default action - store the variable
242 else:
242 else:
243 # %store foo >file.txt or >>file.txt
243 # %store foo >file.txt or >>file.txt
@@ -251,7 +251,7 b" def magic_jot(self, parameter_s=''):"
251 print "Writing '%s' (%s) to file '%s'." % (args[0],
251 print "Writing '%s' (%s) to file '%s'." % (args[0],
252 obj.__class__.__name__, fnam)
252 obj.__class__.__name__, fnam)
253
253
254
254
255 if not isinstance (obj,basestring):
255 if not isinstance (obj,basestring):
256 from pprint import pprint
256 from pprint import pprint
257 pprint(obj,fil)
257 pprint(obj,fil)
@@ -259,10 +259,10 b" def magic_jot(self, parameter_s=''):"
259 fil.write(obj)
259 fil.write(obj)
260 if not obj.endswith('\n'):
260 if not obj.endswith('\n'):
261 fil.write('\n')
261 fil.write('\n')
262
262
263 fil.close()
263 fil.close()
264 return
264 return
265
265
266 # %note foo
266 # %note foo
267 try:
267 try:
268 obj = ip.user_ns[args[0]]
268 obj = ip.user_ns[args[0]]
@@ -270,18 +270,18 b" def magic_jot(self, parameter_s=''):"
270 # this should not be alias, for aliases, use %store
270 # this should not be alias, for aliases, use %store
271 print
271 print
272 print "Error: %s doesn't exist." % args[0]
272 print "Error: %s doesn't exist." % args[0]
273 print
273 print
274 print "Use %note -r <var> to retrieve variables. This should not be used " +\
274 print "Use %note -r <var> to retrieve variables. This should not be used " +\
275 "to store alias, for saving aliases, use %store"
275 "to store alias, for saving aliases, use %store"
276 return
276 return
277 else:
277 else:
278 if isinstance(inspect.getmodule(obj), FakeModule):
278 if isinstance(inspect.getmodule(obj), FakeModule):
279 print textwrap.dedent("""\
279 print textwrap.dedent("""\
280 Warning:%s is %s
280 Warning:%s is %s
281 Proper storage of interactively declared classes (or instances
281 Proper storage of interactively declared classes (or instances
282 of those classes) is not possible! Only instances
282 of those classes) is not possible! Only instances
283 of classes in real modules on file system can be %%store'd.
283 of classes in real modules on file system can be %%store'd.
284 """ % (args[0], obj) )
284 """ % (args[0], obj) )
285 return
285 return
286 #pickled = pickle.dumps(obj)
286 #pickled = pickle.dumps(obj)
287 #self.db[ 'jot/' + args[0] ] = obj
287 #self.db[ 'jot/' + args[0] ] = obj
@@ -289,11 +289,11 b" def magic_jot(self, parameter_s=''):"
289
289
290
290
291 def magic_read(self, parameter_s=''):
291 def magic_read(self, parameter_s=''):
292 """
292 """
293 %read <var> - Load variable from data that is jotted down.\\
293 %read <var> - Load variable from data that is jotted down.\\
294
294
295 """
295 """
296
296
297 opts,argsl = self.parse_options(parameter_s,'drzl',mode='string')
297 opts,argsl = self.parse_options(parameter_s,'drzl',mode='string')
298 args = argsl.split(None,1)
298 args = argsl.split(None,1)
299 ip = self.getapi()
299 ip = self.getapi()
@@ -45,7 +45,7 b' def lookfor(what, modules=None, import_modules=True, regenerate=False):'
45 cache.update(c)
45 cache.update(c)
46 except ImportError:
46 except ImportError:
47 pass
47 pass
48
48
49 # Search
49 # Search
50 # XXX: maybe using a real stemming search engine would be better?
50 # XXX: maybe using a real stemming search engine would be better?
51 found = []
51 found = []
@@ -53,7 +53,7 b' def lookfor(what, modules=None, import_modules=True, regenerate=False):'
53 if not whats: return
53 if not whats: return
54
54
55 for name, (docstring, kind, index) in cache.iteritems():
55 for name, (docstring, kind, index) in cache.iteritems():
56 if kind in ('module', 'object'):
56 if kind in ('module', 'object'):
57 # don't show modules or objects
57 # don't show modules or objects
58 continue
58 continue
59 ok = True
59 ok = True
@@ -69,7 +69,7 b' def lookfor(what, modules=None, import_modules=True, regenerate=False):'
69 # XXX: this is full Harrison-Stetson heuristics now,
69 # XXX: this is full Harrison-Stetson heuristics now,
70 # XXX: it probably could be improved
70 # XXX: it probably could be improved
71
71
72 kind_relevance = {'func': 1000, 'class': 1000,
72 kind_relevance = {'func': 1000, 'class': 1000,
73 'module': -1000, 'object': -1000}
73 'module': -1000, 'object': -1000}
74
74
75 def relevance(name, docstr, kind, index):
75 def relevance(name, docstr, kind, index):
@@ -138,7 +138,7 b' def _lookfor_generate_cache(module, import_modules, regenerate):'
138 cache : dict {obj_full_name: (docstring, kind, index), ...}
138 cache : dict {obj_full_name: (docstring, kind, index), ...}
139 Docstring cache for the module, either cached one (regenerate=False)
139 Docstring cache for the module, either cached one (regenerate=False)
140 or newly generated.
140 or newly generated.
141
141
142 """
142 """
143 global _lookfor_caches
143 global _lookfor_caches
144
144
@@ -164,7 +164,7 b' def _lookfor_generate_cache(module, import_modules, regenerate):'
164
164
165 index += 1
165 index += 1
166 kind = "object"
166 kind = "object"
167
167
168 if inspect.ismodule(item):
168 if inspect.ismodule(item):
169 kind = "module"
169 kind = "module"
170 try:
170 try:
@@ -190,7 +190,7 b' def _lookfor_generate_cache(module, import_modules, regenerate):'
190 stack.append(("%s.%s" % (name, n), v))
190 stack.append(("%s.%s" % (name, n), v))
191 elif callable(item):
191 elif callable(item):
192 kind = "func"
192 kind = "func"
193
193
194 doc = inspect.getdoc(item)
194 doc = inspect.getdoc(item)
195 if doc is not None:
195 if doc is not None:
196 cache[name] = (doc, kind, index)
196 cache[name] = (doc, kind, index)
@@ -4,9 +4,9 b''
4 Usage:
4 Usage:
5
5
6 %rehashdir c:/bin c:/tools
6 %rehashdir c:/bin c:/tools
7 - Add all executables under c:/bin and c:/tools to alias table, in
7 - Add all executables under c:/bin and c:/tools to alias table, in
8 order to make them directly executable from any directory.
8 order to make them directly executable from any directory.
9
9
10 This also serves as an example on how to extend ipython
10 This also serves as an example on how to extend ipython
11 with new magic functions.
11 with new magic functions.
12
12
@@ -23,27 +23,27 b' import os,re,fnmatch,sys'
23
23
24 def selflaunch(ip,line):
24 def selflaunch(ip,line):
25 """ Launch python script with 'this' interpreter
25 """ Launch python script with 'this' interpreter
26
26
27 e.g. d:\foo\ipykit.exe a.py
27 e.g. d:\foo\ipykit.exe a.py
28
28
29 """
29 """
30
30
31 tup = line.split(None,1)
31 tup = line.split(None,1)
32 if len(tup) == 1:
32 if len(tup) == 1:
33 print "Launching nested ipython session"
33 print "Launching nested ipython session"
34 os.system(sys.executable)
34 os.system(sys.executable)
35 return
35 return
36
36
37 cmd = sys.executable + ' ' + tup[1]
37 cmd = sys.executable + ' ' + tup[1]
38 print ">",cmd
38 print ">",cmd
39 os.system(cmd)
39 os.system(cmd)
40
40
41 class PyLauncher:
41 class PyLauncher:
42 """ Invoke selflanucher on the specified script
42 """ Invoke selflanucher on the specified script
43
43
44 This is mostly useful for associating with scripts using::
44 This is mostly useful for associating with scripts using::
45 _ip.define_alias('foo',PyLauncher('foo_script.py'))
45 _ip.define_alias('foo',PyLauncher('foo_script.py'))
46
46
47 """
47 """
48 def __init__(self,script):
48 def __init__(self,script):
49 self.script = os.path.abspath(script)
49 self.script = os.path.abspath(script)
@@ -57,22 +57,22 b' class PyLauncher:'
57 tail = ' ' + tup[1]
57 tail = ' ' + tup[1]
58 else:
58 else:
59 tail = ''
59 tail = ''
60
60
61 selflaunch(ip,"py " + self.script + tail)
61 selflaunch(ip,"py " + self.script + tail)
62 def __repr__(self):
62 def __repr__(self):
63 return 'PyLauncher("%s")' % self.script
63 return 'PyLauncher("%s")' % self.script
64
64
65 def rehashdir_f(self,arg):
65 def rehashdir_f(self,arg):
66 """ Add executables in all specified dirs to alias table
66 """ Add executables in all specified dirs to alias table
67
67
68 Usage:
68 Usage:
69
69
70 %rehashdir c:/bin;c:/tools
70 %rehashdir c:/bin;c:/tools
71 - Add all executables under c:/bin and c:/tools to alias table, in
71 - Add all executables under c:/bin and c:/tools to alias table, in
72 order to make them directly executable from any directory.
72 order to make them directly executable from any directory.
73
73
74 Without arguments, add all executables in current directory.
74 Without arguments, add all executables in current directory.
75
75
76 """
76 """
77
77
78 # most of the code copied from Magic.magic_rehashx
78 # most of the code copied from Magic.magic_rehashx
@@ -83,13 +83,13 b' def rehashdir_f(self,arg):'
83 if fnmatch.fnmatch(fname, j):
83 if fnmatch.fnmatch(fname, j):
84 return True
84 return True
85 return False
85 return False
86
86
87 created = []
87 created = []
88 if not arg:
88 if not arg:
89 arg = '.'
89 arg = '.'
90 path = map(os.path.abspath,arg.split(';'))
90 path = map(os.path.abspath,arg.split(';'))
91 alias_table = self.shell.alias_manager.alias_table
91 alias_table = self.shell.alias_manager.alias_table
92
92
93 if os.name == 'posix':
93 if os.name == 'posix':
94 isexec = lambda fname:os.path.isfile(fname) and \
94 isexec = lambda fname:os.path.isfile(fname) and \
95 os.access(fname,os.X_OK)
95 os.access(fname,os.X_OK)
@@ -101,7 +101,7 b' def rehashdir_f(self,arg):'
101 winext = 'exe|com|bat|py'
101 winext = 'exe|com|bat|py'
102 if 'py' not in winext:
102 if 'py' not in winext:
103 winext += '|py'
103 winext += '|py'
104
104
105 execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
105 execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
106 isexec = lambda fname:os.path.isfile(fname) and execre.match(fname)
106 isexec = lambda fname:os.path.isfile(fname) and execre.match(fname)
107 savedir = os.getcwdu()
107 savedir = os.getcwdu()
@@ -117,7 +117,7 b' def rehashdir_f(self,arg):'
117 # where N is the number of positional arguments of the
117 # where N is the number of positional arguments of the
118 # alias.
118 # alias.
119 src,tgt = os.path.splitext(ff)[0], os.path.abspath(ff)
119 src,tgt = os.path.splitext(ff)[0], os.path.abspath(ff)
120 created.append(src)
120 created.append(src)
121 alias_table[src] = (0,tgt)
121 alias_table[src] = (0,tgt)
122 else:
122 else:
123 for pdir in path:
123 for pdir in path:
@@ -126,7 +126,7 b' def rehashdir_f(self,arg):'
126 if isexec(ff) and not isjunk(ff):
126 if isexec(ff) and not isjunk(ff):
127 src, tgt = execre.sub(r'\1',ff), os.path.abspath(ff)
127 src, tgt = execre.sub(r'\1',ff), os.path.abspath(ff)
128 src = src.lower()
128 src = src.lower()
129 created.append(src)
129 created.append(src)
130 alias_table[src] = (0,tgt)
130 alias_table[src] = (0,tgt)
131 # Make sure the alias table doesn't contain keywords or builtins
131 # Make sure the alias table doesn't contain keywords or builtins
132 self.shell.alias_table_validate()
132 self.shell.alias_table_validate()
@@ -11,7 +11,7 b' from IPython.external.Itpl import itplns'
11
11
12 def toclip_w32(s):
12 def toclip_w32(s):
13 """ Places contents of s to clipboard
13 """ Places contents of s to clipboard
14
14
15 Needs pyvin32 to work:
15 Needs pyvin32 to work:
16 http://sourceforge.net/projects/pywin32/
16 http://sourceforge.net/projects/pywin32/
17 """
17 """
@@ -23,29 +23,29 b' def toclip_w32(s):'
23 cl.CloseClipboard()
23 cl.CloseClipboard()
24
24
25 try:
25 try:
26 import win32clipboard
26 import win32clipboard
27 toclip = toclip_w32
27 toclip = toclip_w32
28 except ImportError:
28 except ImportError:
29 def toclip(s): pass
29 def toclip(s): pass
30
30
31
31
32 def render(tmpl):
32 def render(tmpl):
33 """ Render a template (Itpl format) from ipython variables
33 """ Render a template (Itpl format) from ipython variables
34
34
35 Example:
35 Example:
36
36
37 $ import ipy_render
37 $ import ipy_render
38 $ my_name = 'Bob' # %store this for convenience
38 $ my_name = 'Bob' # %store this for convenience
39 $ t_submission_form = "Submission report, author: $my_name" # %store also
39 $ t_submission_form = "Submission report, author: $my_name" # %store also
40 $ render t_submission_form
40 $ render t_submission_form
41
41
42 => returns "Submission report, author: Bob" and copies to clipboard on win32
42 => returns "Submission report, author: Bob" and copies to clipboard on win32
43
43
44 # if template exist as a file, read it. Note: ;f hei vaan => f("hei vaan")
44 # if template exist as a file, read it. Note: ;f hei vaan => f("hei vaan")
45 $ ;render c:/templates/greeting.txt
45 $ ;render c:/templates/greeting.txt
46
46
47 Template examples (Ka-Ping Yee's Itpl library):
47 Template examples (Ka-Ping Yee's Itpl library):
48
48
49 Here is a $string.
49 Here is a $string.
50 Here is a $module.member.
50 Here is a $module.member.
51 Here is an $object.member.
51 Here is an $object.member.
@@ -54,10 +54,10 b' def render(tmpl):'
54 Here is an $array[3] member.
54 Here is an $array[3] member.
55 Here is a $dictionary['member'].
55 Here is a $dictionary['member'].
56 """
56 """
57
57
58 if os.path.isfile(tmpl):
58 if os.path.isfile(tmpl):
59 tmpl = open(tmpl).read()
59 tmpl = open(tmpl).read()
60
60
61 res = itplns(tmpl, ip.user_ns)
61 res = itplns(tmpl, ip.user_ns)
62 toclip(res)
62 toclip(res)
63 return res
63 return res
@@ -6,7 +6,7 b' import ipy_server'
6 ipy_server.serve_thread(16455)
6 ipy_server.serve_thread(16455)
7
7
8 Now, to execute the statements in this ipython instance, open a TCP socket
8 Now, to execute the statements in this ipython instance, open a TCP socket
9 (port 16455), write out the statements, and close the socket.
9 (port 16455), write out the statements, and close the socket.
10 You can use e.g. "telnet localhost 16455" or a script to do this.
10 You can use e.g. "telnet localhost 16455" or a script to do this.
11
11
12 This is a bit like 'M-x server-start" or gnuserv in the emacs world.
12 This is a bit like 'M-x server-start" or gnuserv in the emacs world.
@@ -17,27 +17,27 b' ip = ipapi.get()'
17
17
18 def new_ipsystem_posix(cmd):
18 def new_ipsystem_posix(cmd):
19 """ ctrl+c ignoring replacement for system() command in iplib.
19 """ ctrl+c ignoring replacement for system() command in iplib.
20
20
21 Ignore ctrl + c in IPython process during the command execution.
21 Ignore ctrl + c in IPython process during the command execution.
22 The subprocess will still get the ctrl + c signal.
22 The subprocess will still get the ctrl + c signal.
23
23
24 posix implementation
24 posix implementation
25 """
25 """
26
26
27 p = subprocess.Popen(cmd, shell = True)
27 p = subprocess.Popen(cmd, shell = True)
28
28
29 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
29 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
30 pid,status = os.waitpid(p.pid,0)
30 pid,status = os.waitpid(p.pid,0)
31 signal.signal(signal.SIGINT, old_handler)
31 signal.signal(signal.SIGINT, old_handler)
32 if status and ip.options.verbose:
32 if status and ip.options.verbose:
33 print "[exit status: %d]" % status
33 print "[exit status: %d]" % status
34
34
35 def new_ipsystem_win32(cmd):
35 def new_ipsystem_win32(cmd):
36 """ ctrl+c ignoring replacement for system() command in iplib.
36 """ ctrl+c ignoring replacement for system() command in iplib.
37
37
38 Ignore ctrl + c in IPython process during the command execution.
38 Ignore ctrl + c in IPython process during the command execution.
39 The subprocess will still get the ctrl + c signal.
39 The subprocess will still get the ctrl + c signal.
40
40
41 win32 implementation
41 win32 implementation
42 """
42 """
43 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
43 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
@@ -45,8 +45,8 b' def new_ipsystem_win32(cmd):'
45 signal.signal(signal.SIGINT, old_handler)
45 signal.signal(signal.SIGINT, old_handler)
46 if status and ip.options.verbose:
46 if status and ip.options.verbose:
47 print "[exit status: %d]" % status
47 print "[exit status: %d]" % status
48
48
49
49
50 def init():
50 def init():
51 o = ip.options
51 o = ip.options
52 try:
52 try:
@@ -54,9 +54,8 b' def init():'
54 except AttributeError:
54 except AttributeError:
55 o.allow_new_attr (True )
55 o.allow_new_attr (True )
56 o.verbose = 0
56 o.verbose = 0
57
57
58 ip.system = (sys.platform == 'win32' and new_ipsystem_win32 or
58 ip.system = (sys.platform == 'win32' and new_ipsystem_win32 or
59 new_ipsystem_posix)
59 new_ipsystem_posix)
60
60
61 init()
61 init()
62 No newline at end of file
@@ -3,7 +3,7 b''
3 This will be imported by ipython for all users.
3 This will be imported by ipython for all users.
4
4
5 After this ipy_user_conf.py is imported, user specific configuration
5 After this ipy_user_conf.py is imported, user specific configuration
6 should reside there.
6 should reside there.
7
7
8 """
8 """
9
9
@@ -11,7 +11,7 b' from IPython.core import ipapi'
11 ip = ipapi.get()
11 ip = ipapi.get()
12
12
13 # add system wide configuration information, import extensions etc. here.
13 # add system wide configuration information, import extensions etc. here.
14 # nothing here is essential
14 # nothing here is essential
15
15
16 import sys
16 import sys
17
17
@@ -3,8 +3,8 b''
3 Usage:
3 Usage:
4
4
5 %wdb test.py
5 %wdb test.py
6 run test.py, with a winpdb breakpoint at start of the file
6 run test.py, with a winpdb breakpoint at start of the file
7
7
8 %wdb pass
8 %wdb pass
9 Change the password (e.g. if you have forgotten the old one)
9 Change the password (e.g. if you have forgotten the old one)
10
10
@@ -31,16 +31,16 b' rpdb_started = False'
31
31
32 def wdb_f(self, arg):
32 def wdb_f(self, arg):
33 """ Debug a script (like %run -d) in IPython process, Using WinPdb
33 """ Debug a script (like %run -d) in IPython process, Using WinPdb
34
34
35 Usage:
35 Usage:
36
36
37 %wdb test.py
37 %wdb test.py
38 run test.py, with a winpdb breakpoint at start of the file
38 run test.py, with a winpdb breakpoint at start of the file
39
39
40 %wdb pass
40 %wdb pass
41 Change the password (e.g. if you have forgotten the old one)
41 Change the password (e.g. if you have forgotten the old one)
42
42
43 Note that after the script has been run, you need to do "Go" (f5)
43 Note that after the script has been run, you need to do "Go" (f5)
44 in WinPdb to resume normal IPython operation.
44 in WinPdb to resume normal IPython operation.
45 """
45 """
46
46
@@ -48,15 +48,15 b' def wdb_f(self, arg):'
48 if not arg.strip():
48 if not arg.strip():
49 print __doc__
49 print __doc__
50 return
50 return
51
51
52 if arg.strip() == 'pass':
52 if arg.strip() == 'pass':
53 passwd = raw_input('Enter new winpdb session password: ')
53 passwd = raw_input('Enter new winpdb session password: ')
54 ip.db['winpdb_pass'] = passwd
54 ip.db['winpdb_pass'] = passwd
55 print "Winpdb password changed"
55 print "Winpdb password changed"
56 if rpdb_started:
56 if rpdb_started:
57 print "You need to restart IPython to use the new password"
57 print "You need to restart IPython to use the new password"
58 return
58 return
59
59
60 path = os.path.abspath(arg)
60 path = os.path.abspath(arg)
61 if not os.path.isfile(path):
61 if not os.path.isfile(path):
62 raise UsageError("%%wdb: file %s does not exist" % path)
62 raise UsageError("%%wdb: file %s does not exist" % path)
@@ -66,19 +66,19 b' def wdb_f(self, arg):'
66 import textwrap
66 import textwrap
67 print textwrap.dedent("""\
67 print textwrap.dedent("""\
68 Winpdb sessions need a password that you use for attaching the external
68 Winpdb sessions need a password that you use for attaching the external
69 winpdb session. IPython will remember this. You can change the password later
69 winpdb session. IPython will remember this. You can change the password later
70 by '%wpdb pass'
70 by '%wpdb pass'
71 """)
71 """)
72 passwd = raw_input('Enter new winpdb session password: ')
72 passwd = raw_input('Enter new winpdb session password: ')
73 ip.db['winpdb_pass'] = passwd
73 ip.db['winpdb_pass'] = passwd
74
74
75 print "Starting rpdb2 in IPython process"
75 print "Starting rpdb2 in IPython process"
76 rpdb2.start_embedded_debugger(passwd, timeout = 0)
76 rpdb2.start_embedded_debugger(passwd, timeout = 0)
77 rpdb_started = True
77 rpdb_started = True
78
78
79 rpdb2.set_temp_breakpoint(path)
79 rpdb2.set_temp_breakpoint(path)
80 print 'It is time to attach with WinPdb (launch WinPdb if needed, File -> Attach)'
80 print 'It is time to attach with WinPdb (launch WinPdb if needed, File -> Attach)'
81 ip.magic('%run ' + arg)
81 ip.magic('%run ' + arg)
82
82
83
83
84 ip.define_magic('wdb', wdb_f)
84 ip.define_magic('wdb', wdb_f)
@@ -5,14 +5,14 b' import os, subprocess'
5
5
6 workdir = None
6 workdir = None
7 def workdir_f(ip,line):
7 def workdir_f(ip,line):
8 """ Exceute commands residing in cwd elsewhere
8 """ Exceute commands residing in cwd elsewhere
9
9
10 Example::
10 Example::
11
11
12 workdir /myfiles
12 workdir /myfiles
13 cd bin
13 cd bin
14 workdir myscript.py
14 workdir myscript.py
15
15
16 executes myscript.py (stored in bin, but not in path) in /myfiles
16 executes myscript.py (stored in bin, but not in path) in /myfiles
17 """
17 """
18 global workdir
18 global workdir
@@ -1,4 +1,4 b''
1 """ Preliminary "job control" extensions for IPython
1 """ Preliminary "job control" extensions for IPython
2
2
3 requires python 2.4 (or separate 'subprocess' module
3 requires python 2.4 (or separate 'subprocess' module
4
4
@@ -39,7 +39,7 b' Now launch a new IPython prompt and kill the process:'
39 [Q:/ipython]|4>
39 [Q:/ipython]|4>
40
40
41 (you don't need to specify PID for %kill if only one task is running)
41 (you don't need to specify PID for %kill if only one task is running)
42 """
42 """
43
43
44 from subprocess import *
44 from subprocess import *
45 import os,shlex,sys,time
45 import os,shlex,sys,time
@@ -55,8 +55,8 b" if os.name == 'nt':"
55 else:
55 else:
56 def kill_process(pid):
56 def kill_process(pid):
57 os.system('kill -9 %d' % pid)
57 os.system('kill -9 %d' % pid)
58
58
59
59
60
60
61 class IpyPopen(Popen):
61 class IpyPopen(Popen):
62 def go(self):
62 def go(self):
@@ -66,7 +66,7 b' class IpyPopen(Popen):'
66
66
67 def kill(self):
67 def kill(self):
68 kill_process(self.pid)
68 kill_process(self.pid)
69
69
70 def startjob(job):
70 def startjob(job):
71 p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False)
71 p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False)
72 p.line = job
72 p.line = job
@@ -85,7 +85,7 b' class AsyncJobQ(threading.Thread):'
85 self.output.append("** Discarding: '%s' - %s" % (cmd,cwd))
85 self.output.append("** Discarding: '%s' - %s" % (cmd,cwd))
86 continue
86 continue
87 self.output.append("** Task started: '%s' - %s" % (cmd,cwd))
87 self.output.append("** Task started: '%s' - %s" % (cmd,cwd))
88
88
89 p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd = cwd)
89 p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd = cwd)
90 out = p.stdout.read()
90 out = p.stdout.read()
91 self.output.append("** Task complete: '%s'\n" % cmd)
91 self.output.append("** Task complete: '%s'\n" % cmd)
@@ -93,16 +93,16 b' class AsyncJobQ(threading.Thread):'
93
93
94 def add(self,cmd):
94 def add(self,cmd):
95 self.q.put_nowait((cmd, os.getcwdu()))
95 self.q.put_nowait((cmd, os.getcwdu()))
96
96
97 def dumpoutput(self):
97 def dumpoutput(self):
98 while self.output:
98 while self.output:
99 item = self.output.pop(0)
99 item = self.output.pop(0)
100 print item
100 print item
101
101
102 _jobq = None
102 _jobq = None
103
103
104 def jobqueue_f(self, line):
104 def jobqueue_f(self, line):
105
105
106 global _jobq
106 global _jobq
107 if not _jobq:
107 if not _jobq:
108 print "Starting jobqueue - do '&some_long_lasting_system_command' to enqueue"
108 print "Starting jobqueue - do '&some_long_lasting_system_command' to enqueue"
@@ -118,11 +118,11 b' def jobqueue_f(self, line):'
118 if line.strip() == 'start':
118 if line.strip() == 'start':
119 _jobq.stop = False
119 _jobq.stop = False
120 return
120 return
121
121
122 def jobctrl_prefilter_f(self,line):
122 def jobctrl_prefilter_f(self,line):
123 if line.startswith('&'):
123 if line.startswith('&'):
124 pre,fn,rest = self.split_user_input(line[1:])
124 pre,fn,rest = self.split_user_input(line[1:])
125
125
126 line = ip.expand_aliases(fn,rest)
126 line = ip.expand_aliases(fn,rest)
127 if not _jobq:
127 if not _jobq:
128 return 'get_ipython().startjob(%s)' % make_quoted_expr(line)
128 return 'get_ipython().startjob(%s)' % make_quoted_expr(line)
@@ -147,7 +147,7 b' def magic_tasks(self,line):'
147
147
148 A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled.
148 A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled.
149 Tasks can be killed with %kill.
149 Tasks can be killed with %kill.
150
150
151 '%tasks clear' clears the task list (from stale tasks)
151 '%tasks clear' clears the task list (from stale tasks)
152 """
152 """
153 ip = self.getapi()
153 ip = self.getapi()
@@ -156,7 +156,7 b' def magic_tasks(self,line):'
156 print "Clearing",ip.db[k]
156 print "Clearing",ip.db[k]
157 del ip.db[k]
157 del ip.db[k]
158 return
158 return
159
159
160 ents = job_list(ip)
160 ents = job_list(ip)
161 if not ents:
161 if not ents:
162 print "No tasks running"
162 print "No tasks running"
@@ -181,19 +181,19 b' def magic_kill(self,line):'
181 else:
181 else:
182 magic_tasks(self,line)
182 magic_tasks(self,line)
183 return
183 return
184
184
185 try:
185 try:
186 pid = int(line)
186 pid = int(line)
187 kill_process(pid)
187 kill_process(pid)
188 except ValueError:
188 except ValueError:
189 magic_tasks(self,line)
189 magic_tasks(self,line)
190
190
191 if sys.platform == 'win32':
191 if sys.platform == 'win32':
192 shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir start time type ver vol'.split()
192 shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir start time type ver vol'.split()
193 PopenExc = WindowsError
193 PopenExc = WindowsError
194 else:
194 else:
195 # todo linux commands
195 # todo linux commands
196 shell_internal_commands = []
196 shell_internal_commands = []
197 PopenExc = OSError
197 PopenExc = OSError
198
198
199
199
@@ -218,10 +218,10 b' def jobctrl_shellcmd(ip,cmd):'
218 else:
218 else:
219 # have to go via shell, sucks
219 # have to go via shell, sucks
220 p = Popen(cmd,shell = True)
220 p = Popen(cmd,shell = True)
221
221
222 jobentry = 'tasks/t' + str(p.pid)
222 jobentry = 'tasks/t' + str(p.pid)
223 ip.db[jobentry] = (p.pid,cmd,os.getcwdu(),time.time())
223 ip.db[jobentry] = (p.pid,cmd,os.getcwdu(),time.time())
224 p.communicate()
224 p.communicate()
225
225
226 finally:
226 finally:
227 if jobentry:
227 if jobentry:
@@ -238,5 +238,5 b' def install():'
238 ip.define_magic('kill',magic_kill)
238 ip.define_magic('kill',magic_kill)
239 ip.define_magic('tasks',magic_tasks)
239 ip.define_magic('tasks',magic_tasks)
240 ip.define_magic('jobqueue',jobqueue_f)
240 ip.define_magic('jobqueue',jobqueue_f)
241 ip.set_hook('pre_prompt_hook', jobq_output_hook)
241 ip.set_hook('pre_prompt_hook', jobq_output_hook)
242 install()
242 install()
@@ -1,9 +1,9 b''
1 """ Fun magic line editor for ipython
1 """ Fun magic line editor for ipython
2
2
3 Use this to easily edit lists of strings gradually without crafting long
3 Use this to easily edit lists of strings gradually without crafting long
4 list comprehensions.
4 list comprehensions.
5
5
6 'l' is the magic variable name for every line (array element). Save the current
6 'l' is the magic variable name for every line (array element). Save the current
7 result (or more exactly, retrieve the last ipython computation result into
7 result (or more exactly, retrieve the last ipython computation result into
8 %led work area) by running '%led s'. Just run '%led' to show the current work
8 %led work area) by running '%led s'. Just run '%led' to show the current work
9 area data.
9 area data.
@@ -51,9 +51,9 b' curdata = []'
51
51
52 def line_edit_f(self, cmd ):
52 def line_edit_f(self, cmd ):
53 global curdata
53 global curdata
54
54
55 if not cmd:
55 if not cmd:
56
56
57 print "Magic line editor (for lists of strings)"
57 print "Magic line editor (for lists of strings)"
58 if curdata:
58 if curdata:
59 print "current data is:"
59 print "current data is:"
@@ -62,12 +62,12 b' def line_edit_f(self, cmd ):'
62 print "No current data, you should set it by running '%led s'"
62 print "No current data, you should set it by running '%led s'"
63 print "When you have your data in _ (result of last computation)."
63 print "When you have your data in _ (result of last computation)."
64 return
64 return
65
65
66 if cmd == 's':
66 if cmd == 's':
67 curdata = ip.ev('_')
67 curdata = ip.ev('_')
68 print "Data set from last result (_)"
68 print "Data set from last result (_)"
69 newlines = curdata
69 newlines = curdata
70
70
71 else:
71 else:
72 # simple method call, e.g. upper
72 # simple method call, e.g. upper
73 if cmd.isalpha():
73 if cmd.isalpha():
@@ -83,16 +83,16 b' def line_edit_f(self, cmd ):'
83 continue
83 continue
84 newlines.append(l2)
84 newlines.append(l2)
85
85
86
86
87 return newlines
87 return newlines
88
88
89 def line_edit_complete_f(self,event):
89 def line_edit_complete_f(self,event):
90 """ Show all string methods in completions """
90 """ Show all string methods in completions """
91 if event.symbol.startswith('l.'):
91 if event.symbol.startswith('l.'):
92 return ['l.' + func for func in dir('')]
92 return ['l.' + func for func in dir('')]
93
93
94 return dir('') + ['l.' + func for func in dir('')]
94 return dir('') + ['l.' + func for func in dir('')]
95
95
96 ip.set_hook('complete_command', line_edit_complete_f , str_key = '%led')
96 ip.set_hook('complete_command', line_edit_complete_f , str_key = '%led')
97
97
98 ip.define_magic('led', line_edit_f) No newline at end of file
98 ip.define_magic('led', line_edit_f)
@@ -36,56 +36,56 b' def refresh_variables(ip):'
36 else:
36 else:
37 #print "restored",justkey,"=",obj #dbg
37 #print "restored",justkey,"=",obj #dbg
38 ip.user_ns[justkey] = obj
38 ip.user_ns[justkey] = obj
39
39
40
40
41 def restore_dhist(ip):
41 def restore_dhist(ip):
42 db = ip.db
42 db = ip.db
43 ip.user_ns['_dh'] = db.get('dhist',[])
43 ip.user_ns['_dh'] = db.get('dhist',[])
44
44
45 def restore_data(self):
45 def restore_data(self):
46 ip = self.getapi()
46 ip = self.getapi()
47 refresh_variables(ip)
47 refresh_variables(ip)
48 restore_aliases(self)
48 restore_aliases(self)
49 restore_dhist(self)
49 restore_dhist(self)
50 raise TryNext
50 raise TryNext
51
51
52 ip.set_hook('late_startup_hook', restore_data)
52 ip.set_hook('late_startup_hook', restore_data)
53
53
54 def magic_store(self, parameter_s=''):
54 def magic_store(self, parameter_s=''):
55 """Lightweight persistence for python variables.
55 """Lightweight persistence for python variables.
56
56
57 Example:
57 Example:
58
58
59 ville@badger[~]|1> A = ['hello',10,'world']\\
59 ville@badger[~]|1> A = ['hello',10,'world']\\
60 ville@badger[~]|2> %store A\\
60 ville@badger[~]|2> %store A\\
61 ville@badger[~]|3> Exit
61 ville@badger[~]|3> Exit
62
62
63 (IPython session is closed and started again...)
63 (IPython session is closed and started again...)
64
64
65 ville@badger:~$ ipython -p pysh\\
65 ville@badger:~$ ipython -p pysh\\
66 ville@badger[~]|1> print A
66 ville@badger[~]|1> print A
67
67
68 ['hello', 10, 'world']
68 ['hello', 10, 'world']
69
69
70 Usage:
70 Usage:
71
71
72 %store - Show list of all variables and their current values\\
72 %store - Show list of all variables and their current values\\
73 %store <var> - Store the *current* value of the variable to disk\\
73 %store <var> - Store the *current* value of the variable to disk\\
74 %store -d <var> - Remove the variable and its value from storage\\
74 %store -d <var> - Remove the variable and its value from storage\\
75 %store -z - Remove all variables from storage\\
75 %store -z - Remove all variables from storage\\
76 %store -r - Refresh all variables from store (delete current vals)\\
76 %store -r - Refresh all variables from store (delete current vals)\\
77 %store foo >a.txt - Store value of foo to new file a.txt\\
77 %store foo >a.txt - Store value of foo to new file a.txt\\
78 %store foo >>a.txt - Append value of foo to file a.txt\\
78 %store foo >>a.txt - Append value of foo to file a.txt\\
79
79
80 It should be noted that if you change the value of a variable, you
80 It should be noted that if you change the value of a variable, you
81 need to %store it again if you want to persist the new value.
81 need to %store it again if you want to persist the new value.
82
82
83 Note also that the variables will need to be pickleable; most basic
83 Note also that the variables will need to be pickleable; most basic
84 python types can be safely %stored.
84 python types can be safely %stored.
85
85
86 Also aliases can be %store'd across sessions.
86 Also aliases can be %store'd across sessions.
87 """
87 """
88
88
89 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
89 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
90 args = argsl.split(None,1)
90 args = argsl.split(None,1)
91 ip = self.getapi()
91 ip = self.getapi()
@@ -109,16 +109,16 b" def magic_store(self, parameter_s=''):"
109 elif opts.has_key('r'):
109 elif opts.has_key('r'):
110 refresh_variables(ip)
110 refresh_variables(ip)
111
111
112
112
113 # run without arguments -> list variables & values
113 # run without arguments -> list variables & values
114 elif not args:
114 elif not args:
115 vars = self.db.keys('autorestore/*')
115 vars = self.db.keys('autorestore/*')
116 vars.sort()
116 vars.sort()
117 if vars:
117 if vars:
118 size = max(map(len,vars))
118 size = max(map(len,vars))
119 else:
119 else:
120 size = 0
120 size = 0
121
121
122 print 'Stored variables and their in-db values:'
122 print 'Stored variables and their in-db values:'
123 fmt = '%-'+str(size)+'s -> %s'
123 fmt = '%-'+str(size)+'s -> %s'
124 get = db.get
124 get = db.get
@@ -126,7 +126,7 b" def magic_store(self, parameter_s=''):"
126 justkey = os.path.basename(var)
126 justkey = os.path.basename(var)
127 # print 30 first characters from every var
127 # print 30 first characters from every var
128 print fmt % (justkey,repr(get(var,'<unavailable>'))[:50])
128 print fmt % (justkey,repr(get(var,'<unavailable>'))[:50])
129
129
130 # default action - store the variable
130 # default action - store the variable
131 else:
131 else:
132 # %store foo >file.txt or >>file.txt
132 # %store foo >file.txt or >>file.txt
@@ -140,7 +140,7 b" def magic_store(self, parameter_s=''):"
140 print "Writing '%s' (%s) to file '%s'." % (args[0],
140 print "Writing '%s' (%s) to file '%s'." % (args[0],
141 obj.__class__.__name__, fnam)
141 obj.__class__.__name__, fnam)
142
142
143
143
144 if not isinstance (obj,basestring):
144 if not isinstance (obj,basestring):
145 from pprint import pprint
145 from pprint import pprint
146 pprint(obj,fil)
146 pprint(obj,fil)
@@ -148,10 +148,10 b" def magic_store(self, parameter_s=''):"
148 fil.write(obj)
148 fil.write(obj)
149 if not obj.endswith('\n'):
149 if not obj.endswith('\n'):
150 fil.write('\n')
150 fil.write('\n')
151
151
152 fil.close()
152 fil.close()
153 return
153 return
154
154
155 # %store foo
155 # %store foo
156 try:
156 try:
157 obj = ip.user_ns[args[0]]
157 obj = ip.user_ns[args[0]]
@@ -161,20 +161,20 b" def magic_store(self, parameter_s=''):"
161 if args[0] in self.alias_table:
161 if args[0] in self.alias_table:
162 staliases = db.get('stored_aliases',{})
162 staliases = db.get('stored_aliases',{})
163 staliases[ args[0] ] = self.alias_table[ args[0] ]
163 staliases[ args[0] ] = self.alias_table[ args[0] ]
164 db['stored_aliases'] = staliases
164 db['stored_aliases'] = staliases
165 print "Alias stored:", args[0], self.alias_table[ args[0] ]
165 print "Alias stored:", args[0], self.alias_table[ args[0] ]
166 return
166 return
167 else:
167 else:
168 raise UsageError("Unknown variable '%s'" % args[0])
168 raise UsageError("Unknown variable '%s'" % args[0])
169
169
170 else:
170 else:
171 if isinstance(inspect.getmodule(obj), FakeModule):
171 if isinstance(inspect.getmodule(obj), FakeModule):
172 print textwrap.dedent("""\
172 print textwrap.dedent("""\
173 Warning:%s is %s
173 Warning:%s is %s
174 Proper storage of interactively declared classes (or instances
174 Proper storage of interactively declared classes (or instances
175 of those classes) is not possible! Only instances
175 of those classes) is not possible! Only instances
176 of classes in real modules on file system can be %%store'd.
176 of classes in real modules on file system can be %%store'd.
177 """ % (args[0], obj) )
177 """ % (args[0], obj) )
178 return
178 return
179 #pickled = pickle.dumps(obj)
179 #pickled = pickle.dumps(obj)
180 self.db[ 'autorestore/' + args[0] ] = obj
180 self.db[ 'autorestore/' + args[0] ] = obj
@@ -11,12 +11,12 b" def clip_f( self, parameter_s = '' ):"
11 This function uses the same syntax as %macro for line extraction, but
11 This function uses the same syntax as %macro for line extraction, but
12 instead of creating a macro it saves the resulting string to the
12 instead of creating a macro it saves the resulting string to the
13 clipboard.
13 clipboard.
14
14
15 When used without arguments, this returns the text contents of the clipboard.
15 When used without arguments, this returns the text contents of the clipboard.
16 E.g.
16 E.g.
17
17
18 mytext = %clip
18 mytext = %clip
19
19
20 """
20 """
21
21
22 import win32clipboard as cl
22 import win32clipboard as cl
@@ -28,7 +28,7 b" def clip_f( self, parameter_s = '' ):"
28 cl.CloseClipboard()
28 cl.CloseClipboard()
29 return data
29 return data
30 api = self.getapi()
30 api = self.getapi()
31
31
32 if parameter_s.lstrip().startswith('='):
32 if parameter_s.lstrip().startswith('='):
33 rest = parameter_s[parameter_s.index('=')+1:].strip()
33 rest = parameter_s[parameter_s.index('=')+1:].strip()
34 val = str(api.ev(rest))
34 val = str(api.ev(rest))
@@ -41,5 +41,5 b" def clip_f( self, parameter_s = '' ):"
41 cl.CloseClipboard()
41 cl.CloseClipboard()
42 print 'The following text was written to the clipboard'
42 print 'The following text was written to the clipboard'
43 print val
43 print val
44
44
45 ip.define_magic( "clip", clip_f )
45 ip.define_magic( "clip", clip_f )
@@ -150,7 +150,7 b' def make_label_dec(label,ds=None):'
150 labels = [label]
150 labels = [label]
151 else:
151 else:
152 labels = label
152 labels = label
153
153
154 # Validate that the given label(s) are OK for use in setattr() by doing a
154 # Validate that the given label(s) are OK for use in setattr() by doing a
155 # dry run on a dummy function.
155 # dry run on a dummy function.
156 tmp = lambda : None
156 tmp = lambda : None
@@ -162,12 +162,12 b' def make_label_dec(label,ds=None):'
162 for label in labels:
162 for label in labels:
163 setattr(f,label,True)
163 setattr(f,label,True)
164 return f
164 return f
165
165
166 # Apply the user's docstring, or autogenerate a basic one
166 # Apply the user's docstring, or autogenerate a basic one
167 if ds is None:
167 if ds is None:
168 ds = "Labels a test as %r." % label
168 ds = "Labels a test as %r." % label
169 decor.__doc__ = ds
169 decor.__doc__ = ds
170
170
171 return decor
171 return decor
172
172
173
173
@@ -179,11 +179,11 b' def skipif(skip_condition, msg=None):'
179
179
180 Parameters
180 Parameters
181 ----------
181 ----------
182 skip_condition : bool or callable.
182 skip_condition : bool or callable.
183 Flag to determine whether to skip test. If the condition is a
183 Flag to determine whether to skip test. If the condition is a
184 callable, it is used at runtime to dynamically make the decision. This
184 callable, it is used at runtime to dynamically make the decision. This
185 is useful for tests that may require costly imports, to delay the cost
185 is useful for tests that may require costly imports, to delay the cost
186 until the test suite is actually executed.
186 until the test suite is actually executed.
187 msg : string
187 msg : string
188 Message to give on raising a SkipTest exception
188 Message to give on raising a SkipTest exception
189
189
@@ -202,8 +202,8 b' def skipif(skip_condition, msg=None):'
202 '''
202 '''
203
203
204 def skip_decorator(f):
204 def skip_decorator(f):
205 # Local import to avoid a hard nose dependency and only incur the
205 # Local import to avoid a hard nose dependency and only incur the
206 # import time overhead at actual test-time.
206 # import time overhead at actual test-time.
207 import nose
207 import nose
208
208
209 # Allow for both boolean or callable skip conditions.
209 # Allow for both boolean or callable skip conditions.
@@ -225,7 +225,7 b' def skipif(skip_condition, msg=None):'
225 if skip_val():
225 if skip_val():
226 raise nose.SkipTest(get_msg(f,msg))
226 raise nose.SkipTest(get_msg(f,msg))
227 else:
227 else:
228 return f(*args, **kwargs)
228 return f(*args, **kwargs)
229
229
230 def skipper_gen(*args, **kwargs):
230 def skipper_gen(*args, **kwargs):
231 """Skipper for test generators."""
231 """Skipper for test generators."""
@@ -240,7 +240,7 b' def skipif(skip_condition, msg=None):'
240 skipper = skipper_gen
240 skipper = skipper_gen
241 else:
241 else:
242 skipper = skipper_func
242 skipper = skipper_func
243
243
244 return nose.tools.make_decorator(f)(skipper)
244 return nose.tools.make_decorator(f)(skipper)
245
245
246 return skip_decorator
246 return skip_decorator
@@ -38,20 +38,20 b' from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell'
38 class StreamProxy(io.IOStream):
38 class StreamProxy(io.IOStream):
39 """Proxy for sys.stdout/err. This will request the stream *at call time*
39 """Proxy for sys.stdout/err. This will request the stream *at call time*
40 allowing for nose's Capture plugin's redirection of sys.stdout/err.
40 allowing for nose's Capture plugin's redirection of sys.stdout/err.
41
41
42 Parameters
42 Parameters
43 ----------
43 ----------
44 name : str
44 name : str
45 The name of the stream. This will be requested anew at every call
45 The name of the stream. This will be requested anew at every call
46 """
46 """
47
47
48 def __init__(self, name):
48 def __init__(self, name):
49 self.name=name
49 self.name=name
50
50
51 @property
51 @property
52 def stream(self):
52 def stream(self):
53 return getattr(sys, self.name)
53 return getattr(sys, self.name)
54
54
55 def flush(self):
55 def flush(self):
56 self.stream.flush()
56 self.stream.flush()
57
57
@@ -62,7 +62,7 b' class StreamProxy(io.IOStream):'
62 class py_file_finder(object):
62 class py_file_finder(object):
63 def __init__(self,test_filename):
63 def __init__(self,test_filename):
64 self.test_filename = test_filename
64 self.test_filename = test_filename
65
65
66 def __call__(self,name,win32=False):
66 def __call__(self,name,win32=False):
67 from IPython.utils.path import get_py_filename
67 from IPython.utils.path import get_py_filename
68 try:
68 try:
@@ -71,7 +71,7 b' class py_file_finder(object):'
71 test_dir = os.path.dirname(self.test_filename)
71 test_dir = os.path.dirname(self.test_filename)
72 new_path = os.path.join(test_dir,name)
72 new_path = os.path.join(test_dir,name)
73 return get_py_filename(new_path,win32=win32)
73 return get_py_filename(new_path,win32=win32)
74
74
75
75
76 def _run_ns_sync(self,arg_s,runner=None):
76 def _run_ns_sync(self,arg_s,runner=None):
77 """Modified version of %run that syncs testing namespaces.
77 """Modified version of %run that syncs testing namespaces.
@@ -94,7 +94,7 b' class ipnsdict(dict):'
94 which is needed because of how Python's doctest machinery operates with
94 which is needed because of how Python's doctest machinery operates with
95 '_'. See constructor and :meth:`update` for details.
95 '_'. See constructor and :meth:`update` for details.
96 """
96 """
97
97
98 def __init__(self,*a):
98 def __init__(self,*a):
99 dict.__init__(self,*a)
99 dict.__init__(self,*a)
100 self._savedict = {}
100 self._savedict = {}
@@ -102,11 +102,11 b' class ipnsdict(dict):'
102 # remove a key named '_'. This is so that such a dict can be used as a
102 # remove a key named '_'. This is so that such a dict can be used as a
103 # namespace in doctests that call '_'.
103 # namespace in doctests that call '_'.
104 self.protect_underscore = False
104 self.protect_underscore = False
105
105
106 def clear(self):
106 def clear(self):
107 dict.clear(self)
107 dict.clear(self)
108 self.update(self._savedict)
108 self.update(self._savedict)
109
109
110 def _checkpoint(self):
110 def _checkpoint(self):
111 self._savedict.clear()
111 self._savedict.clear()
112 self._savedict.update(self)
112 self._savedict.update(self)
@@ -132,7 +132,7 b' class ipnsdict(dict):'
132 # correct for that ourselves, to ensure consitency with the 'real'
132 # correct for that ourselves, to ensure consitency with the 'real'
133 # ipython.
133 # ipython.
134 self['__builtins__'] = builtin_mod
134 self['__builtins__'] = builtin_mod
135
135
136 def __delitem__(self, key):
136 def __delitem__(self, key):
137 """Part of the test suite checks that we can release all
137 """Part of the test suite checks that we can release all
138 references to an object. So we need to make sure that we're not
138 references to an object. So we need to make sure that we're not
@@ -177,7 +177,7 b' def start_ipython():'
177 if hasattr(start_ipython, 'already_called'):
177 if hasattr(start_ipython, 'already_called'):
178 return
178 return
179 start_ipython.already_called = True
179 start_ipython.already_called = True
180
180
181 # Store certain global objects that IPython modifies
181 # Store certain global objects that IPython modifies
182 _displayhook = sys.displayhook
182 _displayhook = sys.displayhook
183 _excepthook = sys.excepthook
183 _excepthook = sys.excepthook
@@ -187,16 +187,16 b' def start_ipython():'
187 config = tools.default_config()
187 config = tools.default_config()
188
188
189 # Create and initialize our test-friendly IPython instance.
189 # Create and initialize our test-friendly IPython instance.
190 shell = TerminalInteractiveShell.instance(config=config,
190 shell = TerminalInteractiveShell.instance(config=config,
191 user_ns=ipnsdict(),
191 user_ns=ipnsdict(),
192 user_global_ns={}
192 user_global_ns={}
193 )
193 )
194
194
195 # A few more tweaks needed for playing nicely with doctests...
195 # A few more tweaks needed for playing nicely with doctests...
196
196
197 # remove history file
197 # remove history file
198 shell.tempfiles.append(config.HistoryManager.hist_file)
198 shell.tempfiles.append(config.HistoryManager.hist_file)
199
199
200 # These traps are normally only active for interactive use, set them
200 # These traps are normally only active for interactive use, set them
201 # permanently since we'll be mocking interactive sessions.
201 # permanently since we'll be mocking interactive sessions.
202 shell.builtin_trap.activate()
202 shell.builtin_trap.activate()
@@ -205,12 +205,12 b' def start_ipython():'
205 # can capture subcommands and print them to Python's stdout, otherwise the
205 # can capture subcommands and print them to Python's stdout, otherwise the
206 # doctest machinery would miss them.
206 # doctest machinery would miss them.
207 shell.system = py3compat.MethodType(xsys, shell)
207 shell.system = py3compat.MethodType(xsys, shell)
208
208
209
209
210 shell._showtraceback = py3compat.MethodType(_showtraceback, shell)
210 shell._showtraceback = py3compat.MethodType(_showtraceback, shell)
211
211
212 # IPython is ready, now clean up some global state...
212 # IPython is ready, now clean up some global state...
213
213
214 # Deactivate the various python system hooks added by ipython for
214 # Deactivate the various python system hooks added by ipython for
215 # interactive convenience so we don't confuse the doctest system
215 # interactive convenience so we don't confuse the doctest system
216 sys.modules['__main__'] = _main
216 sys.modules['__main__'] = _main
@@ -224,7 +224,7 b' def start_ipython():'
224 get_ipython = _ip.get_ipython
224 get_ipython = _ip.get_ipython
225 builtin_mod._ip = _ip
225 builtin_mod._ip = _ip
226 builtin_mod.get_ipython = get_ipython
226 builtin_mod.get_ipython = get_ipython
227
227
228 # To avoid extra IPython messages during testing, suppress io.stdout/stderr
228 # To avoid extra IPython messages during testing, suppress io.stdout/stderr
229 io.stdout = StreamProxy('stdout')
229 io.stdout = StreamProxy('stdout')
230 io.stderr = StreamProxy('stderr')
230 io.stderr = StreamProxy('stderr')
@@ -88,18 +88,18 b' def extract_version(mod):'
88 def test_for(item, min_version=None, callback=extract_version):
88 def test_for(item, min_version=None, callback=extract_version):
89 """Test to see if item is importable, and optionally check against a minimum
89 """Test to see if item is importable, and optionally check against a minimum
90 version.
90 version.
91
91
92 If min_version is given, the default behavior is to check against the
92 If min_version is given, the default behavior is to check against the
93 `__version__` attribute of the item, but specifying `callback` allows you to
93 `__version__` attribute of the item, but specifying `callback` allows you to
94 extract the value you are interested in. e.g::
94 extract the value you are interested in. e.g::
95
95
96 In [1]: import sys
96 In [1]: import sys
97
97
98 In [2]: from IPython.testing.iptest import test_for
98 In [2]: from IPython.testing.iptest import test_for
99
99
100 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
100 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
101 Out[3]: True
101 Out[3]: True
102
102
103 """
103 """
104 try:
104 try:
105 check = import_item(item)
105 check = import_item(item)
@@ -112,7 +112,7 b' def test_for(item, min_version=None, callback=extract_version):'
112 if callback:
112 if callback:
113 # extra processing step to get version to compare
113 # extra processing step to get version to compare
114 check = callback(check)
114 check = callback(check)
115
115
116 return check >= min_version
116 return check >= min_version
117 else:
117 else:
118 return True
118 return True
@@ -156,7 +156,7 b' def report():'
156
156
157 avail = []
157 avail = []
158 not_avail = []
158 not_avail = []
159
159
160 for k, is_avail in have.items():
160 for k, is_avail in have.items():
161 if is_avail:
161 if is_avail:
162 avail.append(k)
162 avail.append(k)
@@ -172,7 +172,7 b' def report():'
172 out.append('\nTools and libraries NOT available at test time:\n')
172 out.append('\nTools and libraries NOT available at test time:\n')
173 not_avail.sort()
173 not_avail.sort()
174 out.append(' ' + ' '.join(not_avail)+'\n')
174 out.append(' ' + ' '.join(not_avail)+'\n')
175
175
176 return ''.join(out)
176 return ''.join(out)
177
177
178
178
@@ -188,7 +188,7 b' def make_exclude():'
188 # Simple utility to make IPython paths more readably, we need a lot of
188 # Simple utility to make IPython paths more readably, we need a lot of
189 # these below
189 # these below
190 ipjoin = lambda *paths: pjoin('IPython', *paths)
190 ipjoin = lambda *paths: pjoin('IPython', *paths)
191
191
192 exclusions = [ipjoin('external'),
192 exclusions = [ipjoin('external'),
193 pjoin('IPython_doctest_plugin'),
193 pjoin('IPython_doctest_plugin'),
194 ipjoin('quarantine'),
194 ipjoin('quarantine'),
@@ -207,7 +207,7 b' def make_exclude():'
207
207
208 if not have['wx']:
208 if not have['wx']:
209 exclusions.append(ipjoin('lib', 'inputhookwx'))
209 exclusions.append(ipjoin('lib', 'inputhookwx'))
210
210
211 # We do this unconditionally, so that the test suite doesn't import
211 # We do this unconditionally, so that the test suite doesn't import
212 # gtk, changing the default encoding and masking some unicode bugs.
212 # gtk, changing the default encoding and masking some unicode bugs.
213 exclusions.append(ipjoin('lib', 'inputhookgtk'))
213 exclusions.append(ipjoin('lib', 'inputhookgtk'))
@@ -229,7 +229,7 b' def make_exclude():'
229 exclusions.append(ipjoin('parallel'))
229 exclusions.append(ipjoin('parallel'))
230 elif not have['qt']:
230 elif not have['qt']:
231 exclusions.append(ipjoin('frontend', 'qt'))
231 exclusions.append(ipjoin('frontend', 'qt'))
232
232
233 if not have['pymongo']:
233 if not have['pymongo']:
234 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
234 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
235 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
235 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
@@ -259,7 +259,7 b' class IPTester(object):'
259 call_args = None
259 call_args = None
260 #: list, process ids of subprocesses we start (for cleanup)
260 #: list, process ids of subprocesses we start (for cleanup)
261 pids = None
261 pids = None
262
262
263 def __init__(self, runner='iptest', params=None):
263 def __init__(self, runner='iptest', params=None):
264 """Create new test runner."""
264 """Create new test runner."""
265 p = os.path
265 p = os.path
@@ -303,7 +303,7 b' class IPTester(object):'
303 retcode = subp.wait()
303 retcode = subp.wait()
304 self.pids.pop()
304 self.pids.pop()
305 return retcode
305 return retcode
306
306
307 def run(self):
307 def run(self):
308 """Run the stored commands"""
308 """Run the stored commands"""
309 try:
309 try:
@@ -318,7 +318,7 b' class IPTester(object):'
318
318
319 if not hasattr(os, 'kill'):
319 if not hasattr(os, 'kill'):
320 return
320 return
321
321
322 for pid in self.pids:
322 for pid in self.pids:
323 try:
323 try:
324 print 'Cleaning stale PID:', pid
324 print 'Cleaning stale PID:', pid
@@ -326,7 +326,7 b' class IPTester(object):'
326 except OSError:
326 except OSError:
327 # This is just a best effort, if we fail or the process was
327 # This is just a best effort, if we fail or the process was
328 # really gone, ignore it.
328 # really gone, ignore it.
329 pass
329 pass
330
330
331
331
332 def make_runners():
332 def make_runners():
@@ -336,10 +336,10 b' def make_runners():'
336 # Packages to be tested via nose, that only depend on the stdlib
336 # Packages to be tested via nose, that only depend on the stdlib
337 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
337 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
338 'scripts', 'testing', 'utils', 'nbformat' ]
338 'scripts', 'testing', 'utils', 'nbformat' ]
339
339
340 if have['zmq']:
340 if have['zmq']:
341 nose_pkg_names.append('parallel')
341 nose_pkg_names.append('parallel')
342
342
343 # For debugging this code, only load quick stuff
343 # For debugging this code, only load quick stuff
344 #nose_pkg_names = ['core', 'extensions'] # dbg
344 #nose_pkg_names = ['core', 'extensions'] # dbg
345
345
@@ -348,29 +348,29 b' def make_runners():'
348
348
349 # Make runners
349 # Make runners
350 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
350 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
351
351
352 return runners
352 return runners
353
353
354
354
355 def run_iptest():
355 def run_iptest():
356 """Run the IPython test suite using nose.
356 """Run the IPython test suite using nose.
357
357
358 This function is called when this script is **not** called with the form
358 This function is called when this script is **not** called with the form
359 `iptest all`. It simply calls nose with appropriate command line flags
359 `iptest all`. It simply calls nose with appropriate command line flags
360 and accepts all of the standard nose arguments.
360 and accepts all of the standard nose arguments.
361 """
361 """
362
362
363 warnings.filterwarnings('ignore',
363 warnings.filterwarnings('ignore',
364 'This will be removed soon. Use IPython.testing.util instead')
364 'This will be removed soon. Use IPython.testing.util instead')
365
365
366 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
366 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
367
367
368 # Loading ipdoctest causes problems with Twisted, but
368 # Loading ipdoctest causes problems with Twisted, but
369 # our test suite runner now separates things and runs
369 # our test suite runner now separates things and runs
370 # all Twisted tests with trial.
370 # all Twisted tests with trial.
371 '--with-ipdoctest',
371 '--with-ipdoctest',
372 '--ipdoctest-tests','--ipdoctest-extension=txt',
372 '--ipdoctest-tests','--ipdoctest-extension=txt',
373
373
374 # We add --exe because of setuptools' imbecility (it
374 # We add --exe because of setuptools' imbecility (it
375 # blindly does chmod +x on ALL files). Nose does the
375 # blindly does chmod +x on ALL files). Nose does the
376 # right thing and it tries to avoid executables,
376 # right thing and it tries to avoid executables,
@@ -402,7 +402,7 b' def run_iptest():'
402
402
403 def run_iptestall():
403 def run_iptestall():
404 """Run the entire IPython test suite by calling nose and trial.
404 """Run the entire IPython test suite by calling nose and trial.
405
405
406 This function constructs :class:`IPTester` instances for all IPython
406 This function constructs :class:`IPTester` instances for all IPython
407 modules and package and then runs each of them. This causes the modules
407 modules and package and then runs each of them. This causes the modules
408 and packages of IPython to be tested each in their own subprocess using
408 and packages of IPython to be tested each in their own subprocess using
@@ -45,7 +45,7 b' class IndentOut(object):'
45
45
46 Instances of this class trap output to a given stream and first reformat it
46 Instances of this class trap output to a given stream and first reformat it
47 to indent every input line."""
47 to indent every input line."""
48
48
49 def __init__(self,out=sys.stdout,indent=4):
49 def __init__(self,out=sys.stdout,indent=4):
50 """Create an indented writer.
50 """Create an indented writer.
51
51
@@ -57,7 +57,7 b' class IndentOut(object):'
57 - `indent` : int
57 - `indent` : int
58 Number of spaces to indent every input line by.
58 Number of spaces to indent every input line by.
59 """
59 """
60
60
61 self.indent_text = ' '*indent
61 self.indent_text = ' '*indent
62 self.indent = re.compile('^',re.MULTILINE).sub
62 self.indent = re.compile('^',re.MULTILINE).sub
63 self.out = out
63 self.out = out
@@ -77,7 +77,7 b' class IndentOut(object):'
77 data = ''.join(self.buffer)
77 data = ''.join(self.buffer)
78 self.buffer[:] = []
78 self.buffer[:] = []
79 self._write(self.indent(self.indent_text,data))
79 self._write(self.indent(self.indent_text,data))
80
80
81 def close(self):
81 def close(self):
82 self.flush()
82 self.flush()
83 self._closed = True
83 self._closed = True
@@ -96,7 +96,7 b' class RunnerFactory(object):'
96
96
97 def __init__(self,out=sys.stdout):
97 def __init__(self,out=sys.stdout):
98 """Instantiate a code runner."""
98 """Instantiate a code runner."""
99
99
100 self.out = out
100 self.out = out
101 self.runner = None
101 self.runner = None
102 self.runnerClass = None
102 self.runnerClass = None
@@ -105,7 +105,7 b' class RunnerFactory(object):'
105 self.runnerClass = runnerClass
105 self.runnerClass = runnerClass
106 self.runner = runnerClass(out=self.out)
106 self.runner = runnerClass(out=self.out)
107 return self.runner
107 return self.runner
108
108
109 def __call__(self,fname):
109 def __call__(self,fname):
110 """Return a runner for the given filename."""
110 """Return a runner for the given filename."""
111
111
@@ -199,7 +199,7 b' def main():'
199 outfile = open(outfname,'w')
199 outfile = open(outfname,'w')
200
200
201
201
202 # all output from included files will be indented
202 # all output from included files will be indented
203 indentOut = IndentOut(outfile,4)
203 indentOut = IndentOut(outfile,4)
204 getRunner = RunnerFactory(indentOut)
204 getRunner = RunnerFactory(indentOut)
205
205
@@ -3,10 +3,10 b''
3 Limitations:
3 Limitations:
4
4
5 - When generating examples for use as doctests, make sure that you have
5 - When generating examples for use as doctests, make sure that you have
6 pretty-printing OFF. This can be done either by setting the
6 pretty-printing OFF. This can be done either by setting the
7 ``PlainTextFormatter.pprint`` option in your configuration file to False, or
7 ``PlainTextFormatter.pprint`` option in your configuration file to False, or
8 by interactively disabling it with %Pprint. This is required so that IPython
8 by interactively disabling it with %Pprint. This is required so that IPython
9 output matches that of normal Python, which is used by doctest for internal
9 output matches that of normal Python, which is used by doctest for internal
10 execution.
10 execution.
11
11
12 - Do not rely on specific prompt numbers for results (such as using
12 - Do not rely on specific prompt numbers for results (such as using
@@ -68,7 +68,7 b' def is_extension_module(filename):'
68
68
69 class DocTestSkip(object):
69 class DocTestSkip(object):
70 """Object wrapper for doctests to be skipped."""
70 """Object wrapper for doctests to be skipped."""
71
71
72 ds_skip = """Doctest to skip.
72 ds_skip = """Doctest to skip.
73 >>> 1 #doctest: +SKIP
73 >>> 1 #doctest: +SKIP
74 """
74 """
@@ -166,13 +166,13 b' class DocTestFinder(doctest.DocTestFinder):'
166
166
167 class IPDoctestOutputChecker(doctest.OutputChecker):
167 class IPDoctestOutputChecker(doctest.OutputChecker):
168 """Second-chance checker with support for random tests.
168 """Second-chance checker with support for random tests.
169
169
170 If the default comparison doesn't pass, this checker looks in the expected
170 If the default comparison doesn't pass, this checker looks in the expected
171 output string for flags that tell us to ignore the output.
171 output string for flags that tell us to ignore the output.
172 """
172 """
173
173
174 random_re = re.compile(r'#\s*random\s+')
174 random_re = re.compile(r'#\s*random\s+')
175
175
176 def check_output(self, want, got, optionflags):
176 def check_output(self, want, got, optionflags):
177 """Check output, accepting special markers embedded in the output.
177 """Check output, accepting special markers embedded in the output.
178
178
@@ -236,7 +236,7 b' class DocTestCase(doctests.DocTestCase):'
236 def runTest(self):
236 def runTest(self):
237 test = self._dt_test
237 test = self._dt_test
238 runner = self._dt_runner
238 runner = self._dt_runner
239
239
240 old = sys.stdout
240 old = sys.stdout
241 new = StringIO()
241 new = StringIO()
242 optionflags = self._dt_optionflags
242 optionflags = self._dt_optionflags
@@ -289,7 +289,7 b' class DocTestCase(doctests.DocTestCase):'
289 # Restore the behavior of the '_' key in the user namespace to
289 # Restore the behavior of the '_' key in the user namespace to
290 # normal after each doctest, so that unittests behave normally
290 # normal after each doctest, so that unittests behave normally
291 _ip.user_ns.protect_underscore = False
291 _ip.user_ns.protect_underscore = False
292
292
293 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
293 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
294 # it does look like one to me: its tearDown method tries to run
294 # it does look like one to me: its tearDown method tries to run
295 #
295 #
@@ -376,7 +376,7 b' class IPDocTestParser(doctest.DocTestParser):'
376
376
377 # Mark tests to be executed in an external process - currently unsupported.
377 # Mark tests to be executed in an external process - currently unsupported.
378 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
378 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
379
379
380 def ip2py(self,source):
380 def ip2py(self,source):
381 """Convert input IPython source into valid Python."""
381 """Convert input IPython source into valid Python."""
382 out = []
382 out = []
@@ -459,7 +459,7 b' class IPDocTestParser(doctest.DocTestParser):'
459 # Append the random-output marker (it defaults to empty in most
459 # Append the random-output marker (it defaults to empty in most
460 # cases, it's only non-empty for 'all-random' tests):
460 # cases, it's only non-empty for 'all-random' tests):
461 want += random_marker
461 want += random_marker
462
462
463 if Example is IPExternalExample:
463 if Example is IPExternalExample:
464 options[doctest.NORMALIZE_WHITESPACE] = True
464 options[doctest.NORMALIZE_WHITESPACE] = True
465 want += '\n'
465 want += '\n'
@@ -569,7 +569,7 b" SKIP = doctest.register_optionflag('SKIP')"
569 class IPDocTestRunner(doctest.DocTestRunner,object):
569 class IPDocTestRunner(doctest.DocTestRunner,object):
570 """Test runner that synchronizes the IPython namespace with test globals.
570 """Test runner that synchronizes the IPython namespace with test globals.
571 """
571 """
572
572
573 def run(self, test, compileflags=None, out=None, clear_globs=True):
573 def run(self, test, compileflags=None, out=None, clear_globs=True):
574
574
575 # Hack: ipython needs access to the execution context of the example,
575 # Hack: ipython needs access to the execution context of the example,
@@ -582,7 +582,7 b' class IPDocTestRunner(doctest.DocTestRunner,object):'
582 #_ip._ipdoctest_test_filename = test.filename
582 #_ip._ipdoctest_test_filename = test.filename
583
583
584 test.globs.update(_ip.user_ns)
584 test.globs.update(_ip.user_ns)
585
585
586 return super(IPDocTestRunner,self).run(test,
586 return super(IPDocTestRunner,self).run(test,
587 compileflags,out,clear_globs)
587 compileflags,out,clear_globs)
588
588
@@ -669,7 +669,7 b' class ExtensionDoctest(doctests.Doctest):'
669
669
670 def loadTestsFromModule(self, module):
670 def loadTestsFromModule(self, module):
671 #print '*** ipdoctest - lTM',module # dbg
671 #print '*** ipdoctest - lTM',module # dbg
672
672
673 if not self.matches(module.__name__):
673 if not self.matches(module.__name__):
674 log.debug("Doctest doesn't want module %s", module)
674 log.debug("Doctest doesn't want module %s", module)
675 return
675 return
@@ -691,7 +691,7 b' class ExtensionDoctest(doctests.Doctest):'
691 continue
691 continue
692 if not test.filename:
692 if not test.filename:
693 test.filename = module_file
693 test.filename = module_file
694
694
695 yield DocTestCase(test,
695 yield DocTestCase(test,
696 optionflags=optionflags,
696 optionflags=optionflags,
697 checker=self.checker)
697 checker=self.checker)
@@ -26,7 +26,7 b' Notes:'
26 - running this script with python (it has a __main__ section at the end) misses
26 - running this script with python (it has a __main__ section at the end) misses
27 one docstring test, the one embedded in the Foo object method. Since our
27 one docstring test, the one embedded in the Foo object method. Since our
28 approach relies on using decorators that create standalone TestCase
28 approach relies on using decorators that create standalone TestCase
29 instances, it can only be used for functions, not for methods of objects.
29 instances, it can only be used for functions, not for methods of objects.
30 Authors
30 Authors
31 -------
31 -------
32
32
@@ -66,8 +66,8 b' In [20]: print 1'
66
66
67 In [26]: for i in range(10):
67 In [26]: for i in range(10):
68 ....: print i,
68 ....: print i,
69 ....:
69 ....:
70 ....:
70 ....:
71 0 1 2 3 4 5 6 7 8 9
71 0 1 2 3 4 5 6 7 8 9
72
72
73 In [27]: 3+4
73 In [27]: 3+4
@@ -83,8 +83,8 b' def ipdt_indented_test():'
83
83
84 In [26]: for i in range(10):
84 In [26]: for i in range(10):
85 ....: print i,
85 ....: print i,
86 ....:
86 ....:
87 ....:
87 ....:
88 0 1 2 3 4 5 6 7 8 9
88 0 1 2 3 4 5 6 7 8 9
89
89
90 In [27]: 3+4
90 In [27]: 3+4
@@ -107,8 +107,8 b' class Foo(object):'
107
107
108 In [26]: for i in range(10):
108 In [26]: for i in range(10):
109 ....: print i,
109 ....: print i,
110 ....:
110 ....:
111 ....:
111 ....:
112 0 1 2 3 4 5 6 7 8 9
112 0 1 2 3 4 5 6 7 8 9
113
113
114 In [27]: 3+4
114 In [27]: 3+4
@@ -134,7 +134,7 b' def parse_test_output(txt):'
134 nerr = int(err_m.group(1))
134 nerr = int(err_m.group(1))
135 nfail = 0
135 nfail = 0
136 return nerr, nfail
136 return nerr, nfail
137
137
138 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
138 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
139 if fail_m:
139 if fail_m:
140 nerr = 0
140 nerr = 0
@@ -147,7 +147,7 b' def parse_test_output(txt):'
147 nerr = int(both_m.group(1))
147 nerr = int(both_m.group(1))
148 nfail = int(both_m.group(2))
148 nfail = int(both_m.group(2))
149 return nerr, nfail
149 return nerr, nfail
150
150
151 # If the input didn't match any of these forms, assume no error/failures
151 # If the input didn't match any of these forms, assume no error/failures
152 return 0, 0
152 return 0, 0
153
153
@@ -197,15 +197,15 b' def ipexec(fname, options=None):'
197 (stdout, stderr) of ipython subprocess.
197 (stdout, stderr) of ipython subprocess.
198 """
198 """
199 if options is None: options = []
199 if options is None: options = []
200
200
201 # For these subprocess calls, eliminate all prompt printing so we only see
201 # For these subprocess calls, eliminate all prompt printing so we only see
202 # output from script execution
202 # output from script execution
203 prompt_opts = [ '--InteractiveShell.prompt_in1=""',
203 prompt_opts = [ '--InteractiveShell.prompt_in1=""',
204 '--InteractiveShell.prompt_in2=""',
204 '--InteractiveShell.prompt_in2=""',
205 '--InteractiveShell.prompt_out=""'
205 '--InteractiveShell.prompt_out=""'
206 ]
206 ]
207 cmdargs = ' '.join(default_argv() + prompt_opts + options)
207 cmdargs = ' '.join(default_argv() + prompt_opts + options)
208
208
209 _ip = get_ipython()
209 _ip = get_ipython()
210 test_dir = os.path.dirname(__file__)
210 test_dir = os.path.dirname(__file__)
211
211
@@ -256,7 +256,7 b" def ipexec_validate(fname, expected_out, expected_err='',"
256 """
256 """
257
257
258 import nose.tools as nt
258 import nose.tools as nt
259
259
260 out, err = ipexec(fname)
260 out, err = ipexec(fname)
261 #print 'OUT', out # dbg
261 #print 'OUT', out # dbg
262 #print 'ERR', err # dbg
262 #print 'ERR', err # dbg
@@ -276,7 +276,7 b' class TempFileMixin(object):'
276 """Utility class to create temporary Python/IPython files.
276 """Utility class to create temporary Python/IPython files.
277
277
278 Meant as a mixin class for test cases."""
278 Meant as a mixin class for test cases."""
279
279
280 def mktmp(self, src, ext='.py'):
280 def mktmp(self, src, ext='.py'):
281 """Make a valid python temp file."""
281 """Make a valid python temp file."""
282 fname, f = temp_pyfile(src, ext)
282 fname, f = temp_pyfile(src, ext)
@@ -303,16 +303,16 b' pair_fail_msg = ("Testing {0}\\n\\n"'
303 "Got:\n"
303 "Got:\n"
304 " {3!r}\n")
304 " {3!r}\n")
305 def check_pairs(func, pairs):
305 def check_pairs(func, pairs):
306 """Utility function for the common case of checking a function with a
306 """Utility function for the common case of checking a function with a
307 sequence of input/output pairs.
307 sequence of input/output pairs.
308
308
309 Parameters
309 Parameters
310 ----------
310 ----------
311 func : callable
311 func : callable
312 The function to be tested. Should accept a single argument.
312 The function to be tested. Should accept a single argument.
313 pairs : iterable
313 pairs : iterable
314 A list of (input, expected_output) tuples.
314 A list of (input, expected_output) tuples.
315
315
316 Returns
316 Returns
317 -------
317 -------
318 None. Raises an AssertionError if any output does not match the expected
318 None. Raises an AssertionError if any output does not match the expected
@@ -20,7 +20,7 b' Hettinger. http://users.rcn.com/python/download/Descriptor.htm'
20
20
21 Notes
21 Notes
22 -----
22 -----
23 This module is taken from the NiPy project
23 This module is taken from the NiPy project
24 (http://neuroimaging.scipy.org/site/index.html), and is BSD licensed.
24 (http://neuroimaging.scipy.org/site/index.html), and is BSD licensed.
25
25
26 Authors
26 Authors
@@ -57,7 +57,7 b' class ResetMixin(object):'
57 ... def y(self):
57 ... def y(self):
58 ... print '*** y computation executed ***'
58 ... print '*** y computation executed ***'
59 ... return self.x / 2.0
59 ... return self.x / 2.0
60 ...
60 ...
61
61
62 >>> a = A(10)
62 >>> a = A(10)
63
63
@@ -70,7 +70,7 b' class ResetMixin(object):'
70
70
71 Changing x
71 Changing x
72 >>> a.x = 20
72 >>> a.x = 20
73
73
74 a.y doesn't change to 10, since it is a static attribute:
74 a.y doesn't change to 10, since it is a static attribute:
75 >>> a.y
75 >>> a.y
76 5.0
76 5.0
@@ -111,7 +111,7 b' class OneTimeProperty(object):'
111 Parameters
111 Parameters
112 ----------
112 ----------
113 func : method
113 func : method
114
114
115 The method that will be called the first time to compute a value.
115 The method that will be called the first time to compute a value.
116 Afterwards, the method's name will be a standard attribute holding
116 Afterwards, the method's name will be a standard attribute holding
117 the value of this computation.
117 the value of this computation.
@@ -150,7 +150,7 b' def auto_attr(func):'
150 ... @auto_attr
150 ... @auto_attr
151 ... def a(self):
151 ... def a(self):
152 ... return 99
152 ... return 99
153 ...
153 ...
154 >>> x = MagicProp()
154 >>> x = MagicProp()
155 >>> 'a' in x.__dict__
155 >>> 'a' in x.__dict__
156 False
156 False
@@ -3,7 +3,7 b''
3 """Utilities to enable code objects to be pickled.
3 """Utilities to enable code objects to be pickled.
4
4
5 Any process that import this module will be able to pickle code objects. This
5 Any process that import this module will be able to pickle code objects. This
6 includes the func_code attribute of any function. Once unpickled, new
6 includes the func_code attribute of any function. Once unpickled, new
7 functions can be built using new.function(code, globals()). Eventually
7 functions can be built using new.function(code, globals()). Eventually
8 we need to automate all of this so that functions themselves can be pickled.
8 we need to automate all of this so that functions themselves can be pickled.
9
9
@@ -28,7 +28,7 b' import types, copy_reg'
28
28
29 def code_ctor(*args):
29 def code_ctor(*args):
30 return types.CodeType(*args)
30 return types.CodeType(*args)
31
31
32 def reduce_code(co):
32 def reduce_code(co):
33 if co.co_freevars or co.co_cellvars:
33 if co.co_freevars or co.co_cellvars:
34 raise ValueError("Sorry, cannot pickle code objects with closures")
34 raise ValueError("Sorry, cannot pickle code objects with closures")
@@ -19,7 +19,7 b' def make_color_table(in_class):'
19 """Build a set of color attributes in a class.
19 """Build a set of color attributes in a class.
20
20
21 Helper function for building the *TermColors classes."""
21 Helper function for building the *TermColors classes."""
22
22
23 color_templates = (
23 color_templates = (
24 # Dark colors
24 # Dark colors
25 ("Black" , "0;30"),
25 ("Black" , "0;30"),
@@ -38,7 +38,7 b' def make_color_table(in_class):'
38 ("LightBlue" , "1;34"),
38 ("LightBlue" , "1;34"),
39 ("LightPurple" , "1;35"),
39 ("LightPurple" , "1;35"),
40 ("LightCyan" , "1;36"),
40 ("LightCyan" , "1;36"),
41 ("White" , "1;37"),
41 ("White" , "1;37"),
42 # Blinking colors. Probably should not be used in anything serious.
42 # Blinking colors. Probably should not be used in anything serious.
43 ("BlinkBlack" , "5;30"),
43 ("BlinkBlack" , "5;30"),
44 ("BlinkRed" , "5;31"),
44 ("BlinkRed" , "5;31"),
@@ -56,13 +56,13 b' def make_color_table(in_class):'
56 class TermColors:
56 class TermColors:
57 """Color escape sequences.
57 """Color escape sequences.
58
58
59 This class defines the escape sequences for all the standard (ANSI?)
59 This class defines the escape sequences for all the standard (ANSI?)
60 colors in terminals. Also defines a NoColor escape which is just the null
60 colors in terminals. Also defines a NoColor escape which is just the null
61 string, suitable for defining 'dummy' color schemes in terminals which get
61 string, suitable for defining 'dummy' color schemes in terminals which get
62 confused by color escapes.
62 confused by color escapes.
63
63
64 This class should be used as a mixin for building color schemes."""
64 This class should be used as a mixin for building color schemes."""
65
65
66 NoColor = '' # for color schemes in color-less terminals.
66 NoColor = '' # for color schemes in color-less terminals.
67 Normal = '\033[0m' # Reset normal coloring
67 Normal = '\033[0m' # Reset normal coloring
68 _base = '\033[%sm' # Template for all other colors
68 _base = '\033[%sm' # Template for all other colors
@@ -78,13 +78,13 b' class InputTermColors:'
78 can wrap lines accordingly. Use this class for any colored text which
78 can wrap lines accordingly. Use this class for any colored text which
79 needs to be used in input prompts, such as in calls to raw_input().
79 needs to be used in input prompts, such as in calls to raw_input().
80
80
81 This class defines the escape sequences for all the standard (ANSI?)
81 This class defines the escape sequences for all the standard (ANSI?)
82 colors in terminals. Also defines a NoColor escape which is just the null
82 colors in terminals. Also defines a NoColor escape which is just the null
83 string, suitable for defining 'dummy' color schemes in terminals which get
83 string, suitable for defining 'dummy' color schemes in terminals which get
84 confused by color escapes.
84 confused by color escapes.
85
85
86 This class should be used as a mixin for building color schemes."""
86 This class should be used as a mixin for building color schemes."""
87
87
88 NoColor = '' # for color schemes in color-less terminals.
88 NoColor = '' # for color schemes in color-less terminals.
89
89
90 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
90 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
@@ -112,13 +112,13 b' class ColorScheme:'
112 if name is None:
112 if name is None:
113 name = self.name
113 name = self.name
114 return ColorScheme(name, self.colors.dict())
114 return ColorScheme(name, self.colors.dict())
115
115
116 class ColorSchemeTable(dict):
116 class ColorSchemeTable(dict):
117 """General class to handle tables of color schemes.
117 """General class to handle tables of color schemes.
118
118
119 It's basically a dict of color schemes with a couple of shorthand
119 It's basically a dict of color schemes with a couple of shorthand
120 attributes and some convenient methods.
120 attributes and some convenient methods.
121
121
122 active_scheme_name -> obvious
122 active_scheme_name -> obvious
123 active_colors -> actual color table of the active scheme"""
123 active_colors -> actual color table of the active scheme"""
124
124
@@ -129,11 +129,11 b' class ColorSchemeTable(dict):'
129 created with a list of valid color schemes AND the specification for
129 created with a list of valid color schemes AND the specification for
130 the default active scheme.
130 the default active scheme.
131 """
131 """
132
132
133 # create object attributes to be set later
133 # create object attributes to be set later
134 self.active_scheme_name = ''
134 self.active_scheme_name = ''
135 self.active_colors = None
135 self.active_colors = None
136
136
137 if scheme_list:
137 if scheme_list:
138 if default_scheme == '':
138 if default_scheme == '':
139 raise ValueError,'you must specify the default color scheme'
139 raise ValueError,'you must specify the default color scheme'
@@ -150,7 +150,7 b' class ColorSchemeTable(dict):'
150 if not isinstance(new_scheme,ColorScheme):
150 if not isinstance(new_scheme,ColorScheme):
151 raise ValueError,'ColorSchemeTable only accepts ColorScheme instances'
151 raise ValueError,'ColorSchemeTable only accepts ColorScheme instances'
152 self[new_scheme.name] = new_scheme
152 self[new_scheme.name] = new_scheme
153
153
154 def set_active_scheme(self,scheme,case_sensitive=0):
154 def set_active_scheme(self,scheme,case_sensitive=0):
155 """Set the currently active scheme.
155 """Set the currently active scheme.
156
156
@@ -41,7 +41,7 b' def complete_object(obj, prev_completions):'
41 List of attributes discovered so far.
41 List of attributes discovered so far.
42
42
43 This should return the list of attributes in obj. If you only wish to
43 This should return the list of attributes in obj. If you only wish to
44 add to the attributes already discovered normally, return
44 add to the attributes already discovered normally, return
45 own_attrs + prev_completions.
45 own_attrs + prev_completions.
46 """
46 """
47 raise TryNext
47 raise TryNext
@@ -34,7 +34,7 b' class Notifier(object):'
34
34
35 def _notify(self, title, msg):
35 def _notify(self, title, msg):
36 if self.g_notifier is not None:
36 if self.g_notifier is not None:
37 self.g_notifier.notify('core', title, msg)
37 self.g_notifier.notify('core', title, msg)
38
38
39 def notify(self, title, msg):
39 def notify(self, title, msg):
40 self._notify(title, msg)
40 self._notify(title, msg)
@@ -32,13 +32,13 b' class IOStream:'
32 raise ValueError("fallback required, but not specified")
32 raise ValueError("fallback required, but not specified")
33 self.stream = stream
33 self.stream = stream
34 self._swrite = stream.write
34 self._swrite = stream.write
35
35
36 # clone all methods not overridden:
36 # clone all methods not overridden:
37 def clone(meth):
37 def clone(meth):
38 return not hasattr(self, meth) and not meth.startswith('_')
38 return not hasattr(self, meth) and not meth.startswith('_')
39 for meth in filter(clone, dir(stream)):
39 for meth in filter(clone, dir(stream)):
40 setattr(self, meth, getattr(stream, meth))
40 setattr(self, meth, getattr(stream, meth))
41
41
42 def write(self,data):
42 def write(self,data):
43 try:
43 try:
44 self._swrite(data)
44 self._swrite(data)
@@ -52,7 +52,7 b' class IOStream:'
52 # if we get here, something is seriously broken.
52 # if we get here, something is seriously broken.
53 print('ERROR - failed to write data to stream:', self.stream,
53 print('ERROR - failed to write data to stream:', self.stream,
54 file=sys.stderr)
54 file=sys.stderr)
55
55
56 def writelines(self, lines):
56 def writelines(self, lines):
57 if isinstance(lines, basestring):
57 if isinstance(lines, basestring):
58 lines = [lines]
58 lines = [lines]
@@ -66,7 +66,7 b' class IOStream:'
66 @property
66 @property
67 def closed(self):
67 def closed(self):
68 return self.stream.closed
68 return self.stream.closed
69
69
70 def close(self):
70 def close(self):
71 pass
71 pass
72
72
@@ -114,11 +114,11 b' class Tee(object):'
114 mode : optional, valid mode for open().
114 mode : optional, valid mode for open().
115 If a filename was give, open with this mode.
115 If a filename was give, open with this mode.
116
116
117 channel : str, one of ['stdout', 'stderr']
117 channel : str, one of ['stdout', 'stderr']
118 """
118 """
119 if channel not in ['stdout', 'stderr']:
119 if channel not in ['stdout', 'stderr']:
120 raise ValueError('Invalid channel spec %s' % channel)
120 raise ValueError('Invalid channel spec %s' % channel)
121
121
122 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
122 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
123 self.file = file_or_name
123 self.file = file_or_name
124 else:
124 else:
@@ -302,7 +302,7 b" def temp_pyfile(src, ext='.py'):"
302
302
303 def raw_print(*args, **kw):
303 def raw_print(*args, **kw):
304 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
304 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
305
305
306 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
306 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
307 file=sys.__stdout__)
307 file=sys.__stdout__)
308 sys.__stdout__.flush()
308 sys.__stdout__.flush()
@@ -310,7 +310,7 b' def raw_print(*args, **kw):'
310
310
311 def raw_print_err(*args, **kw):
311 def raw_print_err(*args, **kw):
312 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
312 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
313
313
314 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
314 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
315 file=sys.__stderr__)
315 file=sys.__stderr__)
316 sys.__stderr__.flush()
316 sys.__stderr__.flush()
@@ -1,7 +1,7 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """A dict subclass that supports attribute style access.
2 """A dict subclass that supports attribute style access.
3
3
4 Authors:
4 Authors:
5
5
6 * Fernando Perez (original)
6 * Fernando Perez (original)
7 * Brian Granger (refactoring to a dict subclass)
7 * Brian Granger (refactoring to a dict subclass)
@@ -81,7 +81,7 b' class Struct(dict):'
81 ... s['b'] = 20
81 ... s['b'] = 20
82 ... except KeyError:
82 ... except KeyError:
83 ... print 'this is not allowed'
83 ... print 'this is not allowed'
84 ...
84 ...
85 this is not allowed
85 this is not allowed
86 """
86 """
87 if not self._allownew and not self.has_key(key):
87 if not self._allownew and not self.has_key(key):
@@ -92,7 +92,7 b' class Struct(dict):'
92 def __setattr__(self, key, value):
92 def __setattr__(self, key, value):
93 """Set an attr with protection of class members.
93 """Set an attr with protection of class members.
94
94
95 This calls :meth:`self.__setitem__` but convert :exc:`KeyError` to
95 This calls :meth:`self.__setitem__` but convert :exc:`KeyError` to
96 :exc:`AttributeError`.
96 :exc:`AttributeError`.
97
97
98 Examples
98 Examples
@@ -106,13 +106,13 b' class Struct(dict):'
106 ... s.get = 10
106 ... s.get = 10
107 ... except AttributeError:
107 ... except AttributeError:
108 ... print "you can't set a class member"
108 ... print "you can't set a class member"
109 ...
109 ...
110 you can't set a class member
110 you can't set a class member
111 """
111 """
112 # If key is an str it might be a class member or instance var
112 # If key is an str it might be a class member or instance var
113 if isinstance(key, str):
113 if isinstance(key, str):
114 # I can't simply call hasattr here because it calls getattr, which
114 # I can't simply call hasattr here because it calls getattr, which
115 # calls self.__getattr__, which returns True for keys in
115 # calls self.__getattr__, which returns True for keys in
116 # self._data. But I only want keys in the class and in
116 # self._data. But I only want keys in the class and in
117 # self.__dict__
117 # self.__dict__
118 if key in self.__dict__ or hasattr(Struct, key):
118 if key in self.__dict__ or hasattr(Struct, key):
@@ -127,7 +127,7 b' class Struct(dict):'
127 def __getattr__(self, key):
127 def __getattr__(self, key):
128 """Get an attr by calling :meth:`dict.__getitem__`.
128 """Get an attr by calling :meth:`dict.__getitem__`.
129
129
130 Like :meth:`__setattr__`, this method converts :exc:`KeyError` to
130 Like :meth:`__setattr__`, this method converts :exc:`KeyError` to
131 :exc:`AttributeError`.
131 :exc:`AttributeError`.
132
132
133 Examples
133 Examples
@@ -142,7 +142,7 b' class Struct(dict):'
142 ... s.b
142 ... s.b
143 ... except AttributeError:
143 ... except AttributeError:
144 ... print "I don't have that key"
144 ... print "I don't have that key"
145 ...
145 ...
146 I don't have that key
146 I don't have that key
147 """
147 """
148 try:
148 try:
@@ -154,10 +154,10 b' class Struct(dict):'
154
154
155 def __iadd__(self, other):
155 def __iadd__(self, other):
156 """s += s2 is a shorthand for s.merge(s2).
156 """s += s2 is a shorthand for s.merge(s2).
157
157
158 Examples
158 Examples
159 --------
159 --------
160
160
161 >>> s = Struct(a=10,b=30)
161 >>> s = Struct(a=10,b=30)
162 >>> s2 = Struct(a=20,c=40)
162 >>> s2 = Struct(a=20,c=40)
163 >>> s += s2
163 >>> s += s2
@@ -169,10 +169,10 b' class Struct(dict):'
169
169
170 def __add__(self,other):
170 def __add__(self,other):
171 """s + s2 -> New Struct made from s.merge(s2).
171 """s + s2 -> New Struct made from s.merge(s2).
172
172
173 Examples
173 Examples
174 --------
174 --------
175
175
176 >>> s1 = Struct(a=10,b=30)
176 >>> s1 = Struct(a=10,b=30)
177 >>> s2 = Struct(a=20,c=40)
177 >>> s2 = Struct(a=20,c=40)
178 >>> s = s1 + s2
178 >>> s = s1 + s2
@@ -185,10 +185,10 b' class Struct(dict):'
185
185
186 def __sub__(self,other):
186 def __sub__(self,other):
187 """s1 - s2 -> remove keys in s2 from s1.
187 """s1 - s2 -> remove keys in s2 from s1.
188
188
189 Examples
189 Examples
190 --------
190 --------
191
191
192 >>> s1 = Struct(a=10,b=30)
192 >>> s1 = Struct(a=10,b=30)
193 >>> s2 = Struct(a=40)
193 >>> s2 = Struct(a=40)
194 >>> s = s1 - s2
194 >>> s = s1 - s2
@@ -201,10 +201,10 b' class Struct(dict):'
201
201
202 def __isub__(self,other):
202 def __isub__(self,other):
203 """Inplace remove keys from self that are in other.
203 """Inplace remove keys from self that are in other.
204
204
205 Examples
205 Examples
206 --------
206 --------
207
207
208 >>> s1 = Struct(a=10,b=30)
208 >>> s1 = Struct(a=10,b=30)
209 >>> s2 = Struct(a=40)
209 >>> s2 = Struct(a=40)
210 >>> s1 -= s2
210 >>> s1 -= s2
@@ -217,9 +217,9 b' class Struct(dict):'
217 return self
217 return self
218
218
219 def __dict_invert(self, data):
219 def __dict_invert(self, data):
220 """Helper function for merge.
220 """Helper function for merge.
221
221
222 Takes a dictionary whose values are lists and returns a dict with
222 Takes a dictionary whose values are lists and returns a dict with
223 the elements of each list as keys and the original keys as values.
223 the elements of each list as keys and the original keys as values.
224 """
224 """
225 outdict = {}
225 outdict = {}
@@ -235,10 +235,10 b' class Struct(dict):'
235
235
236 def copy(self):
236 def copy(self):
237 """Return a copy as a Struct.
237 """Return a copy as a Struct.
238
238
239 Examples
239 Examples
240 --------
240 --------
241
241
242 >>> s = Struct(a=10,b=30)
242 >>> s = Struct(a=10,b=30)
243 >>> s2 = s.copy()
243 >>> s2 = s.copy()
244 >>> s2
244 >>> s2
@@ -304,14 +304,14 b' class Struct(dict):'
304
304
305 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
305 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
306 solve key conflicts. Here is an example::
306 solve key conflicts. Here is an example::
307
307
308 __conflict_solve = dict(
308 __conflict_solve = dict(
309 func1=['a','b','c'],
309 func1=['a','b','c'],
310 func2=['d','e']
310 func2=['d','e']
311 )
311 )
312
312
313 In this case, the function :func:`func1` will be used to resolve
313 In this case, the function :func:`func1` will be used to resolve
314 keys 'a', 'b' and 'c' and the function :func:`func2` will be used for
314 keys 'a', 'b' and 'c' and the function :func:`func2` will be used for
315 keys 'd' and 'e'. This could also be written as::
315 keys 'd' and 'e'. This could also be written as::
316
316
317 __conflict_solve = dict(func1='a b c',func2='d e')
317 __conflict_solve = dict(func1='a b c',func2='d e')
@@ -344,15 +344,15 b' class Struct(dict):'
344 --------
344 --------
345
345
346 This show the default policy:
346 This show the default policy:
347
347
348 >>> s = Struct(a=10,b=30)
348 >>> s = Struct(a=10,b=30)
349 >>> s2 = Struct(a=20,c=40)
349 >>> s2 = Struct(a=20,c=40)
350 >>> s.merge(s2)
350 >>> s.merge(s2)
351 >>> s
351 >>> s
352 {'a': 10, 'c': 40, 'b': 30}
352 {'a': 10, 'c': 40, 'b': 30}
353
353
354 Now, show how to specify a conflict dict:
354 Now, show how to specify a conflict dict:
355
355
356 >>> s = Struct(a=10,b=30)
356 >>> s = Struct(a=10,b=30)
357 >>> s2 = Struct(a=20,b=40)
357 >>> s2 = Struct(a=20,b=40)
358 >>> conflict = {'update':'a','add':'b'}
358 >>> conflict = {'update':'a','add':'b'}
@@ -1,7 +1,7 b''
1 """Simple utility for building a list of local IPs using the socket module.
1 """Simple utility for building a list of local IPs using the socket module.
2 This module defines two constants:
2 This module defines two constants:
3
3
4 LOCALHOST : The loopback interface, or the first interface that points to this
4 LOCALHOST : The loopback interface, or the first interface that points to this
5 machine. It will *almost* always be '127.0.0.1'
5 machine. It will *almost* always be '127.0.0.1'
6
6
7 LOCAL_IPS : A list of IP addresses, loopback first, that point to this machine.
7 LOCAL_IPS : A list of IP addresses, loopback first, that point to this machine.
@@ -29,7 +29,7 b' class NotificationError(Exception):'
29
29
30 class NotificationCenter(object):
30 class NotificationCenter(object):
31 """Synchronous notification center.
31 """Synchronous notification center.
32
32
33 Examples
33 Examples
34 --------
34 --------
35 Here is a simple example of how to use this::
35 Here is a simple example of how to use this::
@@ -83,9 +83,9 b' class NotificationCenter(object):'
83 "Notification type and sender are required.")
83 "Notification type and sender are required.")
84
84
85 # If there are no registered observers for the type/sender pair
85 # If there are no registered observers for the type/sender pair
86 if((ntype not in self.registered_types and
86 if((ntype not in self.registered_types and
87 None not in self.registered_types) or
87 None not in self.registered_types) or
88 (sender not in self.registered_senders and
88 (sender not in self.registered_senders and
89 None not in self.registered_senders)):
89 None not in self.registered_senders)):
90 return
90 return
91
91
@@ -110,11 +110,11 b' class NotificationCenter(object):'
110
110
111 def add_observer(self, callback, ntype, sender):
111 def add_observer(self, callback, ntype, sender):
112 """Add an observer callback to this notification center.
112 """Add an observer callback to this notification center.
113
113
114 The given callback will be called upon posting of notifications of
114 The given callback will be called upon posting of notifications of
115 the given type/sender and will receive any additional arguments passed
115 the given type/sender and will receive any additional arguments passed
116 to post_notification.
116 to post_notification.
117
117
118 Parameters
118 Parameters
119 ----------
119 ----------
120 callback : callable
120 callback : callable
@@ -131,12 +131,12 b' class NotificationCenter(object):'
131 self.registered_types.add(ntype)
131 self.registered_types.add(ntype)
132 self.registered_senders.add(sender)
132 self.registered_senders.add(sender)
133 self.observers.setdefault((ntype,sender), set()).add(callback)
133 self.observers.setdefault((ntype,sender), set()).add(callback)
134
134
135 def remove_all_observers(self):
135 def remove_all_observers(self):
136 """Removes all observers from this notification center"""
136 """Removes all observers from this notification center"""
137
137
138 self._init_observers()
138 self._init_observers()
139
139
140
140
141
141
142 shared_center = NotificationCenter()
142 shared_center = NotificationCenter()
@@ -89,7 +89,7 b' def get_py_filename(name, force_win32=None):'
89
89
90 If the given name is not a file, it adds '.py' and searches again.
90 If the given name is not a file, it adds '.py' and searches again.
91 Raises IOError with an informative message if the file isn't found.
91 Raises IOError with an informative message if the file isn't found.
92
92
93 On Windows, apply Windows semantics to the filename. In particular, remove
93 On Windows, apply Windows semantics to the filename. In particular, remove
94 any quoting that has been applied to it. This option can be forced for
94 any quoting that has been applied to it. This option can be forced for
95 testing purposes.
95 testing purposes.
@@ -125,7 +125,7 b' def filefind(filename, path_dirs=None):'
125
125
126 Will find the file in the users home directory. This function does not
126 Will find the file in the users home directory. This function does not
127 automatically try any paths, such as the cwd or the user's home directory.
127 automatically try any paths, such as the cwd or the user's home directory.
128
128
129 Parameters
129 Parameters
130 ----------
130 ----------
131 filename : str
131 filename : str
@@ -136,30 +136,30 b' def filefind(filename, path_dirs=None):'
136 put into a sequence and the searched. If a sequence, walk through
136 put into a sequence and the searched. If a sequence, walk through
137 each element and join with ``filename``, calling :func:`expandvars`
137 each element and join with ``filename``, calling :func:`expandvars`
138 and :func:`expanduser` before testing for existence.
138 and :func:`expanduser` before testing for existence.
139
139
140 Returns
140 Returns
141 -------
141 -------
142 Raises :exc:`IOError` or returns absolute path to file.
142 Raises :exc:`IOError` or returns absolute path to file.
143 """
143 """
144
144
145 # If paths are quoted, abspath gets confused, strip them...
145 # If paths are quoted, abspath gets confused, strip them...
146 filename = filename.strip('"').strip("'")
146 filename = filename.strip('"').strip("'")
147 # If the input is an absolute path, just check it exists
147 # If the input is an absolute path, just check it exists
148 if os.path.isabs(filename) and os.path.isfile(filename):
148 if os.path.isabs(filename) and os.path.isfile(filename):
149 return filename
149 return filename
150
150
151 if path_dirs is None:
151 if path_dirs is None:
152 path_dirs = ("",)
152 path_dirs = ("",)
153 elif isinstance(path_dirs, basestring):
153 elif isinstance(path_dirs, basestring):
154 path_dirs = (path_dirs,)
154 path_dirs = (path_dirs,)
155
155
156 for path in path_dirs:
156 for path in path_dirs:
157 if path == '.': path = os.getcwdu()
157 if path == '.': path = os.getcwdu()
158 testname = expand_path(os.path.join(path, filename))
158 testname = expand_path(os.path.join(path, filename))
159 if os.path.isfile(testname):
159 if os.path.isfile(testname):
160 return os.path.abspath(testname)
160 return os.path.abspath(testname)
161
161
162 raise IOError("File %r does not exist in any of the search paths: %r" %
162 raise IOError("File %r does not exist in any of the search paths: %r" %
163 (filename, path_dirs) )
163 (filename, path_dirs) )
164
164
165
165
@@ -178,7 +178,7 b' def get_home_dir():'
178 - Registry hack for My Documents
178 - Registry hack for My Documents
179 - %HOME%: rare, but some people with unix-like setups may have defined it
179 - %HOME%: rare, but some people with unix-like setups may have defined it
180 * On Dos C:\
180 * On Dos C:\
181
181
182 Currently only Posix and NT are implemented, a HomeDirError exception is
182 Currently only Posix and NT are implemented, a HomeDirError exception is
183 raised for all other OSes.
183 raised for all other OSes.
184 """
184 """
@@ -191,7 +191,7 b' def get_home_dir():'
191 if hasattr(sys, "frozen"): #Is frozen by py2exe
191 if hasattr(sys, "frozen"): #Is frozen by py2exe
192 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
192 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
193 root, rest = IPython.__file__.lower().split('library.zip')
193 root, rest = IPython.__file__.lower().split('library.zip')
194 else:
194 else:
195 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
195 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
196 root=os.path.abspath(root).rstrip('\\')
196 root=os.path.abspath(root).rstrip('\\')
197 if _writable_dir(os.path.join(root, '_ipython')):
197 if _writable_dir(os.path.join(root, '_ipython')):
@@ -208,7 +208,7 b' def get_home_dir():'
208 # still knows it - reported once as:
208 # still knows it - reported once as:
209 # https://github.com/ipython/ipython/issues/154
209 # https://github.com/ipython/ipython/issues/154
210 from subprocess import Popen, PIPE
210 from subprocess import Popen, PIPE
211 homedir = Popen('echo $HOME', shell=True,
211 homedir = Popen('echo $HOME', shell=True,
212 stdout=PIPE).communicate()[0].strip()
212 stdout=PIPE).communicate()[0].strip()
213 if homedir:
213 if homedir:
214 return py3compat.cast_unicode(homedir, fs_encoding)
214 return py3compat.cast_unicode(homedir, fs_encoding)
@@ -221,7 +221,7 b' def get_home_dir():'
221 # For some strange reason all of these return 'nt' for os.name.
221 # For some strange reason all of these return 'nt' for os.name.
222 # First look for a network home directory. This will return the UNC
222 # First look for a network home directory. This will return the UNC
223 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
223 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
224 # is needed when running IPython on cluster where all paths have to
224 # is needed when running IPython on cluster where all paths have to
225 # be UNC.
225 # be UNC.
226 try:
226 try:
227 homedir = env['HOMESHARE']
227 homedir = env['HOMESHARE']
@@ -284,36 +284,36 b' def get_home_dir():'
284
284
285 def get_xdg_dir():
285 def get_xdg_dir():
286 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
286 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
287
287
288 This is only for posix (Linux,Unix,OS X, etc) systems.
288 This is only for posix (Linux,Unix,OS X, etc) systems.
289 """
289 """
290
290
291 env = os.environ
291 env = os.environ
292
292
293 if os.name == 'posix':
293 if os.name == 'posix':
294 # Linux, Unix, AIX, OS X
294 # Linux, Unix, AIX, OS X
295 # use ~/.config if not set OR empty
295 # use ~/.config if not set OR empty
296 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
296 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
297 if xdg and _writable_dir(xdg):
297 if xdg and _writable_dir(xdg):
298 return py3compat.cast_unicode(xdg, fs_encoding)
298 return py3compat.cast_unicode(xdg, fs_encoding)
299
299
300 return None
300 return None
301
301
302
302
303 def get_ipython_dir():
303 def get_ipython_dir():
304 """Get the IPython directory for this platform and user.
304 """Get the IPython directory for this platform and user.
305
305
306 This uses the logic in `get_home_dir` to find the home directory
306 This uses the logic in `get_home_dir` to find the home directory
307 and then adds .ipython to the end of the path.
307 and then adds .ipython to the end of the path.
308 """
308 """
309
309
310 env = os.environ
310 env = os.environ
311 pjoin = os.path.join
311 pjoin = os.path.join
312
312
313
313
314 ipdir_def = '.ipython'
314 ipdir_def = '.ipython'
315 xdg_def = 'ipython'
315 xdg_def = 'ipython'
316
316
317 home_dir = get_home_dir()
317 home_dir = get_home_dir()
318 xdg_dir = get_xdg_dir()
318 xdg_dir = get_xdg_dir()
319 # import pdb; pdb.set_trace() # dbg
319 # import pdb; pdb.set_trace() # dbg
@@ -324,18 +324,18 b' def get_ipython_dir():'
324 if xdg_dir:
324 if xdg_dir:
325 # use XDG, as long as the user isn't already
325 # use XDG, as long as the user isn't already
326 # using $HOME/.ipython and *not* XDG/ipython
326 # using $HOME/.ipython and *not* XDG/ipython
327
327
328 xdg_ipdir = pjoin(xdg_dir, xdg_def)
328 xdg_ipdir = pjoin(xdg_dir, xdg_def)
329
329
330 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
330 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
331 ipdir = xdg_ipdir
331 ipdir = xdg_ipdir
332
332
333 if ipdir is None:
333 if ipdir is None:
334 # not using XDG
334 # not using XDG
335 ipdir = home_ipdir
335 ipdir = home_ipdir
336
336
337 ipdir = os.path.normpath(os.path.expanduser(ipdir))
337 ipdir = os.path.normpath(os.path.expanduser(ipdir))
338
338
339 if os.path.exists(ipdir) and not _writable_dir(ipdir):
339 if os.path.exists(ipdir) and not _writable_dir(ipdir):
340 # ipdir exists, but is not writable
340 # ipdir exists, but is not writable
341 warn.warn("IPython dir '%s' is not a writable location,"
341 warn.warn("IPython dir '%s' is not a writable location,"
@@ -377,7 +377,7 b' def expand_path(s):'
377 """Expand $VARS and ~names in a string, like a shell
377 """Expand $VARS and ~names in a string, like a shell
378
378
379 :Examples:
379 :Examples:
380
380
381 In [2]: os.environ['FOO']='test'
381 In [2]: os.environ['FOO']='test'
382
382
383 In [3]: expand_path('variable FOO is $FOO')
383 In [3]: expand_path('variable FOO is $FOO')
@@ -464,7 +464,7 b' def check_for_old_config(ipython_dir=None):'
464 else:
464 else:
465 warn.warn("Found old IPython config file %r (modified by user)"%f)
465 warn.warn("Found old IPython config file %r (modified by user)"%f)
466 warned = True
466 warned = True
467
467
468 if warned:
468 if warned:
469 warn.info("""
469 warn.info("""
470 The IPython configuration system has changed as of 0.11, and these files will
470 The IPython configuration system has changed as of 0.11, and these files will
@@ -2,16 +2,16 b''
2
2
3 """ PickleShare - a small 'shelve' like datastore with concurrency support
3 """ PickleShare - a small 'shelve' like datastore with concurrency support
4
4
5 Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
5 Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
6 shelve, many processes can access the database simultaneously. Changing a
6 shelve, many processes can access the database simultaneously. Changing a
7 value in database is immediately visible to other processes accessing the
7 value in database is immediately visible to other processes accessing the
8 same database.
8 same database.
9
9
10 Concurrency is possible because the values are stored in separate files. Hence
10 Concurrency is possible because the values are stored in separate files. Hence
11 the "database" is a directory where *all* files are governed by PickleShare.
11 the "database" is a directory where *all* files are governed by PickleShare.
12
12
13 Example usage::
13 Example usage::
14
14
15 from pickleshare import *
15 from pickleshare import *
16 db = PickleShareDB('~/testpickleshare')
16 db = PickleShareDB('~/testpickleshare')
17 db.clear()
17 db.clear()
@@ -22,8 +22,8 b' Example usage::'
22 print db.keys()
22 print db.keys()
23 del db['aku ankka']
23 del db['aku ankka']
24
24
25 This module is certainly not ZODB, but can be used for low-load
25 This module is certainly not ZODB, but can be used for low-load
26 (non-mission-critical) situations where tiny code size trumps the
26 (non-mission-critical) situations where tiny code size trumps the
27 advanced features of a "real" object database.
27 advanced features of a "real" object database.
28
28
29 Installation guide: easy_install pickleshare
29 Installation guide: easy_install pickleshare
@@ -53,7 +53,7 b' class PickleShareDB(collections.MutableMapping):'
53 self.root.makedirs()
53 self.root.makedirs()
54 # cache has { 'key' : (obj, orig_mod_time) }
54 # cache has { 'key' : (obj, orig_mod_time) }
55 self.cache = {}
55 self.cache = {}
56
56
57
57
58 def __getitem__(self,key):
58 def __getitem__(self,key):
59 """ db['key'] reading """
59 """ db['key'] reading """
@@ -70,10 +70,10 b' class PickleShareDB(collections.MutableMapping):'
70 obj = pickle.load(fil.open())
70 obj = pickle.load(fil.open())
71 except:
71 except:
72 raise KeyError(key)
72 raise KeyError(key)
73
73
74 self.cache[fil] = (obj,mtime)
74 self.cache[fil] = (obj,mtime)
75 return obj
75 return obj
76
76
77 def __setitem__(self,key,value):
77 def __setitem__(self,key,value):
78 """ db['key'] = 5 """
78 """ db['key'] = 5 """
79 fil = self.root / key
79 fil = self.root / key
@@ -86,7 +86,7 b' class PickleShareDB(collections.MutableMapping):'
86 except OSError,e:
86 except OSError,e:
87 if e.errno != 2:
87 if e.errno != 2:
88 raise
88 raise
89
89
90 def hset(self, hashroot, key, value):
90 def hset(self, hashroot, key, value):
91 """ hashed set """
91 """ hashed set """
92 hroot = self.root / hashroot
92 hroot = self.root / hashroot
@@ -95,27 +95,27 b' class PickleShareDB(collections.MutableMapping):'
95 hfile = hroot / gethashfile(key)
95 hfile = hroot / gethashfile(key)
96 d = self.get(hfile, {})
96 d = self.get(hfile, {})
97 d.update( {key : value})
97 d.update( {key : value})
98 self[hfile] = d
98 self[hfile] = d
99
100
99
101
100
101
102 def hget(self, hashroot, key, default = _sentinel, fast_only = True):
102 def hget(self, hashroot, key, default = _sentinel, fast_only = True):
103 """ hashed get """
103 """ hashed get """
104 hroot = self.root / hashroot
104 hroot = self.root / hashroot
105 hfile = hroot / gethashfile(key)
105 hfile = hroot / gethashfile(key)
106
106
107 d = self.get(hfile, _sentinel )
107 d = self.get(hfile, _sentinel )
108 #print "got dict",d,"from",hfile
108 #print "got dict",d,"from",hfile
109 if d is _sentinel:
109 if d is _sentinel:
110 if fast_only:
110 if fast_only:
111 if default is _sentinel:
111 if default is _sentinel:
112 raise KeyError(key)
112 raise KeyError(key)
113
113
114 return default
114 return default
115
115
116 # slow mode ok, works even after hcompress()
116 # slow mode ok, works even after hcompress()
117 d = self.hdict(hashroot)
117 d = self.hdict(hashroot)
118
118
119 return d.get(key, default)
119 return d.get(key, default)
120
120
121 def hdict(self, hashroot):
121 def hdict(self, hashroot):
@@ -126,9 +126,9 b' class PickleShareDB(collections.MutableMapping):'
126 if last.endswith('xx'):
126 if last.endswith('xx'):
127 # print "using xx"
127 # print "using xx"
128 hfiles = [last] + hfiles[:-1]
128 hfiles = [last] + hfiles[:-1]
129
129
130 all = {}
130 all = {}
131
131
132 for f in hfiles:
132 for f in hfiles:
133 # print "using",f
133 # print "using",f
134 try:
134 try:
@@ -136,17 +136,17 b' class PickleShareDB(collections.MutableMapping):'
136 except KeyError:
136 except KeyError:
137 print "Corrupt",f,"deleted - hset is not threadsafe!"
137 print "Corrupt",f,"deleted - hset is not threadsafe!"
138 del self[f]
138 del self[f]
139
139
140 self.uncache(f)
140 self.uncache(f)
141
141
142 return all
142 return all
143
143
144 def hcompress(self, hashroot):
144 def hcompress(self, hashroot):
145 """ Compress category 'hashroot', so hset is fast again
145 """ Compress category 'hashroot', so hset is fast again
146
146
147 hget will fail if fast_only is True for compressed items (that were
147 hget will fail if fast_only is True for compressed items (that were
148 hset before hcompress).
148 hset before hcompress).
149
149
150 """
150 """
151 hfiles = self.keys(hashroot + "/*")
151 hfiles = self.keys(hashroot + "/*")
152 all = {}
152 all = {}
@@ -154,16 +154,16 b' class PickleShareDB(collections.MutableMapping):'
154 # print "using",f
154 # print "using",f
155 all.update(self[f])
155 all.update(self[f])
156 self.uncache(f)
156 self.uncache(f)
157
157
158 self[hashroot + '/xx'] = all
158 self[hashroot + '/xx'] = all
159 for f in hfiles:
159 for f in hfiles:
160 p = self.root / f
160 p = self.root / f
161 if p.basename() == 'xx':
161 if p.basename() == 'xx':
162 continue
162 continue
163 p.remove()
163 p.remove()
164
164
165
165
166
166
167 def __delitem__(self,key):
167 def __delitem__(self,key):
168 """ del db["key"] """
168 """ del db["key"] """
169 fil = self.root / key
169 fil = self.root / key
@@ -174,53 +174,53 b' class PickleShareDB(collections.MutableMapping):'
174 # notfound and permission denied are ok - we
174 # notfound and permission denied are ok - we
175 # lost, the other process wins the conflict
175 # lost, the other process wins the conflict
176 pass
176 pass
177
177
178 def _normalized(self, p):
178 def _normalized(self, p):
179 """ Make a key suitable for user's eyes """
179 """ Make a key suitable for user's eyes """
180 return str(self.root.relpathto(p)).replace('\\','/')
180 return str(self.root.relpathto(p)).replace('\\','/')
181
181
182 def keys(self, globpat = None):
182 def keys(self, globpat = None):
183 """ All keys in DB, or all keys matching a glob"""
183 """ All keys in DB, or all keys matching a glob"""
184
184
185 if globpat is None:
185 if globpat is None:
186 files = self.root.walkfiles()
186 files = self.root.walkfiles()
187 else:
187 else:
188 files = [Path(p) for p in glob.glob(self.root/globpat)]
188 files = [Path(p) for p in glob.glob(self.root/globpat)]
189 return [self._normalized(p) for p in files if p.isfile()]
189 return [self._normalized(p) for p in files if p.isfile()]
190
190
191 def __iter__(self):
191 def __iter__(self):
192 return iter(keys)
192 return iter(keys)
193
193
194 def __len__(self):
194 def __len__(self):
195 return len(keys)
195 return len(keys)
196
196
197 def uncache(self,*items):
197 def uncache(self,*items):
198 """ Removes all, or specified items from cache
198 """ Removes all, or specified items from cache
199
199
200 Use this after reading a large amount of large objects
200 Use this after reading a large amount of large objects
201 to free up memory, when you won't be needing the objects
201 to free up memory, when you won't be needing the objects
202 for a while.
202 for a while.
203
203
204 """
204 """
205 if not items:
205 if not items:
206 self.cache = {}
206 self.cache = {}
207 for it in items:
207 for it in items:
208 self.cache.pop(it,None)
208 self.cache.pop(it,None)
209
209
210 def waitget(self,key, maxwaittime = 60 ):
210 def waitget(self,key, maxwaittime = 60 ):
211 """ Wait (poll) for a key to get a value
211 """ Wait (poll) for a key to get a value
212
212
213 Will wait for `maxwaittime` seconds before raising a KeyError.
213 Will wait for `maxwaittime` seconds before raising a KeyError.
214 The call exits normally if the `key` field in db gets a value
214 The call exits normally if the `key` field in db gets a value
215 within the timeout period.
215 within the timeout period.
216
216
217 Use this for synchronizing different processes or for ensuring
217 Use this for synchronizing different processes or for ensuring
218 that an unfortunately timed "db['key'] = newvalue" operation
218 that an unfortunately timed "db['key'] = newvalue" operation
219 in another process (which causes all 'get' operation to cause a
219 in another process (which causes all 'get' operation to cause a
220 KeyError for the duration of pickling) won't screw up your program
220 KeyError for the duration of pickling) won't screw up your program
221 logic.
221 logic.
222 """
222 """
223
223
224 wtimes = [0.2] * 3 + [0.5] * 2 + [1]
224 wtimes = [0.2] * 3 + [0.5] * 2 + [1]
225 tries = 0
225 tries = 0
226 waited = 0
226 waited = 0
@@ -230,24 +230,24 b' class PickleShareDB(collections.MutableMapping):'
230 return val
230 return val
231 except KeyError:
231 except KeyError:
232 pass
232 pass
233
233
234 if waited > maxwaittime:
234 if waited > maxwaittime:
235 raise KeyError(key)
235 raise KeyError(key)
236
236
237 time.sleep(wtimes[tries])
237 time.sleep(wtimes[tries])
238 waited+=wtimes[tries]
238 waited+=wtimes[tries]
239 if tries < len(wtimes) -1:
239 if tries < len(wtimes) -1:
240 tries+=1
240 tries+=1
241
241
242 def getlink(self,folder):
242 def getlink(self,folder):
243 """ Get a convenient link for accessing items """
243 """ Get a convenient link for accessing items """
244 return PickleShareLink(self, folder)
244 return PickleShareLink(self, folder)
245
245
246 def __repr__(self):
246 def __repr__(self):
247 return "PickleShareDB('%s')" % self.root
247 return "PickleShareDB('%s')" % self.root
248
248
249
249
250
250
251 class PickleShareLink:
251 class PickleShareLink:
252 """ A shortdand for accessing nested PickleShare data conveniently.
252 """ A shortdand for accessing nested PickleShare data conveniently.
253
253
@@ -256,11 +256,11 b' class PickleShareLink:'
256 lnk = db.getlink('myobjects/test')
256 lnk = db.getlink('myobjects/test')
257 lnk.foo = 2
257 lnk.foo = 2
258 lnk.bar = lnk.foo + 5
258 lnk.bar = lnk.foo + 5
259
259
260 """
260 """
261 def __init__(self, db, keydir ):
261 def __init__(self, db, keydir ):
262 self.__dict__.update(locals())
262 self.__dict__.update(locals())
263
263
264 def __getattr__(self,key):
264 def __getattr__(self,key):
265 return self.__dict__['db'][self.__dict__['keydir']+'/' + key]
265 return self.__dict__['db'][self.__dict__['keydir']+'/' + key]
266 def __setattr__(self,key,val):
266 def __setattr__(self,key,val):
@@ -271,8 +271,8 b' class PickleShareLink:'
271 return "<PickleShareLink '%s': %s>" % (
271 return "<PickleShareLink '%s': %s>" % (
272 self.__dict__['keydir'],
272 self.__dict__['keydir'],
273 ";".join([Path(k).basename() for k in keys]))
273 ";".join([Path(k).basename() for k in keys]))
274
274
275
275
276 def test():
276 def test():
277 db = PickleShareDB('~/testpickleshare')
277 db = PickleShareDB('~/testpickleshare')
278 db.clear()
278 db.clear()
@@ -308,22 +308,22 b' def stress():'
308
308
309 if j%33 == 0:
309 if j%33 == 0:
310 time.sleep(0.02)
310 time.sleep(0.02)
311
311
312 db[str(j)] = db.get(str(j), []) + [(i,j,"proc %d" % os.getpid())]
312 db[str(j)] = db.get(str(j), []) + [(i,j,"proc %d" % os.getpid())]
313 db.hset('hash',j, db.hget('hash',j,15) + 1 )
313 db.hset('hash',j, db.hget('hash',j,15) + 1 )
314
314
315 print i,
315 print i,
316 sys.stdout.flush()
316 sys.stdout.flush()
317 if i % 10 == 0:
317 if i % 10 == 0:
318 db.uncache()
318 db.uncache()
319
319
320 def main():
320 def main():
321 import textwrap
321 import textwrap
322 usage = textwrap.dedent("""\
322 usage = textwrap.dedent("""\
323 pickleshare - manage PickleShare databases
323 pickleshare - manage PickleShare databases
324
324
325 Usage:
325 Usage:
326
326
327 pickleshare dump /path/to/db > dump.txt
327 pickleshare dump /path/to/db > dump.txt
328 pickleshare load /path/to/db < dump.txt
328 pickleshare load /path/to/db < dump.txt
329 pickleshare test /path/to/db
329 pickleshare test /path/to/db
@@ -333,7 +333,7 b' def main():'
333 if len(sys.argv) < 2:
333 if len(sys.argv) < 2:
334 print usage
334 print usage
335 return
335 return
336
336
337 cmd = sys.argv[1]
337 cmd = sys.argv[1]
338 args = sys.argv[2:]
338 args = sys.argv[2:]
339 if cmd == 'dump':
339 if cmd == 'dump':
@@ -355,8 +355,8 b' def main():'
355 elif cmd == 'test':
355 elif cmd == 'test':
356 test()
356 test()
357 stress()
357 stress()
358
358
359 if __name__== "__main__":
359 if __name__== "__main__":
360 main()
360 main()
361
361
362
362
@@ -32,8 +32,8 b' class CannedObject(object):'
32 self.obj = copy.copy(obj)
32 self.obj = copy.copy(obj)
33 for key in keys:
33 for key in keys:
34 setattr(self.obj, key, can(getattr(obj, key)))
34 setattr(self.obj, key, can(getattr(obj, key)))
35
35
36
36
37 def getObject(self, g=None):
37 def getObject(self, g=None):
38 if g is None:
38 if g is None:
39 g = globals()
39 g = globals()
@@ -47,10 +47,10 b' class Reference(CannedObject):'
47 if not isinstance(name, basestring):
47 if not isinstance(name, basestring):
48 raise TypeError("illegal name: %r"%name)
48 raise TypeError("illegal name: %r"%name)
49 self.name = name
49 self.name = name
50
50
51 def __repr__(self):
51 def __repr__(self):
52 return "<Reference: %r>"%self.name
52 return "<Reference: %r>"%self.name
53
53
54 def getObject(self, g=None):
54 def getObject(self, g=None):
55 if g is None:
55 if g is None:
56 g = globals()
56 g = globals()
@@ -58,20 +58,20 b' class Reference(CannedObject):'
58 return g[self.name]
58 return g[self.name]
59 except KeyError:
59 except KeyError:
60 raise NameError("name %r is not defined"%self.name)
60 raise NameError("name %r is not defined"%self.name)
61
61
62
62
63 class CannedFunction(CannedObject):
63 class CannedFunction(CannedObject):
64
64
65 def __init__(self, f):
65 def __init__(self, f):
66 self._checkType(f)
66 self._checkType(f)
67 self.code = f.func_code
67 self.code = f.func_code
68 self.defaults = f.func_defaults
68 self.defaults = f.func_defaults
69 self.module = f.__module__ or '__main__'
69 self.module = f.__module__ or '__main__'
70 self.__name__ = f.__name__
70 self.__name__ = f.__name__
71
71
72 def _checkType(self, obj):
72 def _checkType(self, obj):
73 assert isinstance(obj, FunctionType), "Not a function type"
73 assert isinstance(obj, FunctionType), "Not a function type"
74
74
75 def getObject(self, g=None):
75 def getObject(self, g=None):
76 # try to load function back into its module:
76 # try to load function back into its module:
77 if not self.module.startswith('__'):
77 if not self.module.startswith('__'):
@@ -81,7 +81,7 b' class CannedFunction(CannedObject):'
81 pass
81 pass
82 else:
82 else:
83 g = sys.modules[self.module].__dict__
83 g = sys.modules[self.module].__dict__
84
84
85 if g is None:
85 if g is None:
86 g = globals()
86 g = globals()
87 newFunc = FunctionType(self.code, g, self.__name__, self.defaults)
87 newFunc = FunctionType(self.code, g, self.__name__, self.defaults)
@@ -40,7 +40,7 b' class FindCmdError(Exception):'
40
40
41 def find_cmd(cmd):
41 def find_cmd(cmd):
42 """Find absolute path to executable cmd in a cross platform manner.
42 """Find absolute path to executable cmd in a cross platform manner.
43
43
44 This function tries to determine the full path to a command line program
44 This function tries to determine the full path to a command line program
45 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
45 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
46 time it will use the version that is first on the users `PATH`. If
46 time it will use the version that is first on the users `PATH`. If
@@ -49,7 +49,7 b' def find_cmd(cmd):'
49 Warning, don't use this to find IPython command line programs as there
49 Warning, don't use this to find IPython command line programs as there
50 is a risk you will find the wrong one. Instead find those using the
50 is a risk you will find the wrong one. Instead find those using the
51 following code and looking for the application itself::
51 following code and looking for the application itself::
52
52
53 from IPython.utils.path import get_ipython_module_path
53 from IPython.utils.path import get_ipython_module_path
54 from IPython.utils.process import pycmd2argv
54 from IPython.utils.process import pycmd2argv
55 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
55 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
@@ -79,7 +79,7 b' def pycmd2argv(cmd):'
79 sure the right version is used.
79 sure the right version is used.
80
80
81 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
81 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
82 .com or .bat, and [, cmd] otherwise.
82 .com or .bat, and [, cmd] otherwise.
83
83
84 Parameters
84 Parameters
85 ----------
85 ----------
@@ -97,7 +97,7 b' def pycmd2argv(cmd):'
97 if sys.platform == 'win32':
97 if sys.platform == 'win32':
98 # The -u option here turns on unbuffered output, which is required
98 # The -u option here turns on unbuffered output, which is required
99 # on Win32 to prevent wierd conflict and problems with Twisted.
99 # on Win32 to prevent wierd conflict and problems with Twisted.
100 # Also, use sys.executable to make sure we are picking up the
100 # Also, use sys.executable to make sure we are picking up the
101 # right python exe.
101 # right python exe.
102 return [sys.executable, '-u', cmd]
102 return [sys.executable, '-u', cmd]
103 else:
103 else:
@@ -20,21 +20,21 b' class StrDispatch(object):'
20 >>> print list(dis.flat_matches('hei'))
20 >>> print list(dis.flat_matches('hei'))
21 [123, 34, 686]
21 [123, 34, 686]
22 """
22 """
23
23
24 def __init__(self):
24 def __init__(self):
25 self.strs = {}
25 self.strs = {}
26 self.regexs = {}
26 self.regexs = {}
27
27
28 def add_s(self, s, obj, priority= 0 ):
28 def add_s(self, s, obj, priority= 0 ):
29 """ Adds a target 'string' for dispatching """
29 """ Adds a target 'string' for dispatching """
30
30
31 chain = self.strs.get(s, CommandChainDispatcher())
31 chain = self.strs.get(s, CommandChainDispatcher())
32 chain.add(obj,priority)
32 chain.add(obj,priority)
33 self.strs[s] = chain
33 self.strs[s] = chain
34
34
35 def add_re(self, regex, obj, priority= 0 ):
35 def add_re(self, regex, obj, priority= 0 ):
36 """ Adds a target regexp for dispatching """
36 """ Adds a target regexp for dispatching """
37
37
38 chain = self.regexs.get(regex, CommandChainDispatcher())
38 chain = self.regexs.get(regex, CommandChainDispatcher())
39 chain.add(obj,priority)
39 chain.add(obj,priority)
40 self.regexs[regex] = chain
40 self.regexs[regex] = chain
@@ -43,23 +43,23 b' class StrDispatch(object):'
43 """ Get a seq of Commandchain objects that match key """
43 """ Get a seq of Commandchain objects that match key """
44 if key in self.strs:
44 if key in self.strs:
45 yield self.strs[key]
45 yield self.strs[key]
46
46
47 for r, obj in self.regexs.items():
47 for r, obj in self.regexs.items():
48 if re.match(r, key):
48 if re.match(r, key):
49 yield obj
49 yield obj
50 else:
50 else:
51 #print "nomatch",key # dbg
51 #print "nomatch",key # dbg
52 pass
52 pass
53
53
54 def __repr__(self):
54 def __repr__(self):
55 return "<Strdispatch %s, %s>" % (self.strs, self.regexs)
55 return "<Strdispatch %s, %s>" % (self.strs, self.regexs)
56
56
57 def s_matches(self, key):
57 def s_matches(self, key):
58 if key not in self.strs:
58 if key not in self.strs:
59 return
59 return
60 for el in self.strs[key]:
60 for el in self.strs[key]:
61 yield el[1]
61 yield el[1]
62
62
63 def flat_matches(self, key):
63 def flat_matches(self, key):
64 """ Yield all 'value' targets, without priority """
64 """ Yield all 'value' targets, without priority """
65 for val in self.dispatch(key):
65 for val in self.dispatch(key):
@@ -24,7 +24,7 b' from IPython.utils.notification import shared_center'
24
24
25 class Observer(object):
25 class Observer(object):
26
26
27 def __init__(self, expected_ntype, expected_sender,
27 def __init__(self, expected_ntype, expected_sender,
28 center=shared_center, *args, **kwargs):
28 center=shared_center, *args, **kwargs):
29 super(Observer, self).__init__()
29 super(Observer, self).__init__()
30 self.expected_ntype = expected_ntype
30 self.expected_ntype = expected_ntype
@@ -32,10 +32,10 b' class Observer(object):'
32 self.expected_args = args
32 self.expected_args = args
33 self.expected_kwargs = kwargs
33 self.expected_kwargs = kwargs
34 self.recieved = False
34 self.recieved = False
35 center.add_observer(self.callback,
35 center.add_observer(self.callback,
36 self.expected_ntype,
36 self.expected_ntype,
37 self.expected_sender)
37 self.expected_sender)
38
38
39 def callback(self, ntype, sender, *args, **kwargs):
39 def callback(self, ntype, sender, *args, **kwargs):
40 assert(ntype == self.expected_ntype or
40 assert(ntype == self.expected_ntype or
41 self.expected_ntype == None)
41 self.expected_ntype == None)
@@ -44,10 +44,10 b' class Observer(object):'
44 assert(args == self.expected_args)
44 assert(args == self.expected_args)
45 assert(kwargs == self.expected_kwargs)
45 assert(kwargs == self.expected_kwargs)
46 self.recieved = True
46 self.recieved = True
47
47
48 def verify(self):
48 def verify(self):
49 assert(self.recieved)
49 assert(self.recieved)
50
50
51 def reset(self):
51 def reset(self):
52 self.recieved = False
52 self.recieved = False
53
53
@@ -74,7 +74,7 b' class NotificationTests(unittest.TestCase):'
74
74
75 def tearDown(self):
75 def tearDown(self):
76 shared_center.remove_all_observers()
76 shared_center.remove_all_observers()
77
77
78 def test_notification_delivered(self):
78 def test_notification_delivered(self):
79 """Test that notifications are delivered"""
79 """Test that notifications are delivered"""
80
80
@@ -100,7 +100,7 b' class NotificationTests(unittest.TestCase):'
100
100
101 def test_sender_specificity(self):
101 def test_sender_specificity(self):
102 """Test that observers are registered by sender"""
102 """Test that observers are registered by sender"""
103
103
104 expected_ntype = "EXPECTED_TYPE"
104 expected_ntype = "EXPECTED_TYPE"
105 sender1 = Notifier(expected_ntype)
105 sender1 = Notifier(expected_ntype)
106 sender2 = Notifier(expected_ntype)
106 sender2 = Notifier(expected_ntype)
@@ -117,7 +117,7 b' class NotificationTests(unittest.TestCase):'
117 for i in xrange(10):
117 for i in xrange(10):
118 Observer('TYPE', None, center=shared_center)
118 Observer('TYPE', None, center=shared_center)
119
119
120 self.assert_(len(shared_center.observers[('TYPE',None)]) >= 10,
120 self.assert_(len(shared_center.observers[('TYPE',None)]) >= 10,
121 "observers registered")
121 "observers registered")
122
122
123 shared_center.remove_all_observers()
123 shared_center.remove_all_observers()
@@ -139,7 +139,7 b' class NotificationTests(unittest.TestCase):'
139 def test_post_performance(self):
139 def test_post_performance(self):
140 """Test that post_notification, even with many registered irrelevant
140 """Test that post_notification, even with many registered irrelevant
141 observers is fast"""
141 observers is fast"""
142
142
143 for i in xrange(10):
143 for i in xrange(10):
144 Observer("UNRELATED_TYPE", None)
144 Observer("UNRELATED_TYPE", None)
145
145
@@ -44,7 +44,7 b' except ImportError:'
44 import _winreg as wreg
44 import _winreg as wreg
45 #Add entries that needs to be stubbed by the testing code
45 #Add entries that needs to be stubbed by the testing code
46 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
46 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
47
47
48 try:
48 try:
49 reload
49 reload
50 except NameError: # Python 3
50 except NameError: # Python 3
@@ -65,7 +65,7 b" IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')"
65
65
66 def setup():
66 def setup():
67 """Setup testenvironment for the module:
67 """Setup testenvironment for the module:
68
68
69 - Adds dummy home dir tree
69 - Adds dummy home dir tree
70 """
70 """
71 # Do not mask exceptions here. In particular, catching WindowsError is a
71 # Do not mask exceptions here. In particular, catching WindowsError is a
@@ -76,7 +76,7 b' def setup():'
76
76
77 def teardown():
77 def teardown():
78 """Teardown testenvironment for the module:
78 """Teardown testenvironment for the module:
79
79
80 - Remove dummy home dir tree
80 - Remove dummy home dir tree
81 """
81 """
82 # Note: we remove the parent test dir, which is the root of all test
82 # Note: we remove the parent test dir, which is the root of all test
@@ -86,10 +86,10 b' def teardown():'
86
86
87
87
88 def setup_environment():
88 def setup_environment():
89 """Setup testenvironment for some functions that are tested
89 """Setup testenvironment for some functions that are tested
90 in this module. In particular this functions stores attributes
90 in this module. In particular this functions stores attributes
91 and other things that we need to stub in some test functions.
91 and other things that we need to stub in some test functions.
92 This needs to be done on a function level and not module level because
92 This needs to be done on a function level and not module level because
93 each testfunction needs a pristine environment.
93 each testfunction needs a pristine environment.
94 """
94 """
95 global oldstuff, platformstuff
95 global oldstuff, platformstuff
@@ -105,7 +105,7 b' def teardown_environment():'
105 (oldenv, os.name, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
105 (oldenv, os.name, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
106 os.chdir(old_wd)
106 os.chdir(old_wd)
107 reload(path)
107 reload(path)
108
108
109 for key in env.keys():
109 for key in env.keys():
110 if key not in oldenv:
110 if key not in oldenv:
111 del env[key]
111 del env[key]
@@ -125,10 +125,10 b' def test_get_home_dir_1():'
125 """Testcase for py2exe logic, un-compressed lib
125 """Testcase for py2exe logic, un-compressed lib
126 """
126 """
127 sys.frozen = True
127 sys.frozen = True
128
128
129 #fake filename for IPython.__init__
129 #fake filename for IPython.__init__
130 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
130 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
131
131
132 home_dir = path.get_home_dir()
132 home_dir = path.get_home_dir()
133 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
133 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
134
134
@@ -141,7 +141,7 b' def test_get_home_dir_2():'
141 sys.frozen = True
141 sys.frozen = True
142 #fake filename for IPython.__init__
142 #fake filename for IPython.__init__
143 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
143 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
144
144
145 home_dir = path.get_home_dir()
145 home_dir = path.get_home_dir()
146 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
146 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
147
147
@@ -158,9 +158,9 b' def test_get_home_dir_3():'
158 @with_environment
158 @with_environment
159 @skip_win32
159 @skip_win32
160 def test_get_home_dir_4():
160 def test_get_home_dir_4():
161 """Testcase $HOME is not set, os=='posix'.
161 """Testcase $HOME is not set, os=='posix'.
162 This should fail with HomeDirError"""
162 This should fail with HomeDirError"""
163
163
164 os.name = 'posix'
164 os.name = 'posix'
165 if 'HOME' in env: del env['HOME']
165 if 'HOME' in env: del env['HOME']
166 nt.assert_raises(path.HomeDirError, path.get_home_dir)
166 nt.assert_raises(path.HomeDirError, path.get_home_dir)
@@ -208,13 +208,13 b' def test_get_home_dir_7():'
208 home_dir = path.get_home_dir()
208 home_dir = path.get_home_dir()
209 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
209 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
210
210
211
211
212 # Should we stub wreg fully so we can run the test on all platforms?
212 # Should we stub wreg fully so we can run the test on all platforms?
213 @skip_if_not_win32
213 @skip_if_not_win32
214 @with_environment
214 @with_environment
215 def test_get_home_dir_8():
215 def test_get_home_dir_8():
216 """Using registry hack for 'My Documents', os=='nt'
216 """Using registry hack for 'My Documents', os=='nt'
217
217
218 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
218 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
219 """
219 """
220 os.name = 'nt'
220 os.name = 'nt'
@@ -333,7 +333,7 b' def test_get_xdg_dir_1():'
333 env.pop('IPYTHON_DIR', None)
333 env.pop('IPYTHON_DIR', None)
334 env.pop('IPYTHONDIR', None)
334 env.pop('IPYTHONDIR', None)
335 env.pop('XDG_CONFIG_HOME', None)
335 env.pop('XDG_CONFIG_HOME', None)
336
336
337 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
337 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
338
338
339
339
@@ -359,7 +359,7 b' def test_get_xdg_dir_2():'
359 env.pop('XDG_CONFIG_HOME', None)
359 env.pop('XDG_CONFIG_HOME', None)
360 cfgdir=os.path.join(path.get_home_dir(), '.config')
360 cfgdir=os.path.join(path.get_home_dir(), '.config')
361 os.makedirs(cfgdir)
361 os.makedirs(cfgdir)
362
362
363 nt.assert_equal(path.get_xdg_dir(), cfgdir)
363 nt.assert_equal(path.get_xdg_dir(), cfgdir)
364
364
365 def test_filefind():
365 def test_filefind():
@@ -384,7 +384,7 b' def test_get_ipython_module_path():'
384 @dec.skip_if_not_win32
384 @dec.skip_if_not_win32
385 def test_get_long_path_name_win32():
385 def test_get_long_path_name_win32():
386 p = path.get_long_path_name('c:\\docume~1')
386 p = path.get_long_path_name('c:\\docume~1')
387 nt.assert_equals(p,u'c:\\Documents and Settings')
387 nt.assert_equals(p,u'c:\\Documents and Settings')
388
388
389
389
390 @dec.skip_win32
390 @dec.skip_win32
@@ -75,7 +75,7 b' class TestTraitType(TestCase):'
75 return -1
75 return -1
76 class A(HasTraitsStub):
76 class A(HasTraitsStub):
77 tt = MyTT
77 tt = MyTT
78
78
79 a = A()
79 a = A()
80 a.tt = 10
80 a.tt = 10
81 self.assertEquals(a.tt, -1)
81 self.assertEquals(a.tt, -1)
@@ -393,7 +393,7 b' class TestHasTraits(TestCase):'
393 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
393 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
394 self.assertEquals(traits, dict(i=A.i))
394 self.assertEquals(traits, dict(i=A.i))
395
395
396 # This passes, but it shouldn't because I am replicating a bug in
396 # This passes, but it shouldn't because I am replicating a bug in
397 # traits.
397 # traits.
398 traits = a.traits(config_key=lambda v: True)
398 traits = a.traits(config_key=lambda v: True)
399 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
399 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
@@ -432,7 +432,7 b' class TestType(TestCase):'
432 class C(object): pass
432 class C(object): pass
433 class A(HasTraits):
433 class A(HasTraits):
434 klass = Type(B)
434 klass = Type(B)
435
435
436 a = A()
436 a = A()
437 self.assertEquals(a.klass, B)
437 self.assertEquals(a.klass, B)
438 self.assertRaises(TraitError, setattr, a, 'klass', C)
438 self.assertRaises(TraitError, setattr, a, 'klass', C)
@@ -486,7 +486,7 b' class TestType(TestCase):'
486 a = A()
486 a = A()
487 a.klass = Struct
487 a.klass = Struct
488 self.assertEquals(a.klass, Struct)
488 self.assertEquals(a.klass, Struct)
489
489
490 self.assertRaises(TraitError, setattr, a, 'klass', 10)
490 self.assertRaises(TraitError, setattr, a, 'klass', 10)
491
491
492 class TestInstance(TestCase):
492 class TestInstance(TestCase):
@@ -495,7 +495,7 b' class TestInstance(TestCase):'
495 class Foo(object): pass
495 class Foo(object): pass
496 class Bar(Foo): pass
496 class Bar(Foo): pass
497 class Bah(object): pass
497 class Bah(object): pass
498
498
499 class A(HasTraits):
499 class A(HasTraits):
500 inst = Instance(Foo)
500 inst = Instance(Foo)
501
501
@@ -547,7 +547,7 b' class TestInstance(TestCase):'
547
547
548 class A(HasTraits):
548 class A(HasTraits):
549 inst = Instance(Foo, allow_none=False)
549 inst = Instance(Foo, allow_none=False)
550
550
551 self.assertRaises(TraitError, A)
551 self.assertRaises(TraitError, A)
552
552
553 def test_instance(self):
553 def test_instance(self):
@@ -556,7 +556,7 b' class TestInstance(TestCase):'
556 def inner():
556 def inner():
557 class A(HasTraits):
557 class A(HasTraits):
558 inst = Instance(Foo())
558 inst = Instance(Foo())
559
559
560 self.assertRaises(TraitError, inner)
560 self.assertRaises(TraitError, inner)
561
561
562
562
@@ -576,7 +576,7 b' class TestThis(TestCase):'
576 def test_this_inst(self):
576 def test_this_inst(self):
577 class Foo(HasTraits):
577 class Foo(HasTraits):
578 this = This()
578 this = This()
579
579
580 f = Foo()
580 f = Foo()
581 f.this = Foo()
581 f.this = Foo()
582 self.assert_(isinstance(f.this, Foo))
582 self.assert_(isinstance(f.this, Foo))
@@ -696,7 +696,7 b' class TestComplex(TraitTestBase):'
696 obj = ComplexTrait()
696 obj = ComplexTrait()
697
697
698 _default_value = 99.0-99.0j
698 _default_value = 99.0-99.0j
699 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
699 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
700 10.1j, 10.1+10.1j, 10.1-10.1j]
700 10.1j, 10.1+10.1j, 10.1-10.1j]
701 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
701 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
702
702
@@ -725,7 +725,7 b' class TestUnicode(TraitTestBase):'
725 obj = UnicodeTrait()
725 obj = UnicodeTrait()
726
726
727 _default_value = u'unicode'
727 _default_value = u'unicode'
728 _good_values = ['10', '-10', '10L', '-10L', '10.1',
728 _good_values = ['10', '-10', '10L', '-10L', '10.1',
729 '-10.1', '', u'', 'string', u'string', u"€"]
729 '-10.1', '', u'', 'string', u'string', u"€"]
730 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
730 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
731 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
731 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
@@ -733,10 +733,10 b' class TestUnicode(TraitTestBase):'
733
733
734 class ObjectNameTrait(HasTraits):
734 class ObjectNameTrait(HasTraits):
735 value = ObjectName("abc")
735 value = ObjectName("abc")
736
736
737 class TestObjectName(TraitTestBase):
737 class TestObjectName(TraitTestBase):
738 obj = ObjectNameTrait()
738 obj = ObjectNameTrait()
739
739
740 _default_value = "abc"
740 _default_value = "abc"
741 _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"]
741 _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"]
742 _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]",
742 _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]",
@@ -752,7 +752,7 b' class DottedObjectNameTrait(HasTraits):'
752
752
753 class TestDottedObjectName(TraitTestBase):
753 class TestDottedObjectName(TraitTestBase):
754 obj = DottedObjectNameTrait()
754 obj = DottedObjectNameTrait()
755
755
756 _default_value = "a.b"
756 _default_value = "a.b"
757 _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"]
757 _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"]
758 _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc."]
758 _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc."]
@@ -39,7 +39,7 b' from IPython.utils.data import flatten'
39 # won't need to make changes all over IPython.
39 # won't need to make changes all over IPython.
40 def getdefaultencoding():
40 def getdefaultencoding():
41 """Return IPython's guess for the default encoding for bytes as text.
41 """Return IPython's guess for the default encoding for bytes as text.
42
42
43 Asks for stdin.encoding first, to match the calling Terminal, but that
43 Asks for stdin.encoding first, to match the calling Terminal, but that
44 is often None for subprocesses. Fall back on locale.getpreferredencoding()
44 is often None for subprocesses. Fall back on locale.getpreferredencoding()
45 which should be a sensible platform default (that respects LANG environment),
45 which should be a sensible platform default (that respects LANG environment),
@@ -124,8 +124,8 b' class LSString(str):'
124 # """ Prettier (non-repr-like) and more informative printer for LSString """
124 # """ Prettier (non-repr-like) and more informative printer for LSString """
125 # print "LSString (.p, .n, .l, .s available). Value:"
125 # print "LSString (.p, .n, .l, .s available). Value:"
126 # print arg
126 # print arg
127 #
127 #
128 #
128 #
129 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
129 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
130
130
131
131
@@ -283,9 +283,9 b' class SList(list):'
283 # if hasattr(arg, 'hideonce') and arg.hideonce:
283 # if hasattr(arg, 'hideonce') and arg.hideonce:
284 # arg.hideonce = False
284 # arg.hideonce = False
285 # return
285 # return
286 #
286 #
287 # nlprint(arg)
287 # nlprint(arg)
288 #
288 #
289 # print_slist = result_display.when_type(SList)(print_slist)
289 # print_slist = result_display.when_type(SList)(print_slist)
290
290
291
291
@@ -421,10 +421,10 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
421 """Indent a string a given number of spaces or tabstops.
421 """Indent a string a given number of spaces or tabstops.
422
422
423 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
423 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
424
424
425 Parameters
425 Parameters
426 ----------
426 ----------
427
427
428 instr : basestring
428 instr : basestring
429 The string to be indented.
429 The string to be indented.
430 nspaces : int (default: 4)
430 nspaces : int (default: 4)
@@ -435,12 +435,12 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
435 Whether to scrub existing indentation. If True, all lines will be
435 Whether to scrub existing indentation. If True, all lines will be
436 aligned to the same indentation. If False, existing indentation will
436 aligned to the same indentation. If False, existing indentation will
437 be strictly increased.
437 be strictly increased.
438
438
439 Returns
439 Returns
440 -------
440 -------
441
441
442 str|unicode : string indented by ntabs and nspaces.
442 str|unicode : string indented by ntabs and nspaces.
443
443
444 """
444 """
445 if instr is None:
445 if instr is None:
446 return
446 return
@@ -547,25 +547,25 b' def format_screen(strng):'
547
547
548 def dedent(text):
548 def dedent(text):
549 """Equivalent of textwrap.dedent that ignores unindented first line.
549 """Equivalent of textwrap.dedent that ignores unindented first line.
550
550
551 This means it will still dedent strings like:
551 This means it will still dedent strings like:
552 '''foo
552 '''foo
553 is a bar
553 is a bar
554 '''
554 '''
555
555
556 For use in wrap_paragraphs.
556 For use in wrap_paragraphs.
557 """
557 """
558
558
559 if text.startswith('\n'):
559 if text.startswith('\n'):
560 # text starts with blank line, don't ignore the first line
560 # text starts with blank line, don't ignore the first line
561 return textwrap.dedent(text)
561 return textwrap.dedent(text)
562
562
563 # split first line
563 # split first line
564 splits = text.split('\n',1)
564 splits = text.split('\n',1)
565 if len(splits) == 1:
565 if len(splits) == 1:
566 # only one line
566 # only one line
567 return textwrap.dedent(text)
567 return textwrap.dedent(text)
568
568
569 first, rest = splits
569 first, rest = splits
570 # dedent everything but the first line
570 # dedent everything but the first line
571 rest = textwrap.dedent(rest)
571 rest = textwrap.dedent(rest)
@@ -573,13 +573,13 b' def dedent(text):'
573
573
574 def wrap_paragraphs(text, ncols=80):
574 def wrap_paragraphs(text, ncols=80):
575 """Wrap multiple paragraphs to fit a specified width.
575 """Wrap multiple paragraphs to fit a specified width.
576
576
577 This is equivalent to textwrap.wrap, but with support for multiple
577 This is equivalent to textwrap.wrap, but with support for multiple
578 paragraphs, as separated by empty lines.
578 paragraphs, as separated by empty lines.
579
579
580 Returns
580 Returns
581 -------
581 -------
582
582
583 list of complete paragraphs, wrapped to fill `ncols` columns.
583 list of complete paragraphs, wrapped to fill `ncols` columns.
584 """
584 """
585 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
585 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
@@ -588,42 +588,42 b' def wrap_paragraphs(text, ncols=80):'
588 out_ps = []
588 out_ps = []
589 indent_re = re.compile(r'\n\s+', re.MULTILINE)
589 indent_re = re.compile(r'\n\s+', re.MULTILINE)
590 for p in paragraphs:
590 for p in paragraphs:
591 # presume indentation that survives dedent is meaningful formatting,
591 # presume indentation that survives dedent is meaningful formatting,
592 # so don't fill unless text is flush.
592 # so don't fill unless text is flush.
593 if indent_re.search(p) is None:
593 if indent_re.search(p) is None:
594 # wrap paragraph
594 # wrap paragraph
595 p = textwrap.fill(p, ncols)
595 p = textwrap.fill(p, ncols)
596 out_ps.append(p)
596 out_ps.append(p)
597 return out_ps
597 return out_ps
598
598
599
599
600
600
601 class EvalFormatter(Formatter):
601 class EvalFormatter(Formatter):
602 """A String Formatter that allows evaluation of simple expressions.
602 """A String Formatter that allows evaluation of simple expressions.
603
603
604 Any time a format key is not found in the kwargs,
604 Any time a format key is not found in the kwargs,
605 it will be tried as an expression in the kwargs namespace.
605 it will be tried as an expression in the kwargs namespace.
606
606
607 This is to be used in templating cases, such as the parallel batch
607 This is to be used in templating cases, such as the parallel batch
608 script templates, where simple arithmetic on arguments is useful.
608 script templates, where simple arithmetic on arguments is useful.
609
609
610 Examples
610 Examples
611 --------
611 --------
612
612
613 In [1]: f = EvalFormatter()
613 In [1]: f = EvalFormatter()
614 In [2]: f.format('{n//4}', n=8)
614 In [2]: f.format('{n//4}', n=8)
615 Out[2]: '2'
615 Out[2]: '2'
616
616
617 In [3]: f.format('{list(range(3))}')
617 In [3]: f.format('{list(range(3))}')
618 Out[3]: '[0, 1, 2]'
618 Out[3]: '[0, 1, 2]'
619
619
620 In [4]: f.format('{3*2}')
620 In [4]: f.format('{3*2}')
621 Out[4]: '6'
621 Out[4]: '6'
622 """
622 """
623
623
624 # should we allow slicing by disabling the format_spec feature?
624 # should we allow slicing by disabling the format_spec feature?
625 allow_slicing = True
625 allow_slicing = True
626
626
627 # copied from Formatter._vformat with minor changes to allow eval
627 # copied from Formatter._vformat with minor changes to allow eval
628 # and replace the format_spec code with slicing
628 # and replace the format_spec code with slicing
629 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
629 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
@@ -677,7 +677,7 b" def columnize(items, separator=' ', displaywidth=80):"
677
677
678 displaywidth : int, optional [default is 80]
678 displaywidth : int, optional [default is 80]
679 Width of the display in number of characters.
679 Width of the display in number of characters.
680
680
681 Returns
681 Returns
682 -------
682 -------
683 The formatted string.
683 The formatted string.
@@ -687,7 +687,7 b" def columnize(items, separator=' ', displaywidth=80):"
687
687
688 # Some degenerate cases.
688 # Some degenerate cases.
689 size = len(items)
689 size = len(items)
690 if size == 0:
690 if size == 0:
691 return '\n'
691 return '\n'
692 elif size == 1:
692 elif size == 1:
693 return '%s\n' % items[0]
693 return '%s\n' % items[0]
@@ -715,9 +715,9 b" def columnize(items, separator=' ', displaywidth=80):"
715 colwidth = max(colwidth, len_x)
715 colwidth = max(colwidth, len_x)
716 colwidths.append(colwidth)
716 colwidths.append(colwidth)
717 totwidth += colwidth + len(separator)
717 totwidth += colwidth + len(separator)
718 if totwidth > displaywidth:
718 if totwidth > displaywidth:
719 break
719 break
720 if totwidth <= displaywidth:
720 if totwidth <= displaywidth:
721 break
721 break
722
722
723 # The smallest number of rows computed and the max widths for each
723 # The smallest number of rows computed and the max widths for each
@@ -58,7 +58,7 b' try:'
58 ClassTypes = (ClassType, type)
58 ClassTypes = (ClassType, type)
59 except:
59 except:
60 ClassTypes = (type,)
60 ClassTypes = (type,)
61
61
62 from .importstring import import_item
62 from .importstring import import_item
63 from IPython.utils import py3compat
63 from IPython.utils import py3compat
64
64
@@ -119,10 +119,10 b' def repr_type(obj):'
119
119
120 def parse_notifier_name(name):
120 def parse_notifier_name(name):
121 """Convert the name argument to a list of names.
121 """Convert the name argument to a list of names.
122
122
123 Examples
123 Examples
124 --------
124 --------
125
125
126 >>> parse_notifier_name('a')
126 >>> parse_notifier_name('a')
127 ['a']
127 ['a']
128 >>> parse_notifier_name(['a','b'])
128 >>> parse_notifier_name(['a','b'])
@@ -153,7 +153,7 b' class _SimpleTest:'
153 def getmembers(object, predicate=None):
153 def getmembers(object, predicate=None):
154 """A safe version of inspect.getmembers that handles missing attributes.
154 """A safe version of inspect.getmembers that handles missing attributes.
155
155
156 This is useful when there are descriptor based attributes that for
156 This is useful when there are descriptor based attributes that for
157 some reason raise AttributeError even though they exist. This happens
157 some reason raise AttributeError even though they exist. This happens
158 in zope.inteface with the __provides__ attribute.
158 in zope.inteface with the __provides__ attribute.
159 """
159 """
@@ -192,7 +192,7 b' class TraitType(object):'
192 This is used by the :class:`This` trait to allow subclasses to
192 This is used by the :class:`This` trait to allow subclasses to
193 accept superclasses for :class:`This` values.
193 accept superclasses for :class:`This` values.
194 """
194 """
195
195
196
196
197 metadata = {}
197 metadata = {}
198 default_value = Undefined
198 default_value = Undefined
@@ -231,7 +231,7 b' class TraitType(object):'
231 created.
231 created.
232
232
233 This method trigger the creation and validation of default values
233 This method trigger the creation and validation of default values
234 and also things like the resolution of str given class names in
234 and also things like the resolution of str given class names in
235 :class:`Type` and :class`Instance`.
235 :class:`Type` and :class`Instance`.
236
236
237 Parameters
237 Parameters
@@ -246,7 +246,7 b' class TraitType(object):'
246 """Set the default value on a per instance basis.
246 """Set the default value on a per instance basis.
247
247
248 This method is called by :meth:`instance_init` to create and
248 This method is called by :meth:`instance_init` to create and
249 validate the default value. The creation and validation of
249 validate the default value. The creation and validation of
250 default values must be delayed until the parent :class:`HasTraits`
250 default values must be delayed until the parent :class:`HasTraits`
251 class has been instantiated.
251 class has been instantiated.
252 """
252 """
@@ -270,7 +270,7 b' class TraitType(object):'
270 """Get the value of the trait by self.name for the instance.
270 """Get the value of the trait by self.name for the instance.
271
271
272 Default values are instantiated when :meth:`HasTraits.__new__`
272 Default values are instantiated when :meth:`HasTraits.__new__`
273 is called. Thus by the time this method gets called either the
273 is called. Thus by the time this method gets called either the
274 default value or a user defined value (they called :meth:`__set__`)
274 default value or a user defined value (they called :meth:`__set__`)
275 is in the :class:`HasTraits` instance.
275 is in the :class:`HasTraits` instance.
276 """
276 """
@@ -347,14 +347,14 b' class TraitType(object):'
347
347
348 class MetaHasTraits(type):
348 class MetaHasTraits(type):
349 """A metaclass for HasTraits.
349 """A metaclass for HasTraits.
350
350
351 This metaclass makes sure that any TraitType class attributes are
351 This metaclass makes sure that any TraitType class attributes are
352 instantiated and sets their name attribute.
352 instantiated and sets their name attribute.
353 """
353 """
354
354
355 def __new__(mcls, name, bases, classdict):
355 def __new__(mcls, name, bases, classdict):
356 """Create the HasTraits class.
356 """Create the HasTraits class.
357
357
358 This instantiates all TraitTypes in the class dict and sets their
358 This instantiates all TraitTypes in the class dict and sets their
359 :attr:`name` attribute.
359 :attr:`name` attribute.
360 """
360 """
@@ -373,7 +373,7 b' class MetaHasTraits(type):'
373
373
374 def __init__(cls, name, bases, classdict):
374 def __init__(cls, name, bases, classdict):
375 """Finish initializing the HasTraits class.
375 """Finish initializing the HasTraits class.
376
376
377 This sets the :attr:`this_class` attribute of each TraitType in the
377 This sets the :attr:`this_class` attribute of each TraitType in the
378 class dict to the newly created class ``cls``.
378 class dict to the newly created class ``cls``.
379 """
379 """
@@ -398,7 +398,7 b' class HasTraits(object):'
398 inst._trait_notifiers = {}
398 inst._trait_notifiers = {}
399 inst._trait_dyn_inits = {}
399 inst._trait_dyn_inits = {}
400 # Here we tell all the TraitType instances to set their default
400 # Here we tell all the TraitType instances to set their default
401 # values on the instance.
401 # values on the instance.
402 for key in dir(cls):
402 for key in dir(cls):
403 # Some descriptors raise AttributeError like zope.interface's
403 # Some descriptors raise AttributeError like zope.interface's
404 # __provides__ attributes even though they exist. This causes
404 # __provides__ attributes even though they exist. This causes
@@ -462,7 +462,7 b' class HasTraits(object):'
462 else:
462 else:
463 raise TraitError('a trait changed callback '
463 raise TraitError('a trait changed callback '
464 'must be callable.')
464 'must be callable.')
465
465
466
466
467 def _add_notifiers(self, handler, name):
467 def _add_notifiers(self, handler, name):
468 if not self._trait_notifiers.has_key(name):
468 if not self._trait_notifiers.has_key(name):
@@ -487,17 +487,17 b' class HasTraits(object):'
487 """Setup a handler to be called when a trait changes.
487 """Setup a handler to be called when a trait changes.
488
488
489 This is used to setup dynamic notifications of trait changes.
489 This is used to setup dynamic notifications of trait changes.
490
490
491 Static handlers can be created by creating methods on a HasTraits
491 Static handlers can be created by creating methods on a HasTraits
492 subclass with the naming convention '_[traitname]_changed'. Thus,
492 subclass with the naming convention '_[traitname]_changed'. Thus,
493 to create static handler for the trait 'a', create the method
493 to create static handler for the trait 'a', create the method
494 _a_changed(self, name, old, new) (fewer arguments can be used, see
494 _a_changed(self, name, old, new) (fewer arguments can be used, see
495 below).
495 below).
496
496
497 Parameters
497 Parameters
498 ----------
498 ----------
499 handler : callable
499 handler : callable
500 A callable that is called when a trait changes. Its
500 A callable that is called when a trait changes. Its
501 signature can be handler(), handler(name), handler(name, new)
501 signature can be handler(), handler(name), handler(name, new)
502 or handler(name, old, new).
502 or handler(name, old, new).
503 name : list, str, None
503 name : list, str, None
@@ -651,12 +651,12 b' class Type(ClassBasedTraitType):'
651 default_value : class, str or None
651 default_value : class, str or None
652 The default value must be a subclass of klass. If an str,
652 The default value must be a subclass of klass. If an str,
653 the str must be a fully specified class name, like 'foo.bar.Bah'.
653 the str must be a fully specified class name, like 'foo.bar.Bah'.
654 The string is resolved into real class, when the parent
654 The string is resolved into real class, when the parent
655 :class:`HasTraits` class is instantiated.
655 :class:`HasTraits` class is instantiated.
656 klass : class, str, None
656 klass : class, str, None
657 Values of this trait must be a subclass of klass. The klass
657 Values of this trait must be a subclass of klass. The klass
658 may be specified in a string like: 'foo.bar.MyClass'.
658 may be specified in a string like: 'foo.bar.MyClass'.
659 The string is resolved into real class, when the parent
659 The string is resolved into real class, when the parent
660 :class:`HasTraits` class is instantiated.
660 :class:`HasTraits` class is instantiated.
661 allow_none : boolean
661 allow_none : boolean
662 Indicates whether None is allowed as an assignable value. Even if
662 Indicates whether None is allowed as an assignable value. Even if
@@ -725,11 +725,11 b' class DefaultValueGenerator(object):'
725
725
726 class Instance(ClassBasedTraitType):
726 class Instance(ClassBasedTraitType):
727 """A trait whose value must be an instance of a specified class.
727 """A trait whose value must be an instance of a specified class.
728
728
729 The value can also be an instance of a subclass of the specified class.
729 The value can also be an instance of a subclass of the specified class.
730 """
730 """
731
731
732 def __init__(self, klass=None, args=None, kw=None,
732 def __init__(self, klass=None, args=None, kw=None,
733 allow_none=True, **metadata ):
733 allow_none=True, **metadata ):
734 """Construct an Instance trait.
734 """Construct an Instance trait.
735
735
@@ -754,7 +754,7 b' class Instance(ClassBasedTraitType):'
754 -------------
754 -------------
755 If both ``args`` and ``kw`` are None, then the default value is None.
755 If both ``args`` and ``kw`` are None, then the default value is None.
756 If ``args`` is a tuple and ``kw`` is a dict, then the default is
756 If ``args`` is a tuple and ``kw`` is a dict, then the default is
757 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
757 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
758 not (but not both), None is replace by ``()`` or ``{}``.
758 not (but not both), None is replace by ``()`` or ``{}``.
759 """
759 """
760
760
@@ -764,7 +764,7 b' class Instance(ClassBasedTraitType):'
764 raise TraitError('The klass argument must be a class'
764 raise TraitError('The klass argument must be a class'
765 ' you gave: %r' % klass)
765 ' you gave: %r' % klass)
766 self.klass = klass
766 self.klass = klass
767
767
768 # self.klass is a class, so handle default_value
768 # self.klass is a class, so handle default_value
769 if args is None and kw is None:
769 if args is None and kw is None:
770 default_value = None
770 default_value = None
@@ -775,12 +775,12 b' class Instance(ClassBasedTraitType):'
775 elif kw is None:
775 elif kw is None:
776 # args is not None
776 # args is not None
777 kw = {}
777 kw = {}
778
778
779 if not isinstance(kw, dict):
779 if not isinstance(kw, dict):
780 raise TraitError("The 'kw' argument must be a dict or None.")
780 raise TraitError("The 'kw' argument must be a dict or None.")
781 if not isinstance(args, tuple):
781 if not isinstance(args, tuple):
782 raise TraitError("The 'args' argument must be a tuple or None.")
782 raise TraitError("The 'args' argument must be a tuple or None.")
783
783
784 default_value = DefaultValueGenerator(*args, **kw)
784 default_value = DefaultValueGenerator(*args, **kw)
785
785
786 super(Instance, self).__init__(default_value, **metadata)
786 super(Instance, self).__init__(default_value, **metadata)
@@ -817,7 +817,7 b' class Instance(ClassBasedTraitType):'
817
817
818 def get_default_value(self):
818 def get_default_value(self):
819 """Instantiate a default value instance.
819 """Instantiate a default value instance.
820
820
821 This is called when the containing HasTraits classes'
821 This is called when the containing HasTraits classes'
822 :meth:`__new__` method is called to ensure that a unique instance
822 :meth:`__new__` method is called to ensure that a unique instance
823 is created for each HasTraits instance.
823 is created for each HasTraits instance.
@@ -833,7 +833,7 b' class This(ClassBasedTraitType):'
833 """A trait for instances of the class containing this trait.
833 """A trait for instances of the class containing this trait.
834
834
835 Because how how and when class bodies are executed, the ``This``
835 Because how how and when class bodies are executed, the ``This``
836 trait can only have a default value of None. This, and because we
836 trait can only have a default value of None. This, and because we
837 always validate default values, ``allow_none`` is *always* true.
837 always validate default values, ``allow_none`` is *always* true.
838 """
838 """
839
839
@@ -1002,18 +1002,18 b' class CUnicode(Unicode):'
1002 return unicode(value)
1002 return unicode(value)
1003 except:
1003 except:
1004 self.error(obj, value)
1004 self.error(obj, value)
1005
1005
1006
1006
1007 class ObjectName(TraitType):
1007 class ObjectName(TraitType):
1008 """A string holding a valid object name in this version of Python.
1008 """A string holding a valid object name in this version of Python.
1009
1009
1010 This does not check that the name exists in any scope."""
1010 This does not check that the name exists in any scope."""
1011 info_text = "a valid object identifier in Python"
1011 info_text = "a valid object identifier in Python"
1012
1012
1013 if py3compat.PY3:
1013 if py3compat.PY3:
1014 # Python 3:
1014 # Python 3:
1015 coerce_str = staticmethod(lambda _,s: s)
1015 coerce_str = staticmethod(lambda _,s: s)
1016
1016
1017 else:
1017 else:
1018 # Python 2:
1018 # Python 2:
1019 def coerce_str(self, obj, value):
1019 def coerce_str(self, obj, value):
@@ -1024,10 +1024,10 b' class ObjectName(TraitType):'
1024 except UnicodeEncodeError:
1024 except UnicodeEncodeError:
1025 self.error(obj, value)
1025 self.error(obj, value)
1026 return value
1026 return value
1027
1027
1028 def validate(self, obj, value):
1028 def validate(self, obj, value):
1029 value = self.coerce_str(obj, value)
1029 value = self.coerce_str(obj, value)
1030
1030
1031 if isinstance(value, str) and py3compat.isidentifier(value):
1031 if isinstance(value, str) and py3compat.isidentifier(value):
1032 return value
1032 return value
1033 self.error(obj, value)
1033 self.error(obj, value)
@@ -1036,7 +1036,7 b' class DottedObjectName(ObjectName):'
1036 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1036 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1037 def validate(self, obj, value):
1037 def validate(self, obj, value):
1038 value = self.coerce_str(obj, value)
1038 value = self.coerce_str(obj, value)
1039
1039
1040 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1040 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1041 return value
1041 return value
1042 self.error(obj, value)
1042 self.error(obj, value)
@@ -1360,7 +1360,7 b' class Dict(Instance):'
1360 def __init__(self, default_value=None, allow_none=True, **metadata):
1360 def __init__(self, default_value=None, allow_none=True, **metadata):
1361 """Create a dict trait type from a dict.
1361 """Create a dict trait type from a dict.
1362
1362
1363 The default value is created by doing ``dict(default_value)``,
1363 The default value is created by doing ``dict(default_value)``,
1364 which creates a copy of the ``default_value``.
1364 which creates a copy of the ``default_value``.
1365 """
1365 """
1366 if default_value is None:
1366 if default_value is None:
@@ -1372,7 +1372,7 b' class Dict(Instance):'
1372 else:
1372 else:
1373 raise TypeError('default value of Dict was %s' % default_value)
1373 raise TypeError('default value of Dict was %s' % default_value)
1374
1374
1375 super(Dict,self).__init__(klass=dict, args=args,
1375 super(Dict,self).__init__(klass=dict, args=args,
1376 allow_none=allow_none, **metadata)
1376 allow_none=allow_none, **metadata)
1377
1377
1378 class TCPAddress(TraitType):
1378 class TCPAddress(TraitType):
@@ -24,7 +24,7 b' def create_typestr2type_dicts(dont_include_in_type2typestr=["lambda"]):'
24 objects from the types package, and vice versa."""
24 objects from the types package, and vice versa."""
25 typenamelist = [tname for tname in dir(types) if tname.endswith("Type")]
25 typenamelist = [tname for tname in dir(types) if tname.endswith("Type")]
26 typestr2type, type2typestr = {}, {}
26 typestr2type, type2typestr = {}, {}
27
27
28 for tname in typenamelist:
28 for tname in typenamelist:
29 name = tname[:-4].lower() # Cut 'Type' off the end of the name
29 name = tname[:-4].lower() # Cut 'Type' off the end of the name
30 obj = getattr(types, tname)
30 obj = getattr(types, tname)
@@ -36,10 +36,10 b' def create_typestr2type_dicts(dont_include_in_type2typestr=["lambda"]):'
36 typestr2type, type2typestr = create_typestr2type_dicts()
36 typestr2type, type2typestr = create_typestr2type_dicts()
37
37
38 def is_type(obj, typestr_or_type):
38 def is_type(obj, typestr_or_type):
39 """is_type(obj, typestr_or_type) verifies if obj is of a certain type. It
39 """is_type(obj, typestr_or_type) verifies if obj is of a certain type. It
40 can take strings or actual python types for the second argument, i.e.
40 can take strings or actual python types for the second argument, i.e.
41 'tuple'<->TupleType. 'all' matches all types.
41 'tuple'<->TupleType. 'all' matches all types.
42
42
43 TODO: Should be extended for choosing more than one type."""
43 TODO: Should be extended for choosing more than one type."""
44 if typestr_or_type == "all":
44 if typestr_or_type == "all":
45 return True
45 return True
@@ -71,7 +71,7 b' def dict_dir(obj):'
71 except AttributeError:
71 except AttributeError:
72 pass
72 pass
73 return ns
73 return ns
74
74
75 def filter_ns(ns, name_pattern="*", type_pattern="all", ignore_case=True,
75 def filter_ns(ns, name_pattern="*", type_pattern="all", ignore_case=True,
76 show_all=True):
76 show_all=True):
77 """Filter a namespace dictionary by name pattern and item type."""
77 """Filter a namespace dictionary by name pattern and item type."""
@@ -80,7 +80,7 b' def filter_ns(ns, name_pattern="*", type_pattern="all", ignore_case=True,'
80 reg = re.compile(pattern+"$", re.I)
80 reg = re.compile(pattern+"$", re.I)
81 else:
81 else:
82 reg = re.compile(pattern+"$")
82 reg = re.compile(pattern+"$")
83
83
84 # Check each one matches regex; shouldn't be hidden; of correct type.
84 # Check each one matches regex; shouldn't be hidden; of correct type.
85 return dict((key,obj) for key, obj in ns.iteritems() if reg.match(key) \
85 return dict((key,obj) for key, obj in ns.iteritems() if reg.match(key) \
86 and show_hidden(key, show_all) \
86 and show_hidden(key, show_all) \
@@ -103,7 +103,7 b' def list_namespace(namespace, type_pattern, filter, ignore_case=False, show_all='
103 ignore_case=ignore_case, show_all=show_all)
103 ignore_case=ignore_case, show_all=show_all)
104 results = {}
104 results = {}
105 for name, obj in filtered.iteritems():
105 for name, obj in filtered.iteritems():
106 ns = list_namespace(dict_dir(obj), type_pattern,
106 ns = list_namespace(dict_dir(obj), type_pattern,
107 ".".join(pattern_list[1:]),
107 ".".join(pattern_list[1:]),
108 ignore_case=ignore_case, show_all=show_all)
108 ignore_case=ignore_case, show_all=show_all)
109 for inner_name, inner_obj in ns.iteritems():
109 for inner_name, inner_obj in ns.iteritems():
@@ -44,7 +44,7 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0'
44 executable : str, optional (default sys.executable)
44 executable : str, optional (default sys.executable)
45 The Python executable to use for the kernel process.
45 The Python executable to use for the kernel process.
46
46
47 independent : bool, optional (default False)
47 independent : bool, optional (default False)
48 If set, the kernel process is guaranteed to survive if this process
48 If set, the kernel process is guaranteed to survive if this process
49 dies. If not set, an effort is made to ensure that the kernel is killed
49 dies. If not set, an effort is made to ensure that the kernel is killed
50 when this process dies. Note that in this case it is still good practice
50 when this process dies. Note that in this case it is still good practice
@@ -104,11 +104,11 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0'
104 # If this process in running on pythonw, we know that stdin, stdout, and
104 # If this process in running on pythonw, we know that stdin, stdout, and
105 # stderr are all invalid.
105 # stderr are all invalid.
106 redirect_out = sys.executable.endswith('pythonw.exe')
106 redirect_out = sys.executable.endswith('pythonw.exe')
107 if redirect_out:
107 if redirect_out:
108 _stdout = PIPE if stdout is None else stdout
108 _stdout = PIPE if stdout is None else stdout
109 _stderr = PIPE if stderr is None else stderr
109 _stderr = PIPE if stderr is None else stderr
110 else:
110 else:
111 _stdout, _stderr = stdout, stderr
111 _stdout, _stderr = stdout, stderr
112
112
113 # Spawn a kernel.
113 # Spawn a kernel.
114 if sys.platform == 'win32':
114 if sys.platform == 'win32':
@@ -130,14 +130,14 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0'
130
130
131 # Launch the kernel process.
131 # Launch the kernel process.
132 if independent:
132 if independent:
133 proc = Popen(arguments,
133 proc = Popen(arguments,
134 creationflags=512, # CREATE_NEW_PROCESS_GROUP
134 creationflags=512, # CREATE_NEW_PROCESS_GROUP
135 stdin=_stdin, stdout=_stdout, stderr=_stderr)
135 stdin=_stdin, stdout=_stdout, stderr=_stderr)
136 else:
136 else:
137 from _subprocess import DuplicateHandle, GetCurrentProcess, \
137 from _subprocess import DuplicateHandle, GetCurrentProcess, \
138 DUPLICATE_SAME_ACCESS
138 DUPLICATE_SAME_ACCESS
139 pid = GetCurrentProcess()
139 pid = GetCurrentProcess()
140 handle = DuplicateHandle(pid, pid, pid, 0,
140 handle = DuplicateHandle(pid, pid, pid, 0,
141 True, # Inheritable by new processes.
141 True, # Inheritable by new processes.
142 DUPLICATE_SAME_ACCESS)
142 DUPLICATE_SAME_ACCESS)
143 proc = Popen(arguments + ['--parent=%i'%int(handle)],
143 proc = Popen(arguments + ['--parent=%i'%int(handle)],
@@ -163,5 +163,5 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0'
163 proc.stdout.close()
163 proc.stdout.close()
164 if stderr is None:
164 if stderr is None:
165 proc.stderr.close()
165 proc.stderr.close()
166
166
167 return proc, shell_port, iopub_port, stdin_port, hb_port
167 return proc, shell_port, iopub_port, stdin_port, hb_port
@@ -69,14 +69,14 b' class Console(code.InteractiveConsole):'
69
69
70 def print_pyerr(self, err):
70 def print_pyerr(self, err):
71 print >> sys.stderr, err.etype,':', err.evalue
71 print >> sys.stderr, err.etype,':', err.evalue
72 print >> sys.stderr, ''.join(err.traceback)
72 print >> sys.stderr, ''.join(err.traceback)
73
73
74 def handle_pyerr(self, omsg):
74 def handle_pyerr(self, omsg):
75 if omsg.parent_header.session == self.session.session:
75 if omsg.parent_header.session == self.session.session:
76 return
76 return
77 print >> sys.stderr, '[ERR from %s]' % omsg.parent_header.username
77 print >> sys.stderr, '[ERR from %s]' % omsg.parent_header.username
78 self.print_pyerr(omsg.content)
78 self.print_pyerr(omsg.content)
79
79
80 def handle_stream(self, omsg):
80 def handle_stream(self, omsg):
81 if omsg.content.name == 'stdout':
81 if omsg.content.name == 'stdout':
82 outstream = sys.stdout
82 outstream = sys.stdout
@@ -104,7 +104,7 b' class Console(code.InteractiveConsole):'
104 if rep is None:
104 if rep is None:
105 return
105 return
106 if rep.content.status == 'error':
106 if rep.content.status == 'error':
107 self.print_pyerr(rep.content)
107 self.print_pyerr(rep.content)
108 elif rep.content.status == 'aborted':
108 elif rep.content.status == 'aborted':
109 print >> sys.stderr, "ERROR: ABORTED"
109 print >> sys.stderr, "ERROR: ABORTED"
110 ab = self.messages[rep.parent_header.msg_id].content
110 ab = self.messages[rep.parent_header.msg_id].content
@@ -137,7 +137,7 b' class Console(code.InteractiveConsole):'
137 omsg = self.session.send(self.request_socket,
137 omsg = self.session.send(self.request_socket,
138 'execute_request', dict(code=src))
138 'execute_request', dict(code=src))
139 self.messages[omsg.header.msg_id] = omsg
139 self.messages[omsg.header.msg_id] = omsg
140
140
141 # Fake asynchronicity by letting the user put ';' at the end of the line
141 # Fake asynchronicity by letting the user put ';' at the end of the line
142 if src.endswith(';'):
142 if src.endswith(';'):
143 self.backgrounded += 1
143 self.backgrounded += 1
@@ -162,7 +162,7 b' class InteractiveClient(object):'
162 self.sub_socket = sub_socket
162 self.sub_socket = sub_socket
163 self.console = Console(None, '<zmq-console>',
163 self.console = Console(None, '<zmq-console>',
164 session, request_socket, sub_socket)
164 session, request_socket, sub_socket)
165
165
166 def interact(self):
166 def interact(self):
167 self.console.interact()
167 self.console.interact()
168
168
@@ -176,12 +176,12 b' def main():'
176 connection = ('tcp://%s' % ip) + ':%i'
176 connection = ('tcp://%s' % ip) + ':%i'
177 req_conn = connection % port_base
177 req_conn = connection % port_base
178 sub_conn = connection % (port_base+1)
178 sub_conn = connection % (port_base+1)
179
179
180 # Create initial sockets
180 # Create initial sockets
181 c = zmq.Context()
181 c = zmq.Context()
182 request_socket = c.socket(zmq.DEALER)
182 request_socket = c.socket(zmq.DEALER)
183 request_socket.connect(req_conn)
183 request_socket.connect(req_conn)
184
184
185 sub_socket = c.socket(zmq.SUB)
185 sub_socket = c.socket(zmq.SUB)
186 sub_socket.connect(sub_conn)
186 sub_socket.connect(sub_conn)
187 sub_socket.setsockopt(zmq.SUBSCRIBE, '')
187 sub_socket.setsockopt(zmq.SUBSCRIBE, '')
@@ -111,7 +111,7 b' class Kernel(Configurable):'
111 self.shell._reply_content = None
111 self.shell._reply_content = None
112
112
113 # Build dict of handlers for message types
113 # Build dict of handlers for message types
114 msg_types = [ 'execute_request', 'complete_request',
114 msg_types = [ 'execute_request', 'complete_request',
115 'object_info_request', 'history_request',
115 'object_info_request', 'history_request',
116 'connect_request', 'shutdown_request']
116 'connect_request', 'shutdown_request']
117 self.handlers = {}
117 self.handlers = {}
@@ -135,7 +135,7 b' class Kernel(Configurable):'
135 # We now require 2.0.8 or above, so we can uncomment for safety.
135 # We now require 2.0.8 or above, so we can uncomment for safety.
136 # print(ident,msg, file=sys.__stdout__)
136 # print(ident,msg, file=sys.__stdout__)
137 assert ident is not None, "Missing message part."
137 assert ident is not None, "Missing message part."
138
138
139 # Print some info about this message and leave a '--->' marker, so it's
139 # Print some info about this message and leave a '--->' marker, so it's
140 # easier to trace visually the message chain when debugging. Each
140 # easier to trace visually the message chain when debugging. Each
141 # handler prints its message at the end.
141 # handler prints its message at the end.
@@ -148,7 +148,7 b' class Kernel(Configurable):'
148 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
148 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
149 else:
149 else:
150 handler(ident, msg)
150 handler(ident, msg)
151
151
152 # Check whether we should exit, in case the incoming message set the
152 # Check whether we should exit, in case the incoming message set the
153 # exit flag on
153 # exit flag on
154 if self.shell.exit_now:
154 if self.shell.exit_now:
@@ -169,7 +169,7 b' class Kernel(Configurable):'
169 # reason for this to be anything less than ~ 0.1s
169 # reason for this to be anything less than ~ 0.1s
170 # since it is a real poller and will respond
170 # since it is a real poller and will respond
171 # to events immediately
171 # to events immediately
172
172
173 # double nested try/except, to properly catch KeyboardInterrupt
173 # double nested try/except, to properly catch KeyboardInterrupt
174 # due to pyzmq Issue #130
174 # due to pyzmq Issue #130
175 try:
175 try:
@@ -199,17 +199,17 b' class Kernel(Configurable):'
199 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
199 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
200
200
201 def execute_request(self, ident, parent):
201 def execute_request(self, ident, parent):
202
202
203 status_msg = self.session.send(self.iopub_socket,
203 status_msg = self.session.send(self.iopub_socket,
204 u'status',
204 u'status',
205 {u'execution_state':u'busy'},
205 {u'execution_state':u'busy'},
206 parent=parent
206 parent=parent
207 )
207 )
208
208
209 try:
209 try:
210 content = parent[u'content']
210 content = parent[u'content']
211 code = content[u'code']
211 code = content[u'code']
212 silent = content[u'silent']
212 silent = content[u'silent']
213 except:
213 except:
214 self.log.error("Got bad msg: ")
214 self.log.error("Got bad msg: ")
215 self.log.error(str(Message(parent)))
215 self.log.error(str(Message(parent)))
@@ -217,7 +217,7 b' class Kernel(Configurable):'
217
217
218 shell = self.shell # we'll need this a lot here
218 shell = self.shell # we'll need this a lot here
219
219
220 # Replace raw_input. Note that is not sufficient to replace
220 # Replace raw_input. Note that is not sufficient to replace
221 # raw_input in the user namespace.
221 # raw_input in the user namespace.
222 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
222 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
223 if py3compat.PY3:
223 if py3compat.PY3:
@@ -261,7 +261,7 b' class Kernel(Configurable):'
261 status = u'ok'
261 status = u'ok'
262
262
263 reply_content[u'status'] = status
263 reply_content[u'status'] = status
264
264
265 # Return the execution counter so clients can display prompts
265 # Return the execution counter so clients can display prompts
266 reply_content['execution_count'] = shell.execution_count -1
266 reply_content['execution_count'] = shell.execution_count -1
267
267
@@ -301,7 +301,7 b' class Kernel(Configurable):'
301 # to better understand what's going on.
301 # to better understand what's going on.
302 if self._execute_sleep:
302 if self._execute_sleep:
303 time.sleep(self._execute_sleep)
303 time.sleep(self._execute_sleep)
304
304
305 # Send the reply.
305 # Send the reply.
306 reply_content = json_clean(reply_content)
306 reply_content = json_clean(reply_content)
307 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
307 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
@@ -345,18 +345,18 b' class Kernel(Configurable):'
345 n = parent['content']['n']
345 n = parent['content']['n']
346 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
346 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
347 include_latest=True)
347 include_latest=True)
348
348
349 elif hist_access_type == 'range':
349 elif hist_access_type == 'range':
350 session = parent['content']['session']
350 session = parent['content']['session']
351 start = parent['content']['start']
351 start = parent['content']['start']
352 stop = parent['content']['stop']
352 stop = parent['content']['stop']
353 hist = self.shell.history_manager.get_range(session, start, stop,
353 hist = self.shell.history_manager.get_range(session, start, stop,
354 raw=raw, output=output)
354 raw=raw, output=output)
355
355
356 elif hist_access_type == 'search':
356 elif hist_access_type == 'search':
357 pattern = parent['content']['pattern']
357 pattern = parent['content']['pattern']
358 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
358 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
359
359
360 else:
360 else:
361 hist = []
361 hist = []
362 content = {'history' : list(hist)}
362 content = {'history' : list(hist)}
@@ -430,7 +430,7 b' class Kernel(Configurable):'
430 self.log.error(str(Message(parent)))
430 self.log.error(str(Message(parent)))
431 value = ''
431 value = ''
432 return value
432 return value
433
433
434 def _complete(self, msg):
434 def _complete(self, msg):
435 c = msg['content']
435 c = msg['content']
436 try:
436 try:
@@ -562,11 +562,11 b' class TkKernel(Kernel):'
562 self.app = Tkinter.Tk()
562 self.app = Tkinter.Tk()
563 self.app.withdraw()
563 self.app.withdraw()
564 self.func = func
564 self.func = func
565
565
566 def on_timer(self):
566 def on_timer(self):
567 self.func()
567 self.func()
568 self.app.after(poll_interval, self.on_timer)
568 self.app.after(poll_interval, self.on_timer)
569
569
570 def start(self):
570 def start(self):
571 self.on_timer() # Call it once to get things going.
571 self.on_timer() # Call it once to get things going.
572 self.app.mainloop()
572 self.app.mainloop()
@@ -577,11 +577,11 b' class TkKernel(Kernel):'
577
577
578 class GTKKernel(Kernel):
578 class GTKKernel(Kernel):
579 """A Kernel subclass with GTK support."""
579 """A Kernel subclass with GTK support."""
580
580
581 def start(self):
581 def start(self):
582 """Start the kernel, coordinating with the GTK event loop"""
582 """Start the kernel, coordinating with the GTK event loop"""
583 from .gui.gtkembed import GTKEmbed
583 from .gui.gtkembed import GTKEmbed
584
584
585 gtk_kernel = GTKEmbed(self)
585 gtk_kernel = GTKEmbed(self)
586 gtk_kernel.start()
586 gtk_kernel.start()
587
587
@@ -666,7 +666,7 b' class IPKernelApp(KernelApp, InteractiveShellApp):'
666 import_all = self.pylab_import_all
666 import_all = self.pylab_import_all
667 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
667 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
668 shell=kernel.shell)
668 shell=kernel.shell)
669
669
670 def init_shell(self):
670 def init_shell(self):
671 self.shell = self.kernel.shell
671 self.shell = self.kernel.shell
672
672
@@ -122,12 +122,12 b' class ZMQSocketChannel(Thread):'
122 raise
122 raise
123 else:
123 else:
124 break
124 break
125
125
126 def stop(self):
126 def stop(self):
127 """Stop the channel's activity.
127 """Stop the channel's activity.
128
128
129 This calls :method:`Thread.join` and returns when the thread
129 This calls :method:`Thread.join` and returns when the thread
130 terminates. :class:`RuntimeError` will be raised if
130 terminates. :class:`RuntimeError` will be raised if
131 :method:`self.start` is called again.
131 :method:`self.start` is called again.
132 """
132 """
133 self.join()
133 self.join()
@@ -135,7 +135,7 b' class ZMQSocketChannel(Thread):'
135 @property
135 @property
136 def address(self):
136 def address(self):
137 """Get the channel's address as an (ip, port) tuple.
137 """Get the channel's address as an (ip, port) tuple.
138
138
139 By the default, the address is (localhost, 0), where 0 means a random
139 By the default, the address is (localhost, 0), where 0 means a random
140 port.
140 port.
141 """
141 """
@@ -191,7 +191,7 b' class ShellSocketChannel(ZMQSocketChannel):'
191 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
191 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
192 self.socket.connect('tcp://%s:%i' % self.address)
192 self.socket.connect('tcp://%s:%i' % self.address)
193 self.iostate = POLLERR|POLLIN
193 self.iostate = POLLERR|POLLIN
194 self.ioloop.add_handler(self.socket, self._handle_events,
194 self.ioloop.add_handler(self.socket, self._handle_events,
195 self.iostate)
195 self.iostate)
196 self._run_loop()
196 self._run_loop()
197
197
@@ -217,7 +217,7 b' class ShellSocketChannel(ZMQSocketChannel):'
217 ----------
217 ----------
218 code : str
218 code : str
219 A string of Python code.
219 A string of Python code.
220
220
221 silent : bool, optional (default False)
221 silent : bool, optional (default False)
222 If set, the kernel will execute the code as quietly possible.
222 If set, the kernel will execute the code as quietly possible.
223
223
@@ -225,7 +225,7 b' class ShellSocketChannel(ZMQSocketChannel):'
225 A list of variable names to pull from the user's namespace. They
225 A list of variable names to pull from the user's namespace. They
226 will come back as a dict with these names as keys and their
226 will come back as a dict with these names as keys and their
227 :func:`repr` as values.
227 :func:`repr` as values.
228
228
229 user_expressions : dict, optional
229 user_expressions : dict, optional
230 A dict with string keys and to pull from the user's
230 A dict with string keys and to pull from the user's
231 namespace. They will come back as a dict with these names as keys
231 namespace. They will come back as a dict with these names as keys
@@ -239,7 +239,7 b' class ShellSocketChannel(ZMQSocketChannel):'
239 user_variables = []
239 user_variables = []
240 if user_expressions is None:
240 if user_expressions is None:
241 user_expressions = {}
241 user_expressions = {}
242
242
243 # Don't waste network traffic if inputs are invalid
243 # Don't waste network traffic if inputs are invalid
244 if not isinstance(code, basestring):
244 if not isinstance(code, basestring):
245 raise ValueError('code %r must be a string' % code)
245 raise ValueError('code %r must be a string' % code)
@@ -263,7 +263,7 b' class ShellSocketChannel(ZMQSocketChannel):'
263 text : str
263 text : str
264 The text to complete.
264 The text to complete.
265 line : str
265 line : str
266 The full line of text that is the surrounding context for the
266 The full line of text that is the surrounding context for the
267 text to complete.
267 text to complete.
268 cursor_pos : int
268 cursor_pos : int
269 The position of the cursor in the line where the completion was
269 The position of the cursor in the line where the completion was
@@ -287,7 +287,7 b' class ShellSocketChannel(ZMQSocketChannel):'
287 ----------
287 ----------
288 oname : str
288 oname : str
289 A string specifying the object name.
289 A string specifying the object name.
290
290
291 Returns
291 Returns
292 -------
292 -------
293 The msg_id of the message sent.
293 The msg_id of the message sent.
@@ -309,7 +309,7 b' class ShellSocketChannel(ZMQSocketChannel):'
309 hist_access_type : str
309 hist_access_type : str
310 'range' (fill in session, start and stop params), 'tail' (fill in n)
310 'range' (fill in session, start and stop params), 'tail' (fill in n)
311 or 'search' (fill in pattern param).
311 or 'search' (fill in pattern param).
312
312
313 session : int
313 session : int
314 For a range request, the session from which to get lines. Session
314 For a range request, the session from which to get lines. Session
315 numbers are positive integers; negative ones count back from the
315 numbers are positive integers; negative ones count back from the
@@ -318,10 +318,10 b' class ShellSocketChannel(ZMQSocketChannel):'
318 The first line number of a history range.
318 The first line number of a history range.
319 stop : int
319 stop : int
320 The final (excluded) line number of a history range.
320 The final (excluded) line number of a history range.
321
321
322 n : int
322 n : int
323 The number of lines of history to get for a tail request.
323 The number of lines of history to get for a tail request.
324
324
325 pattern : str
325 pattern : str
326 The glob-syntax pattern for a search request.
326 The glob-syntax pattern for a search request.
327
327
@@ -398,7 +398,7 b' class SubSocketChannel(ZMQSocketChannel):'
398 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
398 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
399 self.socket.connect('tcp://%s:%i' % self.address)
399 self.socket.connect('tcp://%s:%i' % self.address)
400 self.iostate = POLLIN|POLLERR
400 self.iostate = POLLIN|POLLERR
401 self.ioloop.add_handler(self.socket, self._handle_events,
401 self.ioloop.add_handler(self.socket, self._handle_events,
402 self.iostate)
402 self.iostate)
403 self._run_loop()
403 self._run_loop()
404
404
@@ -486,7 +486,7 b' class StdInSocketChannel(ZMQSocketChannel):'
486 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
486 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
487 self.socket.connect('tcp://%s:%i' % self.address)
487 self.socket.connect('tcp://%s:%i' % self.address)
488 self.iostate = POLLERR|POLLIN
488 self.iostate = POLLERR|POLLIN
489 self.ioloop.add_handler(self.socket, self._handle_events,
489 self.ioloop.add_handler(self.socket, self._handle_events,
490 self.iostate)
490 self.iostate)
491 self._run_loop()
491 self._run_loop()
492
492
@@ -616,7 +616,7 b' class HBSocketChannel(ZMQSocketChannel):'
616 raise
616 raise
617 else:
617 else:
618 break
618 break
619
619
620 since_last_heartbeat = time.time()-request_time
620 since_last_heartbeat = time.time()-request_time
621 if since_last_heartbeat > self.time_to_dead:
621 if since_last_heartbeat > self.time_to_dead:
622 self.call_handlers(since_last_heartbeat)
622 self.call_handlers(since_last_heartbeat)
@@ -671,15 +671,15 b' class KernelManager(HasTraits):'
671
671
672 The SUB channel is for the frontend to receive messages published by the
672 The SUB channel is for the frontend to receive messages published by the
673 kernel.
673 kernel.
674
674
675 The REQ channel is for the frontend to make requests of the kernel.
675 The REQ channel is for the frontend to make requests of the kernel.
676
676
677 The REP channel is for the kernel to request stdin (raw_input) from the
677 The REP channel is for the kernel to request stdin (raw_input) from the
678 frontend.
678 frontend.
679 """
679 """
680 # config object for passing to child configurables
680 # config object for passing to child configurables
681 config = Instance(Config)
681 config = Instance(Config)
682
682
683 # The PyZMQ Context to use for communication with the kernel.
683 # The PyZMQ Context to use for communication with the kernel.
684 context = Instance(zmq.Context)
684 context = Instance(zmq.Context)
685 def _context_default(self):
685 def _context_default(self):
@@ -691,7 +691,7 b' class KernelManager(HasTraits):'
691 # The kernel process with which the KernelManager is communicating.
691 # The kernel process with which the KernelManager is communicating.
692 kernel = Instance(Popen)
692 kernel = Instance(Popen)
693
693
694 # The addresses for the communication channels.
694 # The addresses for the communication channels.
695 shell_address = TCPAddress((LOCALHOST, 0))
695 shell_address = TCPAddress((LOCALHOST, 0))
696 sub_address = TCPAddress((LOCALHOST, 0))
696 sub_address = TCPAddress((LOCALHOST, 0))
697 stdin_address = TCPAddress((LOCALHOST, 0))
697 stdin_address = TCPAddress((LOCALHOST, 0))
@@ -788,7 +788,7 b' class KernelManager(HasTraits):'
788 "configured properly. "
788 "configured properly. "
789 "Currently valid addresses are: %s"%LOCAL_IPS
789 "Currently valid addresses are: %s"%LOCAL_IPS
790 )
790 )
791
791
792 self._launch_args = kw.copy()
792 self._launch_args = kw.copy()
793 launch_kernel = kw.pop('launcher', None)
793 launch_kernel = kw.pop('launcher', None)
794 if launch_kernel is None:
794 if launch_kernel is None:
@@ -830,10 +830,10 b' class KernelManager(HasTraits):'
830 # OK, we've waited long enough.
830 # OK, we've waited long enough.
831 if self.has_kernel:
831 if self.has_kernel:
832 self.kill_kernel()
832 self.kill_kernel()
833
833
834 def restart_kernel(self, now=False, **kw):
834 def restart_kernel(self, now=False, **kw):
835 """Restarts a kernel with the arguments that were used to launch it.
835 """Restarts a kernel with the arguments that were used to launch it.
836
836
837 If the old kernel was launched with random ports, the same ports will be
837 If the old kernel was launched with random ports, the same ports will be
838 used for the new kernel.
838 used for the new kernel.
839
839
@@ -977,7 +977,7 b' class KernelManager(HasTraits):'
977 """Get the heartbeat socket channel object to check that the
977 """Get the heartbeat socket channel object to check that the
978 kernel is alive."""
978 kernel is alive."""
979 if self._hb_channel is None:
979 if self._hb_channel is None:
980 self._hb_channel = self.hb_channel_class(self.context,
980 self._hb_channel = self.hb_channel_class(self.context,
981 self.session,
981 self.session,
982 self.hb_address)
982 self.hb_address)
983 return self._hb_channel
983 return self._hb_channel
@@ -10,14 +10,14 b' from IPython.utils.warn import warn'
10
10
11
11
12 class ParentPollerUnix(Thread):
12 class ParentPollerUnix(Thread):
13 """ A Unix-specific daemon thread that terminates the program immediately
13 """ A Unix-specific daemon thread that terminates the program immediately
14 when the parent process no longer exists.
14 when the parent process no longer exists.
15 """
15 """
16
16
17 def __init__(self):
17 def __init__(self):
18 super(ParentPollerUnix, self).__init__()
18 super(ParentPollerUnix, self).__init__()
19 self.daemon = True
19 self.daemon = True
20
20
21 def run(self):
21 def run(self):
22 # We cannot use os.waitpid because it works only for child processes.
22 # We cannot use os.waitpid because it works only for child processes.
23 from errno import EINTR
23 from errno import EINTR
@@ -37,7 +37,7 b' class ParentPollerWindows(Thread):'
37 signals an interrupt and, optionally, terminates the program immediately
37 signals an interrupt and, optionally, terminates the program immediately
38 when the parent process no longer exists.
38 when the parent process no longer exists.
39 """
39 """
40
40
41 def __init__(self, interrupt_handle=None, parent_handle=None):
41 def __init__(self, interrupt_handle=None, parent_handle=None):
42 """ Create the poller. At least one of the optional parameters must be
42 """ Create the poller. At least one of the optional parameters must be
43 provided.
43 provided.
@@ -49,7 +49,7 b' class ParentPollerWindows(Thread):'
49 handle is signaled.
49 handle is signaled.
50
50
51 parent_handle : HANDLE (int), optional
51 parent_handle : HANDLE (int), optional
52 If provided, the program will terminate immediately when this
52 If provided, the program will terminate immediately when this
53 handle is signaled.
53 handle is signaled.
54 """
54 """
55 assert(interrupt_handle or parent_handle)
55 assert(interrupt_handle or parent_handle)
@@ -71,8 +71,8 b' class ParentPollerWindows(Thread):'
71 # handle by new processes.
71 # handle by new processes.
72 # FIXME: We can clean up this mess by requiring pywin32 for IPython.
72 # FIXME: We can clean up this mess by requiring pywin32 for IPython.
73 class SECURITY_ATTRIBUTES(ctypes.Structure):
73 class SECURITY_ATTRIBUTES(ctypes.Structure):
74 _fields_ = [ ("nLength", ctypes.c_int),
74 _fields_ = [ ("nLength", ctypes.c_int),
75 ("lpSecurityDescriptor", ctypes.c_void_p),
75 ("lpSecurityDescriptor", ctypes.c_void_p),
76 ("bInheritHandle", ctypes.c_int) ]
76 ("bInheritHandle", ctypes.c_int) ]
77 sa = SECURITY_ATTRIBUTES()
77 sa = SECURITY_ATTRIBUTES()
78 sa_p = ctypes.pointer(sa)
78 sa_p = ctypes.pointer(sa)
@@ -71,7 +71,7 b' class Kernel(HasTraits):'
71 self.completer = KernelCompleter(self.user_ns)
71 self.completer = KernelCompleter(self.user_ns)
72
72
73 # Build dict of handlers for message types
73 # Build dict of handlers for message types
74 msg_types = [ 'execute_request', 'complete_request',
74 msg_types = [ 'execute_request', 'complete_request',
75 'object_info_request', 'shutdown_request' ]
75 'object_info_request', 'shutdown_request' ]
76 self.handlers = {}
76 self.handlers = {}
77 for msg_type in msg_types:
77 for msg_type in msg_types:
@@ -114,7 +114,7 b' class Kernel(HasTraits):'
114 try:
114 try:
115 comp_code = self.compiler(code, '<zmq-kernel>')
115 comp_code = self.compiler(code, '<zmq-kernel>')
116
116
117 # Replace raw_input. Note that is not sufficient to replace
117 # Replace raw_input. Note that is not sufficient to replace
118 # raw_input in the user namespace.
118 # raw_input in the user namespace.
119 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
119 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
120 if py3compat.PY3:
120 if py3compat.PY3:
@@ -141,7 +141,7 b' class Kernel(HasTraits):'
141 reply_content = exc_content
141 reply_content = exc_content
142 else:
142 else:
143 reply_content = { 'status' : 'ok', 'payload' : {} }
143 reply_content = { 'status' : 'ok', 'payload' : {} }
144
144
145 # Flush output before sending the reply.
145 # Flush output before sending the reply.
146 sys.stderr.flush()
146 sys.stderr.flush()
147 sys.stdout.flush()
147 sys.stdout.flush()
@@ -259,7 +259,7 b' class Kernel(HasTraits):'
259
259
260 def launch_kernel(*args, **kwargs):
260 def launch_kernel(*args, **kwargs):
261 """ Launches a simple Python kernel, binding to the specified ports.
261 """ Launches a simple Python kernel, binding to the specified ports.
262
262
263 This function simply calls entry_point.base_launch_kernel with the right first
263 This function simply calls entry_point.base_launch_kernel with the right first
264 command to start a pykernel. See base_launch_kernel for arguments.
264 command to start a pykernel. See base_launch_kernel for arguments.
265
265
@@ -92,36 +92,36 b' class SessionFactory(LoggingConfigurable):'
92 """The Base class for configurables that have a Session, Context, logger,
92 """The Base class for configurables that have a Session, Context, logger,
93 and IOLoop.
93 and IOLoop.
94 """
94 """
95
95
96 logname = Unicode('')
96 logname = Unicode('')
97 def _logname_changed(self, name, old, new):
97 def _logname_changed(self, name, old, new):
98 self.log = logging.getLogger(new)
98 self.log = logging.getLogger(new)
99
99
100 # not configurable:
100 # not configurable:
101 context = Instance('zmq.Context')
101 context = Instance('zmq.Context')
102 def _context_default(self):
102 def _context_default(self):
103 return zmq.Context.instance()
103 return zmq.Context.instance()
104
104
105 session = Instance('IPython.zmq.session.Session')
105 session = Instance('IPython.zmq.session.Session')
106
106
107 loop = Instance('zmq.eventloop.ioloop.IOLoop', allow_none=False)
107 loop = Instance('zmq.eventloop.ioloop.IOLoop', allow_none=False)
108 def _loop_default(self):
108 def _loop_default(self):
109 return IOLoop.instance()
109 return IOLoop.instance()
110
110
111 def __init__(self, **kwargs):
111 def __init__(self, **kwargs):
112 super(SessionFactory, self).__init__(**kwargs)
112 super(SessionFactory, self).__init__(**kwargs)
113
113
114 if self.session is None:
114 if self.session is None:
115 # construct the session
115 # construct the session
116 self.session = Session(**kwargs)
116 self.session = Session(**kwargs)
117
117
118
118
119 class Message(object):
119 class Message(object):
120 """A simple message object that maps dict keys to attributes.
120 """A simple message object that maps dict keys to attributes.
121
121
122 A Message can be created from a dict and a dict from a Message instance
122 A Message can be created from a dict and a dict from a Message instance
123 simply by calling dict(msg_obj)."""
123 simply by calling dict(msg_obj)."""
124
124
125 def __init__(self, msg_dict):
125 def __init__(self, msg_dict):
126 dct = self.__dict__
126 dct = self.__dict__
127 for k, v in dict(msg_dict).iteritems():
127 for k, v in dict(msg_dict).iteritems():
@@ -132,7 +132,7 b' class Message(object):'
132 # Having this iterator lets dict(msg_obj) work out of the box.
132 # Having this iterator lets dict(msg_obj) work out of the box.
133 def __iter__(self):
133 def __iter__(self):
134 return iter(self.__dict__.iteritems())
134 return iter(self.__dict__.iteritems())
135
135
136 def __repr__(self):
136 def __repr__(self):
137 return repr(self.__dict__)
137 return repr(self.__dict__)
138
138
@@ -171,28 +171,28 b' def extract_header(msg_or_header):'
171
171
172 class Session(Configurable):
172 class Session(Configurable):
173 """Object for handling serialization and sending of messages.
173 """Object for handling serialization and sending of messages.
174
174
175 The Session object handles building messages and sending them
175 The Session object handles building messages and sending them
176 with ZMQ sockets or ZMQStream objects. Objects can communicate with each
176 with ZMQ sockets or ZMQStream objects. Objects can communicate with each
177 other over the network via Session objects, and only need to work with the
177 other over the network via Session objects, and only need to work with the
178 dict-based IPython message spec. The Session will handle
178 dict-based IPython message spec. The Session will handle
179 serialization/deserialization, security, and metadata.
179 serialization/deserialization, security, and metadata.
180
180
181 Sessions support configurable serialiization via packer/unpacker traits,
181 Sessions support configurable serialiization via packer/unpacker traits,
182 and signing with HMAC digests via the key/keyfile traits.
182 and signing with HMAC digests via the key/keyfile traits.
183
183
184 Parameters
184 Parameters
185 ----------
185 ----------
186
186
187 debug : bool
187 debug : bool
188 whether to trigger extra debugging statements
188 whether to trigger extra debugging statements
189 packer/unpacker : str : 'json', 'pickle' or import_string
189 packer/unpacker : str : 'json', 'pickle' or import_string
190 importstrings for methods to serialize message parts. If just
190 importstrings for methods to serialize message parts. If just
191 'json' or 'pickle', predefined JSON and pickle packers will be used.
191 'json' or 'pickle', predefined JSON and pickle packers will be used.
192 Otherwise, the entire importstring must be used.
192 Otherwise, the entire importstring must be used.
193
193
194 The functions must accept at least valid JSON input, and output *bytes*.
194 The functions must accept at least valid JSON input, and output *bytes*.
195
195
196 For example, to use msgpack:
196 For example, to use msgpack:
197 packer = 'msgpack.packb', unpacker='msgpack.unpackb'
197 packer = 'msgpack.packb', unpacker='msgpack.unpackb'
198 pack/unpack : callables
198 pack/unpack : callables
@@ -207,11 +207,11 b' class Session(Configurable):'
207 keyfile : filepath
207 keyfile : filepath
208 The file containing a key. If this is set, `key` will be initialized
208 The file containing a key. If this is set, `key` will be initialized
209 to the contents of the file.
209 to the contents of the file.
210
210
211 """
211 """
212
212
213 debug=Bool(False, config=True, help="""Debug output in the Session""")
213 debug=Bool(False, config=True, help="""Debug output in the Session""")
214
214
215 packer = DottedObjectName('json',config=True,
215 packer = DottedObjectName('json',config=True,
216 help="""The name of the packer for serializing messages.
216 help="""The name of the packer for serializing messages.
217 Should be one of 'json', 'pickle', or an import name
217 Should be one of 'json', 'pickle', or an import name
@@ -238,23 +238,23 b' class Session(Configurable):'
238 self.unpack = pickle_unpacker
238 self.unpack = pickle_unpacker
239 else:
239 else:
240 self.unpack = import_item(str(new))
240 self.unpack = import_item(str(new))
241
241
242 session = CUnicode(u'', config=True,
242 session = CUnicode(u'', config=True,
243 help="""The UUID identifying this session.""")
243 help="""The UUID identifying this session.""")
244 def _session_default(self):
244 def _session_default(self):
245 u = unicode(uuid.uuid4())
245 u = unicode(uuid.uuid4())
246 self.bsession = u.encode('ascii')
246 self.bsession = u.encode('ascii')
247 return u
247 return u
248
248
249 def _session_changed(self, name, old, new):
249 def _session_changed(self, name, old, new):
250 self.bsession = self.session.encode('ascii')
250 self.bsession = self.session.encode('ascii')
251
251
252 # bsession is the session as bytes
252 # bsession is the session as bytes
253 bsession = CBytes(b'')
253 bsession = CBytes(b'')
254
254
255 username = Unicode(os.environ.get('USER',u'username'), config=True,
255 username = Unicode(os.environ.get('USER',u'username'), config=True,
256 help="""Username for the Session. Default is your system username.""")
256 help="""Username for the Session. Default is your system username.""")
257
257
258 # message signature related traits:
258 # message signature related traits:
259 key = CBytes(b'', config=True,
259 key = CBytes(b'', config=True,
260 help="""execution key, for extra authentication.""")
260 help="""execution key, for extra authentication.""")
@@ -265,7 +265,7 b' class Session(Configurable):'
265 self.auth = None
265 self.auth = None
266 auth = Instance(hmac.HMAC)
266 auth = Instance(hmac.HMAC)
267 digest_history = Set()
267 digest_history = Set()
268
268
269 keyfile = Unicode('', config=True,
269 keyfile = Unicode('', config=True,
270 help="""path to file containing execution key.""")
270 help="""path to file containing execution key.""")
271 def _keyfile_changed(self, name, old, new):
271 def _keyfile_changed(self, name, old, new):
@@ -276,16 +276,16 b' class Session(Configurable):'
276 def _pack_changed(self, name, old, new):
276 def _pack_changed(self, name, old, new):
277 if not callable(new):
277 if not callable(new):
278 raise TypeError("packer must be callable, not %s"%type(new))
278 raise TypeError("packer must be callable, not %s"%type(new))
279
279
280 unpack = Any(default_unpacker) # the actual packer function
280 unpack = Any(default_unpacker) # the actual packer function
281 def _unpack_changed(self, name, old, new):
281 def _unpack_changed(self, name, old, new):
282 # unpacker is not checked - it is assumed to be
282 # unpacker is not checked - it is assumed to be
283 if not callable(new):
283 if not callable(new):
284 raise TypeError("unpacker must be callable, not %s"%type(new))
284 raise TypeError("unpacker must be callable, not %s"%type(new))
285
285
286 def __init__(self, **kwargs):
286 def __init__(self, **kwargs):
287 """create a Session object
287 """create a Session object
288
288
289 Parameters
289 Parameters
290 ----------
290 ----------
291
291
@@ -305,7 +305,7 b' class Session(Configurable):'
305 You can also set the pack/unpack callables for serialization
305 You can also set the pack/unpack callables for serialization
306 directly.
306 directly.
307 session : unicode (must be ascii)
307 session : unicode (must be ascii)
308 the ID of this Session object. The default is to generate a new
308 the ID of this Session object. The default is to generate a new
309 UUID.
309 UUID.
310 bsession : bytes
310 bsession : bytes
311 The session as bytes
311 The session as bytes
@@ -315,7 +315,7 b' class Session(Configurable):'
315 The key used to initialize an HMAC signature. If unset, messages
315 The key used to initialize an HMAC signature. If unset, messages
316 will not be signed or checked.
316 will not be signed or checked.
317 keyfile : filepath
317 keyfile : filepath
318 The file containing a key. If this is set, `key` will be
318 The file containing a key. If this is set, `key` will be
319 initialized to the contents of the file.
319 initialized to the contents of the file.
320 """
320 """
321 super(Session, self).__init__(**kwargs)
321 super(Session, self).__init__(**kwargs)
@@ -333,24 +333,24 b' class Session(Configurable):'
333 """check packers for binary data and datetime support."""
333 """check packers for binary data and datetime support."""
334 pack = self.pack
334 pack = self.pack
335 unpack = self.unpack
335 unpack = self.unpack
336
336
337 # check simple serialization
337 # check simple serialization
338 msg = dict(a=[1,'hi'])
338 msg = dict(a=[1,'hi'])
339 try:
339 try:
340 packed = pack(msg)
340 packed = pack(msg)
341 except Exception:
341 except Exception:
342 raise ValueError("packer could not serialize a simple message")
342 raise ValueError("packer could not serialize a simple message")
343
343
344 # ensure packed message is bytes
344 # ensure packed message is bytes
345 if not isinstance(packed, bytes):
345 if not isinstance(packed, bytes):
346 raise ValueError("message packed to %r, but bytes are required"%type(packed))
346 raise ValueError("message packed to %r, but bytes are required"%type(packed))
347
347
348 # check that unpack is pack's inverse
348 # check that unpack is pack's inverse
349 try:
349 try:
350 unpacked = unpack(packed)
350 unpacked = unpack(packed)
351 except Exception:
351 except Exception:
352 raise ValueError("unpacker could not handle the packer's output")
352 raise ValueError("unpacker could not handle the packer's output")
353
353
354 # check datetime support
354 # check datetime support
355 msg = dict(t=datetime.now())
355 msg = dict(t=datetime.now())
356 try:
356 try:
@@ -358,7 +358,7 b' class Session(Configurable):'
358 except Exception:
358 except Exception:
359 self.pack = lambda o: pack(squash_dates(o))
359 self.pack = lambda o: pack(squash_dates(o))
360 self.unpack = lambda s: extract_dates(unpack(s))
360 self.unpack = lambda s: extract_dates(unpack(s))
361
361
362 def msg_header(self, msg_type):
362 def msg_header(self, msg_type):
363 return msg_header(self.msg_id, msg_type, self.username, self.session)
363 return msg_header(self.msg_id, msg_type, self.username, self.session)
364
364
@@ -386,7 +386,7 b' class Session(Configurable):'
386 Parameters
386 Parameters
387 ----------
387 ----------
388 msg_list : list
388 msg_list : list
389 The [p_header,p_parent,p_content] part of the message list.
389 The [p_header,p_parent,p_content] part of the message list.
390 """
390 """
391 if self.auth is None:
391 if self.auth is None:
392 return b''
392 return b''
@@ -394,7 +394,7 b' class Session(Configurable):'
394 for m in msg_list:
394 for m in msg_list:
395 h.update(m)
395 h.update(m)
396 return str_to_bytes(h.hexdigest())
396 return str_to_bytes(h.hexdigest())
397
397
398 def serialize(self, msg, ident=None):
398 def serialize(self, msg, ident=None):
399 """Serialize the message components to bytes.
399 """Serialize the message components to bytes.
400
400
@@ -429,12 +429,12 b' class Session(Configurable):'
429 content = content.encode('utf8')
429 content = content.encode('utf8')
430 else:
430 else:
431 raise TypeError("Content incorrect type: %s"%type(content))
431 raise TypeError("Content incorrect type: %s"%type(content))
432
432
433 real_message = [self.pack(msg['header']),
433 real_message = [self.pack(msg['header']),
434 self.pack(msg['parent_header']),
434 self.pack(msg['parent_header']),
435 content
435 content
436 ]
436 ]
437
437
438 to_send = []
438 to_send = []
439
439
440 if isinstance(ident, list):
440 if isinstance(ident, list):
@@ -443,14 +443,14 b' class Session(Configurable):'
443 elif ident is not None:
443 elif ident is not None:
444 to_send.append(ident)
444 to_send.append(ident)
445 to_send.append(DELIM)
445 to_send.append(DELIM)
446
446
447 signature = self.sign(real_message)
447 signature = self.sign(real_message)
448 to_send.append(signature)
448 to_send.append(signature)
449
449
450 to_send.extend(real_message)
450 to_send.extend(real_message)
451
451
452 return to_send
452 return to_send
453
453
454 def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
454 def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
455 buffers=None, subheader=None, track=False, header=None):
455 buffers=None, subheader=None, track=False, header=None):
456 """Build and send a message via stream or socket.
456 """Build and send a message via stream or socket.
@@ -458,21 +458,21 b' class Session(Configurable):'
458 The message format used by this function internally is as follows:
458 The message format used by this function internally is as follows:
459
459
460 [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_content,
460 [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_content,
461 buffer1,buffer2,...]
461 buffer1,buffer2,...]
462
462
463 The serialize/unserialize methods convert the nested message dict into this
463 The serialize/unserialize methods convert the nested message dict into this
464 format.
464 format.
465
465
466 Parameters
466 Parameters
467 ----------
467 ----------
468
468
469 stream : zmq.Socket or ZMQStream
469 stream : zmq.Socket or ZMQStream
470 The socket-like object used to send the data.
470 The socket-like object used to send the data.
471 msg_or_type : str or Message/dict
471 msg_or_type : str or Message/dict
472 Normally, msg_or_type will be a msg_type unless a message is being
472 Normally, msg_or_type will be a msg_type unless a message is being
473 sent more than once. If a header is supplied, this can be set to
473 sent more than once. If a header is supplied, this can be set to
474 None and the msg_type will be pulled from the header.
474 None and the msg_type will be pulled from the header.
475
475
476 content : dict or None
476 content : dict or None
477 The content of the message (ignored if msg_or_type is a message).
477 The content of the message (ignored if msg_or_type is a message).
478 header : dict or None
478 header : dict or None
@@ -490,29 +490,29 b' class Session(Configurable):'
490 track : bool
490 track : bool
491 Whether to track. Only for use with Sockets, because ZMQStream
491 Whether to track. Only for use with Sockets, because ZMQStream
492 objects cannot track messages.
492 objects cannot track messages.
493
493
494 Returns
494 Returns
495 -------
495 -------
496 msg : dict
496 msg : dict
497 The constructed message.
497 The constructed message.
498 (msg,tracker) : (dict, MessageTracker)
498 (msg,tracker) : (dict, MessageTracker)
499 if track=True, then a 2-tuple will be returned,
499 if track=True, then a 2-tuple will be returned,
500 the first element being the constructed
500 the first element being the constructed
501 message, and the second being the MessageTracker
501 message, and the second being the MessageTracker
502
502
503 """
503 """
504
504
505 if not isinstance(stream, (zmq.Socket, ZMQStream)):
505 if not isinstance(stream, (zmq.Socket, ZMQStream)):
506 raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
506 raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
507 elif track and isinstance(stream, ZMQStream):
507 elif track and isinstance(stream, ZMQStream):
508 raise TypeError("ZMQStream cannot track messages")
508 raise TypeError("ZMQStream cannot track messages")
509
509
510 if isinstance(msg_or_type, (Message, dict)):
510 if isinstance(msg_or_type, (Message, dict)):
511 # We got a Message or message dict, not a msg_type so don't
511 # We got a Message or message dict, not a msg_type so don't
512 # build a new Message.
512 # build a new Message.
513 msg = msg_or_type
513 msg = msg_or_type
514 else:
514 else:
515 msg = self.msg(msg_or_type, content=content, parent=parent,
515 msg = self.msg(msg_or_type, content=content, parent=parent,
516 subheader=subheader, header=header)
516 subheader=subheader, header=header)
517
517
518 buffers = [] if buffers is None else buffers
518 buffers = [] if buffers is None else buffers
@@ -540,9 +540,9 b' class Session(Configurable):'
540 pprint.pprint(msg)
540 pprint.pprint(msg)
541 pprint.pprint(to_send)
541 pprint.pprint(to_send)
542 pprint.pprint(buffers)
542 pprint.pprint(buffers)
543
543
544 msg['tracker'] = tracker
544 msg['tracker'] = tracker
545
545
546 return msg
546 return msg
547
547
548 def send_raw(self, stream, msg_list, flags=0, copy=True, ident=None):
548 def send_raw(self, stream, msg_list, flags=0, copy=True, ident=None):
@@ -571,7 +571,7 b' class Session(Configurable):'
571 to_send.append(self.sign(msg_list))
571 to_send.append(self.sign(msg_list))
572 to_send.extend(msg_list)
572 to_send.extend(msg_list)
573 stream.send_multipart(msg_list, flags, copy=copy)
573 stream.send_multipart(msg_list, flags, copy=copy)
574
574
575 def recv(self, socket, mode=zmq.NOBLOCK, content=True, copy=True):
575 def recv(self, socket, mode=zmq.NOBLOCK, content=True, copy=True):
576 """Receive and unpack a message.
576 """Receive and unpack a message.
577
577
@@ -605,21 +605,21 b' class Session(Configurable):'
605 except Exception as e:
605 except Exception as e:
606 # TODO: handle it
606 # TODO: handle it
607 raise e
607 raise e
608
608
609 def feed_identities(self, msg_list, copy=True):
609 def feed_identities(self, msg_list, copy=True):
610 """Split the identities from the rest of the message.
610 """Split the identities from the rest of the message.
611
611
612 Feed until DELIM is reached, then return the prefix as idents and
612 Feed until DELIM is reached, then return the prefix as idents and
613 remainder as msg_list. This is easily broken by setting an IDENT to DELIM,
613 remainder as msg_list. This is easily broken by setting an IDENT to DELIM,
614 but that would be silly.
614 but that would be silly.
615
615
616 Parameters
616 Parameters
617 ----------
617 ----------
618 msg_list : a list of Message or bytes objects
618 msg_list : a list of Message or bytes objects
619 The message to be split.
619 The message to be split.
620 copy : bool
620 copy : bool
621 flag determining whether the arguments are bytes or Messages
621 flag determining whether the arguments are bytes or Messages
622
622
623 Returns
623 Returns
624 -------
624 -------
625 (idents, msg_list) : two lists
625 (idents, msg_list) : two lists
@@ -642,7 +642,7 b' class Session(Configurable):'
642 raise ValueError("DELIM not in msg_list")
642 raise ValueError("DELIM not in msg_list")
643 idents, msg_list = msg_list[:idx], msg_list[idx+1:]
643 idents, msg_list = msg_list[:idx], msg_list[idx+1:]
644 return [m.bytes for m in idents], msg_list
644 return [m.bytes for m in idents], msg_list
645
645
646 def unserialize(self, msg_list, content=True, copy=True):
646 def unserialize(self, msg_list, content=True, copy=True):
647 """Unserialize a msg_list to a nested message dict.
647 """Unserialize a msg_list to a nested message dict.
648
648
@@ -694,7 +694,7 b' class Session(Configurable):'
694 message['content'] = self.unpack(msg_list[3])
694 message['content'] = self.unpack(msg_list[3])
695 else:
695 else:
696 message['content'] = msg_list[3]
696 message['content'] = msg_list[3]
697
697
698 message['buffers'] = msg_list[4:]
698 message['buffers'] = msg_list[4:]
699 return message
699 return message
700
700
@@ -706,10 +706,10 b' def test_msg2obj():'
706 am['y'] = dict(z=1)
706 am['y'] = dict(z=1)
707 ao = Message(am)
707 ao = Message(am)
708 assert ao.y.z == am['y']['z']
708 assert ao.y.z == am['y']['z']
709
709
710 k1, k2 = 'y', 'z'
710 k1, k2 = 'y', 'z'
711 assert ao[k1][k2] == am[k1][k2]
711 assert ao[k1][k2] == am[k1][k2]
712
712
713 am2 = dict(ao)
713 am2 = dict(ao)
714 assert am['x'] == am2['x']
714 assert am['x'] == am2['x']
715 assert am['y']['z'] == am2['y']['z']
715 assert am['y']['z'] == am2['y']['z']
@@ -21,7 +21,7 b' from zmq.eventloop.zmqstream import ZMQStream'
21 from IPython.zmq import session as ss
21 from IPython.zmq import session as ss
22
22
23 class SessionTestCase(BaseZMQTestCase):
23 class SessionTestCase(BaseZMQTestCase):
24
24
25 def setUp(self):
25 def setUp(self):
26 BaseZMQTestCase.setUp(self)
26 BaseZMQTestCase.setUp(self)
27 self.session = ss.Session()
27 self.session = ss.Session()
@@ -43,7 +43,7 b' class MockSocket(zmq.Socket):'
43 return self.data
43 return self.data
44
44
45 class TestSession(SessionTestCase):
45 class TestSession(SessionTestCase):
46
46
47 def test_msg(self):
47 def test_msg(self):
48 """message format"""
48 """message format"""
49 msg = self.session.msg('execute')
49 msg = self.session.msg('execute')
@@ -82,7 +82,7 b' class TestSession(SessionTestCase):'
82 self.assertEquals(new_msg['msg_type'],msg['msg_type'])
82 self.assertEquals(new_msg['msg_type'],msg['msg_type'])
83 self.assertEquals(new_msg['header'],msg['header'])
83 self.assertEquals(new_msg['header'],msg['header'])
84 self.assertEquals(new_msg['content'],msg['content'])
84 self.assertEquals(new_msg['content'],msg['content'])
85 self.assertEquals(new_msg['parent_header'],msg['parent_header'])
85 self.assertEquals(new_msg['parent_header'],msg['parent_header'])
86 self.assertEquals(new_msg['buffers'],[b'bar'])
86 self.assertEquals(new_msg['buffers'],[b'bar'])
87
87
88 socket.data = []
88 socket.data = []
@@ -100,7 +100,7 b' class TestSession(SessionTestCase):'
100 self.assertEquals(new_msg['msg_type'],msg['msg_type'])
100 self.assertEquals(new_msg['msg_type'],msg['msg_type'])
101 self.assertEquals(new_msg['header'],msg['header'])
101 self.assertEquals(new_msg['header'],msg['header'])
102 self.assertEquals(new_msg['content'],msg['content'])
102 self.assertEquals(new_msg['content'],msg['content'])
103 self.assertEquals(new_msg['parent_header'],msg['parent_header'])
103 self.assertEquals(new_msg['parent_header'],msg['parent_header'])
104 self.assertEquals(new_msg['buffers'],[b'bar'])
104 self.assertEquals(new_msg['buffers'],[b'bar'])
105
105
106 socket.data = []
106 socket.data = []
@@ -112,7 +112,7 b' class TestSession(SessionTestCase):'
112 self.assertEquals(new_msg['msg_type'],msg['msg_type'])
112 self.assertEquals(new_msg['msg_type'],msg['msg_type'])
113 self.assertEquals(new_msg['header'],msg['header'])
113 self.assertEquals(new_msg['header'],msg['header'])
114 self.assertEquals(new_msg['content'],msg['content'])
114 self.assertEquals(new_msg['content'],msg['content'])
115 self.assertEquals(new_msg['parent_header'],msg['parent_header'])
115 self.assertEquals(new_msg['parent_header'],msg['parent_header'])
116 self.assertEquals(new_msg['buffers'],[b'bar'])
116 self.assertEquals(new_msg['buffers'],[b'bar'])
117
117
118 socket.close()
118 socket.close()
@@ -123,17 +123,17 b' class TestSession(SessionTestCase):'
123 self.assertTrue(s.pack is ss.default_packer)
123 self.assertTrue(s.pack is ss.default_packer)
124 self.assertTrue(s.unpack is ss.default_unpacker)
124 self.assertTrue(s.unpack is ss.default_unpacker)
125 self.assertEquals(s.username, os.environ.get('USER', u'username'))
125 self.assertEquals(s.username, os.environ.get('USER', u'username'))
126
126
127 s = ss.Session()
127 s = ss.Session()
128 self.assertEquals(s.username, os.environ.get('USER', u'username'))
128 self.assertEquals(s.username, os.environ.get('USER', u'username'))
129
129
130 self.assertRaises(TypeError, ss.Session, pack='hi')
130 self.assertRaises(TypeError, ss.Session, pack='hi')
131 self.assertRaises(TypeError, ss.Session, unpack='hi')
131 self.assertRaises(TypeError, ss.Session, unpack='hi')
132 u = str(uuid.uuid4())
132 u = str(uuid.uuid4())
133 s = ss.Session(username=u'carrot', session=u)
133 s = ss.Session(username=u'carrot', session=u)
134 self.assertEquals(s.session, u)
134 self.assertEquals(s.session, u)
135 self.assertEquals(s.username, u'carrot')
135 self.assertEquals(s.username, u'carrot')
136
136
137 def test_tracking(self):
137 def test_tracking(self):
138 """test tracking messages"""
138 """test tracking messages"""
139 a,b = self.create_bound_pair(zmq.PAIR, zmq.PAIR)
139 a,b = self.create_bound_pair(zmq.PAIR, zmq.PAIR)
@@ -150,26 +150,26 b' class TestSession(SessionTestCase):'
150 self.assertRaises(zmq.NotDone, t.wait, .1)
150 self.assertRaises(zmq.NotDone, t.wait, .1)
151 del M
151 del M
152 t.wait(1) # this will raise
152 t.wait(1) # this will raise
153
153
154
154
155 # def test_rekey(self):
155 # def test_rekey(self):
156 # """rekeying dict around json str keys"""
156 # """rekeying dict around json str keys"""
157 # d = {'0': uuid.uuid4(), 0:uuid.uuid4()}
157 # d = {'0': uuid.uuid4(), 0:uuid.uuid4()}
158 # self.assertRaises(KeyError, ss.rekey, d)
158 # self.assertRaises(KeyError, ss.rekey, d)
159 #
159 #
160 # d = {'0': uuid.uuid4(), 1:uuid.uuid4(), 'asdf':uuid.uuid4()}
160 # d = {'0': uuid.uuid4(), 1:uuid.uuid4(), 'asdf':uuid.uuid4()}
161 # d2 = {0:d['0'],1:d[1],'asdf':d['asdf']}
161 # d2 = {0:d['0'],1:d[1],'asdf':d['asdf']}
162 # rd = ss.rekey(d)
162 # rd = ss.rekey(d)
163 # self.assertEquals(d2,rd)
163 # self.assertEquals(d2,rd)
164 #
164 #
165 # d = {'1.5':uuid.uuid4(),'1':uuid.uuid4()}
165 # d = {'1.5':uuid.uuid4(),'1':uuid.uuid4()}
166 # d2 = {1.5:d['1.5'],1:d['1']}
166 # d2 = {1.5:d['1.5'],1:d['1']}
167 # rd = ss.rekey(d)
167 # rd = ss.rekey(d)
168 # self.assertEquals(d2,rd)
168 # self.assertEquals(d2,rd)
169 #
169 #
170 # d = {'1.0':uuid.uuid4(),'1':uuid.uuid4()}
170 # d = {'1.0':uuid.uuid4(),'1':uuid.uuid4()}
171 # self.assertRaises(KeyError, ss.rekey, d)
171 # self.assertRaises(KeyError, ss.rekey, d)
172 #
172 #
173 def test_unique_msg_ids(self):
173 def test_unique_msg_ids(self):
174 """test that messages receive unique ids"""
174 """test that messages receive unique ids"""
175 ids = set()
175 ids = set()
@@ -178,14 +178,14 b' class TestSession(SessionTestCase):'
178 msg_id = h['msg_id']
178 msg_id = h['msg_id']
179 self.assertTrue(msg_id not in ids)
179 self.assertTrue(msg_id not in ids)
180 ids.add(msg_id)
180 ids.add(msg_id)
181
181
182 def test_feed_identities(self):
182 def test_feed_identities(self):
183 """scrub the front for zmq IDENTITIES"""
183 """scrub the front for zmq IDENTITIES"""
184 theids = "engine client other".split()
184 theids = "engine client other".split()
185 content = dict(code='whoda',stuff=object())
185 content = dict(code='whoda',stuff=object())
186 themsg = self.session.msg('execute',content=content)
186 themsg = self.session.msg('execute',content=content)
187 pmsg = theids
187 pmsg = theids
188
188
189 def test_session_id(self):
189 def test_session_id(self):
190 session = ss.Session()
190 session = ss.Session()
191 # get bs before us
191 # get bs before us
@@ -206,5 +206,5 b' class TestSession(SessionTestCase):'
206 # get us before bs
206 # get us before bs
207 self.assertEquals(session.bsession, session.session.encode('ascii'))
207 self.assertEquals(session.bsession, session.session.encode('ascii'))
208 self.assertEquals(b'stuff', session.bsession)
208 self.assertEquals(b'stuff', session.bsession)
209
209
210
210
@@ -80,7 +80,7 b' class ZMQInteractiveShell(InteractiveShell):'
80
80
81 displayhook_class = Type(ZMQShellDisplayHook)
81 displayhook_class = Type(ZMQShellDisplayHook)
82 display_pub_class = Type(ZMQDisplayPublisher)
82 display_pub_class = Type(ZMQDisplayPublisher)
83
83
84 # Override the traitlet in the parent class, because there's no point using
84 # Override the traitlet in the parent class, because there's no point using
85 # readline for the kernel. Can be removed when the readline code is moved
85 # readline for the kernel. Can be removed when the readline code is moved
86 # to the terminal frontend.
86 # to the terminal frontend.
@@ -89,7 +89,7 b' class ZMQInteractiveShell(InteractiveShell):'
89 # autoindent has no meaning in a zmqshell, and attempting to enable it
89 # autoindent has no meaning in a zmqshell, and attempting to enable it
90 # will print a warning in the absence of readline.
90 # will print a warning in the absence of readline.
91 autoindent = CBool(False)
91 autoindent = CBool(False)
92
92
93 exiter = Instance(ZMQExitAutocall)
93 exiter = Instance(ZMQExitAutocall)
94 def _exiter_default(self):
94 def _exiter_default(self):
95 return ZMQExitAutocall(self)
95 return ZMQExitAutocall(self)
@@ -122,7 +122,7 b' class ZMQInteractiveShell(InteractiveShell):'
122 transformed_input=new,
122 transformed_input=new,
123 )
123 )
124 self.payload_manager.write_payload(payload)
124 self.payload_manager.write_payload(payload)
125
125
126 def ask_exit(self):
126 def ask_exit(self):
127 """Engage the exit actions."""
127 """Engage the exit actions."""
128 payload = dict(
128 payload = dict(
@@ -153,7 +153,7 b' class ZMQInteractiveShell(InteractiveShell):'
153 exc_content[u'status'] = u'error'
153 exc_content[u'status'] = u'error'
154 self._reply_content = exc_content
154 self._reply_content = exc_content
155 # /FIXME
155 # /FIXME
156
156
157 return exc_content
157 return exc_content
158
158
159 #------------------------------------------------------------------------
159 #------------------------------------------------------------------------
@@ -205,7 +205,7 b' class ZMQInteractiveShell(InteractiveShell):'
205 save_dstore('rc_pprint', ptformatter.pprint)
205 save_dstore('rc_pprint', ptformatter.pprint)
206 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
206 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
207 save_dstore('xmode', shell.InteractiveTB.mode)
207 save_dstore('xmode', shell.InteractiveTB.mode)
208
208
209 if mode == False:
209 if mode == False:
210 # turn on
210 # turn on
211 ptformatter.pprint = False
211 ptformatter.pprint = False
@@ -221,7 +221,7 b' class ZMQInteractiveShell(InteractiveShell):'
221 dstore.mode = bool(1-int(mode))
221 dstore.mode = bool(1-int(mode))
222 mode_label = ['OFF','ON'][dstore.mode]
222 mode_label = ['OFF','ON'][dstore.mode]
223 print('Doctest mode is:', mode_label)
223 print('Doctest mode is:', mode_label)
224
224
225 # Send the payload back so that clients can modify their prompt display
225 # Send the payload back so that clients can modify their prompt display
226 payload = dict(
226 payload = dict(
227 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
227 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
@@ -240,7 +240,7 b' class ZMQInteractiveShell(InteractiveShell):'
240
240
241 This command allows you to conveniently edit multi-line code right in
241 This command allows you to conveniently edit multi-line code right in
242 your IPython session.
242 your IPython session.
243
243
244 If called without arguments, %edit opens up an empty editor with a
244 If called without arguments, %edit opens up an empty editor with a
245 temporary file and will execute the contents of this file when you
245 temporary file and will execute the contents of this file when you
246 close it (don't forget to save it!).
246 close it (don't forget to save it!).
@@ -253,7 +253,7 b' class ZMQInteractiveShell(InteractiveShell):'
253 you can configure this by providing your own modified hook if your
253 you can configure this by providing your own modified hook if your
254 favorite editor supports line-number specifications with a different
254 favorite editor supports line-number specifications with a different
255 syntax.
255 syntax.
256
256
257 -p: this will call the editor with the same data as the previous time
257 -p: this will call the editor with the same data as the previous time
258 it was used, regardless of how long ago (in your current session) it
258 it was used, regardless of how long ago (in your current session) it
259 was.
259 was.
@@ -264,7 +264,7 b' class ZMQInteractiveShell(InteractiveShell):'
264 this option is given, the raw input as typed as the command line is
264 this option is given, the raw input as typed as the command line is
265 used instead. When you exit the editor, it will be executed by
265 used instead. When you exit the editor, it will be executed by
266 IPython's own processor.
266 IPython's own processor.
267
267
268 -x: do not execute the edited code immediately upon exit. This is
268 -x: do not execute the edited code immediately upon exit. This is
269 mainly useful if you are editing programs which need to be called with
269 mainly useful if you are editing programs which need to be called with
270 command line arguments, which you can then do using %run.
270 command line arguments, which you can then do using %run.
@@ -319,18 +319,18 b' class ZMQInteractiveShell(InteractiveShell):'
319 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
319 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
320
320
321 We can then call the function foo():
321 We can then call the function foo():
322
322
323 In [2]: foo()
323 In [2]: foo()
324 foo() was defined in an editing session
324 foo() was defined in an editing session
325
325
326 Now we edit foo. IPython automatically loads the editor with the
326 Now we edit foo. IPython automatically loads the editor with the
327 (temporary) file where foo() was previously defined:
327 (temporary) file where foo() was previously defined:
328
328
329 In [3]: ed foo
329 In [3]: ed foo
330 Editing... done. Executing edited code...
330 Editing... done. Executing edited code...
331
331
332 And if we call foo() again we get the modified version:
332 And if we call foo() again we get the modified version:
333
333
334 In [4]: foo()
334 In [4]: foo()
335 foo() has now been changed!
335 foo() has now been changed!
336
336
@@ -356,9 +356,9 b' class ZMQInteractiveShell(InteractiveShell):'
356 hello again
356 hello again
357 Out[7]: "print 'hello again'n"
357 Out[7]: "print 'hello again'n"
358 """
358 """
359
359
360 opts,args = self.parse_options(parameter_s,'prn:')
360 opts,args = self.parse_options(parameter_s,'prn:')
361
361
362 try:
362 try:
363 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
363 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
364 except MacroToEdit as e:
364 except MacroToEdit as e:
@@ -400,7 +400,7 b' class ZMQInteractiveShell(InteractiveShell):'
400 magic_cls = magic_clear
400 magic_cls = magic_clear
401
401
402 # Terminal pagers won't work over pexpect, but we do have our own pager
402 # Terminal pagers won't work over pexpect, but we do have our own pager
403
403
404 def magic_less(self, arg_s):
404 def magic_less(self, arg_s):
405 """Show a file through the pager.
405 """Show a file through the pager.
406
406
@@ -427,12 +427,12 b' class ZMQInteractiveShell(InteractiveShell):'
427 """Show a basic reference about the GUI console."""
427 """Show a basic reference about the GUI console."""
428 from IPython.core.usage import gui_reference
428 from IPython.core.usage import gui_reference
429 page.page(gui_reference, auto_html=True)
429 page.page(gui_reference, auto_html=True)
430
430
431 def set_next_input(self, text):
431 def set_next_input(self, text):
432 """Send the specified text to the frontend to be presented at the next
432 """Send the specified text to the frontend to be presented at the next
433 input cell."""
433 input cell."""
434 payload = dict(
434 payload = dict(
435 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
435 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
436 text=text
436 text=text
437 )
437 )
438 self.payload_manager.write_payload(payload)
438 self.payload_manager.write_payload(payload)
@@ -14,7 +14,7 b' In [6]: %run gui-wx.py'
14
14
15 Ref: Modified from wxPython source code wxPython/samples/simple/simple.py
15 Ref: Modified from wxPython source code wxPython/samples/simple/simple.py
16
16
17 This example can only be run once in a given IPython session because when
17 This example can only be run once in a given IPython session because when
18 the frame is closed, wx goes through its shutdown sequence, killing further
18 the frame is closed, wx goes through its shutdown sequence, killing further
19 attempts. I am sure someone who knows wx can fix this issue.
19 attempts. I am sure someone who knows wx can fix this issue.
20
20
@@ -42,7 +42,7 b' class MyFrame(wx.Frame):'
42 # Create the menubar
42 # Create the menubar
43 menuBar = wx.MenuBar()
43 menuBar = wx.MenuBar()
44
44
45 # and a menu
45 # and a menu
46 menu = wx.Menu()
46 menu = wx.Menu()
47
47
48 # add an item to the menu, using \tKeyName automatically
48 # add an item to the menu, using \tKeyName automatically
@@ -4,7 +4,7 b' Ken Kinder <ken@kenkinder.com>'
4
4
5 Updated for newparallel by Min Ragan-Kelley <benjaminrk@gmail.com>
5 Updated for newparallel by Min Ragan-Kelley <benjaminrk@gmail.com>
6
6
7 This module gives an example of how the task interface to the
7 This module gives an example of how the task interface to the
8 IPython controller works. Before running this script start the IPython controller
8 IPython controller works. Before running this script start the IPython controller
9 and some engines using something like::
9 and some engines using something like::
10
10
@@ -34,42 +34,42 b' def fetchAndParse(url, data=None):'
34 return links
34 return links
35
35
36 class DistributedSpider(object):
36 class DistributedSpider(object):
37
37
38 # Time to wait between polling for task results.
38 # Time to wait between polling for task results.
39 pollingDelay = 0.5
39 pollingDelay = 0.5
40
40
41 def __init__(self, site):
41 def __init__(self, site):
42 self.client = Client()
42 self.client = Client()
43 self.view = self.client.load_balanced_view()
43 self.view = self.client.load_balanced_view()
44 self.mux = self.client[:]
44 self.mux = self.client[:]
45
45
46 self.allLinks = []
46 self.allLinks = []
47 self.linksWorking = {}
47 self.linksWorking = {}
48 self.linksDone = {}
48 self.linksDone = {}
49
49
50 self.site = site
50 self.site = site
51
51
52 def visitLink(self, url):
52 def visitLink(self, url):
53 if url not in self.allLinks:
53 if url not in self.allLinks:
54 self.allLinks.append(url)
54 self.allLinks.append(url)
55 if url.startswith(self.site):
55 if url.startswith(self.site):
56 print ' ', url
56 print ' ', url
57 self.linksWorking[url] = self.view.apply(fetchAndParse, url)
57 self.linksWorking[url] = self.view.apply(fetchAndParse, url)
58
58
59 def onVisitDone(self, links, url):
59 def onVisitDone(self, links, url):
60 print url, ':'
60 print url, ':'
61 self.linksDone[url] = None
61 self.linksDone[url] = None
62 del self.linksWorking[url]
62 del self.linksWorking[url]
63 for link in links:
63 for link in links:
64 self.visitLink(link)
64 self.visitLink(link)
65
65
66 def run(self):
66 def run(self):
67 self.visitLink(self.site)
67 self.visitLink(self.site)
68 while self.linksWorking:
68 while self.linksWorking:
69 print len(self.linksWorking), 'pending...'
69 print len(self.linksWorking), 'pending...'
70 self.synchronize()
70 self.synchronize()
71 time.sleep(self.pollingDelay)
71 time.sleep(self.pollingDelay)
72
72
73 def synchronize(self):
73 def synchronize(self):
74 for url, ar in self.linksWorking.items():
74 for url, ar in self.linksWorking.items():
75 # Calling get_task_result with block=False will return None if the
75 # Calling get_task_result with block=False will return None if the
@@ -196,7 +196,7 b' except error.CompositeError:'
196 try:
196 try:
197 ar.r
197 ar.r
198 except error.CompositeError:
198 except error.CompositeError:
199 print "Caught ZeroDivisionError OK."
199 print "Caught ZeroDivisionError OK."
200
200
201 # push/pull
201 # push/pull
202
202
@@ -50,7 +50,7 b' c = Client(profile=cluster_profile)'
50
50
51 # <markdowncell>
51 # <markdowncell>
52
52
53 # A LoadBalancedView is an interface to the engines that provides dynamic load
53 # A LoadBalancedView is an interface to the engines that provides dynamic load
54 # balancing at the expense of not knowing which engine will execute the code.
54 # balancing at the expense of not knowing which engine will execute the code.
55
55
56 # <codecell>
56 # <codecell>
@@ -115,7 +115,7 b' prices = np.empty(n_strikes*n_sigmas,'
115
115
116 for i, price in enumerate(results):
116 for i, price in enumerate(results):
117 prices[i] = tuple(price)
117 prices[i] = tuple(price)
118
118
119 prices.shape = (n_strikes, n_sigmas)
119 prices.shape = (n_strikes, n_sigmas)
120 strike_mesh, sigma_mesh = np.meshgrid(strike_vals, sigma_vals)
120 strike_mesh, sigma_mesh = np.meshgrid(strike_vals, sigma_vals)
121
121
@@ -12,9 +12,9 b' variable below.'
12 The dataset we have been using for this is the 200 million digit one here:
12 The dataset we have been using for this is the 200 million digit one here:
13 ftp://pi.super-computing.org/.2/pi200m/
13 ftp://pi.super-computing.org/.2/pi200m/
14
14
15 and the files used will be downloaded if they are not in the working directory
15 and the files used will be downloaded if they are not in the working directory
16 of the IPython engines.
16 of the IPython engines.
17 """
17 """
18
18
19 from IPython.parallel import Client
19 from IPython.parallel import Client
20 from matplotlib import pyplot as plt
20 from matplotlib import pyplot as plt
@@ -1,7 +1,7 b''
1 """Compute statistics on the digits of pi.
1 """Compute statistics on the digits of pi.
2
2
3 This uses precomputed digits of pi from the website
3 This uses precomputed digits of pi from the website
4 of Professor Yasumasa Kanada at the University of
4 of Professor Yasumasa Kanada at the University of
5 Tokoyo: http://www.super-computing.org/
5 Tokoyo: http://www.super-computing.org/
6
6
7 Currently, there are only functions to read the
7 Currently, there are only functions to read the
@@ -70,7 +70,7 b' def compute_n_digit_freqs(filename, n):'
70 return freqs
70 return freqs
71
71
72 # Read digits from a txt file
72 # Read digits from a txt file
73
73
74 def txt_file_to_digits(filename, the_type=str):
74 def txt_file_to_digits(filename, the_type=str):
75 """
75 """
76 Yield the digits of pi read from a .txt file.
76 Yield the digits of pi read from a .txt file.
@@ -81,7 +81,7 b' def txt_file_to_digits(filename, the_type=str):'
81 if c != '\n' and c!= ' ':
81 if c != '\n' and c!= ' ':
82 yield the_type(c)
82 yield the_type(c)
83
83
84 # Actual counting functions
84 # Actual counting functions
85
85
86 def one_digit_freqs(digits, normalize=False):
86 def one_digit_freqs(digits, normalize=False):
87 """
87 """
@@ -93,7 +93,7 b' def one_digit_freqs(digits, normalize=False):'
93 if normalize:
93 if normalize:
94 freqs = freqs/freqs.sum()
94 freqs = freqs/freqs.sum()
95 return freqs
95 return freqs
96
96
97 def two_digit_freqs(digits, normalize=False):
97 def two_digit_freqs(digits, normalize=False):
98 """
98 """
99 Consume digits of pi and compute 2 digits freq. counts.
99 Consume digits of pi and compute 2 digits freq. counts.
@@ -2,15 +2,15 b''
2
2
3 The two files plotting_frontend.py and plotting_backend.py go together.
3 The two files plotting_frontend.py and plotting_backend.py go together.
4
4
5 This file (plotting_backend.py) performs the actual computation. For this
5 This file (plotting_backend.py) performs the actual computation. For this
6 example, the computation just generates a set of random numbers that
6 example, the computation just generates a set of random numbers that
7 look like a distribution of particles with 2D position (x,y) and
7 look like a distribution of particles with 2D position (x,y) and
8 momentum (px,py). In a real situation, this file would do some time
8 momentum (px,py). In a real situation, this file would do some time
9 consuming and complicated calculation, and could possibly make calls
9 consuming and complicated calculation, and could possibly make calls
10 to MPI.
10 to MPI.
11
11
12 One important feature is that this script can also be run standalone without
12 One important feature is that this script can also be run standalone without
13 IPython. This is nice as it allows it to be run in more traditional
13 IPython. This is nice as it allows it to be run in more traditional
14 settings where IPython isn't being used.
14 settings where IPython isn't being used.
15
15
16 When used with IPython.parallel, this code is run on the engines. Because this
16 When used with IPython.parallel, this code is run on the engines. Because this
@@ -6,7 +6,7 b''
6
6
7 # <markdowncell>
7 # <markdowncell>
8
8
9 # The eigenvalues of random matrices obey certain statistical laws. Here we construct random matrices
9 # The eigenvalues of random matrices obey certain statistical laws. Here we construct random matrices
10 # from the Gaussian Orthogonal Ensemble (GOE), find their eigenvalues and then investigate the nearest
10 # from the Gaussian Orthogonal Ensemble (GOE), find their eigenvalues and then investigate the nearest
11 # neighbor eigenvalue distribution $\rho(s)$.
11 # neighbor eigenvalue distribution $\rho(s)$.
12
12
@@ -24,7 +24,7 b' from IPython.parallel import Client'
24
24
25 # The Wigner distribution gives the theoretical result for the nearest neighbor eigenvalue distribution
25 # The Wigner distribution gives the theoretical result for the nearest neighbor eigenvalue distribution
26 # for the GOE:
26 # for the GOE:
27 #
27 #
28 # $$\rho(s) = \frac{\pi s}{2} \exp(-\pi s^2/4)$$
28 # $$\rho(s) = \frac{\pi s}{2} \exp(-\pi s^2/4)$$
29
29
30 # <codecell>
30 # <codecell>
@@ -3,7 +3,7 b''
3
3
4 This script submits a set of tasks via a LoadBalancedView. The tasks
4 This script submits a set of tasks via a LoadBalancedView. The tasks
5 are basically just a time.sleep(t), where t is a random number between
5 are basically just a time.sleep(t), where t is a random number between
6 two limits that can be configured at the command line. To run
6 two limits that can be configured at the command line. To run
7 the script there must first be an IPython controller and engines running::
7 the script there must first be an IPython controller and engines running::
8
8
9 ipclusterz start -n 16
9 ipclusterz start -n 16
@@ -12,7 +12,7 b' A good test to run with 16 engines is::'
12
12
13 python task_profiler.py -n 128 -t 0.01 -T 1.0
13 python task_profiler.py -n 128 -t 0.01 -T 1.0
14
14
15 This should show a speedup of 13-14x. The limitation here is that the
15 This should show a speedup of 13-14x. The limitation here is that the
16 overhead of a single task is about 0.001-0.01 seconds.
16 overhead of a single task is about 0.001-0.01 seconds.
17 """
17 """
18 import random, sys
18 import random, sys
@@ -27,19 +27,19 b' def main():'
27 parser.set_defaults(tmin=1e-3)
27 parser.set_defaults(tmin=1e-3)
28 parser.set_defaults(tmax=1)
28 parser.set_defaults(tmax=1)
29 parser.set_defaults(profile='default')
29 parser.set_defaults(profile='default')
30
30
31 parser.add_option("-n", type='int', dest='n',
31 parser.add_option("-n", type='int', dest='n',
32 help='the number of tasks to run')
32 help='the number of tasks to run')
33 parser.add_option("-t", type='float', dest='tmin',
33 parser.add_option("-t", type='float', dest='tmin',
34 help='the minimum task length in seconds')
34 help='the minimum task length in seconds')
35 parser.add_option("-T", type='float', dest='tmax',
35 parser.add_option("-T", type='float', dest='tmax',
36 help='the maximum task length in seconds')
36 help='the maximum task length in seconds')
37 parser.add_option("-p", '--profile', type='str', dest='profile',
37 parser.add_option("-p", '--profile', type='str', dest='profile',
38 help="the cluster profile [default: 'default']")
38 help="the cluster profile [default: 'default']")
39
39
40 (opts, args) = parser.parse_args()
40 (opts, args) = parser.parse_args()
41 assert opts.tmax >= opts.tmin, "tmax must not be smaller than tmin"
41 assert opts.tmax >= opts.tmin, "tmax must not be smaller than tmin"
42
42
43 rc = Client()
43 rc = Client()
44 view = rc.load_balanced_view()
44 view = rc.load_balanced_view()
45 print view
45 print view
@@ -51,7 +51,7 b' def main():'
51 # the jobs should take a random time within a range
51 # the jobs should take a random time within a range
52 times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)]
52 times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)]
53 stime = sum(times)
53 stime = sum(times)
54
54
55 print "executing %i tasks, totalling %.1f secs on %i engines"%(opts.n, stime, nengines)
55 print "executing %i tasks, totalling %.1f secs on %i engines"%(opts.n, stime, nengines)
56 time.sleep(1)
56 time.sleep(1)
57 start = time.time()
57 start = time.time()
@@ -61,7 +61,7 b' def main():'
61
61
62 ptime = stop-start
62 ptime = stop-start
63 scale = stime/ptime
63 scale = stime/ptime
64
64
65 print "executed %.1f secs in %.1f secs"%(stime, ptime)
65 print "executed %.1f secs in %.1f secs"%(stime, ptime)
66 print "%.3fx parallel performance on %i engines"%(scale, nengines)
66 print "%.3fx parallel performance on %i engines"%(scale, nengines)
67 print "%.1f%% of theoretical max"%(100*scale/nengines)
67 print "%.1f%% of theoretical max"%(100*scale/nengines)
@@ -11,7 +11,7 b' An example of running the program is (8 processors, 4x2 partition,'
11 $ ipclusterz start --profile mpi -n 8 # start 8 engines (assuming mpi profile has been configured)
11 $ ipclusterz start --profile mpi -n 8 # start 8 engines (assuming mpi profile has been configured)
12 $ ./parallelwave-mpi.py --grid 400 100 --partition 4 2 --profile mpi
12 $ ./parallelwave-mpi.py --grid 400 100 --partition 4 2 --profile mpi
13
13
14 See also parallelwave-mpi, which runs the same program, but uses MPI
14 See also parallelwave-mpi, which runs the same program, but uses MPI
15 (via mpi4py) for the inter-engine communication.
15 (via mpi4py) for the inter-engine communication.
16
16
17 Authors
17 Authors
@@ -50,11 +50,11 b' def wave_saver(u, x, y, t):'
50 global t_hist
50 global t_hist
51 t_hist.append(t)
51 t_hist.append(t)
52 u_hist.append(1.0*u)
52 u_hist.append(1.0*u)
53
53
54
54
55 # main program:
55 # main program:
56 if __name__ == '__main__':
56 if __name__ == '__main__':
57
57
58 parser = argparse.ArgumentParser()
58 parser = argparse.ArgumentParser()
59 paa = parser.add_argument
59 paa = parser.add_argument
60 paa('--grid', '-g',
60 paa('--grid', '-g',
@@ -75,16 +75,16 b" if __name__ == '__main__':"
75 paa('-t', '--tstop',
75 paa('-t', '--tstop',
76 type=float, default=1.,
76 type=float, default=1.,
77 help="Time units to run")
77 help="Time units to run")
78 paa('--profile',
78 paa('--profile',
79 type=unicode, default=u'default',
79 type=unicode, default=u'default',
80 help="Specify the ipcluster profile for the client to connect to.")
80 help="Specify the ipcluster profile for the client to connect to.")
81 paa('--save',
81 paa('--save',
82 action='store_true',
82 action='store_true',
83 help="Add this flag to save the time/wave history during the run.")
83 help="Add this flag to save the time/wave history during the run.")
84 paa('--scalar',
84 paa('--scalar',
85 action='store_true',
85 action='store_true',
86 help="Also run with scalar interior implementation, to see vector speedup.")
86 help="Also run with scalar interior implementation, to see vector speedup.")
87
87
88 ns = parser.parse_args()
88 ns = parser.parse_args()
89 # set up arguments
89 # set up arguments
90 grid = ns.grid
90 grid = ns.grid
@@ -97,22 +97,22 b" if __name__ == '__main__':"
97 user_action = wave_saver
97 user_action = wave_saver
98 else:
98 else:
99 user_action = None
99 user_action = None
100
100
101 num_cells = 1.0*(grid[0]-1)*(grid[1]-1)
101 num_cells = 1.0*(grid[0]-1)*(grid[1]-1)
102 final_test = True
102 final_test = True
103
103
104 # create the Client
104 # create the Client
105 rc = Client(profile=ns.profile)
105 rc = Client(profile=ns.profile)
106 num_procs = len(rc.ids)
106 num_procs = len(rc.ids)
107
107
108 if partition is None:
108 if partition is None:
109 partition = [1,num_procs]
109 partition = [1,num_procs]
110
110
111 assert partition[0]*partition[1] == num_procs, "can't map partition %s to %i engines"%(partition, num_procs)
111 assert partition[0]*partition[1] == num_procs, "can't map partition %s to %i engines"%(partition, num_procs)
112
112
113 view = rc[:]
113 view = rc[:]
114 print "Running %s system on %s processes until %f"%(grid, partition, tstop)
114 print "Running %s system on %s processes until %f"%(grid, partition, tstop)
115
115
116 # functions defining initial/boundary/source conditions
116 # functions defining initial/boundary/source conditions
117 def I(x,y):
117 def I(x,y):
118 from numpy import exp
118 from numpy import exp
@@ -123,28 +123,28 b" if __name__ == '__main__':"
123 # return 10*exp(-(x - sin(100*t))**2)
123 # return 10*exp(-(x - sin(100*t))**2)
124 def bc(x,y,t):
124 def bc(x,y,t):
125 return 0.0
125 return 0.0
126
126
127 # initial imports, setup rank
127 # initial imports, setup rank
128 view.execute('\n'.join([
128 view.execute('\n'.join([
129 "from mpi4py import MPI",
129 "from mpi4py import MPI",
130 "import numpy",
130 "import numpy",
131 "mpi = MPI.COMM_WORLD",
131 "mpi = MPI.COMM_WORLD",
132 "my_id = MPI.COMM_WORLD.Get_rank()"]), block=True)
132 "my_id = MPI.COMM_WORLD.Get_rank()"]), block=True)
133
133
134 # initialize t_hist/u_hist for saving the state at each step (optional)
134 # initialize t_hist/u_hist for saving the state at each step (optional)
135 view['t_hist'] = []
135 view['t_hist'] = []
136 view['u_hist'] = []
136 view['u_hist'] = []
137
137
138 # set vector/scalar implementation details
138 # set vector/scalar implementation details
139 impl = {}
139 impl = {}
140 impl['ic'] = 'vectorized'
140 impl['ic'] = 'vectorized'
141 impl['inner'] = 'scalar'
141 impl['inner'] = 'scalar'
142 impl['bc'] = 'vectorized'
142 impl['bc'] = 'vectorized'
143
143
144 # execute some files so that the classes we need will be defined on the engines:
144 # execute some files so that the classes we need will be defined on the engines:
145 view.run('RectPartitioner.py')
145 view.run('RectPartitioner.py')
146 view.run('wavesolver.py')
146 view.run('wavesolver.py')
147
147
148 # setup remote partitioner
148 # setup remote partitioner
149 # note that Reference means that the argument passed to setup_partitioner will be the
149 # note that Reference means that the argument passed to setup_partitioner will be the
150 # object named 'my_id' in the engine's namespace
150 # object named 'my_id' in the engine's namespace
@@ -156,7 +156,7 b" if __name__ == '__main__':"
156
156
157 # lambda for calling solver.solve:
157 # lambda for calling solver.solve:
158 _solve = lambda *args, **kwargs: solver.solve(*args, **kwargs)
158 _solve = lambda *args, **kwargs: solver.solve(*args, **kwargs)
159
159
160 if ns.scalar:
160 if ns.scalar:
161 impl['inner'] = 'scalar'
161 impl['inner'] = 'scalar'
162 # run first with element-wise Python operations for each cell
162 # run first with element-wise Python operations for each cell
@@ -171,12 +171,12 b" if __name__ == '__main__':"
171 norm = -1
171 norm = -1
172 t1 = time.time()
172 t1 = time.time()
173 print 'scalar inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
173 print 'scalar inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
174
174
175 impl['inner'] = 'vectorized'
175 impl['inner'] = 'vectorized'
176 # setup new solvers
176 # setup new solvers
177 view.apply_sync(setup_solver, I,f,c,bc,Lx,Ly,partitioner=Reference('partitioner'), dt=0,implementation=impl)
177 view.apply_sync(setup_solver, I,f,c,bc,Lx,Ly,partitioner=Reference('partitioner'), dt=0,implementation=impl)
178 view.execute('mpi.barrier()')
178 view.execute('mpi.barrier()')
179
179
180 # run again with numpy vectorized inner-implementation
180 # run again with numpy vectorized inner-implementation
181 t0 = time.time()
181 t0 = time.time()
182 ar = view.apply_async(_solve, tstop, dt=0, verbose=True, final_test=final_test)#, user_action=wave_saver)
182 ar = view.apply_async(_solve, tstop, dt=0, verbose=True, final_test=final_test)#, user_action=wave_saver)
@@ -189,7 +189,7 b" if __name__ == '__main__':"
189 norm = -1
189 norm = -1
190 t1 = time.time()
190 t1 = time.time()
191 print 'vector inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
191 print 'vector inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
192
192
193 # if ns.save is True, then u_hist stores the history of u as a list
193 # if ns.save is True, then u_hist stores the history of u as a list
194 # If the partion scheme is Nx1, then u can be reconstructed via 'gather':
194 # If the partion scheme is Nx1, then u can be reconstructed via 'gather':
195 if ns.save and partition[-1] == 1:
195 if ns.save and partition[-1] == 1:
@@ -11,7 +11,7 b' An example of running the program is (8 processors, 4x2 partition,'
11 $ ipclusterz start -n 8 # start 8 engines
11 $ ipclusterz start -n 8 # start 8 engines
12 $ ./parallelwave.py --grid 200 200 --partition 4 2
12 $ ./parallelwave.py --grid 200 200 --partition 4 2
13
13
14 See also parallelwave-mpi, which runs the same program, but uses MPI
14 See also parallelwave-mpi, which runs the same program, but uses MPI
15 (via mpi4py) for the inter-engine communication.
15 (via mpi4py) for the inter-engine communication.
16
16
17 Authors
17 Authors
@@ -50,11 +50,11 b' def wave_saver(u, x, y, t):'
50 global t_hist
50 global t_hist
51 t_hist.append(t)
51 t_hist.append(t)
52 u_hist.append(1.0*u)
52 u_hist.append(1.0*u)
53
53
54
54
55 # main program:
55 # main program:
56 if __name__ == '__main__':
56 if __name__ == '__main__':
57
57
58 parser = argparse.ArgumentParser()
58 parser = argparse.ArgumentParser()
59 paa = parser.add_argument
59 paa = parser.add_argument
60 paa('--grid', '-g',
60 paa('--grid', '-g',
@@ -75,16 +75,16 b" if __name__ == '__main__':"
75 paa('-t', '--tstop',
75 paa('-t', '--tstop',
76 type=float, default=1.,
76 type=float, default=1.,
77 help="Time units to run")
77 help="Time units to run")
78 paa('--profile',
78 paa('--profile',
79 type=unicode, default=u'default',
79 type=unicode, default=u'default',
80 help="Specify the ipcluster profile for the client to connect to.")
80 help="Specify the ipcluster profile for the client to connect to.")
81 paa('--save',
81 paa('--save',
82 action='store_true',
82 action='store_true',
83 help="Add this flag to save the time/wave history during the run.")
83 help="Add this flag to save the time/wave history during the run.")
84 paa('--scalar',
84 paa('--scalar',
85 action='store_true',
85 action='store_true',
86 help="Also run with scalar interior implementation, to see vector speedup.")
86 help="Also run with scalar interior implementation, to see vector speedup.")
87
87
88 ns = parser.parse_args()
88 ns = parser.parse_args()
89 # set up arguments
89 # set up arguments
90 grid = ns.grid
90 grid = ns.grid
@@ -97,25 +97,25 b" if __name__ == '__main__':"
97 user_action = wave_saver
97 user_action = wave_saver
98 else:
98 else:
99 user_action = None
99 user_action = None
100
100
101 num_cells = 1.0*(grid[0]-1)*(grid[1]-1)
101 num_cells = 1.0*(grid[0]-1)*(grid[1]-1)
102 final_test = True
102 final_test = True
103
103
104 # create the Client
104 # create the Client
105 rc = Client(profile=ns.profile)
105 rc = Client(profile=ns.profile)
106 num_procs = len(rc.ids)
106 num_procs = len(rc.ids)
107
107
108 if partition is None:
108 if partition is None:
109 partition = [num_procs,1]
109 partition = [num_procs,1]
110 else:
110 else:
111 num_procs = min(num_procs, partition[0]*partition[1])
111 num_procs = min(num_procs, partition[0]*partition[1])
112
112
113 assert partition[0]*partition[1] == num_procs, "can't map partition %s to %i engines"%(partition, num_procs)
113 assert partition[0]*partition[1] == num_procs, "can't map partition %s to %i engines"%(partition, num_procs)
114
114
115 # construct the View:
115 # construct the View:
116 view = rc[:num_procs]
116 view = rc[:num_procs]
117 print "Running %s system on %s processes until %f"%(grid, partition, tstop)
117 print "Running %s system on %s processes until %f"%(grid, partition, tstop)
118
118
119 # functions defining initial/boundary/source conditions
119 # functions defining initial/boundary/source conditions
120 def I(x,y):
120 def I(x,y):
121 from numpy import exp
121 from numpy import exp
@@ -126,7 +126,7 b" if __name__ == '__main__':"
126 # return 10*exp(-(x - sin(100*t))**2)
126 # return 10*exp(-(x - sin(100*t))**2)
127 def bc(x,y,t):
127 def bc(x,y,t):
128 return 0.0
128 return 0.0
129
129
130 # initialize t_hist/u_hist for saving the state at each step (optional)
130 # initialize t_hist/u_hist for saving the state at each step (optional)
131 view['t_hist'] = []
131 view['t_hist'] = []
132 view['u_hist'] = []
132 view['u_hist'] = []
@@ -136,16 +136,16 b" if __name__ == '__main__':"
136 impl['ic'] = 'vectorized'
136 impl['ic'] = 'vectorized'
137 impl['inner'] = 'scalar'
137 impl['inner'] = 'scalar'
138 impl['bc'] = 'vectorized'
138 impl['bc'] = 'vectorized'
139
139
140 # execute some files so that the classes we need will be defined on the engines:
140 # execute some files so that the classes we need will be defined on the engines:
141 view.execute('import numpy')
141 view.execute('import numpy')
142 view.run('communicator.py')
142 view.run('communicator.py')
143 view.run('RectPartitioner.py')
143 view.run('RectPartitioner.py')
144 view.run('wavesolver.py')
144 view.run('wavesolver.py')
145
145
146 # scatter engine IDs
146 # scatter engine IDs
147 view.scatter('my_id', range(num_procs), flatten=True)
147 view.scatter('my_id', range(num_procs), flatten=True)
148
148
149 # create the engine connectors
149 # create the engine connectors
150 view.execute('com = EngineCommunicator()')
150 view.execute('com = EngineCommunicator()')
151
151
@@ -154,7 +154,7 b" if __name__ == '__main__':"
154 peers = ar.get_dict()
154 peers = ar.get_dict()
155 # print peers
155 # print peers
156 # this is a dict, keyed by engine ID, of the connection info for the EngineCommunicators
156 # this is a dict, keyed by engine ID, of the connection info for the EngineCommunicators
157
157
158 # setup remote partitioner
158 # setup remote partitioner
159 # note that Reference means that the argument passed to setup_partitioner will be the
159 # note that Reference means that the argument passed to setup_partitioner will be the
160 # object named 'com' in the engine's namespace
160 # object named 'com' in the engine's namespace
@@ -180,14 +180,14 b" if __name__ == '__main__':"
180 norm = -1
180 norm = -1
181 t1 = time.time()
181 t1 = time.time()
182 print 'scalar inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
182 print 'scalar inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
183
183
184 # run again with faster numpy-vectorized inner implementation:
184 # run again with faster numpy-vectorized inner implementation:
185 impl['inner'] = 'vectorized'
185 impl['inner'] = 'vectorized'
186 # setup remote solvers
186 # setup remote solvers
187 view.apply_sync(setup_solver, I,f,c,bc,Lx,Ly,partitioner=Reference('partitioner'), dt=0,implementation=impl)
187 view.apply_sync(setup_solver, I,f,c,bc,Lx,Ly,partitioner=Reference('partitioner'), dt=0,implementation=impl)
188
188
189 t0 = time.time()
189 t0 = time.time()
190
190
191 ar = view.apply_async(_solve, tstop, dt=0, verbose=True, final_test=final_test)#, user_action=wave_saver)
191 ar = view.apply_async(_solve, tstop, dt=0, verbose=True, final_test=final_test)#, user_action=wave_saver)
192 if final_test:
192 if final_test:
193 # this sum is performed element-wise as results finish
193 # this sum is performed element-wise as results finish
@@ -198,7 +198,7 b" if __name__ == '__main__':"
198 norm = -1
198 norm = -1
199 t1 = time.time()
199 t1 = time.time()
200 print 'vector inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
200 print 'vector inner-version, Wtime=%g, norm=%g'%(t1-t0, norm)
201
201
202 # if ns.save is True, then u_hist stores the history of u as a list
202 # if ns.save is True, then u_hist stores the history of u as a list
203 # If the partion scheme is Nx1, then u can be reconstructed via 'gather':
203 # If the partion scheme is Nx1, then u can be reconstructed via 'gather':
204 if ns.save and partition[-1] == 1:
204 if ns.save and partition[-1] == 1:
@@ -40,7 +40,7 b' class WaveSolver(object):'
40 tstop is the stop time for the simulation.
40 tstop is the stop time for the simulation.
41
41
42 I, f are functions: I(x,y), f(x,y,t)
42 I, f are functions: I(x,y), f(x,y,t)
43
43
44 user_action: function of (u, x, y, t) called at each time
44 user_action: function of (u, x, y, t) called at each time
45 level (x and y are one-dimensional coordinate vectors).
45 level (x and y are one-dimensional coordinate vectors).
46 This function allows the calling code to plot the solution,
46 This function allows the calling code to plot the solution,
@@ -67,13 +67,13 b' class WaveSolver(object):'
67 final_test: true means the discrete L2-norm of the final solution is
67 final_test: true means the discrete L2-norm of the final solution is
68 to be computed.
68 to be computed.
69 """
69 """
70
70
71 def __init__(self, I, f, c, bc, Lx, Ly, partitioner=None, dt=-1,
71 def __init__(self, I, f, c, bc, Lx, Ly, partitioner=None, dt=-1,
72 user_action=None,
72 user_action=None,
73 implementation={'ic': 'vectorized', # or 'scalar'
73 implementation={'ic': 'vectorized', # or 'scalar'
74 'inner': 'vectorized',
74 'inner': 'vectorized',
75 'bc': 'vectorized'}):
75 'bc': 'vectorized'}):
76
76
77 nx = partitioner.global_num_cells[0] # number of global cells in x dir
77 nx = partitioner.global_num_cells[0] # number of global cells in x dir
78 ny = partitioner.global_num_cells[1] # number of global cells in y dir
78 ny = partitioner.global_num_cells[1] # number of global cells in y dir
79 dx = Lx/float(nx)
79 dx = Lx/float(nx)
@@ -130,7 +130,7 b' class WaveSolver(object):'
130 u_2[i,j] = u_1[i,j] + \
130 u_2[i,j] = u_1[i,j] + \
131 0.5*Cx2*(u_1[i-1,j] - 2*u_1[i,j] + u_1[i+1,j]) + \
131 0.5*Cx2*(u_1[i-1,j] - 2*u_1[i,j] + u_1[i+1,j]) + \
132 0.5*Cy2*(u_1[i,j-1] - 2*u_1[i,j] + u_1[i,j+1]) + \
132 0.5*Cy2*(u_1[i,j-1] - 2*u_1[i,j] + u_1[i,j+1]) + \
133 dt2*f(x[i], y[j], 0.0)
133 dt2*f(x[i], y[j], 0.0)
134
134
135 # boundary values of u_2 (equals u(t=dt) due to du/dt=0)
135 # boundary values of u_2 (equals u(t=dt) due to du/dt=0)
136 i = 0
136 i = 0
@@ -162,8 +162,8 b' class WaveSolver(object):'
162 user_action(u_1, x, y, t) # allow user to plot etc.
162 user_action(u_1, x, y, t) # allow user to plot etc.
163 # print list(self.us[2][2])
163 # print list(self.us[2][2])
164 self.us = (u,u_1,u_2)
164 self.us = (u,u_1,u_2)
165
165
166
166
167 def solve(self, tstop, dt=-1, user_action=None, verbose=False, final_test=False):
167 def solve(self, tstop, dt=-1, user_action=None, verbose=False, final_test=False):
168 t0=time.time()
168 t0=time.time()
169 f=self.f
169 f=self.f
@@ -191,7 +191,7 b' class WaveSolver(object):'
191 upper_y_neigh = partitioner.upper_neighbors[1]
191 upper_y_neigh = partitioner.upper_neighbors[1]
192 u,u_1,u_2 = self.us
192 u,u_1,u_2 = self.us
193 # u_1 = self.u_1
193 # u_1 = self.u_1
194
194
195 t = 0.0
195 t = 0.0
196 while t <= tstop:
196 while t <= tstop:
197 t_old = t; t += dt
197 t_old = t; t += dt
@@ -211,7 +211,7 b' class WaveSolver(object):'
211 Cx2*(u_1[0:nx-1,1:ny] - 2*u_1[1:nx,1:ny] + u_1[2:nx+1,1:ny]) + \
211 Cx2*(u_1[0:nx-1,1:ny] - 2*u_1[1:nx,1:ny] + u_1[2:nx+1,1:ny]) + \
212 Cy2*(u_1[1:nx,0:ny-1] - 2*u_1[1:nx,1:ny] + u_1[1:nx,2:ny+1]) + \
212 Cy2*(u_1[1:nx,0:ny-1] - 2*u_1[1:nx,1:ny] + u_1[1:nx,2:ny+1]) + \
213 dt2*f(xv[1:nx,1:ny], yv[1:nx,1:ny], t_old)
213 dt2*f(xv[1:nx,1:ny], yv[1:nx,1:ny], t_old)
214
214
215 # insert boundary conditions (if there's no neighbor):
215 # insert boundary conditions (if there's no neighbor):
216 if lower_x_neigh < 0:
216 if lower_x_neigh < 0:
217 if implementation['bc'] == 'scalar':
217 if implementation['bc'] == 'scalar':
@@ -63,14 +63,14 b' def rescale_arr(arr,amin,amax):'
63 >>> rescale_arr(a,3,6)
63 >>> rescale_arr(a,3,6)
64 array([ 3. , 3.75, 4.5 , 5.25, 6. ])
64 array([ 3. , 3.75, 4.5 , 5.25, 6. ])
65 """
65 """
66
66
67 # old bounds
67 # old bounds
68 m = arr.min()
68 m = arr.min()
69 M = arr.max()
69 M = arr.max()
70 # scale/offset
70 # scale/offset
71 s = float(amax-amin)/(M-m)
71 s = float(amax-amin)/(M-m)
72 d = amin - s*m
72 d = amin - s*m
73
73
74 # Apply clip before returning to cut off possible overflows outside the
74 # Apply clip before returning to cut off possible overflows outside the
75 # intended range due to roundoff error, so that we can absolutely guarantee
75 # intended range due to roundoff error, so that we can absolutely guarantee
76 # that on output, there are no values > amax or < amin.
76 # that on output, there are no values > amax or < amin.
@@ -98,7 +98,7 b' def text_cleanup(text, min_length=3,'
98 """
98 """
99 return [w for w in text.lower().split()
99 return [w for w in text.lower().split()
100 if len(w)>=min_length and w not in remove]
100 if len(w)>=min_length and w not in remove]
101
101
102
102
103 def print_vk(lst):
103 def print_vk(lst):
104 """Print a list of value/key pairs nicely formatted in key/value order."""
104 """Print a list of value/key pairs nicely formatted in key/value order."""
@@ -120,7 +120,7 b' def word_freq(text):'
120
120
121 freqs = {}
121 freqs = {}
122 for word in text:
122 for word in text:
123 freqs[word] = freqs.get(word, 0) + 1
123 freqs[word] = freqs.get(word, 0) + 1
124 return freqs
124 return freqs
125
125
126
126
@@ -131,7 +131,7 b' def sort_freqs(freqs):'
131 ----------
131 ----------
132 freqs : dict
132 freqs : dict
133 A dict with string keys and integer values.
133 A dict with string keys and integer values.
134
134
135 Return
135 Return
136 ------
136 ------
137 items : list
137 items : list
@@ -155,7 +155,7 b' def summarize_freq_hist(freqs, n=10):'
155 freqs : dict or list
155 freqs : dict or list
156 Word frequencies, represented either as a dict of word->count, or as a
156 Word frequencies, represented either as a dict of word->count, or as a
157 list of count->word pairs.
157 list of count->word pairs.
158
158
159 n : int
159 n : int
160 The number of least/most frequent words to print.
160 The number of least/most frequent words to print.
161 """
161 """
@@ -199,7 +199,7 b' def get_text_from_url(url):'
199
199
200 def co_occurrences(lines, words):
200 def co_occurrences(lines, words):
201 """Return histogram of co-occurrences of words in a list of lines.
201 """Return histogram of co-occurrences of words in a list of lines.
202
202
203 Parameters
203 Parameters
204 ----------
204 ----------
205 lines : list
205 lines : list
@@ -210,7 +210,7 b' def co_occurrences(lines, words):'
210 searched for co-occurrences.
210 searched for co-occurrences.
211 """
211 """
212 wpairs = all_pairs(words)
212 wpairs = all_pairs(words)
213
213
214 # Now build histogram of co-occurrences
214 # Now build histogram of co-occurrences
215 co_occur = {}
215 co_occur = {}
216 for w1, w2 in wpairs:
216 for w1, w2 in wpairs:
@@ -244,7 +244,7 b' def plot_graph(wgraph, pos=None):'
244 sizes.append(d['count'])
244 sizes.append(d['count'])
245 degrees.append(wgraph.degree(n))
245 degrees.append(wgraph.degree(n))
246 sizes = rescale_arr(np.array(sizes, dtype=float), 100, 1000)
246 sizes = rescale_arr(np.array(sizes, dtype=float), 100, 1000)
247
247
248 # Compute layout and label edges according to weight
248 # Compute layout and label edges according to weight
249 pos = nx.spring_layout(wgraph) if pos is None else pos
249 pos = nx.spring_layout(wgraph) if pos is None else pos
250 labels = {}
250 labels = {}
@@ -256,7 +256,7 b' def plot_graph(wgraph, pos=None):'
256
256
257 # remap width to 1-10 range
257 # remap width to 1-10 range
258 width = rescale_arr(np.array(width, dtype=float), 1, 15)
258 width = rescale_arr(np.array(width, dtype=float), 1, 15)
259
259
260 # Create figure
260 # Create figure
261 fig = plt.figure()
261 fig = plt.figure()
262 ax = fig.add_subplot(111)
262 ax = fig.add_subplot(111)
@@ -283,7 +283,7 b' def plot_word_histogram(freqs, show=10, title=None):'
283 # interpret as a fraction
283 # interpret as a fraction
284 start = -int(round(show*len(freqs)))
284 start = -int(round(show*len(freqs)))
285 show_f = sorted_f[start:]
285 show_f = sorted_f[start:]
286
286
287 # Now, extract words and counts, plot
287 # Now, extract words and counts, plot
288 n_words = len(show_f)
288 n_words = len(show_f)
289 ind = np.arange(n_words)
289 ind = np.arange(n_words)
@@ -329,48 +329,48 b' def summarize_centrality(centrality):'
329 # # Configure user variables here
329 # # Configure user variables here
330 # # Specify the url (can be a local file path) of the text file to analyze.
330 # # Specify the url (can be a local file path) of the text file to analyze.
331 # # If not given, it's read from the command line as the first argument
331 # # If not given, it's read from the command line as the first argument
332 #
332 #
333 # # 11226 titles of recent articles in arxiv/math/prob
333 # # 11226 titles of recent articles in arxiv/math/prob
334 # default_url = "http://bibserver.berkeley.edu/tmp/titles.txt"
334 # default_url = "http://bibserver.berkeley.edu/tmp/titles.txt"
335 # # Number of words to display in detailed histogram
335 # # Number of words to display in detailed histogram
336 # n_words = 15
336 # n_words = 15
337 # # Number of words to use as nodes for co-occurrence graph.
337 # # Number of words to use as nodes for co-occurrence graph.
338 # n_nodes = 15
338 # n_nodes = 15
339 #
339 #
340 # # End of user configuration
340 # # End of user configuration
341 #
341 #
342 # # Actual code starts here
342 # # Actual code starts here
343 # try:
343 # try:
344 # url = sys.argv[1]
344 # url = sys.argv[1]
345 # except IndexError:
345 # except IndexError:
346 # url = default_url
346 # url = default_url
347 #
347 #
348 # # Fetch text and do basic preprocessing
348 # # Fetch text and do basic preprocessing
349 # text = get_text_from_url(url).lower()
349 # text = get_text_from_url(url).lower()
350 # lines = text.splitlines()
350 # lines = text.splitlines()
351 # words = text_cleanup(text)
351 # words = text_cleanup(text)
352 #
352 #
353 # # Compute frequency histogram
353 # # Compute frequency histogram
354 # wf = word_freq(words)
354 # wf = word_freq(words)
355 # sorted_wf = sort_freqs(wf)
355 # sorted_wf = sort_freqs(wf)
356 #
356 #
357 # # Build a graph from the n_nodes most frequent words
357 # # Build a graph from the n_nodes most frequent words
358 # popular = sorted_wf[-n_nodes:]
358 # popular = sorted_wf[-n_nodes:]
359 # pop_words = [wc[0] for wc in popular]
359 # pop_words = [wc[0] for wc in popular]
360 # co_occur = co_occurrences(lines, pop_words)
360 # co_occur = co_occurrences(lines, pop_words)
361 # wgraph = co_occurrences_graph(popular, co_occur, cutoff=1)
361 # wgraph = co_occurrences_graph(popular, co_occur, cutoff=1)
362 # centrality = nx.eigenvector_centrality_numpy(wgraph)
362 # centrality = nx.eigenvector_centrality_numpy(wgraph)
363 #
363 #
364 # # Print summaries of single-word frequencies and graph structure
364 # # Print summaries of single-word frequencies and graph structure
365 # summarize_freq_hist(sorted_wf)
365 # summarize_freq_hist(sorted_wf)
366 # summarize_centrality(centrality)
366 # summarize_centrality(centrality)
367 #
367 #
368 # # Plot histogram and graph
368 # # Plot histogram and graph
369 # plt.close('all')
369 # plt.close('all')
370 # plot_word_histogram(sorted_wf, n_words,
370 # plot_word_histogram(sorted_wf, n_words,
371 # "Frequencies for %s most frequent words" % n_words)
371 # "Frequencies for %s most frequent words" % n_words)
372 # plot_word_histogram(sorted_wf, 1.0, "Frequencies for entire word list")
372 # plot_word_histogram(sorted_wf, 1.0, "Frequencies for entire word list")
373 # plot_graph(wgraph)
373 # plot_graph(wgraph)
374 #
374 #
375 # # Display figures
375 # # Display figures
376 # plt.show()
376 # plt.show()
@@ -42,7 +42,7 b' extensions = ['
42 'sphinx.ext.autodoc',
42 'sphinx.ext.autodoc',
43 'sphinx.ext.doctest',
43 'sphinx.ext.doctest',
44 'inheritance_diagram',
44 'inheritance_diagram',
45 'ipython_console_highlighting',
45 'ipython_console_highlighting',
46 'numpydoc', # to preprocess docstrings
46 'numpydoc', # to preprocess docstrings
47 ]
47 ]
48
48
@@ -168,7 +168,7 b' class ApiDocWriter(object):'
168 functions, classes = self._parse_lines(f)
168 functions, classes = self._parse_lines(f)
169 f.close()
169 f.close()
170 return functions, classes
170 return functions, classes
171
171
172 def _parse_lines(self, linesource):
172 def _parse_lines(self, linesource):
173 ''' Parse lines of text for functions and classes '''
173 ''' Parse lines of text for functions and classes '''
174 functions = []
174 functions = []
@@ -210,9 +210,9 b' class ApiDocWriter(object):'
210 return ''
210 return ''
211
211
212 # Make a shorter version of the uri that omits the package name for
212 # Make a shorter version of the uri that omits the package name for
213 # titles
213 # titles
214 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
214 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
215
215
216 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
216 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
217
217
218 chap_title = uri_short
218 chap_title = uri_short
@@ -291,7 +291,7 b' class ApiDocWriter(object):'
291 elif match_type == 'package':
291 elif match_type == 'package':
292 patterns = self.package_skip_patterns
292 patterns = self.package_skip_patterns
293 else:
293 else:
294 raise ValueError('Cannot interpret match type "%s"'
294 raise ValueError('Cannot interpret match type "%s"'
295 % match_type)
295 % match_type)
296 # Match to URI without package name
296 # Match to URI without package name
297 L = len(self.package_name)
297 L = len(self.package_name)
@@ -307,7 +307,7 b' class ApiDocWriter(object):'
307 return True
307 return True
308
308
309 def discover_modules(self):
309 def discover_modules(self):
310 ''' Return module sequence discovered from ``self.package_name``
310 ''' Return module sequence discovered from ``self.package_name``
311
311
312
312
313 Parameters
313 Parameters
@@ -328,7 +328,7 b' class ApiDocWriter(object):'
328 >>> dw.package_skip_patterns.append('\.util$')
328 >>> dw.package_skip_patterns.append('\.util$')
329 >>> 'sphinx.util' in dw.discover_modules()
329 >>> 'sphinx.util' in dw.discover_modules()
330 False
330 False
331 >>>
331 >>>
332 '''
332 '''
333 modules = [self.package_name]
333 modules = [self.package_name]
334 # raw directory parsing
334 # raw directory parsing
@@ -351,7 +351,7 b' class ApiDocWriter(object):'
351 self._survives_exclude(module_uri, 'module')):
351 self._survives_exclude(module_uri, 'module')):
352 modules.append(module_uri)
352 modules.append(module_uri)
353 return sorted(modules)
353 return sorted(modules)
354
354
355 def write_modules_api(self, modules,outdir):
355 def write_modules_api(self, modules,outdir):
356 # write the list
356 # write the list
357 written_modules = []
357 written_modules = []
@@ -376,7 +376,7 b' class ApiDocWriter(object):'
376 outdir : string
376 outdir : string
377 Directory name in which to store files
377 Directory name in which to store files
378 We create automatic filenames for each module
378 We create automatic filenames for each module
379
379
380 Returns
380 Returns
381 -------
381 -------
382 None
382 None
@@ -390,7 +390,7 b' class ApiDocWriter(object):'
390 # compose list of modules
390 # compose list of modules
391 modules = self.discover_modules()
391 modules = self.discover_modules()
392 self.write_modules_api(modules,outdir)
392 self.write_modules_api(modules,outdir)
393
393
394 def write_index(self, outdir, froot='gen', relative_to=None):
394 def write_index(self, outdir, froot='gen', relative_to=None):
395 """Make a reST API index file from written files
395 """Make a reST API index file from written files
396
396
@@ -13,7 +13,7 b' import re'
13
13
14 # Third party
14 # Third party
15 from pygments.lexer import Lexer, do_insertions
15 from pygments.lexer import Lexer, do_insertions
16 from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer,
16 from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer,
17 PythonTracebackLexer)
17 PythonTracebackLexer)
18 from pygments.token import Comment, Generic
18 from pygments.token import Comment, Generic
19
19
@@ -48,7 +48,7 b' class IPythonConsoleLexer(Lexer):'
48
48
49 - It assumes the default IPython prompts, not customized ones.
49 - It assumes the default IPython prompts, not customized ones.
50 """
50 """
51
51
52 name = 'IPython console session'
52 name = 'IPython console session'
53 aliases = ['ipython']
53 aliases = ['ipython']
54 mimetypes = ['text/x-ipython-console']
54 mimetypes = ['text/x-ipython-console']
@@ -2,8 +2,8 b''
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
@@ -54,9 +54,9 b' from distutils.core import setup'
54 from IPython.utils.path import target_update
54 from IPython.utils.path import target_update
55
55
56 from setupbase import (
56 from setupbase import (
57 setup_args,
57 setup_args,
58 find_packages,
58 find_packages,
59 find_package_data,
59 find_package_data,
60 find_scripts,
60 find_scripts,
61 find_data_files,
61 find_data_files,
62 check_for_dependencies,
62 check_for_dependencies,
@@ -113,7 +113,7 b" if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):"
113 # target_update()
113 # target_update()
114 to_update = [
114 to_update = [
115 # FIXME - Disabled for now: we need to redo an automatic way
115 # FIXME - Disabled for now: we need to redo an automatic way
116 # of generating the magic info inside the rst.
116 # of generating the magic info inside the rst.
117 #('docs/magic.tex',
117 #('docs/magic.tex',
118 #['IPython/Magic.py'],
118 #['IPython/Magic.py'],
119 #"cd doc && ./update_magic.sh" ),
119 #"cd doc && ./update_magic.sh" ),
@@ -176,9 +176,9 b" if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):"
176 docdeps,
176 docdeps,
177 "cd docs && make dist")
177 "cd docs && make dist")
178 )
178 )
179
179
180 [ target_update(*t) for t in to_update ]
180 [ target_update(*t) for t in to_update ]
181
181
182 #---------------------------------------------------------------------------
182 #---------------------------------------------------------------------------
183 # Find all the packages, package data, and data_files
183 # Find all the packages, package data, and data_files
184 #---------------------------------------------------------------------------
184 #---------------------------------------------------------------------------
@@ -207,7 +207,7 b" if sys.platform == 'win32':"
207 if len(needs_setuptools.intersection(sys.argv)) > 0:
207 if len(needs_setuptools.intersection(sys.argv)) > 0:
208 import setuptools
208 import setuptools
209
209
210 # This dict is used for passing extra arguments that are setuptools
210 # This dict is used for passing extra arguments that are setuptools
211 # specific to setup
211 # specific to setup
212 setuptools_extra_args = {}
212 setuptools_extra_args = {}
213
213
@@ -233,7 +233,7 b" if 'setuptools' in sys.modules:"
233 else:
233 else:
234 pass
234 pass
235 # do we want to install readline here?
235 # do we want to install readline here?
236
236
237 # Script to be run by the windows binary installer after the default setup
237 # Script to be run by the windows binary installer after the default setup
238 # routine, to add shortcuts and similar windows-only things. Windows
238 # routine, to add shortcuts and similar windows-only things. Windows
239 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
239 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
@@ -43,7 +43,7 b' pjoin = os.path.join'
43 def oscmd(s):
43 def oscmd(s):
44 print(">", s)
44 print(">", s)
45 os.system(s)
45 os.system(s)
46
46
47 try:
47 try:
48 execfile
48 execfile
49 except NameError:
49 except NameError:
@@ -120,7 +120,7 b' def find_package_data():'
120 """
120 """
121 # This is not enough for these things to appear in an sdist.
121 # This is not enough for these things to appear in an sdist.
122 # We need to muck with the MANIFEST to get this to work
122 # We need to muck with the MANIFEST to get this to work
123
123
124 # walk notebook resources:
124 # walk notebook resources:
125 cwd = os.getcwd()
125 cwd = os.getcwd()
126 os.chdir(os.path.join('IPython', 'frontend', 'html', 'notebook'))
126 os.chdir(os.path.join('IPython', 'frontend', 'html', 'notebook'))
@@ -130,7 +130,7 b' def find_package_data():'
130 for parent, dirs, files in static_walk:
130 for parent, dirs, files in static_walk:
131 for f in files:
131 for f in files:
132 static_data.append(os.path.join(parent, f))
132 static_data.append(os.path.join(parent, f))
133
133
134 package_data = {
134 package_data = {
135 'IPython.config.profile' : ['README', '*/*.py'],
135 'IPython.config.profile' : ['README', '*/*.py'],
136 'IPython.testing' : ['*.txt'],
136 'IPython.testing' : ['*.txt'],
@@ -151,23 +151,23 b' def make_dir_struct(tag,base,out_base):'
151
151
152 XXX - this needs a proper docstring!
152 XXX - this needs a proper docstring!
153 """
153 """
154
154
155 # we'll use these a lot below
155 # we'll use these a lot below
156 lbase = len(base)
156 lbase = len(base)
157 pathsep = os.path.sep
157 pathsep = os.path.sep
158 lpathsep = len(pathsep)
158 lpathsep = len(pathsep)
159
159
160 out = []
160 out = []
161 for (dirpath,dirnames,filenames) in os.walk(base):
161 for (dirpath,dirnames,filenames) in os.walk(base):
162 # we need to strip out the dirpath from the base to map it to the
162 # we need to strip out the dirpath from the base to map it to the
163 # output (installation) path. This requires possibly stripping the
163 # output (installation) path. This requires possibly stripping the
164 # path separator, because otherwise pjoin will not work correctly
164 # path separator, because otherwise pjoin will not work correctly
165 # (pjoin('foo/','/bar') returns '/bar').
165 # (pjoin('foo/','/bar') returns '/bar').
166
166
167 dp_eff = dirpath[lbase:]
167 dp_eff = dirpath[lbase:]
168 if dp_eff.startswith(pathsep):
168 if dp_eff.startswith(pathsep):
169 dp_eff = dp_eff[lpathsep:]
169 dp_eff = dp_eff[lpathsep:]
170 # The output path must be anchored at the out_base marker
170 # The output path must be anchored at the out_base marker
171 out_path = pjoin(out_base,dp_eff)
171 out_path = pjoin(out_base,dp_eff)
172 # Now we can generate the final filenames. Since os.walk only produces
172 # Now we can generate the final filenames. Since os.walk only produces
173 # filenames, we must join back with the dirpath to get full valid file
173 # filenames, we must join back with the dirpath to get full valid file
@@ -178,7 +178,7 b' def make_dir_struct(tag,base,out_base):'
178 out.append((out_path, pfiles))
178 out.append((out_path, pfiles))
179
179
180 return out
180 return out
181
181
182
182
183 def find_data_files():
183 def find_data_files():
184 """
184 """
@@ -186,10 +186,10 b' def find_data_files():'
186
186
187 Most of these are docs.
187 Most of these are docs.
188 """
188 """
189
189
190 docdirbase = pjoin('share', 'doc', 'ipython')
190 docdirbase = pjoin('share', 'doc', 'ipython')
191 manpagebase = pjoin('share', 'man', 'man1')
191 manpagebase = pjoin('share', 'man', 'man1')
192
192
193 # Simple file lists can be made by hand
193 # Simple file lists can be made by hand
194 manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz')))
194 manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz')))
195 if not manpages:
195 if not manpages:
@@ -241,19 +241,19 b' def make_man_update_target(manpage):'
241 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
241 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
242 locals() )
242 locals() )
243 return (manpath_gz, [manpath], gz_cmd)
243 return (manpath_gz, [manpath], gz_cmd)
244
244
245 #---------------------------------------------------------------------------
245 #---------------------------------------------------------------------------
246 # Find scripts
246 # Find scripts
247 #---------------------------------------------------------------------------
247 #---------------------------------------------------------------------------
248
248
249 def find_scripts(entry_points=False, suffix=''):
249 def find_scripts(entry_points=False, suffix=''):
250 """Find IPython's scripts.
250 """Find IPython's scripts.
251
251
252 if entry_points is True:
252 if entry_points is True:
253 return setuptools entry_point-style definitions
253 return setuptools entry_point-style definitions
254 else:
254 else:
255 return file paths of plain scripts [default]
255 return file paths of plain scripts [default]
256
256
257 suffix is appended to script names if entry_points is True, so that the
257 suffix is appended to script names if entry_points is True, so that the
258 Python 3 scripts get named "ipython3" etc.
258 Python 3 scripts get named "ipython3" etc.
259 """
259 """
@@ -293,7 +293,7 b" def find_scripts(entry_points=False, suffix=''):"
293
293
294 def check_for_dependencies():
294 def check_for_dependencies():
295 """Check for IPython's dependencies.
295 """Check for IPython's dependencies.
296
296
297 This function should NOT be called if running under setuptools!
297 This function should NOT be called if running under setuptools!
298 """
298 """
299 from setupext.setupext import (
299 from setupext.setupext import (
@@ -308,7 +308,7 b' def check_for_dependencies():'
308 print_status('platform', sys.platform)
308 print_status('platform', sys.platform)
309 if sys.platform == 'win32':
309 if sys.platform == 'win32':
310 print_status('Windows version', sys.getwindowsversion())
310 print_status('Windows version', sys.getwindowsversion())
311
311
312 print_raw("")
312 print_raw("")
313 print_raw("OPTIONAL DEPENDENCIES")
313 print_raw("OPTIONAL DEPENDENCIES")
314
314
@@ -69,7 +69,7 b' def check_for_sphinx():'
69 print_status('sphinx', "Not found (required for building documentation)")
69 print_status('sphinx', "Not found (required for building documentation)")
70 return False
70 return False
71 else:
71 else:
72 print_status('sphinx', sphinx.__version__)
72 print_status('sphinx', sphinx.__version__)
73 return True
73 return True
74
74
75 def check_for_pygments():
75 def check_for_pygments():
@@ -142,7 +142,7 b' def check_for_pyzmq():'
142 if zmq.__version__ < '2.1.4':
142 if zmq.__version__ < '2.1.4':
143 print_status('pyzmq', "no (have %s, but require >= 2.1.4 for"
143 print_status('pyzmq', "no (have %s, but require >= 2.1.4 for"
144 " qtconsole and parallel computing capabilities)"%zmq.__version__)
144 " qtconsole and parallel computing capabilities)"%zmq.__version__)
145
145
146 else:
146 else:
147 print_status("pyzmq", zmq.__version__)
147 print_status("pyzmq", zmq.__version__)
148 return True
148 return True
@@ -14,7 +14,7 b' def main():'
14 print "Entering ipython for profiling. Type 'Exit' for profiler report"
14 print "Entering ipython for profiling. Type 'Exit' for profiler report"
15 IPython.ipapi.launch_new_instance()
15 IPython.ipapi.launch_new_instance()
16
16
17 if len(sys.argv) == 1:
17 if len(sys.argv) == 1:
18 profile.run('main()', 'ipython_profiler_results')
18 profile.run('main()', 'ipython_profiler_results')
19
19
20 import pstats
20 import pstats
General Comments 0
You need to be logged in to leave comments. Login now