##// END OF EJS Templates
A number of changes to how traitlets and components work....
Brian Granger -
Show More
@@ -0,0 +1,32 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 A simple utility to import something by its string name.
5
6 Authors:
7
8 * Brian Granger
9 """
10
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2009 The IPython Development Team
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
17
18 #-----------------------------------------------------------------------------
19 # Functions and classes
20 #-----------------------------------------------------------------------------
21
22 def import_item(name):
23 """Import and return bar given the string foo.bar."""
24 package = '.'.join(name.split('.')[0:-1])
25 obj = name.split('.')[-1]
26 execString = 'from %s import %s' % (package, obj)
27 try:
28 exec execString
29 except SyntaxError:
30 raise ImportError("Invalid class specification: %s" % name)
31 exec 'temp = %s' % obj
32 return temp
@@ -24,6 +24,8 b' import __builtin__'
24 from IPython.core.component import Component
24 from IPython.core.component import Component
25 from IPython.core.quitter import Quitter
25 from IPython.core.quitter import Quitter
26
26
27 from IPython.utils.traitlets import Instance
28
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28 # Classes and functions
30 # Classes and functions
29 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
@@ -34,12 +36,13 b' BuiltinUndefined = BuiltinUndefined()'
34
36
35
37
36 class BuiltinTrap(Component):
38 class BuiltinTrap(Component):
39 shell = Instance('IPython.core.iplib.InteractiveShell')
37
40
38 def __init__(self, parent, name=None, config=None):
41 def __init__(self, parent, name=None, config=None):
39 super(BuiltinTrap, self).__init__(parent, name, config)
42 super(BuiltinTrap, self).__init__(parent, name, config)
40 # Don't just grab parent!!!
43 # Don't just grab parent!!!
41 from IPython.core.iplib import InteractiveShell
44 self.shell = Component.get_instances(root=self.root,
42 self.shell = InteractiveShell.get_instances(root=self.root)[0]
45 klass='IPython.core.iplib.InteractiveShell')[0]
43 self._orig_builtins = {}
46 self._orig_builtins = {}
44
47
45 def __enter__(self):
48 def __enter__(self):
@@ -24,6 +24,7 b' from copy import deepcopy'
24 import datetime
24 import datetime
25 from weakref import WeakValueDictionary
25 from weakref import WeakValueDictionary
26
26
27 from IPython.utils.importstring import import_item
27 from IPython.utils.ipstruct import Struct
28 from IPython.utils.ipstruct import Struct
28 from IPython.utils.traitlets import (
29 from IPython.utils.traitlets import (
29 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
@@ -64,7 +65,7 b' class MetaComponentTracker(type):'
64
65
65 return instance
66 return instance
66
67
67 def get_instances(cls, name=None, root=None, classname=None):
68 def get_instances(cls, name=None, root=None, klass=None):
68 """Get all instances of cls and its subclasses.
69 """Get all instances of cls and its subclasses.
69
70
70 Parameters
71 Parameters
@@ -73,26 +74,31 b' class MetaComponentTracker(type):'
73 Limit to components with this name.
74 Limit to components with this name.
74 root : Component or subclass
75 root : Component or subclass
75 Limit to components having this root.
76 Limit to components having this root.
76 classname : str
77 klass : class or str
77 The string name of a class to match exactly.
78 Limits to instances of the class or its subclasses. If a str
79 is given ut must be in the form 'foo.bar.MyClass'. The str
80 form of this argument is useful for forward declarations.
78 """
81 """
82 if klass is not None:
83 if isinstance(klass, basestring):
84 klass = import_item(klass)
79 instances = cls.__instance_refs.values()
85 instances = cls.__instance_refs.values()
80 if name is not None:
86 if name is not None:
81 instances = [i for i in instances if i.name == name]
87 instances = [i for i in instances if i.name == name]
82 if classname is not None:
88 if klass is not None:
83 instances = [i for i in instances if i.__class__.__name__ == classname]
89 instances = [i for i in instances if isinstance(i, klass)]
84 if root is not None:
90 if root is not None:
85 instances = [i for i in instances if i.root == root]
91 instances = [i for i in instances if i.root == root]
86 return instances
92 return instances
87
93
88 def get_instances_by_condition(cls, call, name=None, root=None,
94 def get_instances_by_condition(cls, call, name=None, root=None,
89 classname=None):
95 klass=None):
90 """Get all instances of cls, i such that call(i)==True.
96 """Get all instances of cls, i such that call(i)==True.
91
97
92 This also takes the ``name`` and ``root`` and ``classname``
98 This also takes the ``name`` and ``root`` and ``classname``
93 arguments of :meth:`get_instance`
99 arguments of :meth:`get_instance`
94 """
100 """
95 return [i for i in cls.get_instances(name, root, classname) if call(i)]
101 return [i for i in cls.get_instances(name, root, klass) if call(i)]
96
102
97
103
98 class ComponentNameGenerator(object):
104 class ComponentNameGenerator(object):
@@ -1252,8 +1252,8 b' class InteractiveShell(Component, Magic):'
1252 else:
1252 else:
1253 magic_args = self.var_expand(magic_args,1)
1253 magic_args = self.var_expand(magic_args,1)
1254 with self.builtin_trap:
1254 with self.builtin_trap:
1255 result = fn(magic_args)
1255 return fn(magic_args)
1256 return result
1256 # return result
1257
1257
1258 def define_magic(self, magicname, func):
1258 def define_magic(self, magicname, func):
1259 """Expose own function as magic function for ipython
1259 """Expose own function as magic function for ipython
@@ -1357,8 +1357,7 b' class InteractiveShell(Component, Magic):'
1357 Returns the result of evaluation
1357 Returns the result of evaluation
1358 """
1358 """
1359 with self.builtin_trap:
1359 with self.builtin_trap:
1360 result = eval(expr, self.user_global_ns, self.user_ns)
1360 return eval(expr, self.user_global_ns, self.user_ns)
1361 return result
1362
1361
1363 def getoutput(self, cmd):
1362 def getoutput(self, cmd):
1364 return getoutput(self.var_expand(cmd,depth=2),
1363 return getoutput(self.var_expand(cmd,depth=2),
@@ -1414,7 +1413,7 b' class InteractiveShell(Component, Magic):'
1414 outcomps.sort()
1413 outcomps.sort()
1415 #print "T:",text,"OC:",outcomps # dbg
1414 #print "T:",text,"OC:",outcomps # dbg
1416 #print "vars:",self.user_ns.keys()
1415 #print "vars:",self.user_ns.keys()
1417 return outcomps
1416 return outcomps
1418
1417
1419 def set_completer_frame(self, frame=None):
1418 def set_completer_frame(self, frame=None):
1420 if frame:
1419 if frame:
@@ -20,7 +20,7 b' from twisted.internet import defer'
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
21
21
22 from IPython.kernel.config import config_manager as kernel_config_manager
22 from IPython.kernel.config import config_manager as kernel_config_manager
23 from IPython.config.cutils import import_item
23 from IPython.utils.importstring import import_item
24 from IPython.kernel.fcutil import find_furl
24 from IPython.kernel.fcutil import find_furl
25
25
26 co = kernel_config_manager.get_config_obj()
26 co = kernel_config_manager.get_config_obj()
@@ -52,7 +52,7 b' get_log_dir()'
52 get_security_dir()
52 get_security_dir()
53
53
54 from IPython.kernel.config import config_manager as kernel_config_manager
54 from IPython.kernel.config import config_manager as kernel_config_manager
55 from IPython.config.cutils import import_item
55 from IPython.utils.importstring import import_item
56
56
57
57
58 #-------------------------------------------------------------------------------
58 #-------------------------------------------------------------------------------
@@ -31,7 +31,7 b' from twisted.python import log'
31 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
31 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
32
32
33 from IPython.kernel.core.config import config_manager as core_config_manager
33 from IPython.kernel.core.config import config_manager as core_config_manager
34 from IPython.config.cutils import import_item
34 from IPython.utils.importstring import import_item
35 from IPython.kernel.engineservice import EngineService
35 from IPython.kernel.engineservice import EngineService
36
36
37 # Create various ipython directories if they don't exist.
37 # Create various ipython directories if they don't exist.
@@ -377,6 +377,7 b' class TestType(TestCase):'
377
377
378 a = A()
378 a = A()
379 self.assertEquals(a.klass, None)
379 self.assertEquals(a.klass, None)
380
380 a.klass = B
381 a.klass = B
381 self.assertEquals(a.klass, B)
382 self.assertEquals(a.klass, B)
382 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
383 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
@@ -409,11 +410,15 b' class TestType(TestCase):'
409
410
410 def test_validate_klass(self):
411 def test_validate_klass(self):
411
412
412 def inner():
413 class A(HasTraitlets):
413 class A(HasTraitlets):
414 klass = Type('no strings allowed')
414 klass = Type('no strings allowed')
415
415
416 self.assertRaises(TraitletError, inner)
416 self.assertRaises(ImportError, A)
417
418 class A(HasTraitlets):
419 klass = Type('rub.adub.Duck')
420
421 self.assertRaises(ImportError, A)
417
422
418 def test_validate_default(self):
423 def test_validate_default(self):
419
424
@@ -421,13 +426,25 b' class TestType(TestCase):'
421 class A(HasTraitlets):
426 class A(HasTraitlets):
422 klass = Type('bad default', B)
427 klass = Type('bad default', B)
423
428
424 self.assertRaises(TraitletError, A)
429 self.assertRaises(ImportError, A)
425
430
426 class C(HasTraitlets):
431 class C(HasTraitlets):
427 klass = Type(None, B, allow_none=False)
432 klass = Type(None, B, allow_none=False)
428
433
429 self.assertRaises(TraitletError, C)
434 self.assertRaises(TraitletError, C)
430
435
436 def test_str_klass(self):
437
438 class A(HasTraitlets):
439 klass = Type('IPython.utils.ipstruct.Struct')
440
441 from IPython.utils.ipstruct import Struct
442 a = A()
443 a.klass = Struct
444 self.assertEquals(a.klass, Struct)
445
446 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
447
431 class TestInstance(TestCase):
448 class TestInstance(TestCase):
432
449
433 def test_basic(self):
450 def test_basic(self):
@@ -449,7 +466,7 b' class TestInstance(TestCase):'
449 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
466 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
450
467
451 def test_unique_default_value(self):
468 def test_unique_default_value(self):
452 class Foo(object): pass
469 class Foo(object): pass
453 class A(HasTraitlets):
470 class A(HasTraitlets):
454 inst = Instance(Foo,(),{})
471 inst = Instance(Foo,(),{})
455
472
@@ -57,6 +57,8 b' from types import ('
57 ListType, TupleType
57 ListType, TupleType
58 )
58 )
59
59
60 from IPython.utils.importstring import import_item
61
60 ClassTypes = (ClassType, type)
62 ClassTypes = (ClassType, type)
61
63
62 SequenceTypes = (ListType, TupleType)
64 SequenceTypes = (ListType, TupleType)
@@ -142,7 +144,6 b' def parse_notifier_name(name):'
142 class _SimpleTest:
144 class _SimpleTest:
143 def __init__ ( self, value ): self.value = value
145 def __init__ ( self, value ): self.value = value
144 def __call__ ( self, test ):
146 def __call__ ( self, test ):
145 print test, self.value
146 return test == self.value
147 return test == self.value
147 def __repr__(self):
148 def __repr__(self):
148 return "<SimpleTest(%r)" % self.value
149 return "<SimpleTest(%r)" % self.value
@@ -203,11 +204,37 b' class TraitletType(object):'
203 dv = self.default_value
204 dv = self.default_value
204 return dv
205 return dv
205
206
207 def instance_init(self, obj):
208 """This is called by :meth:`HasTraitlets.__new__` to finish init'ing.
209
210 Some stages of initialization must be delayed until the parent
211 :class:`HasTraitlets` instance has been created. This method is
212 called in :meth:`HasTraitlets.__new__` after the instance has been
213 created.
214
215 This method trigger the creation and validation of default values
216 and also things like the resolution of str given class names in
217 :class:`Type` and :class`Instance`.
218
219 Parameters
220 ----------
221 obj : :class:`HasTraitlets` instance
222 The parent :class:`HasTraitlets` instance that has just been
223 created.
224 """
225 self.set_default_value(obj)
226
206 def set_default_value(self, obj):
227 def set_default_value(self, obj):
228 """Set the default value on a per instance basis.
229
230 This method is called by :meth:`instance_init` to create and
231 validate the default value. The creation and validation of
232 default values must be delayed until the parent :class:`HasTraitlets`
233 class has been instantiated.
234 """
207 dv = self.get_default_value()
235 dv = self.get_default_value()
208 newdv = self._validate(obj, dv)
236 newdv = self._validate(obj, dv)
209 obj._traitlet_values[self.name] = newdv
237 obj._traitlet_values[self.name] = newdv
210
211
238
212 def __get__(self, obj, cls=None):
239 def __get__(self, obj, cls=None):
213 """Get the value of the traitlet by self.name for the instance.
240 """Get the value of the traitlet by self.name for the instance.
@@ -289,12 +316,6 b' class MetaHasTraitlets(type):'
289 This instantiates all TraitletTypes in the class dict and sets their
316 This instantiates all TraitletTypes in the class dict and sets their
290 :attr:`name` attribute.
317 :attr:`name` attribute.
291 """
318 """
292 # print "========================="
293 # print "MetaHasTraitlets.__new__"
294 # print "mcls, ", mcls
295 # print "name, ", name
296 # print "bases, ", bases
297 # print "classdict, ", classdict
298 for k,v in classdict.iteritems():
319 for k,v in classdict.iteritems():
299 if isinstance(v, TraitletType):
320 if isinstance(v, TraitletType):
300 v.name = k
321 v.name = k
@@ -311,12 +332,6 b' class MetaHasTraitlets(type):'
311 This sets the :attr:`this_class` attribute of each TraitletType in the
332 This sets the :attr:`this_class` attribute of each TraitletType in the
312 class dict to the newly created class ``cls``.
333 class dict to the newly created class ``cls``.
313 """
334 """
314 # print "========================="
315 # print "MetaHasTraitlets.__init__"
316 # print "cls, ", cls
317 # print "name, ", name
318 # print "bases, ", bases
319 # print "classdict, ", classdict
320 for k, v in classdict.iteritems():
335 for k, v in classdict.iteritems():
321 if isinstance(v, TraitletType):
336 if isinstance(v, TraitletType):
322 v.this_class = cls
337 v.this_class = cls
@@ -335,7 +350,7 b' class HasTraitlets(object):'
335 for key in dir(cls):
350 for key in dir(cls):
336 value = getattr(cls, key)
351 value = getattr(cls, key)
337 if isinstance(value, TraitletType):
352 if isinstance(value, TraitletType):
338 value.set_default_value(inst)
353 value.instance_init(inst)
339 return inst
354 return inst
340
355
341 # def __init__(self):
356 # def __init__(self):
@@ -512,13 +527,21 b' class Type(ClassBasedTraitletType):'
512 A Type traitlet specifies that its values must be subclasses of
527 A Type traitlet specifies that its values must be subclasses of
513 a particular class.
528 a particular class.
514
529
530 If only ``default_value`` is given, it is used for the ``klass`` as
531 well.
532
515 Parameters
533 Parameters
516 ----------
534 ----------
517 default_value : class
535 default_value : class, str or None
518 The default value must be a subclass of klass.
536 The default value must be a subclass of klass. If an str,
537 the str must be a fully specified class name, like 'foo.bar.Bah'.
538 The string is resolved into real class, when the parent
539 :class:`HasTraitlets` class is instantiated.
519 klass : class, str, None
540 klass : class, str, None
520 Values of this traitlet must be a subclass of klass. The klass
541 Values of this traitlet must be a subclass of klass. The klass
521 may be specified in a string like: 'foo.bar.MyClass'.
542 may be specified in a string like: 'foo.bar.MyClass'.
543 The string is resolved into real class, when the parent
544 :class:`HasTraitlets` class is instantiated.
522 allow_none : boolean
545 allow_none : boolean
523 Indicates whether None is allowed as an assignable value. Even if
546 Indicates whether None is allowed as an assignable value. Even if
524 ``False``, the default value may be ``None``.
547 ``False``, the default value may be ``None``.
@@ -529,7 +552,7 b' class Type(ClassBasedTraitletType):'
529 elif klass is None:
552 elif klass is None:
530 klass = default_value
553 klass = default_value
531
554
532 if not inspect.isclass(klass):
555 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
533 raise TraitletError("A Type traitlet must specify a class.")
556 raise TraitletError("A Type traitlet must specify a class.")
534
557
535 self.klass = klass
558 self.klass = klass
@@ -550,23 +573,38 b' class Type(ClassBasedTraitletType):'
550
573
551 def info(self):
574 def info(self):
552 """ Returns a description of the trait."""
575 """ Returns a description of the trait."""
553 klass = self.klass.__name__
576 if isinstance(self.klass, basestring):
577 klass = self.klass
578 else:
579 klass = self.klass.__name__
554 result = 'a subclass of ' + klass
580 result = 'a subclass of ' + klass
555 if self._allow_none:
581 if self._allow_none:
556 return result + ' or None'
582 return result + ' or None'
557 return result
583 return result
558
584
585 def instance_init(self, obj):
586 self._resolve_classes()
587 super(Type, self).instance_init(obj)
588
589 def _resolve_classes(self):
590 if isinstance(self.klass, basestring):
591 self.klass = import_item(self.klass)
592 if isinstance(self.default_value, basestring):
593 self.default_value = import_item(self.default_value)
594
595 def get_default_value(self):
596 return self.default_value
597
559
598
560 class DefaultValueGenerator(object):
599 class DefaultValueGenerator(object):
561 """A class for generating new default value instances."""
600 """A class for generating new default value instances."""
562
601
563 def __init__(self, klass, *args, **kw):
602 def __init__(self, *args, **kw):
564 self.klass = klass
565 self.args = args
603 self.args = args
566 self.kw = kw
604 self.kw = kw
567
605
568 def generate(self):
606 def generate(self, klass):
569 return self.klass(*self.args, **self.kw)
607 return klass(*self.args, **self.kw)
570
608
571
609
572 class Instance(ClassBasedTraitletType):
610 class Instance(ClassBasedTraitletType):
@@ -586,9 +624,9 b' class Instance(ClassBasedTraitletType):'
586
624
587 Parameters
625 Parameters
588 ----------
626 ----------
589 klass : class
627 klass : class, str
590 The class that forms the basis for the traitlet. Instances
628 The class that forms the basis for the traitlet. Class names
591 and strings are not allowed.
629 can also be specified as strings, like 'foo.bar.Bar'.
592 args : tuple
630 args : tuple
593 Positional arguments for generating the default value.
631 Positional arguments for generating the default value.
594 kw : dict
632 kw : dict
@@ -606,7 +644,7 b' class Instance(ClassBasedTraitletType):'
606
644
607 self._allow_none = allow_none
645 self._allow_none = allow_none
608
646
609 if (klass is None) or (not inspect.isclass(klass)):
647 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
610 raise TraitletError('The klass argument must be a class'
648 raise TraitletError('The klass argument must be a class'
611 ' you gave: %r' % klass)
649 ' you gave: %r' % klass)
612 self.klass = klass
650 self.klass = klass
@@ -627,7 +665,7 b' class Instance(ClassBasedTraitletType):'
627 if not isinstance(args, tuple):
665 if not isinstance(args, tuple):
628 raise TraitletError("The 'args' argument must be a tuple or None.")
666 raise TraitletError("The 'args' argument must be a tuple or None.")
629
667
630 default_value = DefaultValueGenerator(self.klass, *args, **kw)
668 default_value = DefaultValueGenerator(*args, **kw)
631
669
632 super(Instance, self).__init__(default_value, **metadata)
670 super(Instance, self).__init__(default_value, **metadata)
633
671
@@ -643,13 +681,24 b' class Instance(ClassBasedTraitletType):'
643 self.error(obj, value)
681 self.error(obj, value)
644
682
645 def info(self):
683 def info(self):
646 klass = self.klass.__name__
684 if isinstance(self.klass, basestring):
685 klass = self.klass
686 else:
687 klass = self.klass.__name__
647 result = class_of(klass)
688 result = class_of(klass)
648 if self._allow_none:
689 if self._allow_none:
649 return result + ' or None'
690 return result + ' or None'
650
691
651 return result
692 return result
652
693
694 def instance_init(self, obj):
695 self._resolve_classes()
696 super(Instance, self).instance_init(obj)
697
698 def _resolve_classes(self):
699 if isinstance(self.klass, basestring):
700 self.klass = import_item(self.klass)
701
653 def get_default_value(self):
702 def get_default_value(self):
654 """Instantiate a default value instance.
703 """Instantiate a default value instance.
655
704
@@ -659,7 +708,7 b' class Instance(ClassBasedTraitletType):'
659 """
708 """
660 dv = self.default_value
709 dv = self.default_value
661 if isinstance(dv, DefaultValueGenerator):
710 if isinstance(dv, DefaultValueGenerator):
662 return dv.generate()
711 return dv.generate(self.klass)
663 else:
712 else:
664 return dv
713 return dv
665
714
General Comments 0
You need to be logged in to leave comments. Login now