diff --git a/IPython/config/loader.py b/IPython/config/loader.py new file mode 100644 index 0000000..8a143b1 --- /dev/null +++ b/IPython/config/loader.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# encoding: utf-8 +"""A factory for creating configuration objects. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os + +from IPython.utils.ipstruct import Struct + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + + +class ConfigLoaderError(Exception): + pass + + +class ConfigLoader(object): + """A object for loading configurations from just about anywhere. + + The resulting configuration is packaged as a :class:`Struct`. + """ + + def __init__(self): + """A base class for config loaders. + + Examples + -------- + + >>> cl = ConfigLoader() + >>> config = cl.load_config() + >>> config + {} + """ + self.clear() + + def clear(self): + self.config = Struct() + + def load_config(self): + """Load a config from somewhere, return a Struct. + + Usually, this will cause self.config to be set and then returned. + """ + return self.config + + +class FileConfigLoader(ConfigLoader): + """A config loader for pure python files. + + This calls execfile on a plain python file and looks for attributes + that are all caps. These attribute are added to the config Struct. + """ + + def __init__(self, filename, path='.'): + """Build a config loader for a filename and path. + + Parameters + ---------- + filename : str + The file name of the config file. + path : str, list, tuple + The path to search for the config file on, or a sequence of + paths to try in order + + Examples + -------- + + + """ + self.filename = filename + self.path = path + self.full_filename = '' + self.data = None + ConfigLoader.__init__(self) + + def find_file(self): + """Implement file finding logic here.""" + self.full_filename = self.filename + + def read_file_as_dict(self): + if not os.path.isfile(self.full_filename): + raise IOError("config file does not exist: %r" % self.fullfilename) + self.data = {} + execfile(self.full_filename, self.data) + + def convert_to_struct(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 + + def load_config(self): + self.find_file() + self.read_file_as_dict() + self.convert_to_struct() + return self.config + +class PyConfigLoader(object): + pass + + +class DefaultFileConfigLoader(object): + + def __init__(self, filename, install_location): + pass + + def load_config(self): + pass + + def install(self, force=False): + pass + + +class CommandLineConfigLoader(ConfigLoader): + + def __init__(self): + self.parser = None + self.parsed_data = None + self.clear() + + + def clear(self): + self.config = Struct() + + def load_config(self, args=None): + self.create_parser() + self.parse_args(args) + self.convert_to_struct() + return self.config + + def create_parser(self): + """Create self.parser""" + + def parse_args(self, args=None): + """self.parser->self.parsed_data""" + if self.parser is None: + raise ConfigLoaderError('self.parser does not exist') + if args is None: + self.parsed_data = parser.parse_args() + else: + self.parse_data = parser.parse_args(args) + + def convert_to_struct(self): + """self.parsed_data->self.config""" + if self.parsed_data is None: + raise ConfigLoaderError('self.parsed_data does not exist') + self.config = Struct(vars(self.parsed_data)) + + +class ArgParseConfigLoader(CommandLineConfigLoader): + + # arguments = [(('-f','--file'),dict(type=str,dest='file'))] + arguments = [] + + def __init__(self, *args, **kw): + """Create a config loader for use with argparse. + + The args and kwargs arguments here are passed onto the constructor + of :class:`argparse.ArgumentParser`. + """ + self.args = args + self.kw = kw + CommandLineConfigLoader.__init__(self) + + def create_parser(self): + self.parser = argparse.ArgumentParser(*self.args, **self.kw) + self.add_arguments() + + def add_arguments(self): + for argument in self.arguments: + self.parser.add_argument(*argument[0],**argument[1]) + diff --git a/IPython/core/component.py b/IPython/core/component.py new file mode 100644 index 0000000..2be6db6 --- /dev/null +++ b/IPython/core/component.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +A lightweight component system for IPython. + +Authors: + +* Brian Granger +* Fernando Perez +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + + +from weakref import WeakValueDictionary + +from IPython.utils.traitlets import ( + HasTraitlets, TraitletError, MetaHasTraitlets, + Int, Float, Str, Bool, Unicode +) + + +#----------------------------------------------------------------------------- +# Helper classes for Components +#----------------------------------------------------------------------------- + + +class Config(object): + pass + + +class MetaComponentTracker(type): + """A metaclass that tracks instances of Components and its subclasses.""" + + def __init__(cls, name, bases, d): + super(MetaComponentTracker, cls).__init__(name, bases, d) + cls.__instance_refs = WeakValueDictionary() + cls.__numcreated = 0 + + def __call__(cls, *args, **kw): + """Called when class is called (instantiated)!!! + + Then a Component or subclass is instantiated, this is called and + the instance is saved in a WeakValueDictionary for tracking. + """ + + instance = super(MetaComponentTracker, cls).__call__(*args, **kw) + for c in cls.__mro__: + if issubclass(cls, c) and issubclass(c, Component): + c.__numcreated += 1 + c.__instance_refs[c.__numcreated] = instance + return instance + + def get_instances(cls): + """Get all instances of cls and its subclasses.""" + return cls.__instance_refs.values() + + def get_instances_by_name(cls, name): + """Get all instances of cls and its subclasses by name.""" + return [i for i in cls.get_instances() if i.name == name] + + def get_instances_by_subclass(cls, thisclass): + """Get all instances of cls that are instances of thisclass. + + This includes all instances of subclasses of thisclass. + """ + return [i for i in cls.get_instances() if isinstance(i, thisclass)] + + def get_instances_by_class(cls, thisclass): + """Get all instances of cls that are instances of thisclass. + + This exclused instances of thisclass subclasses. + """ + + return [i for i in cls.get_instances() if type(i) is thisclass] + + def get_instances_by_condition(cls, call): + """Get all instances of cls, i such that call(i)==True.""" + return [i for i in cls.get_instances() if call(i)] + + +class ComponentNameGenerator(object): + """A Singleton to generate unique component names.""" + + def __init__(self, prefix): + self.prefix = prefix + self.i = 0 + + def __call__(self): + count = self.i + self.i += 1 + return "%s%s" % (self.prefix, count) + + +ComponentNameGenerator = ComponentNameGenerator('ipython.component') + + +class MetaComponent(MetaHasTraitlets, MetaComponentTracker): + pass + + +#----------------------------------------------------------------------------- +# Component implementation +#----------------------------------------------------------------------------- + + +class Component(HasTraitlets): + + __metaclass__ = MetaComponent + + config = Config() + + def __init__(self, parent, name=None, config=None): + """Create a component given a parent. + + Parameters + ---------- + parent : Component subclass + The parent in the component graph. The parent is used + to get the root of the component graph. + name : str + The unique name of the component. If empty, then a unique + one will be autogenerated. + config : Config + If this is empty, self.config = root.config, otherwise + self.config = config and root.config is ignored. This argument + should be used to pass the config to the root. Otherwise, it + can be used to *override* the inheritance of root.config. If a + caller wants to modify root.config (not override), the caller + should make a copy and change attributes and then pass the copy + to this argument. We might think about changing this behavior. + """ + super(Component, self).__init__() + if name is None: + self._name = ComponentNameGenerator() + else: + self._name = name + self.parent = parent # this uses the property and handles None + if config is not None: + self.config = config + else: + if self.parent is not None: + self.config = self.parent.config + + #------------------------------------------------------------------------- + # Properties + #------------------------------------------------------------------------- + + def _set_name(self, name): + # This should use the ComponentNameGenerator to test for uniqueness + self._name = name + + def _get_name(self): + return self._name + + name = property(_get_name, _set_name) + + def _set_parent(self, parent): + if parent is None: + self._parent = None + self._root = self + else: + assert isinstance(parent, Component), 'parent must be a component' + self._parent = parent + self._root = parent.root + + def _get_parent(self): + return self._parent + + parent = property(_get_parent, _set_parent) + + @property + def root(self): + return self._root + diff --git a/IPython/core/shell.py b/IPython/core/shell.py index 12d83cf..f2b2602 100644 --- a/IPython/core/shell.py +++ b/IPython/core/shell.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# encoding: utf-8 """IPython Shell classes. Originally, this module was horribly complicated because of the need to diff --git a/IPython/utils/traitlets.py b/IPython/utils/traitlets.py new file mode 100644 index 0000000..ad2f343 --- /dev/null +++ b/IPython/utils/traitlets.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +A lightweight Traits like module. + +Authors: + +* Brian Granger +* Enthought, Inc. Some of the code in this file comes from enthought.traits + and is licensed under the BSD license. Also, many of the ideas also come + from enthought.traits even though our implementation is very different. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import inspect +import types +from types import InstanceType + +#----------------------------------------------------------------------------- +# Basic classes +#----------------------------------------------------------------------------- + + +class NoDefaultSpecified ( object ): pass +NoDefaultSpecified = NoDefaultSpecified() + + +class Undefined ( object ): pass +Undefined = Undefined() + + +class TraitletError(Exception): + pass + + +#----------------------------------------------------------------------------- +# Utilities +#----------------------------------------------------------------------------- + + +def class_of ( object ): + """ Returns a string containing the class name of an object with the + correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image', + 'a PlotValue'). + """ + if isinstance( object, basestring ): + return add_article( object ) + + return add_article( object.__class__.__name__ ) + + +def add_article ( name ): + """ Returns a string containing the correct indefinite article ('a' or 'an') + prefixed to the specified string. + """ + if name[:1].lower() in 'aeiou': + return 'an ' + name + + return 'a ' + name + + +def repr_type(obj): + """ Return a string representation of a value and its type for readable + error messages. + """ + the_type = type(obj) + if the_type is InstanceType: + # Old-style class. + the_type = obj.__class__ + msg = '%r %r' % (obj, the_type) + return msg + + +def parse_notifier_name(name): + if isinstance(name, str): + return [name] + elif name is None: + return ['anytraitlet'] + elif isinstance(name, (list, tuple)): + for n in name: + assert isinstance(n, str), "names must be strings" + return name + + +#----------------------------------------------------------------------------- +# Base TraitletType for all traitlets +#----------------------------------------------------------------------------- + + +class TraitletType(object): + + metadata = {} + default_value = None + info_text = 'any value' + + # def __init__(self, name, default_value=NoDefaultSpecified, **metadata): + # self.name = name + def __init__(self, default_value=NoDefaultSpecified, **metadata): + if default_value is not NoDefaultSpecified: + self.default_value = default_value + self.metadata.update(metadata) + + def __get__(self, inst, cls=None): + if inst is None: + return self + else: + return inst._traitlet_values.get(self.name, self.default_value) + + def __set__(self, inst, value): + new_value = self._validate(inst, value) + old_value = self.__get__(inst) + if old_value != new_value: + inst._traitlet_values[self.name] = new_value + inst._notify(self.name, old_value, value) + + def _validate(self, inst, value): + if hasattr(self, 'validate'): + return self.validate(inst, value) + elif hasattr(self, 'is_valid_for'): + valid = self.is_valid_for(value) + if valid: + return value + else: + raise TraitletError('invalid value for type: %r' % value) + elif hasattr(self, 'value_for'): + return self.value_for(value) + else: + return value + + def info(self): + return self.info_text + + def error(self, obj, value): + if obj is not None: + e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \ + % (self.name, class_of(obj), + self.info(), repr_type(value)) + else: + e = "The '%s' traitlet must be %s, but a value of %r was specified." \ + % (self.name, self.info(), repr_type(value)) + raise TraitletError(e) + + +#----------------------------------------------------------------------------- +# The HasTraitlets implementation +#----------------------------------------------------------------------------- + + +class MetaHasTraitlets(type): + """A metaclass for HasTraitlets. + + This metaclass makes sure that any TraitletType class attributes are + instantiated and sets their name attribute. + """ + + def __new__(mcls, name, bases, classdict): + for k,v in classdict.iteritems(): + if isinstance(v, TraitletType): + v.name = k + elif inspect.isclass(v): + if issubclass(v, TraitletType): + vinst = v() + vinst.name = k + classdict[k] = vinst + return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict) + + +class HasTraitlets(object): + + __metaclass__ = MetaHasTraitlets + + def __init__(self): + self._traitlet_values = {} + self._notifiers = {} + + def _notify(self, name, old_value, new_value): + callables = self._notifiers.get(name,[]) + more_callables = self._notifiers.get('anytraitlet',[]) + callables.extend(more_callables) + for c in callables: + # Traits catches and logs errors here. I allow them to raise + c(name, old_value, new_value) + + def _add_notifiers(self, handler, name): + if not self._notifiers.has_key(name): + nlist = [] + self._notifiers[name] = nlist + else: + nlist = self._notifiers[name] + if handler not in nlist: + nlist.append(handler) + + def _remove_notifiers(self, handler, name): + if self._notifiers.has_key(name): + nlist = self._notifiers[name] + try: + index = nlist.index(handler) + except ValueError: + pass + else: + del nlist[index] + + def on_traitlet_change(self, handler, name=None, remove=False): + if remove: + names = parse_notifier_name(name) + for n in names: + self._remove_notifiers(handler, n) + else: + names = parse_notifier_name(name) + for n in names: + self._add_notifiers(handler, n) + + +#----------------------------------------------------------------------------- +# Actual TraitletTypes implementations/subclasses +#----------------------------------------------------------------------------- + + +class Any(TraitletType): + default_value = None + info_text = 'any value' + + +class Int(TraitletType): + + evaluate = int + default_value = 0 + info_text = 'an integer' + + def validate(self, obj, value): + if isinstance(value, int): + return value + self.error(obj, value) + + +class Long(TraitletType): + + evaluate = long + default_value = 0L + info_text = 'a long' + + def validate(self, obj, value): + if isinstance(value, long): + return value + if isinstance(value, int): + return long(value) + self.error(obj, value) + + +class Float(TraitletType): + + evaluate = float + default_value = 0.0 + info_text = 'a float' + + def validate(self, obj, value): + if isinstance(value, float): + return value + if isinstance(value, int): + return float(value) + self.error(obj, value) + + +class Complex(TraitletType): + + evaluate = complex + default_value = 0.0 + 0.0j + info_text = 'a complex number' + + def validate(self, obj, value): + if isinstance(value, complex): + return value + if isinstance(value, (float, int)): + return complex(value) + self.error(obj, value) + + +class Str(TraitletType): + + evaluate = lambda x: x + default_value = '' + info_text = 'a string' + + def validate(self, obj, value): + if isinstance(value, str): + return value + self.error(obj, value) + + +class Unicode(TraitletType): + + evaluate = unicode + default_value = u'' + info_text = 'a unicode string' + + def validate(self, obj, value): + if isinstance(value, unicode): + return value + if isinstance(value, str): + return unicode(value) + self.error(obj, value) + + +class Bool(TraitletType): + + evaluate = bool + default_value = False + info_text = 'a boolean' + + def validate(self, obj, value): + if isinstance(value, bool): + return value + self.error(obj, value) +