From 38ab8d91d0b9edd19d8903da1e8a34e8bce0eaae 2009-09-12 15:58:55 From: Brian Granger Date: 2009-09-12 15:58:55 Subject: [PATCH] Massive refactoring of of the core. * The magic aliases now work again. * Prefilters are components. * Fixed a bug that was turning the displayhook off to early, which was preventing magic and !! results from going through the displayhook. * Refactored config system. The config system now supports config subsections, like config.Global. These sections, which must start with an uppercase char, are auto-created upon a getattr. --- diff --git a/IPython/config/default/ipython_config.py b/IPython/config/default/ipython_config.py index 0f1ad8a..c60f159 100644 --- a/IPython/config/default/ipython_config.py +++ b/IPython/config/default/ipython_config.py @@ -1,81 +1,66 @@ #----------------------------------------------------------------------------- -# IPython Shell Configuration Defaults +# Global options #----------------------------------------------------------------------------- +Global.classic = False +Global.nosep = False + #----------------------------------------------------------------------------- -# Startup +# InteractiveShell options #----------------------------------------------------------------------------- -AUTOCALL = True - -AUTOEDIT_SYNTAX = False - -AUTOINDENT = True - -AUTOMAGIC = True - -CACHE_SIZE = 1000 -CLASSIC = False +InteractiveShell.autocall = 1 -COLORS = 'Linux' +InteractiveShell.autoedit_syntax = False -COLOR_INFO = True +InteractiveShell.autoindent = True -CONFIRM_EXIT = True +InteractiveShell.automagic = False -DEEP_RELOAD = False +InteractiveShell.banner1 = 'This if for overriding the default IPython banner' + +InteractiveShell.banner2 = "This is for extra banner text" -EDITOR = 0 +InteractiveShell.cache_size = 1000 -LOG = True +InteractiveShell.colors = 'LightBG' -LOGFILE = '' +InteractiveShell.color_info = True -BANNER = True +InteractiveShell.confirm_exit = True -MESSAGES = True +InteractiveShell.deep_reload = False -PDB = False +InteractiveShell.display_banner = True -PPRINT = True +InteractiveShell.editor = 'nano' -PROMPT_IN1 = 'In [\#]: ' +InteractiveShell.logstart = True -PROMPT_IN2 = ' .\D.: ' +InteractiveShell.logfile = 'ipython_log.py' -PROMPT_OUT = 'Out[\#]: ' +InteractiveShell.logplay = 'mylog.py' -PROMPTS_PAD_LEFT = True +InteractiveShell.object_info_string_level = 0 -QUICK = False +InteractiveShell.pager = 'less' -SCREEN_LENGTH = 0 +InteractiveShell.pdb = False -SEPARATE_IN = '\n' -SEPARATE_OUT = '' -SEPARATE_OUT2 = '' -NOSEP = False +InteractiveShell.pprint = True -WILDCARDS_CASE_SENSITIVE = True +InteractiveShell.prompt_in1 = 'In [\#]: ' +InteractiveShell.prompt_in2 = ' .\D.: ' +InteractiveShell.prompt_out = 'Out[\#]: ' +InteractiveShell.prompts_pad_left = True -OBJECT_INFO_STRING_LEVEL = 0 +InteractiveShell.quiet = False -XMODE = 'Context' +# Readline +InteractiveShell.readline_use = False -MULTI_LINE_SPECIALS = True - -SYSTEM_HEADER = "IPython system call: " - -SYSTEM_VERBOSE = True - -#----------------------------------------------------------------------------- -# Readline -#----------------------------------------------------------------------------- - -READLINE = True - -READLINE_PARSE_AND_BIND = [ +InteractiveShell.readline_parse_and_bind = [ 'tab: complete', '"\C-l": possible-completions', 'set show-all-if-ambiguous on', @@ -92,29 +77,39 @@ READLINE_PARSE_AND_BIND = [ '"\C-k": kill-line', '"\C-u": unix-line-discard', ] +InteractiveShell.readline_remove_delims = '-/~' +InteractiveShell.readline_merge_completions = True +InteractiveShell.readline_omit_names = 0 + +InteractiveShell.screen_length = 0 + +InteractiveShell.separate_in = '\n' +InteractiveShell.separate_out = '' +InteractiveShell.separate_out2 = '' + +InteractiveShell.system_header = "IPython system call: " -READLINE_REMOVE_DELIMS = '-/~' +InteractiveShell.system_verbose = True -READLINE_MERGE_COMPLETIONS = True +InteractiveShell.term_title = False -READLINE_OMIT_NAMES = 0 +InteractiveShell.wildcards_case_sensitive = True + +InteractiveShell.xmode = 'Context' #----------------------------------------------------------------------------- -# Code to execute +# PrefilterManager options #----------------------------------------------------------------------------- -EXECUTE = [ - 'import numpy as np', - 'import sympy', - 'a = 10' -] - -EXECFILE = [] +PrefilterManager.multi_line_specials = True #----------------------------------------------------------------------------- -# Alias +# AliasManager options #----------------------------------------------------------------------------- -ALIAS = [ - ('myls', 'ls -la') +# Do this to enable all defaults +# AliasManager.default_aliases = [] + +AliasManger.user_aliases = [ + ('foo', 'echo Hi') ] \ No newline at end of file diff --git a/IPython/config/loader.py b/IPython/config/loader.py index c77b1b8..aae5b02 100644 --- a/IPython/config/loader.py +++ b/IPython/config/loader.py @@ -1,6 +1,10 @@ #!/usr/bin/env python # encoding: utf-8 -"""A factory for creating configuration objects. +"""A simple configuration system. + +Authors: + +* Brian Granger """ #----------------------------------------------------------------------------- @@ -14,22 +18,138 @@ # Imports #----------------------------------------------------------------------------- +import __builtin__ import os import sys from IPython.external import argparse -from IPython.utils.ipstruct import Struct from IPython.utils.genutils import filefind #----------------------------------------------------------------------------- -# Code +# Exceptions #----------------------------------------------------------------------------- -class ConfigLoaderError(Exception): +class ConfigError(Exception): pass +class ConfigLoaderError(ConfigError): + pass + + +#----------------------------------------------------------------------------- +# Config class for holding config information +#----------------------------------------------------------------------------- + + +class Config(dict): + """An attribute based dict that can do smart merges.""" + + def __init__(self, *args, **kwds): + dict.__init__(self, *args, **kwds) + # This sets self.__dict__ = self, but it has to be done this way + # because we are also overriding __setattr__. + dict.__setattr__(self, '__dict__', self) + + def _merge(self, other): + to_update = {} + for k, v in other.items(): + if not self.has_key(k): + to_update[k] = v + else: # I have this key + if isinstance(v, Config): + # Recursively merge common sub Configs + self[k]._merge(v) + else: + # Plain updates for non-Configs + to_update[k] = v + + self.update(to_update) + + def _is_section_key(self, key): + if key[0].upper()==key[0] and not key.startswith('_'): + return True + else: + return False + + def has_key(self, key): + if self._is_section_key(key): + return True + else: + return dict.has_key(self, key) + + def _has_section(self, key): + if self._is_section_key(key): + if dict.has_key(self, key): + return True + return False + + def copy(self): + return type(self)(dict.copy(self)) + + def __copy__(self): + return self.copy() + + def __deepcopy__(self, memo): + import copy + return type(self)(copy.deepcopy(self.items())) + + def __getitem__(self, key): + # Because we use this for an exec namespace, we need to delegate + # the lookup of names in __builtin__ to itself. This means + # that you can't have section or attribute names that are + # builtins. + try: + return getattr(__builtin__, key) + except AttributeError: + pass + if self._is_section_key(key): + try: + return dict.__getitem__(self, key) + except KeyError: + c = Config() + dict.__setitem__(self, key, c) + return c + else: + return dict.__getitem__(self, key) + + def __setitem__(self, key, value): + # Don't allow names in __builtin__ to be modified. + if hasattr(__builtin__, key): + raise ConfigError('Config variable names cannot have the same name ' + 'as a Python builtin: %s' % key) + if self._is_section_key(key): + if not isinstance(value, Config): + raise ValueError('values whose keys begin with an uppercase ' + 'char must be Config instances: %r, %r' % (key, value)) + else: + dict.__setitem__(self, key, value) + + def __getattr__(self, key): + try: + return self.__getitem__(key) + except KeyError, e: + raise AttributeError(e) + + def __setattr__(self, key, value): + try: + self.__setitem__(key, value) + except KeyError, e: + raise AttributeError(e) + + def __delattr__(self, key): + try: + dict.__delitem__(self, key) + except KeyError, e: + raise AttributeError(e) + + +#----------------------------------------------------------------------------- +# Config loading classes +#----------------------------------------------------------------------------- + + class ConfigLoader(object): """A object for loading configurations from just about anywhere. @@ -59,7 +179,7 @@ class ConfigLoader(object): self.clear() def clear(self): - self.config = Struct() + self.config = Config() def load_config(self): """Load a config from somewhere, return a Struct. @@ -106,7 +226,7 @@ class PyFileConfigLoader(FileConfigLoader): """Load the config from a file and return it as a Struct.""" self._find_file() self._read_file_as_dict() - self._convert_to_struct() + self._convert_to_config() return self.config def _find_file(self): @@ -114,15 +234,12 @@ class PyFileConfigLoader(FileConfigLoader): self.full_filename = filefind(self.filename, self.path) def _read_file_as_dict(self): - self.data = {} - execfile(self.full_filename, self.data) + execfile(self.full_filename, self.config) - def _convert_to_struct(self): + def _convert_to_config(self): if self.data is None: ConfigLoaderError('self.data does not exist') - for k, v in self.data.iteritems(): - if k == k.upper(): - self.config[k] = v + del self.config['__builtins__'] class CommandLineConfigLoader(ConfigLoader): @@ -133,8 +250,8 @@ class CommandLineConfigLoader(ConfigLoader): """ -class NoDefault(object): pass -NoDefault = NoDefault() +class NoConfigDefault(object): pass +NoConfigDefault = NoConfigDefault() class ArgParseConfigLoader(CommandLineConfigLoader): @@ -155,7 +272,7 @@ class ArgParseConfigLoader(CommandLineConfigLoader): """Parse command line arguments and return as a Struct.""" self._create_parser() self._parse_args(args) - self._convert_to_struct() + self._convert_to_config() return self.config def _create_parser(self): @@ -169,7 +286,7 @@ class ArgParseConfigLoader(CommandLineConfigLoader): def _add_arguments(self): for argument in self.arguments: if not argument[1].has_key('default'): - argument[1]['default'] = NoDefault + argument[1]['default'] = NoConfigDefault self.parser.add_argument(*argument[0],**argument[1]) def _parse_args(self, args=None): @@ -179,25 +296,10 @@ class ArgParseConfigLoader(CommandLineConfigLoader): else: self.parsed_data = self.parser.parse_args(args) - def _convert_to_struct(self): + def _convert_to_config(self): """self.parsed_data->self.config""" - self.config = Struct() for k, v in vars(self.parsed_data).items(): - if v is not NoDefault: - setattr(self.config, k, v) - -class IPythonArgParseConfigLoader(ArgParseConfigLoader): + if v is not NoConfigDefault: + exec_str = 'self.config.' + k + '= v' + exec exec_str in locals(), globals() - def _add_other_arguments(self): - self.parser.add_argument('-ipythondir',dest='IPYTHONDIR',type=str, - help='Set to override default location of IPYTHONDIR.', - default=NoDefault) - self.parser.add_argument('-p','-profile',dest='PROFILE',type=str, - help='The string name of the ipython profile to be used.', - default=NoDefault) - self.parser.add_argument('-debug',dest="DEBUG",action='store_true', - help='Debug the application startup process.', - default=NoDefault) - self.parser.add_argument('-config_file',dest='CONFIG_FILE',type=str, - help='Set the config file name to override default.', - default=NoDefault) diff --git a/IPython/config/tests/test_loader.py b/IPython/config/tests/test_loader.py index 5cb71b4..23e1890 100644 --- a/IPython/config/tests/test_loader.py +++ b/IPython/config/tests/test_loader.py @@ -24,7 +24,12 @@ import os from tempfile import mkstemp from unittest import TestCase -from IPython.config.loader import PyFileConfigLoader, ArgParseConfigLoader +from IPython.config.loader import ( + Config, + PyFileConfigLoader, + ArgParseConfigLoader, + ConfigError +) #----------------------------------------------------------------------------- # Actual tests @@ -32,10 +37,11 @@ from IPython.config.loader import PyFileConfigLoader, ArgParseConfigLoader pyfile = """ -A = 10 -B = range(10) -C = True -D = 'hi there' +a = 10 +b = 20 +Foo.Bar.value = 10 +Foo.Bam.value = range(10) +D.C.value = 'hi there' """ class TestPyFileCL(TestCase): @@ -48,10 +54,11 @@ class TestPyFileCL(TestCase): # Unlink the file cl = PyFileConfigLoader(fname) config = cl.load_config() - self.assertEquals(config.A, 10) - self.assertEquals(config.B, range(10)) - self.assertEquals(config.C, True) - self.assertEquals(config.D, 'hi there') + self.assertEquals(config.a, 10) + self.assertEquals(config.b, 20) + self.assertEquals(config.Foo.Bar.value, 10) + self.assertEquals(config.Foo.Bam.value, range(10)) + self.assertEquals(config.D.C.value, 'hi there') class TestArgParseCL(TestCase): @@ -60,18 +67,18 @@ class TestArgParseCL(TestCase): class MyLoader(ArgParseConfigLoader): arguments = ( - (('-f','--foo'), dict(dest='FOO', type=str)), - (('-b',), dict(dest='BAR', type=int)), - (('-n',), dict(dest='N', action='store_true')), - (('BAM',), dict(type=str)) + (('-f','--foo'), dict(dest='Global.foo', type=str)), + (('-b',), dict(dest='MyClass.bar', type=int)), + (('-n',), dict(dest='n', action='store_true')), + (('Global.bam',), dict(type=str)) ) cl = MyLoader() config = cl.load_config('-f hi -b 10 -n wow'.split()) - self.assertEquals(config.FOO, 'hi') - self.assertEquals(config.BAR, 10) - self.assertEquals(config.N, True) - self.assertEquals(config.BAM, 'wow') + self.assertEquals(config.Global.foo, 'hi') + self.assertEquals(config.MyClass.bar, 10) + self.assertEquals(config.n, True) + self.assertEquals(config.Global.bam, 'wow') def test_add_arguments(self): @@ -79,7 +86,7 @@ class TestArgParseCL(TestCase): def _add_arguments(self): subparsers = self.parser.add_subparsers(dest='subparser_name') subparser1 = subparsers.add_parser('1') - subparser1.add_argument('-x') + subparser1.add_argument('-x',dest='Global.x') subparser2 = subparsers.add_parser('2') subparser2.add_argument('y') @@ -89,5 +96,68 @@ class TestArgParseCL(TestCase): self.assertEquals(config.y, 'frobble') config = cl.load_config('1 -x frobble'.split()) self.assertEquals(config.subparser_name, '1') - self.assertEquals(config.x, 'frobble') - + self.assertEquals(config.Global.x, 'frobble') + +class TestConfig(TestCase): + + def test_setget(self): + c = Config() + c.a = 10 + self.assertEquals(c.a, 10) + self.assertEquals(c.has_key('b'), False) + + def test_auto_section(self): + c = Config() + self.assertEquals(c.has_key('A'), True) + self.assertEquals(c._has_section('A'), False) + A = c.A + A.foo = 'hi there' + self.assertEquals(c._has_section('A'), True) + self.assertEquals(c.A.foo, 'hi there') + del c.A + self.assertEquals(len(c.A.keys()),0) + + def test_merge_doesnt_exist(self): + c1 = Config() + c2 = Config() + c2.bar = 10 + c2.Foo.bar = 10 + c1._merge(c2) + self.assertEquals(c1.Foo.bar, 10) + self.assertEquals(c1.bar, 10) + c2.Bar.bar = 10 + c1._merge(c2) + self.assertEquals(c1.Bar.bar, 10) + + def test_merge_exists(self): + c1 = Config() + c2 = Config() + c1.Foo.bar = 10 + c1.Foo.bam = 30 + c2.Foo.bar = 20 + c2.Foo.wow = 40 + c1._merge(c2) + self.assertEquals(c1.Foo.bam, 30) + self.assertEquals(c1.Foo.bar, 20) + self.assertEquals(c1.Foo.wow, 40) + c2.Foo.Bam.bam = 10 + c1._merge(c2) + self.assertEquals(c1.Foo.Bam.bam, 10) + + def test_deepcopy(self): + c1 = Config() + c1.Foo.bar = 10 + c1.Foo.bam = 30 + c1.a = 'asdf' + c1.b = range(10) + import copy + c2 = copy.deepcopy(c1) + self.assertEquals(c1, c2) + self.assert_(c1 is not c2) + self.assert_(c1.Foo is not c2.Foo) + + def test_builtin(self): + c1 = Config() + exec 'foo = True' in c1 + self.assertEquals(c1.foo, True) + self.assertRaises(ConfigError, setattr, c1, 'ValueError', 10) diff --git a/IPython/core/alias.py b/IPython/core/alias.py index a2b8bbf..60cabc1 100644 --- a/IPython/core/alias.py +++ b/IPython/core/alias.py @@ -43,7 +43,7 @@ def default_aliases(): # Make some aliases automatically # Prepare list of shell aliases to auto-define if os.name == 'posix': - auto_alias = ('mkdir mkdir', 'rmdir rmdir', + default_aliases = ('mkdir mkdir', 'rmdir rmdir', 'mv mv -i','rm rm -i','cp cp -i', 'cat cat','less less','clear clear', # a better ls @@ -75,15 +75,15 @@ def default_aliases(): # things which are executable 'lx ls -lF | grep ^-..x', ) - auto_alias = auto_alias + ls_extra + default_aliases = default_aliases + ls_extra elif os.name in ['nt','dos']: - auto_alias = ('ls dir /on', + default_aliases = ('ls dir /on', 'ddir dir /ad /on', 'ldir dir /ad /on', 'mkdir mkdir','rmdir rmdir','echo echo', 'ren ren','cls cls','copy copy') else: - auto_alias = () - return [s.split(None,1) for s in auto_alias] + default_aliases = () + return [s.split(None,1) for s in default_aliases] class AliasError(Exception): @@ -101,8 +101,8 @@ class InvalidAliasError(AliasError): class AliasManager(Component): - auto_alias = List(default_aliases()) - user_alias = List(default_value=[], config_key='USER_ALIAS') + default_aliases = List(default_aliases(), config=True) + user_aliases = List(default_value=[], config=True) def __init__(self, parent, config=None): super(AliasManager, self).__init__(parent, config=config) @@ -137,13 +137,16 @@ class AliasManager(Component): def init_aliases(self): # Load default aliases - for name, cmd in self.auto_alias: + for name, cmd in self.default_aliases: self.soft_define_alias(name, cmd) # Load user aliases - for name, cmd in self.user_alias: + for name, cmd in self.user_aliases: self.soft_define_alias(name, cmd) + def clear_aliases(self): + self.alias_table.clear() + def soft_define_alias(self, name, cmd): """Define an alias, but don't raise on an AliasError.""" try: @@ -160,6 +163,10 @@ class AliasManager(Component): nargs = self.validate_alias(name, cmd) self.alias_table[name] = (nargs, cmd) + def undefine_alias(self, name): + if self.alias_table.has_key(name): + del self.alias_table[name] + def validate_alias(self, name, cmd): """Validate an alias and return the its number of arguments.""" if name in self.no_alias: diff --git a/IPython/core/application.py b/IPython/core/application.py index 9378f9e..882064b 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -26,13 +26,14 @@ Notes import os import sys import traceback - from copy import deepcopy -from IPython.utils.ipstruct import Struct + from IPython.utils.genutils import get_ipython_dir, filefind from IPython.config.loader import ( - IPythonArgParseConfigLoader, - PyFileConfigLoader + PyFileConfigLoader, + ArgParseConfigLoader, + Config, + NoConfigDefault ) #----------------------------------------------------------------------------- @@ -40,6 +41,27 @@ from IPython.config.loader import ( #----------------------------------------------------------------------------- +class IPythonArgParseConfigLoader(ArgParseConfigLoader): + """Default command line options for IPython based applications.""" + + def _add_other_arguments(self): + self.parser.add_argument('-ipythondir',dest='Global.ipythondir',type=str, + help='Set to override default location of Global.ipythondir.', + default=NoConfigDefault, + metavar='Global.ipythondir') + self.parser.add_argument('-p','-profile',dest='Global.profile',type=str, + help='The string name of the ipython profile to be used.', + default=NoConfigDefault, + metavar='Global.profile') + self.parser.add_argument('-debug',dest="Global.debug",action='store_true', + help='Debug the application startup process.', + default=NoConfigDefault) + self.parser.add_argument('-config_file',dest='Global.config_file',type=str, + help='Set the config file name to override default.', + default=NoConfigDefault, + metavar='Global.config_file') + + class ApplicationError(Exception): pass @@ -79,8 +101,8 @@ class Application(object): def create_default_config(self): """Create defaults that can't be set elsewhere.""" - self.default_config = Struct() - self.default_config.IPYTHONDIR = get_ipython_dir() + self.default_config = Config() + self.default_config.Global.ipythondir = get_ipython_dir() def create_command_line_config(self): """Create and return a command line config loader.""" @@ -99,7 +121,7 @@ class Application(object): loader = self.create_command_line_config() self.command_line_config = loader.load_config() try: - self.debug = self.command_line_config.DEBUG + self.debug = self.command_line_config.Global.debug except AttributeError: pass # use class default self.log("Default config loaded:", self.default_config) @@ -120,9 +142,9 @@ class Application(object): """ try: - self.ipythondir = self.command_line_config.IPYTHONDIR + self.ipythondir = self.command_line_config.Global.ipythondir except AttributeError: - self.ipythondir = self.default_config.IPYTHONDIR + self.ipythondir = self.default_config.Global.ipythondir sys.path.append(os.path.abspath(self.ipythondir)) if not os.path.isdir(self.ipythondir): os.makedirs(self.ipythondir, mode = 0777) @@ -138,12 +160,12 @@ class Application(object): """ try: - self.config_file_name = self.command_line_config.CONFIG_FILE + self.config_file_name = self.command_line_config.Global.config_file except AttributeError: pass try: - self.profile_name = self.command_line_config.PROFILE + self.profile_name = self.command_line_config.Global.profile name_parts = self.config_file_name.split('.') name_parts.insert(1, '_' + self.profile_name + '.') self.config_file_name = ''.join(name_parts) @@ -165,15 +187,15 @@ class Application(object): ``CONFIG_FILE`` config variable is set to the resolved config file location. If not successful, an empty config is used. """ - loader = PyFileConfigLoader(self.config_file_name, - self.config_file_paths) + loader = PyFileConfigLoader(self.config_file_name, + path=self.config_file_paths) try: self.file_config = loader.load_config() - self.file_config.CONFIG_FILE = loader.full_filename + self.file_config.Global.config_file = loader.full_filename except IOError: self.log("Config file not found, skipping: %s" % \ self.config_file_name) - self.file_config = Struct() + self.file_config = Config() else: self.log("Config file loaded: %s" % loader.full_filename, self.file_config) @@ -184,10 +206,10 @@ class Application(object): def merge_configs(self): """Merge the default, command line and file config objects.""" - config = Struct() - config.update(self.default_config) - config.update(self.file_config) - config.update(self.command_line_config) + config = Config() + config._merge(self.default_config) + config._merge(self.file_config) + config._merge(self.command_line_config) self.master_config = config self.log("Master config created:", self.master_config) @@ -223,12 +245,13 @@ class Application(object): def attempt(self, func, action='abort'): try: func() + except SystemExit: + self.exit() except: if action == 'abort': self.print_traceback() self.abort() elif action == 'exit': - self.print_traceback() self.exit() def print_traceback(self): diff --git a/IPython/core/builtin_trap.py b/IPython/core/builtin_trap.py index 106f566..a05fc49 100644 --- a/IPython/core/builtin_trap.py +++ b/IPython/core/builtin_trap.py @@ -40,6 +40,9 @@ class BuiltinTrap(Component): def __init__(self, parent): super(BuiltinTrap, self).__init__(parent, None, None) self._orig_builtins = {} + # We define this to track if a single BuiltinTrap is nested. + # Only turn off the trap when the outermost call to __exit__ is made. + self._nested_level = 0 @auto_attr def shell(self): @@ -50,12 +53,16 @@ class BuiltinTrap(Component): return shell def __enter__(self): - self.set() + if self._nested_level == 0: + self.set() + self._nested_level += 1 # I return self, so callers can use add_builtin in a with clause. return self def __exit__(self, type, value, traceback): - self.unset() + if self._nested_level == 1: + self.unset() + self._nested_level -= 1 return True def add_builtin(self, key, value): @@ -108,4 +115,4 @@ class BuiltinTrap(Component): try: del __builtin__.__dict__['__IPYTHON__active'] except KeyError: - pass \ No newline at end of file + pass diff --git a/IPython/core/component.py b/IPython/core/component.py index 176dcd4..04b70a0 100644 --- a/IPython/core/component.py +++ b/IPython/core/component.py @@ -25,7 +25,7 @@ import datetime from weakref import WeakValueDictionary from IPython.utils.importstring import import_item -from IPython.utils.ipstruct import Struct +from IPython.config.loader import Config from IPython.utils.traitlets import ( HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This ) @@ -187,7 +187,7 @@ class Component(HasTraitlets): __metaclass__ = MetaComponent # Traitlets are fun! - config = Instance(Struct,(),{}) + config = Instance(Config,(),{}) parent = This() root = This() created = None @@ -203,7 +203,7 @@ class Component(HasTraitlets): name : str The unique name of the component. If empty, then a unique one will be autogenerated. - config : Struct + config : Config If this is empty, self.config = parent.config, otherwise self.config = config and root.config is ignored. This argument should only be used to *override* the automatic inheritance of @@ -232,7 +232,7 @@ class Component(HasTraitlets): else: self.name = name self.root = self # This is the default, it is set when parent is set - self.parent = parent + self.parent = parent if config is not None: self.config = deepcopy(config) else: @@ -274,15 +274,20 @@ class Component(HasTraitlets): In the future, we might want to do a pop here so stale config info is not passed onto children. """ - # Get all traitlets with a config_key metadata entry - traitlets = self.traitlets('config_key') - for k, v in traitlets.items(): - try: - config_value = new[v.get_metadata('config_key')] - except KeyError: - pass - else: - setattr(self, k, config_value) + # Get all traitlets with a config metadata entry that is True + traitlets = self.traitlets(config=True) + + # Don't do a blind getattr as that would cause the config to + # dynamically create the section with name self.__class__.__name__. + if new._has_section(self.__class__.__name__): + my_config = new[self.__class__.__name__] + for k, v in traitlets.items(): + try: + config_value = my_config[k] + except KeyError: + pass + else: + setattr(self, k, config_value) @property def children(self): diff --git a/IPython/core/display_trap.py b/IPython/core/display_trap.py index 89252a2..ff41a81 100644 --- a/IPython/core/display_trap.py +++ b/IPython/core/display_trap.py @@ -40,9 +40,11 @@ class DisplayTrap(Component): def __init__(self, parent, hook): super(DisplayTrap, self).__init__(parent, None, None) - self.hook = hook self.old_hook = None + # We define this to track if a single BuiltinTrap is nested. + # Only turn off the trap when the outermost call to __exit__ is made. + self._nested_level = 0 @auto_attr def shell(self): @@ -53,11 +55,15 @@ class DisplayTrap(Component): return shell def __enter__(self): - self.set() + if self._nested_level == 0: + self.set() + self._nested_level += 1 return self def __exit__(self, type, value, traceback): - self.unset() + if self._nested_level == 1: + self.unset() + self._nested_level -= 1 return True def set(self): diff --git a/IPython/core/embed.py b/IPython/core/embed.py index bb34f8e..bed686e 100644 --- a/IPython/core/embed.py +++ b/IPython/core/embed.py @@ -30,10 +30,12 @@ from contextlib import nested from IPython.core import ultratb from IPython.core.iplib import InteractiveShell +from IPython.core.ipapp import load_default_config from IPython.utils.traitlets import Bool, Str, CBool from IPython.utils.genutils import ask_yes_no + #----------------------------------------------------------------------------- # Classes and functions #----------------------------------------------------------------------------- @@ -260,6 +262,8 @@ def embed(header='', config=None, usage=None, banner1=None, banner2=None, Full customization can be done by passing a :class:`Struct` in as the config argument. """ + if config is None: + config = load_default_config() global _embedded_shell if _embedded_shell is None: _embedded_shell = InteractiveShellEmbed(config=config, diff --git a/IPython/core/ipapp.py b/IPython/core/ipapp.py index 6988fb1..2c4ace6 100644 --- a/IPython/core/ipapp.py +++ b/IPython/core/ipapp.py @@ -27,13 +27,17 @@ import os import sys import warnings -from IPython.core.application import Application +from IPython.core.application import Application, IPythonArgParseConfigLoader from IPython.core import release from IPython.core.iplib import InteractiveShell -from IPython.config.loader import IPythonArgParseConfigLoader, NoDefault +from IPython.config.loader import ( + NoConfigDefault, + Config, + PyFileConfigLoader +) from IPython.utils.ipstruct import Struct - +from IPython.utils.genutils import get_ipython_dir #----------------------------------------------------------------------------- # Utilities and helpers @@ -62,172 +66,187 @@ deprecated. See the %gui magic for information on the new interface. cl_args = ( (('-autocall',), dict( - type=int, dest='AUTOCALL', default=NoDefault, - help='Set the autocall value (0,1,2).') + type=int, dest='InteractiveShell.autocall', default=NoConfigDefault, + help='Set the autocall value (0,1,2).', + metavar='InteractiveShell.autocall') ), (('-autoindent',), dict( - action='store_true', dest='AUTOINDENT', default=NoDefault, + action='store_true', dest='InteractiveShell.autoindent', default=NoConfigDefault, help='Turn on autoindenting.') ), (('-noautoindent',), dict( - action='store_false', dest='AUTOINDENT', default=NoDefault, + action='store_false', dest='InteractiveShell.autoindent', default=NoConfigDefault, help='Turn off autoindenting.') ), (('-automagic',), dict( - action='store_true', dest='AUTOMAGIC', default=NoDefault, + action='store_true', dest='InteractiveShell.automagic', default=NoConfigDefault, help='Turn on the auto calling of magic commands.') ), (('-noautomagic',), dict( - action='store_false', dest='AUTOMAGIC', default=NoDefault, + action='store_false', dest='InteractiveShell.automagic', default=NoConfigDefault, help='Turn off the auto calling of magic commands.') ), (('-autoedit_syntax',), dict( - action='store_true', dest='AUTOEDIT_SYNTAX', default=NoDefault, + action='store_true', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault, help='Turn on auto editing of files with syntax errors.') ), (('-noautoedit_syntax',), dict( - action='store_false', dest='AUTOEDIT_SYNTAX', default=NoDefault, + action='store_false', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault, help='Turn off auto editing of files with syntax errors.') ), (('-banner',), dict( - action='store_true', dest='DISPLAY_BANNER', default=NoDefault, + action='store_true', dest='InteractiveShell.display_banner', default=NoConfigDefault, help='Display a banner upon starting IPython.') ), (('-nobanner',), dict( - action='store_false', dest='DISPLAY_BANNER', default=NoDefault, + action='store_false', dest='InteractiveShell.display_banner', default=NoConfigDefault, help="Don't display a banner upon starting IPython.") ), (('-c',), dict( - type=str, dest='C', default=NoDefault, - help="Execute the given command string.") + type=str, dest='InteractiveShell.c', default=NoConfigDefault, + help="Execute the given command string.", + metavar='InteractiveShell.c') ), (('-cache_size',), dict( - type=int, dest='CACHE_SIZE', default=NoDefault, - help="Set the size of the output cache.") + type=int, dest='InteractiveShell.cache_size', default=NoConfigDefault, + help="Set the size of the output cache.", + metavar='InteractiveShell.cache_size') ), (('-classic',), dict( - action='store_true', dest='CLASSIC', default=NoDefault, + action='store_true', dest='Global.classic', default=NoConfigDefault, help="Gives IPython a similar feel to the classic Python prompt.") ), (('-colors',), dict( - type=str, dest='COLORS', default=NoDefault, - help="Set the color scheme (NoColor, Linux, and LightBG).") + type=str, dest='InteractiveShell.colors', default=NoConfigDefault, + help="Set the color scheme (NoColor, Linux, and LightBG).", + metavar='InteractiveShell.colors') ), (('-color_info',), dict( - action='store_true', dest='COLOR_INFO', default=NoDefault, + action='store_true', dest='InteractiveShell.color_info', default=NoConfigDefault, help="Enable using colors for info related things.") ), (('-nocolor_info',), dict( - action='store_false', dest='COLOR_INFO', default=NoDefault, + action='store_false', dest='InteractiveShell.color_info', default=NoConfigDefault, help="Disable using colors for info related things.") ), (('-confirm_exit',), dict( - action='store_true', dest='CONFIRM_EXIT', default=NoDefault, + action='store_true', dest='InteractiveShell.confirm_exit', default=NoConfigDefault, help="Prompt the user when existing.") ), (('-noconfirm_exit',), dict( - action='store_false', dest='CONFIRM_EXIT', default=NoDefault, + action='store_false', dest='InteractiveShell.confirm_exit', default=NoConfigDefault, help="Don't prompt the user when existing.") ), (('-deep_reload',), dict( - action='store_true', dest='DEEP_RELOAD', default=NoDefault, + action='store_true', dest='InteractiveShell.deep_reload', default=NoConfigDefault, help="Enable deep (recursive) reloading by default.") ), (('-nodeep_reload',), dict( - action='store_false', dest='DEEP_RELOAD', default=NoDefault, + action='store_false', dest='InteractiveShell.deep_reload', default=NoConfigDefault, help="Disable deep (recursive) reloading by default.") ), (('-editor',), dict( - type=str, dest='EDITOR', default=NoDefault, - help="Set the editor used by IPython (default to $EDITOR/vi/notepad).") + type=str, dest='InteractiveShell.editor', default=NoConfigDefault, + help="Set the editor used by IPython (default to $EDITOR/vi/notepad).", + metavar='InteractiveShell.editor') ), (('-log','-l'), dict( - action='store_true', dest='LOGSTART', default=NoDefault, + action='store_true', dest='InteractiveShell.logstart', default=NoConfigDefault, help="Start logging to the default file (./ipython_log.py).") ), (('-logfile','-lf'), dict( - type=str, dest='LOGFILE', default=NoDefault, - help="Specify the name of your logfile.") + type=str, dest='InteractiveShell.logfile', default=NoConfigDefault, + help="Specify the name of your logfile.", + metavar='InteractiveShell.logfile') ), (('-logplay','-lp'), dict( - type=str, dest='LOGPLAY', default=NoDefault, - help="Re-play a log file and then append to it.") + type=str, dest='InteractiveShell.logplay', default=NoConfigDefault, + help="Re-play a log file and then append to it.", + metavar='InteractiveShell.logplay') ), (('-pdb',), dict( - action='store_true', dest='PDB', default=NoDefault, + action='store_true', dest='InteractiveShell.pdb', default=NoConfigDefault, help="Enable auto calling the pdb debugger after every exception.") ), (('-nopdb',), dict( - action='store_false', dest='PDB', default=NoDefault, + action='store_false', dest='InteractiveShell.pdb', default=NoConfigDefault, help="Disable auto calling the pdb debugger after every exception.") ), (('-pprint',), dict( - action='store_true', dest='PPRINT', default=NoDefault, + action='store_true', dest='InteractiveShell.pprint', default=NoConfigDefault, help="Enable auto pretty printing of results.") ), (('-nopprint',), dict( - action='store_false', dest='PPRINT', default=NoDefault, + action='store_false', dest='InteractiveShell.pprint', default=NoConfigDefault, help="Disable auto auto pretty printing of results.") ), (('-prompt_in1','-pi1'), dict( - type=str, dest='PROMPT_IN1', default=NoDefault, - help="Set the main input prompt ('In [\#]: ')") + type=str, dest='InteractiveShell.prompt_in1', default=NoConfigDefault, + help="Set the main input prompt ('In [\#]: ')", + metavar='InteractiveShell.prompt_in1') ), (('-prompt_in2','-pi2'), dict( - type=str, dest='PROMPT_IN2', default=NoDefault, - help="Set the secondary input prompt (' .\D.: ')") + type=str, dest='InteractiveShell.prompt_in2', default=NoConfigDefault, + help="Set the secondary input prompt (' .\D.: ')", + metavar='InteractiveShell.prompt_in2') ), (('-prompt_out','-po'), dict( - type=str, dest='PROMPT_OUT', default=NoDefault, - help="Set the output prompt ('Out[\#]:')") + type=str, dest='InteractiveShell.prompt_out', default=NoConfigDefault, + help="Set the output prompt ('Out[\#]:')", + metavar='InteractiveShell.prompt_out') ), (('-quick',), dict( - action='store_true', dest='QUICK', default=NoDefault, + action='store_true', dest='Global.quick', default=NoConfigDefault, help="Enable quick startup with no config files.") ), (('-readline',), dict( - action='store_true', dest='READLINE_USE', default=NoDefault, + action='store_true', dest='InteractiveShell.readline_use', default=NoConfigDefault, help="Enable readline for command line usage.") ), (('-noreadline',), dict( - action='store_false', dest='READLINE_USE', default=NoDefault, + action='store_false', dest='InteractiveShell.readline_use', default=NoConfigDefault, help="Disable readline for command line usage.") ), (('-screen_length','-sl'), dict( - type=int, dest='SCREEN_LENGTH', default=NoDefault, - help='Number of lines on screen, used to control printing of long strings.') + type=int, dest='InteractiveShell.screen_length', default=NoConfigDefault, + help='Number of lines on screen, used to control printing of long strings.', + metavar='InteractiveShell.screen_length') ), (('-separate_in','-si'), dict( - type=str, dest='SEPARATE_IN', default=NoDefault, - help="Separator before input prompts. Default '\n'.") + type=str, dest='InteractiveShell.separate_in', default=NoConfigDefault, + help="Separator before input prompts. Default '\n'.", + metavar='InteractiveShell.separate_in') ), (('-separate_out','-so'), dict( - type=str, dest='SEPARATE_OUT', default=NoDefault, - help="Separator before output prompts. Default 0 (nothing).") + type=str, dest='InteractiveShell.separate_out', default=NoConfigDefault, + help="Separator before output prompts. Default 0 (nothing).", + metavar='InteractiveShell.separate_out') ), (('-separate_out2','-so2'), dict( - type=str, dest='SEPARATE_OUT2', default=NoDefault, - help="Separator after output prompts. Default 0 (nonight).") + type=str, dest='InteractiveShell.separate_out2', default=NoConfigDefault, + help="Separator after output prompts. Default 0 (nonight).", + metavar='InteractiveShell.separate_out2') ), (('-nosep',), dict( - action='store_true', dest='NOSEP', default=NoDefault, + action='store_true', dest='Global.nosep', default=NoConfigDefault, help="Eliminate all spacing between prompts.") ), (('-term_title',), dict( - action='store_true', dest='TERM_TITLE', default=NoDefault, + action='store_true', dest='InteractiveShell.term_title', default=NoConfigDefault, help="Enable auto setting the terminal title.") ), (('-noterm_title',), dict( - action='store_false', dest='TERM_TITLE', default=NoDefault, + action='store_false', dest='InteractiveShell.term_title', default=NoConfigDefault, help="Disable auto setting the terminal title.") ), (('-xmode',), dict( - type=str, dest='XMODE', default=NoDefault, - help="Exception mode ('Plain','Context','Verbose')") + type=str, dest='InteractiveShell.xmode', default=NoConfigDefault, + help="Exception mode ('Plain','Context','Verbose')", + metavar='InteractiveShell.xmode') ), # These are only here to get the proper deprecation warnings (('-pylab','-wthread','-qthread','-q4thread','-gthread'), dict( - action='store_true', dest='THREADED_SHELL', default=NoDefault, + action='store_true', dest='Global.threaded_shell', default=NoConfigDefault, help="These command line flags are deprecated, see the 'gui' magic.") ), ) @@ -238,9 +257,11 @@ class IPythonAppCLConfigLoader(IPythonArgParseConfigLoader): arguments = cl_args +_default_config_file_name = 'ipython_config.py' + class IPythonApp(Application): name = 'ipython' - config_file_name = 'ipython_config.py' + config_file_name = _default_config_file_name def create_command_line_config(self): """Create and return a command line config loader.""" @@ -252,19 +273,15 @@ class IPythonApp(Application): """Do actions after loading cl config.""" clc = self.command_line_config - # This needs to be set here, the rest are set in pre_construct. - if hasattr(clc, 'CLASSIC'): - if clc.CLASSIC: clc.QUICK = 1 - # Display the deprecation warnings about threaded shells - if hasattr(clc, 'THREADED_SHELL'): + if hasattr(clc.Global, 'threaded_shell'): threaded_shell_warning() - del clc['THREADED_SHELL'] + del clc.Global['threaded_shell'] def load_file_config(self): - if hasattr(self.command_line_config, 'QUICK'): - if self.command_line_config.QUICK: - self.file_config = Struct() + if hasattr(self.command_line_config.Global, 'quick'): + if self.command_line_config.Global.quick: + self.file_config = Config() return super(IPythonApp, self).load_file_config() @@ -274,25 +291,28 @@ class IPythonApp(Application): def pre_construct(self): config = self.master_config - if hasattr(config, 'CLASSIC'): - if config.CLASSIC: - config.QUICK = 1 - config.CACHE_SIZE = 0 - config.PPRINT = 0 - config.PROMPT_IN1 = '>>> ' - config.PROMPT_IN2 = '... ' - config.PROMPT_OUT = '' - config.SEPARATE_IN = config.SEPARATE_OUT = config.SEPARATE_OUT2 = '' - config.COLORS = 'NoColor' - config.XMODE = 'Plain' + if hasattr(config.Global, 'classic'): + if config.Global.classic: + config.InteractiveShell.cache_size = 0 + config.InteractiveShell.pprint = 0 + config.InteractiveShell.prompt_in1 = '>>> ' + config.InteractiveShell.prompt_in2 = '... ' + config.InteractiveShell.prompt_out = '' + config.InteractiveShell.separate_in = \ + config.InteractiveShell.separate_out = \ + config.InteractiveShell.separate_out2 = '' + config.InteractiveShell.colors = 'NoColor' + config.InteractiveShell.xmode = 'Plain' # All this should be moved to traitlet handlers in InteractiveShell # But, currently InteractiveShell doesn't have support for changing # these values at runtime. Once we support that, this should # be moved there!!! - if hasattr(config, 'NOSEP'): - if config.NOSEP: - config.SEPARATE_IN = config.SEPARATE_OUT = config.SEPARATE_OUT2 = '0' + if hasattr(config.Global, 'nosep'): + if config.Global.nosep: + config.InteractiveShell.separate_in = \ + config.InteractiveShell.separate_out = \ + config.InteractiveShell.separate_out2 = '0' def construct(self): # I am a little hesitant to put these into InteractiveShell itself. @@ -307,12 +327,23 @@ class IPythonApp(Application): parent=None, config=self.master_config ) - print self.shell def start_app(self): self.shell.mainloop() +def load_default_config(ipythondir=None): + """Load the default config file from the default ipythondir. + + This is useful for embedded shells. + """ + if ipythondir is None: + ipythondir = get_ipython_dir() + cl = PyFileConfigLoader(_default_config_file_name, ipythondir) + config = cl.load_config() + return config + + if __name__ == '__main__': app = IPythonApp() app.start() \ No newline at end of file diff --git a/IPython/core/iplib.py b/IPython/core/iplib.py index d39c09d..200acfb 100644 --- a/IPython/core/iplib.py +++ b/IPython/core/iplib.py @@ -62,6 +62,7 @@ from IPython.lib.backgroundjobs import BackgroundJobManager from IPython.utils.ipstruct import Struct from IPython.utils import PyColorize from IPython.utils.genutils import * +from IPython.utils.genutils import get_ipython_dir from IPython.utils.strdispatch import StrDispatch from IPython.utils.platutils import toggle_set_term_title, set_term_title @@ -188,50 +189,47 @@ class SeparateStr(Str): class InteractiveShell(Component, Magic): """An enhanced, interactive shell for Python.""" - autocall = Enum((0,1,2), config_key='AUTOCALL') - autoedit_syntax = CBool(False, config_key='AUTOEDIT_SYNTAX') - autoindent = CBool(True, config_key='AUTOINDENT') - automagic = CBool(True, config_key='AUTOMAGIC') - display_banner = CBool(True, config_key='DISPLAY_BANNER') + autocall = Enum((0,1,2), config=True) + autoedit_syntax = CBool(False, config=True) + autoindent = CBool(True, config=True) + automagic = CBool(True, config=True) + display_banner = CBool(True, config=True) banner = Str('') - banner1 = Str(default_banner, config_key='BANNER1') - banner2 = Str('', config_key='BANNER2') - c = Str('', config_key='C') - cache_size = Int(1000, config_key='CACHE_SIZE') - classic = CBool(False, config_key='CLASSIC') - color_info = CBool(True, config_key='COLOR_INFO') + banner1 = Str(default_banner, config=True) + banner2 = Str('', config=True) + c = Str('', config=True) + cache_size = Int(1000, config=True) + color_info = CBool(True, config=True) colors = CaselessStrEnum(('NoColor','LightBG','Linux'), - default_value='LightBG', config_key='COLORS') - confirm_exit = CBool(True, config_key='CONFIRM_EXIT') - debug = CBool(False, config_key='DEBUG') - deep_reload = CBool(False, config_key='DEEP_RELOAD') + default_value='LightBG', config=True) + confirm_exit = CBool(True, config=True) + debug = CBool(False, config=True) + deep_reload = CBool(False, config=True) embedded = CBool(False) embedded_active = CBool(False) - editor = Str(get_default_editor(), config_key='EDITOR') + editor = Str(get_default_editor(), config=True) filename = Str("") - interactive = CBool(False, config_key='INTERACTIVE') - ipythondir= Unicode('', config_key='IPYTHONDIR') # Set to os.getcwd() in __init__ - logstart = CBool(False, config_key='LOGSTART') - logfile = Str('', config_key='LOGFILE') - logplay = Str('', config_key='LOGPLAY') + interactive = CBool(False, config=True) + ipythondir= Unicode('', config=True) # Set to get_ipython_dir() in __init__ + logstart = CBool(False, config=True) + logfile = Str('', config=True) + logplay = Str('', config=True) object_info_string_level = Enum((0,1,2), default_value=0, - config_keys='OBJECT_INFO_STRING_LEVEL') - pager = Str('less', config_key='PAGER') - pdb = CBool(False, config_key='PDB') - pprint = CBool(True, config_key='PPRINT') - profile = Str('', config_key='PROFILE') - prompt_in1 = Str('In [\\#]: ', config_key='PROMPT_IN1') - prompt_in2 = Str(' .\\D.: ', config_key='PROMPT_IN2') - prompt_out = Str('Out[\\#]: ', config_key='PROMPT_OUT1') - prompts_pad_left = CBool(True, config_key='PROMPTS_PAD_LEFT') - quiet = CBool(False, config_key='QUIET') - - readline_use = CBool(True, config_key='READLINE_USE') - readline_merge_completions = CBool(True, - config_key='READLINE_MERGE_COMPLETIONS') - readline_omit__names = Enum((0,1,2), default_value=0, - config_key='READLINE_OMIT_NAMES') - readline_remove_delims = Str('-/~', config_key='READLINE_REMOVE_DELIMS') + config=True) + pager = Str('less', config=True) + pdb = CBool(False, config=True) + pprint = CBool(True, config=True) + profile = Str('', config=True) + prompt_in1 = Str('In [\\#]: ', config=True) + prompt_in2 = Str(' .\\D.: ', config=True) + prompt_out = Str('Out[\\#]: ', config=True) + prompts_pad_left = CBool(True, config=True) + quiet = CBool(False, config=True) + + readline_use = CBool(True, config=True) + readline_merge_completions = CBool(True, config=True) + readline_omit__names = Enum((0,1,2), default_value=0, config=True) + readline_remove_delims = Str('-/~', config=True) readline_parse_and_bind = List([ 'tab: complete', '"\C-l": possible-completions', @@ -248,24 +246,22 @@ class InteractiveShell(Component, Magic): '"\e[B": history-search-forward', '"\C-k": kill-line', '"\C-u": unix-line-discard', - ], allow_none=False, config_key='READLINE_PARSE_AND_BIND' - ) + ], allow_none=False, config=True) - screen_length = Int(0, config_key='SCREEN_LENGTH') + screen_length = Int(0, config=True) # Use custom TraitletTypes that convert '0'->'' and '\\n'->'\n' - separate_in = SeparateStr('\n', config_key='SEPARATE_IN') - separate_out = SeparateStr('', config_key='SEPARATE_OUT') - separate_out2 = SeparateStr('', config_key='SEPARATE_OUT2') - - system_header = Str('IPython system call: ', config_key='SYSTEM_HEADER') - system_verbose = CBool(False, config_key='SYSTEM_VERBOSE') - term_title = CBool(False, config_key='TERM_TITLE') - wildcards_case_sensitive = CBool(True, config_key='WILDCARDS_CASE_SENSITIVE') + separate_in = SeparateStr('\n', config=True) + separate_out = SeparateStr('', config=True) + separate_out2 = SeparateStr('', config=True) + + system_header = Str('IPython system call: ', config=True) + system_verbose = CBool(False, config=True) + term_title = CBool(False, config=True) + wildcards_case_sensitive = CBool(True, config=True) xmode = CaselessStrEnum(('Context','Plain', 'Verbose'), - default_value='Context', config_key='XMODE') + default_value='Context', config=True) - alias = List(allow_none=False, config_key='ALIAS') autoexec = List(allow_none=False) # class attribute to indicate whether the class supports threads or not. @@ -279,7 +275,7 @@ class InteractiveShell(Component, Magic): # This is where traitlets with a config_key argument are updated # from the values on config. - super(InteractiveShell, self).__init__(parent, config=config, name='__IP') + super(InteractiveShell, self).__init__(parent, config=config) # These are relatively independent and stateless self.init_ipythondir(ipythondir) @@ -288,7 +284,7 @@ class InteractiveShell(Component, Magic): self.init_usage(usage) self.init_banner(banner1, banner2) - # Create namespaces (user_ns, user_global_ns, alias_table, etc.) + # Create namespaces (user_ns, user_global_ns, etc.) self.init_create_namespaces(user_ns, user_global_ns) # This has to be done after init_create_namespaces because it uses # something in self.user_ns, but before init_sys_modules, which @@ -327,6 +323,9 @@ class InteractiveShell(Component, Magic): self.init_pdb() self.hooks.late_startup_hook() + def get_ipython(self): + return self + #------------------------------------------------------------------------- # Traitlet changed handlers #------------------------------------------------------------------------- @@ -337,6 +336,10 @@ class InteractiveShell(Component, Magic): def _banner2_changed(self): self.compute_banner() + def _ipythondir_changed(self, name, new): + if not os.path.isdir(new): + os.makedirs(new, mode = 0777) + @property def usable_screen_length(self): if self.screen_length == 0: @@ -370,22 +373,16 @@ class InteractiveShell(Component, Magic): def init_ipythondir(self, ipythondir): if ipythondir is not None: self.ipythondir = ipythondir - self.config.IPYTHONDIR = self.ipythondir + self.config.Global.ipythondir = self.ipythondir return - if hasattr(self.config, 'IPYTHONDIR'): - self.ipythondir = self.config.IPYTHONDIR - if not hasattr(self.config, 'IPYTHONDIR'): - # cdw is always defined - self.ipythondir = os.getcwd() - - # The caller must make sure that ipythondir exists. We should - # probably handle this using a Dir traitlet. - if not os.path.isdir(self.ipythondir): - raise IOError('IPython dir does not exist: %s' % self.ipythondir) + if hasattr(self.config.Global, 'ipythondir'): + self.ipythondir = self.config.Global.ipythondir + else: + self.ipythondir = get_ipython_dir() # All children can just read this - self.config.IPYTHONDIR = self.ipythondir + self.config.Global.ipythondir = self.ipythondir def init_instance_attrs(self): self.jobs = BackgroundJobManager() @@ -825,11 +822,6 @@ class InteractiveShell(Component, Magic): # them from cluttering user-visible stuff. Will be updated later self.internal_ns = {} - # Namespace of system aliases. Each entry in the alias - # table must be a 2-tuple of the form (N,name), where N is the number - # of positional arguments of the alias. - self.alias_table = {} - # Now that FakeModule produces a real module, we've run into a nasty # problem: after script execution (via %run), the module where the user # code ran is deleted. Now that this object is a true module (needed @@ -863,7 +855,6 @@ class InteractiveShell(Component, Magic): # introspection facilities can search easily. self.ns_table = {'user':user_ns, 'user_global':user_global_ns, - 'alias':self.alias_table, 'internal':self.internal_ns, 'builtin':__builtin__.__dict__ } @@ -872,8 +863,7 @@ class InteractiveShell(Component, Magic): # we can safely clear (so it can NOT include builtin). This one can be # a simple list. self.ns_refs_table = [ user_ns, user_global_ns, self.user_config_ns, - self.alias_table, self.internal_ns, - self._main_ns_cache ] + self.internal_ns, self._main_ns_cache ] def init_sys_modules(self): # We need to insert into sys.modules something that looks like a @@ -961,11 +951,8 @@ class InteractiveShell(Component, Magic): method. If they were not empty before, data will simply be added to therm. """ - # The user namespace MUST have a pointer to the shell itself. - self.user_ns[self.name] = self - # Store myself as the public api!!! - self.user_ns['_ip'] = self + self.user_ns['get_ipython'] = self.get_ipython # make global variables for user access to the histories self.user_ns['_ih'] = self.input_hist @@ -994,13 +981,19 @@ class InteractiveShell(Component, Magic): for ns in self.ns_refs_table: ns.clear() + self.alias_manager.clear_aliases() + # Clear input and output histories self.input_hist[:] = [] self.input_hist_raw[:] = [] self.output_hist.clear() + # Restore the user namespaces to minimal usability self.init_user_ns() + # Restore the default and user aliases + self.alias_manager.init_aliases() + def push(self, variables, interactive=True): """Inject a group of variables into the IPython user namespace. @@ -1076,7 +1069,7 @@ class InteractiveShell(Component, Magic): histfname = 'history-%s' % self.profile except AttributeError: histfname = 'history' - self.histfile = os.path.join(self.config.IPYTHONDIR, histfname) + self.histfile = os.path.join(self.ipythondir, histfname) # Fill the history zero entry, user counter starts at 1 self.input_hist.append('\n') @@ -1084,12 +1077,12 @@ class InteractiveShell(Component, Magic): def init_shadow_hist(self): try: - self.db = pickleshare.PickleShareDB(self.config.IPYTHONDIR + "/db") + self.db = pickleshare.PickleShareDB(self.ipythondir + "/db") except exceptions.UnicodeDecodeError: print "Your ipythondir can't be decoded to unicode!" print "Please set HOME environment variable to something that" print r"only has ASCII characters, e.g. c:\home" - print "Now it is", self.config.IPYTHONDIR + print "Now it is", self.ipythondir sys.exit() self.shadowhist = ipcorehist.ShadowHist(self.db) @@ -1472,10 +1465,10 @@ class InteractiveShell(Component, Magic): import atexit from IPython.core.completer import IPCompleter self.Completer = IPCompleter(self, - self.user_ns, - self.user_global_ns, - self.readline_omit__names, - self.alias_table) + self.user_ns, + self.user_global_ns, + self.readline_omit__names, + self.alias_manager.alias_table) sdisp = self.strdispatchers.get('complete_command', StrDispatch()) self.strdispatchers['complete_command'] = sdisp self.Completer.custom_completers = sdisp @@ -1607,8 +1600,11 @@ class InteractiveShell(Component, Magic): error("Magic function `%s` not found." % magic_name) else: magic_args = self.var_expand(magic_args,1) - with nested(self.builtin_trap, self.display_trap): - return fn(magic_args) + with nested(self.builtin_trap,): + result = fn(magic_args) + # Unfortunately, the return statement is what will trigger + # the displayhook, but it is no longer set! + return result def define_magic(self, magicname, func): """Expose own function as magic function for ipython @@ -1666,6 +1662,7 @@ class InteractiveShell(Component, Magic): def init_alias(self): self.alias_manager = AliasManager(self, config=self.config) + self.ns_table['alias'] = self.alias_manager.alias_table, #------------------------------------------------------------------------- # Things related to the running of code @@ -1673,7 +1670,7 @@ class InteractiveShell(Component, Magic): def ex(self, cmd): """Execute a normal python statement in user namespace.""" - with nested(self.builtin_trap, self.display_trap): + with nested(self.builtin_trap,): exec cmd in self.user_global_ns, self.user_ns def ev(self, expr): @@ -1681,7 +1678,7 @@ class InteractiveShell(Component, Magic): Returns the result of evaluation """ - with nested(self.builtin_trap, self.display_trap): + with nested(self.builtin_trap,): return eval(expr, self.user_global_ns, self.user_ns) def mainloop(self, banner=None): diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b7d1398..9d33b97 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -47,6 +47,7 @@ from IPython.utils import wildcard from IPython.core import debugger, oinspect from IPython.core.error import TryNext from IPython.core.fakemodule import FakeModule +from IPython.core.prefilter import ESC_MAGIC from IPython.external.Itpl import Itpl, itpl, printpl,itplns from IPython.utils.PyColorize import Parser from IPython.utils.ipstruct import Struct @@ -205,9 +206,9 @@ python-profiler package from non-free.""") namespaces = [ ('Interactive', self.shell.user_ns), ('IPython internal', self.shell.internal_ns), ('Python builtin', __builtin__.__dict__), - ('Alias', self.shell.alias_table), + ('Alias', self.shell.alias_manager.alias_table), ] - alias_ns = self.shell.alias_table + alias_ns = self.shell.alias_manager.alias_table # initialize results to 'null' found = 0; obj = None; ospace = None; ds = None; @@ -244,7 +245,7 @@ python-profiler package from non-free.""") # Try to see if it's magic if not found: - if oname.startswith(self.shell.ESC_MAGIC): + if oname.startswith(ESC_MAGIC): oname = oname[1:] obj = getattr(self,'magic_'+oname,None) if obj is not None: @@ -272,10 +273,10 @@ python-profiler package from non-free.""") # Characters that need to be escaped for latex: escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE) # Magic command names as headers: - cmd_name_re = re.compile(r'^(%s.*?):' % self.shell.ESC_MAGIC, + cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC, re.MULTILINE) # Magic commands - cmd_re = re.compile(r'(?P%s.+?\b)(?!\}\}:)' % self.shell.ESC_MAGIC, + cmd_re = re.compile(r'(?P%s.+?\b)(?!\}\}:)' % ESC_MAGIC, re.MULTILINE) # Paragraph continue par_re = re.compile(r'\\$',re.MULTILINE) @@ -376,7 +377,7 @@ python-profiler package from non-free.""") # Functions for IPython shell work (vars,funcs, config, etc) def magic_lsmagic(self, parameter_s = ''): """List currently available magic functions.""" - mesc = self.shell.ESC_MAGIC + mesc = ESC_MAGIC print 'Available magic functions:\n'+mesc+\ (' '+mesc).join(self.lsmagic()) print '\n' + Magic.auto_status[self.shell.automagic] @@ -424,11 +425,11 @@ python-profiler package from non-free.""") if mode == 'rest': - rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(self.shell.ESC_MAGIC, + rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC, fname,fndoc)) else: - magic_docs.append('%s%s:\n\t%s\n' %(self.shell.ESC_MAGIC, + magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC, fname,fndoc)) magic_docs = ''.join(magic_docs) @@ -479,7 +480,7 @@ of any of them, type %magic_name?, e.g. '%cd?'. Currently the magic system has the following functions:\n""" - mesc = self.shell.ESC_MAGIC + mesc = ESC_MAGIC outmsg = ("%s\n%s\n\nSummary of magic functions (from %slsmagic):" "\n\n%s%s\n\n%s" % (outmsg, magic_docs,mesc,mesc, @@ -2620,52 +2621,27 @@ Defaulting color scheme to 'NoColor'""" par = parameter_s.strip() if not par: stored = self.db.get('stored_aliases', {} ) - atab = self.shell.alias_table - aliases = atab.keys() - aliases.sort() - res = [] - showlast = [] - for alias in aliases: - special = False - try: - tgt = atab[alias][1] - except (TypeError, AttributeError): - # unsubscriptable? probably a callable - tgt = atab[alias] - special = True - # 'interesting' aliases - if (alias in stored or - special or - alias.lower() != os.path.splitext(tgt)[0].lower() or - ' ' in tgt): - showlast.append((alias, tgt)) - else: - res.append((alias, tgt )) - - # show most interesting aliases last - res.extend(showlast) - print "Total number of aliases:",len(aliases) - return res + aliases = sorted(self.shell.alias_manager.aliases) + # for k, v in stored: + # atab.append(k, v[0]) + + print "Total number of aliases:", len(aliases) + return aliases + + # Now try to define a new one try: - alias,cmd = par.split(None,1) + alias,cmd = par.split(None, 1) except: print oinspect.getdoc(self.magic_alias) else: - nargs = cmd.count('%s') - if nargs>0 and cmd.find('%l')>=0: - error('The %s and %l specifiers are mutually exclusive ' - 'in alias definitions.') - else: # all looks OK - self.shell.alias_table[alias] = (nargs,cmd) - self.shell.alias_table_validate(verbose=0) + self.shell.alias_manager.soft_define_alias(alias, cmd) # end magic_alias def magic_unalias(self, parameter_s = ''): """Remove an alias""" aname = parameter_s.strip() - if aname in self.shell.alias_table: - del self.shell.alias_table[aname] + self.shell.alias_manager.undefine_alias(aname) stored = self.db.get('stored_aliases', {} ) if aname in stored: print "Removing %stored alias",aname @@ -2686,6 +2662,7 @@ Defaulting color scheme to 'NoColor'""" This function also resets the root module cache of module completer, used on slow filesystems. """ + from IPython.core.alias import InvalidAliasError # for the benefit of module completer in ipy_completers.py del self.db['rootmodules'] @@ -2693,14 +2670,13 @@ Defaulting color scheme to 'NoColor'""" path = [os.path.abspath(os.path.expanduser(p)) for p in os.environ.get('PATH','').split(os.pathsep)] path = filter(os.path.isdir,path) - - alias_table = self.shell.alias_table + syscmdlist = [] + # Now define isexec in a cross platform manner. if os.name == 'posix': isexec = lambda fname:os.path.isfile(fname) and \ os.access(fname,os.X_OK) else: - try: winext = os.environ['pathext'].replace(';','|').replace('.','') except KeyError: @@ -2710,6 +2686,8 @@ Defaulting color scheme to 'NoColor'""" execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) savedir = os.getcwd() + + # Now walk the paths looking for executables to alias. try: # write the whole loop for posix/Windows so we don't have an if in # the innermost part @@ -2717,14 +2695,16 @@ Defaulting color scheme to 'NoColor'""" for pdir in path: os.chdir(pdir) for ff in os.listdir(pdir): - if isexec(ff) and ff not in self.shell.no_alias: - # each entry in the alias table must be (N,name), - # where N is the number of positional arguments of the - # alias. - # Dots will be removed from alias names, since ipython - # assumes names with dots to be python code - alias_table[ff.replace('.','')] = (0,ff) - syscmdlist.append(ff) + if isexec(ff): + try: + # Removes dots from the name since ipython + # will assume names with dots to be python. + self.shell.alias_manager.define_alias( + ff.replace('.',''), ff) + except InvalidAliasError: + pass + else: + syscmdlist.append(ff) else: for pdir in path: os.chdir(pdir) @@ -2733,16 +2713,14 @@ Defaulting color scheme to 'NoColor'""" if isexec(ff) and base.lower() not in self.shell.no_alias: if ext.lower() == '.exe': ff = base - alias_table[base.lower().replace('.','')] = (0,ff) - syscmdlist.append(ff) - # Make sure the alias table doesn't contain keywords or builtins - self.shell.alias_table_validate() - # Call again init_auto_alias() so we get 'rm -i' and other - # modified aliases since %rehashx will probably clobber them - - # no, we don't want them. if %rehashx clobbers them, good, - # we'll probably get better versions - # self.shell.init_auto_alias() + try: + # Removes dots from the name since ipython + # will assume names with dots to be python. + self.shell.alias_manager.define_alias( + base.lower().replace('.',''), ff) + except InvalidAliasError: + pass + syscmdlist.append(ff) db = self.db db['syscmdlist'] = syscmdlist finally: @@ -3167,7 +3145,7 @@ Defaulting color scheme to 'NoColor'""" """ start = parameter_s.strip() - esc_magic = self.shell.ESC_MAGIC + esc_magic = ESC_MAGIC # Identify magic commands even if automagic is on (which means # the in-memory version is different from that typed by the user). if self.shell.automagic: diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index 77f2401..fd994a1 100644 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -174,7 +174,7 @@ class PrefilterManager(Component): to transform the input line. """ - multi_line_specials = CBool(True, config_key='MULTI_LINE_SPECIALS') + multi_line_specials = CBool(True, config=True) def __init__(self, parent, config=None): super(PrefilterManager, self).__init__(parent, config=config) @@ -274,16 +274,16 @@ class PrefilterManager(Component): # the input history needs to track even empty lines stripped = line.strip() - handle_normal = self.get_handler_by_name('normal') + normal_handler = self.get_handler_by_name('normal') if not stripped: if not continue_prompt: self.shell.outputcache.prompt_count -= 1 - return handle_normal(line_info) + return normal_handler.handle(line_info) # special handlers are only allowed for single line statements if continue_prompt and not self.multi_line_specials: - return handle_normal(line_info) + return normal_handler.handle(line_info) return self.prefilter_line_info(line_info) @@ -310,7 +310,7 @@ class PrefilterManager(Component): class PrefilterChecker(Component): """Inspect an input line and return a handler for that line.""" - priority = Int(100) + priority = Int(100, config=True) shell = Any prefilter_manager = Any @@ -336,7 +336,7 @@ class PrefilterChecker(Component): class EmacsChecker(PrefilterChecker): - priority = Int(100) + priority = Int(100, config=True) def check(self, line_info): "Emacs ipython-mode tags certain input lines." @@ -348,7 +348,7 @@ class EmacsChecker(PrefilterChecker): class ShellEscapeChecker(PrefilterChecker): - priority = Int(200) + priority = Int(200, config=True) def check(self, line_info): if line_info.line.lstrip().startswith(ESC_SHELL): @@ -357,7 +357,7 @@ class ShellEscapeChecker(PrefilterChecker): class IPyAutocallChecker(PrefilterChecker): - priority = Int(300) + priority = Int(300, config=True) def check(self, line_info): "Instances of IPyAutocall in user_ns get autocalled immediately" @@ -371,7 +371,7 @@ class IPyAutocallChecker(PrefilterChecker): class MultiLineMagicChecker(PrefilterChecker): - priority = Int(400) + priority = Int(400, config=True) def check(self, line_info): "Allow ! and !! in multi-line statements if multi_line_specials is on" @@ -388,7 +388,7 @@ class MultiLineMagicChecker(PrefilterChecker): class EscCharsChecker(PrefilterChecker): - priority = Int(500) + priority = Int(500, config=True) def check(self, line_info): """Check for escape character and return either a handler to handle it, @@ -406,7 +406,7 @@ class EscCharsChecker(PrefilterChecker): class AssignmentChecker(PrefilterChecker): - priority = Int(600) + priority = Int(600, config=True) def check(self, line_info): """Check to see if user is assigning to a var for the first time, in @@ -423,7 +423,7 @@ class AssignmentChecker(PrefilterChecker): class AutoMagicChecker(PrefilterChecker): - priority = Int(700) + priority = Int(700, config=True) def check(self, line_info): """If the ifun is magic, and automagic is on, run it. Note: normal, @@ -447,7 +447,7 @@ class AutoMagicChecker(PrefilterChecker): class AliasChecker(PrefilterChecker): - priority = Int(800) + priority = Int(800, config=True) @auto_attr def alias_manager(self): @@ -467,7 +467,7 @@ class AliasChecker(PrefilterChecker): class PythonOpsChecker(PrefilterChecker): - priority = Int(900) + priority = Int(900, config=True) def check(self, line_info): """If the 'rest' of the line begins with a function call or pretty much @@ -482,7 +482,7 @@ class PythonOpsChecker(PrefilterChecker): class AutocallChecker(PrefilterChecker): - priority = Int(1000) + priority = Int(1000, config=True) def check(self, line_info): "Check if the initial word/function is callable and autocall is on." @@ -567,7 +567,7 @@ class AliasHandler(PrefilterHandler): transformed = self.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest) # pre is needed, because it carries the leading whitespace. Otherwise # aliases won't work in indented sections. - line_out = '%s_ip.system(%s)' % (line_info.pre_whitespace, + line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace, make_quoted_expr(transformed)) self.shell.log(line_info.line, line_out, line_info.continue_prompt) @@ -597,7 +597,7 @@ class ShellEscapeHandler(PrefilterHandler): return magic_handler.handle(line_info) else: cmd = line.lstrip().lstrip(ESC_SHELL) - line_out = '%s_ip.system(%s)' % (line_info.pre_whitespace, + line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace, make_quoted_expr(cmd)) # update cache/log and return self.shell.log(line, line_out, line_info.continue_prompt) @@ -607,13 +607,13 @@ class ShellEscapeHandler(PrefilterHandler): class MagicHandler(PrefilterHandler): handler_name = Str('magic') - esc_strings = List(['%']) + esc_strings = List([ESC_MAGIC]) def handle(self, line_info): """Execute magic functions.""" ifun = line_info.ifun the_rest = line_info.the_rest - cmd = '%s_ip.magic(%s)' % (line_info.pre_whitespace, + cmd = '%sget_ipython().magic(%s)' % (line_info.pre_whitespace, make_quoted_expr(ifun + " " + the_rest)) self.shell.log(line_info.line, cmd, line_info.continue_prompt) return cmd @@ -656,7 +656,7 @@ class AutoHandler(PrefilterHandler): # We only apply it to argument-less calls if the autocall # parameter is set to 2. We only need to check that autocall is < # 2, since this function isn't called unless it's at least 1. - if not the_rest and (self.autocall < 2) and not force_auto: + if not the_rest and (self.shell.autocall < 2) and not force_auto: newcmd = '%s %s' % (ifun,the_rest) auto_rewrite = False else: diff --git a/IPython/core/prompts.py b/IPython/core/prompts.py index 5bb31bc..228edb7 100644 --- a/IPython/core/prompts.py +++ b/IPython/core/prompts.py @@ -546,8 +546,11 @@ class CachedOutput: # don't use print, puts an extra space cout_write(self.output_sep) outprompt = self.shell.hooks.generate_output_prompt() + # print "Got prompt: ", outprompt if self.do_full_cache: cout_write(outprompt) + else: + print "self.do_full_cache = False" # and now call a possibly user-defined print mechanism manipulated_val = self.display(arg) diff --git a/IPython/core/tests/obj_del.py b/IPython/core/tests/obj_del.py index 56c8700..d925dc6 100644 --- a/IPython/core/tests/obj_del.py +++ b/IPython/core/tests/obj_del.py @@ -31,4 +31,5 @@ class A(object): a = A() # Now, we force an exit, the caller will check that the del printout was given +_ip = get_ipython() _ip.ask_exit() diff --git a/IPython/core/tests/test_component.py b/IPython/core/tests/test_component.py index 5db9ed9..e6713a9 100644 --- a/IPython/core/tests/test_component.py +++ b/IPython/core/tests/test_component.py @@ -26,7 +26,7 @@ from IPython.core.component import Component, ComponentError from IPython.utils.traitlets import ( TraitletError, Int, Float, Str ) -from IPython.utils.ipstruct import Struct +from IPython.config.loader import Config #----------------------------------------------------------------------------- @@ -138,9 +138,9 @@ class TestComponentConfig(TestCase): self.assertEquals(c2.config, c3.config) def test_custom(self): - config = Struct() - config.FOO = 'foo' - config.BAR = 'bar' + config = Config() + config.foo = 'foo' + config.bar = 'bar' c1 = Component(None, config=config) c2 = Component(c1) c3 = Component(c2) @@ -156,19 +156,19 @@ class TestComponentConfig(TestCase): def test_inheritance(self): class MyComponent(Component): - a = Int(1, config_key='A') - b = Float(1.0, config_key='B') + a = Int(1, config=True) + b = Float(1.0, config=True) c = Str('no config') - config = Struct() - config.A = 2 - config.B = 2.0 + config = Config() + config.MyComponent.a = 2 + config.MyComponent.b = 2.0 c1 = MyComponent(None, config=config) c2 = MyComponent(c1) - self.assertEquals(c1.a, config.A) - self.assertEquals(c1.b, config.B) - self.assertEquals(c2.a, config.A) - self.assertEquals(c2.b, config.B) - c4 = MyComponent(c2, config=Struct()) + self.assertEquals(c1.a, config.MyComponent.a) + self.assertEquals(c1.b, config.MyComponent.b) + self.assertEquals(c2.a, config.MyComponent.a) + self.assertEquals(c2.b, config.MyComponent.b) + c4 = MyComponent(c2, config=Config()) self.assertEquals(c4.a, 1) self.assertEquals(c4.b, 1.0) diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index f1f97a8..f2eb516 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -20,14 +20,15 @@ from IPython.testing import tools as tt def test_rehashx(): # clear up everything - _ip.alias_table.clear() + _ip = get_ipython() + _ip.alias_manager.alias_table.clear() del _ip.db['syscmdlist'] _ip.magic('rehashx') # Practically ALL ipython development systems will have more than 10 aliases - yield (nt.assert_true, len(_ip.alias_table) > 10) - for key, val in _ip.alias_table.items(): + yield (nt.assert_true, len(_ip.alias_manager.alias_table) > 10) + for key, val in _ip.alias_manager.alias_table.items(): # we must strip dots from alias names nt.assert_true('.' not in key) @@ -65,6 +66,7 @@ def doctest_hist_r(): # This test is known to fail on win32. # See ticket https://bugs.launchpad.net/bugs/366334 def test_obj_del(): + _ip = get_ipython() """Test that object's __del__ methods are called on exit.""" test_dir = os.path.dirname(__file__) del_file = os.path.join(test_dir,'obj_del.py') @@ -221,13 +223,14 @@ class TestMagicRun(object): self.fname = fname def run_tmpfile(self): + _ip = get_ipython() # This fails on Windows if self.tmpfile.name has spaces or "~" in it. # See below and ticket https://bugs.launchpad.net/bugs/366353 _ip.magic('run "%s"' % self.fname) def test_builtins_id(self): """Check that %run doesn't damage __builtins__ """ - + _ip = get_ipython() # Test that the id of __builtins__ is not modified by %run bid1 = id(_ip.user_ns['__builtins__']) self.run_tmpfile() @@ -241,12 +244,14 @@ class TestMagicRun(object): be a dict (it should be a module) by a previous use of %run. So we also check explicitly that it really is a module: """ + _ip = get_ipython() self.run_tmpfile() tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys)) def test_prompts(self): """Test that prompts correctly generate after %run""" self.run_tmpfile() + _ip = get_ipython() p2 = str(_ip.outputcache.prompt2).strip() nt.assert_equals(p2[:3], '...') @@ -261,7 +266,7 @@ class TestMagicRun(object): # Multiple tests for clipboard pasting def test_paste(): - + _ip = get_ipython() def paste(txt, flags='-q'): """Paste input text, by default in quiet mode""" hooks.clipboard_get = lambda : txt diff --git a/IPython/extensions/ext_rescapture.py b/IPython/extensions/ext_rescapture.py index fdb7a4d..d4b8508 100644 --- a/IPython/extensions/ext_rescapture.py +++ b/IPython/extensions/ext_rescapture.py @@ -21,14 +21,14 @@ def hnd_magic(line,mo): var = mo.group('varname') cmd = mo.group('cmd') expr = make_quoted_expr(cmd) - return itpl('$var = _ip.magic($expr)') + return itpl('$var = get_ipython().magic($expr)') def hnd_syscmd(line,mo): """ Handle a = !ls """ var = mo.group('varname') cmd = mo.group('cmd') expr = make_quoted_expr(itpl("sc -l =$cmd")) - return itpl('$var = _ip.magic($expr)') + return itpl('$var = get_ipython().magic($expr)') def install_re_handler(pat, hnd): ip.meta.re_prefilters.append((re.compile(pat), hnd)) diff --git a/IPython/extensions/ipipe.py b/IPython/extensions/ipipe.py index 13b18ba..74215bc 100644 --- a/IPython/extensions/ipipe.py +++ b/IPython/extensions/ipipe.py @@ -1644,7 +1644,7 @@ class ialias(Table): def __iter__(self): api = ipapi.get() - for (name, (args, command)) in api.alias_table.iteritems(): + for (name, (args, command)) in api.alias_manager.alias_table.iteritems(): yield Alias(name, args, command) diff --git a/IPython/extensions/ipy_profile_sh.py b/IPython/extensions/ipy_profile_sh.py index d678e57..571f5c4 100644 --- a/IPython/extensions/ipy_profile_sh.py +++ b/IPython/extensions/ipy_profile_sh.py @@ -109,7 +109,7 @@ def main(): cmd = noext key = mapper(cmd) - if key not in ip.alias_table: + if key not in ip.alias_manager.alias_table: # Dots will be removed from alias names, since ipython # assumes names with dots to be python code @@ -159,7 +159,7 @@ def slash_prefilter_f(self,line): """ from IPython.utils import genutils if re.match('(?:[.~]|/[a-zA-Z_0-9]+)/', line): - return "_ip.system(" + genutils.make_quoted_expr(line)+")" + return "get_ipython().system(" + genutils.make_quoted_expr(line)+")" raise TryNext # XXX You do not need to understand the next function! diff --git a/IPython/extensions/ipy_rehashdir.py b/IPython/extensions/ipy_rehashdir.py index 274c932..7250470 100644 --- a/IPython/extensions/ipy_rehashdir.py +++ b/IPython/extensions/ipy_rehashdir.py @@ -88,7 +88,7 @@ def rehashdir_f(self,arg): if not arg: arg = '.' path = map(os.path.abspath,arg.split(';')) - alias_table = self.shell.alias_table + alias_table = self.shell.alias_manager.alias_table if os.name == 'posix': isexec = lambda fname:os.path.isfile(fname) and \ diff --git a/IPython/extensions/jobctrl.py b/IPython/extensions/jobctrl.py index 8d1aa3a..9a38ce5 100644 --- a/IPython/extensions/jobctrl.py +++ b/IPython/extensions/jobctrl.py @@ -126,8 +126,8 @@ def jobctrl_prefilter_f(self,line): line = ip.expand_aliases(fn,rest) if not _jobq: - return '_ip.startjob(%s)' % genutils.make_quoted_expr(line) - return '_ip.jobq(%s)' % genutils.make_quoted_expr(line) + return 'get_ipython().startjob(%s)' % genutils.make_quoted_expr(line) + return 'get_ipython().jobq(%s)' % genutils.make_quoted_expr(line) raise TryNext diff --git a/IPython/extensions/pspersistence.py b/IPython/extensions/pspersistence.py index c937b49..dbba916 100644 --- a/IPython/extensions/pspersistence.py +++ b/IPython/extensions/pspersistence.py @@ -157,6 +157,7 @@ def magic_store(self, parameter_s=''): obj = ip.user_ns[args[0]] except KeyError: # it might be an alias + # This needs to be refactored to use the new AliasManager stuff. if args[0] in self.alias_table: staliases = db.get('stored_aliases',{}) staliases[ args[0] ] = self.alias_table[ args[0] ] diff --git a/IPython/kernel/magic.py b/IPython/kernel/magic.py index 4eab117..0d40f6a 100644 --- a/IPython/kernel/magic.py +++ b/IPython/kernel/magic.py @@ -99,7 +99,7 @@ def pxrunsource(self, source, filename="", symbol="single"): # Case 3 # Because autopx is enabled, we now call executeAll or disable autopx if # %autopx or autopx has been called - if '_ip.magic("%autopx' in source or '_ip.magic("autopx' in source: + if 'get_ipython().magic("%autopx' in source or 'get_ipython().magic("autopx' in source: _disable_autopx(self) return False else: diff --git a/IPython/utils/tests/test_traitlets.py b/IPython/utils/tests/test_traitlets.py index 09661eb..e7c0e75 100644 --- a/IPython/utils/tests/test_traitlets.py +++ b/IPython/utils/tests/test_traitlets.py @@ -353,14 +353,17 @@ class TestHasTraitlets(TestCase): class A(HasTraitlets): i = Int(config_key='VALUE1', other_thing='VALUE2') f = Float(config_key='VALUE3', other_thing='VALUE2') + j = Int(0) a = A() - self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f)) - traitlets = a.traitlets(config_key=lambda v: True) - self.assertEquals(traitlets, dict(i=A.i, f=A.f)) + self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f, j=A.j)) traitlets = a.traitlets(config_key='VALUE1', other_thing='VALUE2') self.assertEquals(traitlets, dict(i=A.i)) - traitlets = a.traitlets('config_key') - self.assertEquals(traitlets, dict(i=A.i, f=A.f)) + + # This passes, but it shouldn't because I am replicating a bug in + # traits. + traitlets = a.traitlets(config_key=lambda v: True) + self.assertEquals(traitlets, dict(i=A.i, f=A.f, j=A.j)) + #----------------------------------------------------------------------------- # Tests for specific traitlet types diff --git a/IPython/utils/traitlets.py b/IPython/utils/traitlets.py index 9438d9a..b02e165 100644 --- a/IPython/utils/traitlets.py +++ b/IPython/utils/traitlets.py @@ -458,19 +458,22 @@ class HasTraitlets(object): """Get a list of all the names of this classes traitlets.""" return self.traitlets(**metadata).keys() - def traitlets(self, *args, **metadata): + def traitlets(self, **metadata): """Get a list of all the traitlets of this class. The TraitletTypes returned don't know anything about the values that the various HasTraitlet's instances are holding. + + This follows the same algorithm as traits does and does not allow + for any simple way of specifying merely that a metadata name + exists, but has any value. This is because get_metadata returns + None if a metadata key doesn't exist. """ traitlets = dict([memb for memb in inspect.getmembers(self.__class__) if \ isinstance(memb[1], TraitletType)]) - if len(metadata) == 0 and len(args) == 0: - return traitlets - for meta_name in args: - metadata[meta_name] = lambda _: True + if len(metadata) == 0: + return traitlets for meta_name, meta_eval in metadata.items(): if type(meta_eval) is not FunctionType: