##// 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 24 from IPython.core.component import Component
25 25 from IPython.core.quitter import Quitter
26 26
27 from IPython.utils.traitlets import Instance
28
27 29 #-----------------------------------------------------------------------------
28 30 # Classes and functions
29 31 #-----------------------------------------------------------------------------
@@ -34,12 +36,13 b' BuiltinUndefined = BuiltinUndefined()'
34 36
35 37
36 38 class BuiltinTrap(Component):
39 shell = Instance('IPython.core.iplib.InteractiveShell')
37 40
38 41 def __init__(self, parent, name=None, config=None):
39 42 super(BuiltinTrap, self).__init__(parent, name, config)
40 43 # Don't just grab parent!!!
41 from IPython.core.iplib import InteractiveShell
42 self.shell = InteractiveShell.get_instances(root=self.root)[0]
44 self.shell = Component.get_instances(root=self.root,
45 klass='IPython.core.iplib.InteractiveShell')[0]
43 46 self._orig_builtins = {}
44 47
45 48 def __enter__(self):
@@ -24,6 +24,7 b' from copy import deepcopy'
24 24 import datetime
25 25 from weakref import WeakValueDictionary
26 26
27 from IPython.utils.importstring import import_item
27 28 from IPython.utils.ipstruct import Struct
28 29 from IPython.utils.traitlets import (
29 30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
@@ -64,7 +65,7 b' class MetaComponentTracker(type):'
64 65
65 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 69 """Get all instances of cls and its subclasses.
69 70
70 71 Parameters
@@ -73,26 +74,31 b' class MetaComponentTracker(type):'
73 74 Limit to components with this name.
74 75 root : Component or subclass
75 76 Limit to components having this root.
76 classname : str
77 The string name of a class to match exactly.
77 klass : class or str
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 85 instances = cls.__instance_refs.values()
80 86 if name is not None:
81 87 instances = [i for i in instances if i.name == name]
82 if classname is not None:
83 instances = [i for i in instances if i.__class__.__name__ == classname]
88 if klass is not None:
89 instances = [i for i in instances if isinstance(i, klass)]
84 90 if root is not None:
85 91 instances = [i for i in instances if i.root == root]
86 92 return instances
87 93
88 94 def get_instances_by_condition(cls, call, name=None, root=None,
89 classname=None):
95 klass=None):
90 96 """Get all instances of cls, i such that call(i)==True.
91 97
92 98 This also takes the ``name`` and ``root`` and ``classname``
93 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 104 class ComponentNameGenerator(object):
@@ -1252,8 +1252,8 b' class InteractiveShell(Component, Magic):'
1252 1252 else:
1253 1253 magic_args = self.var_expand(magic_args,1)
1254 1254 with self.builtin_trap:
1255 result = fn(magic_args)
1256 return result
1255 return fn(magic_args)
1256 # return result
1257 1257
1258 1258 def define_magic(self, magicname, func):
1259 1259 """Expose own function as magic function for ipython
@@ -1357,8 +1357,7 b' class InteractiveShell(Component, Magic):'
1357 1357 Returns the result of evaluation
1358 1358 """
1359 1359 with self.builtin_trap:
1360 result = eval(expr, self.user_global_ns, self.user_ns)
1361 return result
1360 return eval(expr, self.user_global_ns, self.user_ns)
1362 1361
1363 1362 def getoutput(self, cmd):
1364 1363 return getoutput(self.var_expand(cmd,depth=2),
@@ -1414,7 +1413,7 b' class InteractiveShell(Component, Magic):'
1414 1413 outcomps.sort()
1415 1414 #print "T:",text,"OC:",outcomps # dbg
1416 1415 #print "vars:",self.user_ns.keys()
1417 return outcomps
1416 return outcomps
1418 1417
1419 1418 def set_completer_frame(self, frame=None):
1420 1419 if frame:
@@ -20,7 +20,7 b' from twisted.internet import defer'
20 20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
21 21
22 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 24 from IPython.kernel.fcutil import find_furl
25 25
26 26 co = kernel_config_manager.get_config_obj()
@@ -52,7 +52,7 b' get_log_dir()'
52 52 get_security_dir()
53 53
54 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 31 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
32 32
33 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 35 from IPython.kernel.engineservice import EngineService
36 36
37 37 # Create various ipython directories if they don't exist.
@@ -377,6 +377,7 b' class TestType(TestCase):'
377 377
378 378 a = A()
379 379 self.assertEquals(a.klass, None)
380
380 381 a.klass = B
381 382 self.assertEquals(a.klass, B)
382 383 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
@@ -409,11 +410,15 b' class TestType(TestCase):'
409 410
410 411 def test_validate_klass(self):
411 412
412 def inner():
413 class A(HasTraitlets):
414 klass = Type('no strings allowed')
413 class A(HasTraitlets):
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 423 def test_validate_default(self):
419 424
@@ -421,13 +426,25 b' class TestType(TestCase):'
421 426 class A(HasTraitlets):
422 427 klass = Type('bad default', B)
423 428
424 self.assertRaises(TraitletError, A)
429 self.assertRaises(ImportError, A)
425 430
426 431 class C(HasTraitlets):
427 432 klass = Type(None, B, allow_none=False)
428 433
429 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 448 class TestInstance(TestCase):
432 449
433 450 def test_basic(self):
@@ -449,7 +466,7 b' class TestInstance(TestCase):'
449 466 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
450 467
451 468 def test_unique_default_value(self):
452 class Foo(object): pass
469 class Foo(object): pass
453 470 class A(HasTraitlets):
454 471 inst = Instance(Foo,(),{})
455 472
@@ -57,6 +57,8 b' from types import ('
57 57 ListType, TupleType
58 58 )
59 59
60 from IPython.utils.importstring import import_item
61
60 62 ClassTypes = (ClassType, type)
61 63
62 64 SequenceTypes = (ListType, TupleType)
@@ -142,7 +144,6 b' def parse_notifier_name(name):'
142 144 class _SimpleTest:
143 145 def __init__ ( self, value ): self.value = value
144 146 def __call__ ( self, test ):
145 print test, self.value
146 147 return test == self.value
147 148 def __repr__(self):
148 149 return "<SimpleTest(%r)" % self.value
@@ -203,11 +204,37 b' class TraitletType(object):'
203 204 dv = self.default_value
204 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 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 235 dv = self.get_default_value()
208 236 newdv = self._validate(obj, dv)
209 237 obj._traitlet_values[self.name] = newdv
210
211 238
212 239 def __get__(self, obj, cls=None):
213 240 """Get the value of the traitlet by self.name for the instance.
@@ -289,12 +316,6 b' class MetaHasTraitlets(type):'
289 316 This instantiates all TraitletTypes in the class dict and sets their
290 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 319 for k,v in classdict.iteritems():
299 320 if isinstance(v, TraitletType):
300 321 v.name = k
@@ -311,12 +332,6 b' class MetaHasTraitlets(type):'
311 332 This sets the :attr:`this_class` attribute of each TraitletType in the
312 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 335 for k, v in classdict.iteritems():
321 336 if isinstance(v, TraitletType):
322 337 v.this_class = cls
@@ -335,7 +350,7 b' class HasTraitlets(object):'
335 350 for key in dir(cls):
336 351 value = getattr(cls, key)
337 352 if isinstance(value, TraitletType):
338 value.set_default_value(inst)
353 value.instance_init(inst)
339 354 return inst
340 355
341 356 # def __init__(self):
@@ -512,13 +527,21 b' class Type(ClassBasedTraitletType):'
512 527 A Type traitlet specifies that its values must be subclasses of
513 528 a particular class.
514 529
530 If only ``default_value`` is given, it is used for the ``klass`` as
531 well.
532
515 533 Parameters
516 534 ----------
517 default_value : class
518 The default value must be a subclass of klass.
535 default_value : class, str or None
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 540 klass : class, str, None
520 541 Values of this traitlet must be a subclass of klass. The klass
521 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 545 allow_none : boolean
523 546 Indicates whether None is allowed as an assignable value. Even if
524 547 ``False``, the default value may be ``None``.
@@ -529,7 +552,7 b' class Type(ClassBasedTraitletType):'
529 552 elif klass is None:
530 553 klass = default_value
531 554
532 if not inspect.isclass(klass):
555 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
533 556 raise TraitletError("A Type traitlet must specify a class.")
534 557
535 558 self.klass = klass
@@ -550,23 +573,38 b' class Type(ClassBasedTraitletType):'
550 573
551 574 def info(self):
552 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 580 result = 'a subclass of ' + klass
555 581 if self._allow_none:
556 582 return result + ' or None'
557 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 599 class DefaultValueGenerator(object):
561 600 """A class for generating new default value instances."""
562 601
563 def __init__(self, klass, *args, **kw):
564 self.klass = klass
602 def __init__(self, *args, **kw):
565 603 self.args = args
566 604 self.kw = kw
567 605
568 def generate(self):
569 return self.klass(*self.args, **self.kw)
606 def generate(self, klass):
607 return klass(*self.args, **self.kw)
570 608
571 609
572 610 class Instance(ClassBasedTraitletType):
@@ -586,9 +624,9 b' class Instance(ClassBasedTraitletType):'
586 624
587 625 Parameters
588 626 ----------
589 klass : class
590 The class that forms the basis for the traitlet. Instances
591 and strings are not allowed.
627 klass : class, str
628 The class that forms the basis for the traitlet. Class names
629 can also be specified as strings, like 'foo.bar.Bar'.
592 630 args : tuple
593 631 Positional arguments for generating the default value.
594 632 kw : dict
@@ -606,7 +644,7 b' class Instance(ClassBasedTraitletType):'
606 644
607 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 648 raise TraitletError('The klass argument must be a class'
611 649 ' you gave: %r' % klass)
612 650 self.klass = klass
@@ -627,7 +665,7 b' class Instance(ClassBasedTraitletType):'
627 665 if not isinstance(args, tuple):
628 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 670 super(Instance, self).__init__(default_value, **metadata)
633 671
@@ -643,13 +681,24 b' class Instance(ClassBasedTraitletType):'
643 681 self.error(obj, value)
644 682
645 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 688 result = class_of(klass)
648 689 if self._allow_none:
649 690 return result + ' or None'
650 691
651 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 702 def get_default_value(self):
654 703 """Instantiate a default value instance.
655 704
@@ -659,7 +708,7 b' class Instance(ClassBasedTraitletType):'
659 708 """
660 709 dv = self.default_value
661 710 if isinstance(dv, DefaultValueGenerator):
662 return dv.generate()
711 return dv.generate(self.klass)
663 712 else:
664 713 return dv
665 714
General Comments 0
You need to be logged in to leave comments. Login now