##// END OF EJS Templates
Fixing bugs and adding comment
Brian Granger -
Show More
@@ -1,174 +1,174 b''
1 """Global IPython app to support test running.
1 """Global IPython app to support test running.
2
2
3 We must start our own ipython object and heavily muck with it so that all the
3 We must start our own ipython object and heavily muck with it so that all the
4 modifications IPython makes to system behavior don't send the doctest machinery
4 modifications IPython makes to system behavior don't send the doctest machinery
5 into a fit. This code should be considered a gross hack, but it gets the job
5 into a fit. This code should be considered a gross hack, but it gets the job
6 done.
6 done.
7 """
7 """
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009 The IPython Development Team
12 # Copyright (C) 2009 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import __builtin__
22 import __builtin__
23 import commands
23 import commands
24 import os
24 import os
25 import sys
25 import sys
26
26
27 from . import tools
27 from . import tools
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Functions
30 # Functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # Hack to modify the %run command so we can sync the user's namespace with the
33 # Hack to modify the %run command so we can sync the user's namespace with the
34 # test globals. Once we move over to a clean magic system, this will be done
34 # test globals. Once we move over to a clean magic system, this will be done
35 # with much less ugliness.
35 # with much less ugliness.
36
36
37 class py_file_finder(object):
37 class py_file_finder(object):
38 def __init__(self,test_filename):
38 def __init__(self,test_filename):
39 self.test_filename = test_filename
39 self.test_filename = test_filename
40
40
41 def __call__(self,name):
41 def __call__(self,name):
42 from IPython.utils.path import get_py_filename
42 from IPython.utils.path import get_py_filename
43 try:
43 try:
44 return get_py_filename(name)
44 return get_py_filename(name)
45 except IOError:
45 except IOError:
46 test_dir = os.path.dirname(self.test_filename)
46 test_dir = os.path.dirname(self.test_filename)
47 new_path = os.path.join(test_dir,name)
47 new_path = os.path.join(test_dir,name)
48 return get_py_filename(new_path)
48 return get_py_filename(new_path)
49
49
50
50
51 def _run_ns_sync(self,arg_s,runner=None):
51 def _run_ns_sync(self,arg_s,runner=None):
52 """Modified version of %run that syncs testing namespaces.
52 """Modified version of %run that syncs testing namespaces.
53
53
54 This is strictly needed for running doctests that call %run.
54 This is strictly needed for running doctests that call %run.
55 """
55 """
56 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
56 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
57
57
58 _ip = get_ipython()
58 _ip = get_ipython()
59 finder = py_file_finder(arg_s)
59 finder = py_file_finder(arg_s)
60 out = _ip.magic_run_ori(arg_s,runner,finder)
60 out = _ip.magic_run_ori(arg_s,runner,finder)
61 return out
61 return out
62
62
63
63
64 class ipnsdict(dict):
64 class ipnsdict(dict):
65 """A special subclass of dict for use as an IPython namespace in doctests.
65 """A special subclass of dict for use as an IPython namespace in doctests.
66
66
67 This subclass adds a simple checkpointing capability so that when testing
67 This subclass adds a simple checkpointing capability so that when testing
68 machinery clears it (we use it as the test execution context), it doesn't
68 machinery clears it (we use it as the test execution context), it doesn't
69 get completely destroyed.
69 get completely destroyed.
70 """
70 """
71
71
72 def __init__(self,*a):
72 def __init__(self,*a):
73 dict.__init__(self,*a)
73 dict.__init__(self,*a)
74 self._savedict = {}
74 self._savedict = {}
75
75
76 def clear(self):
76 def clear(self):
77 dict.clear(self)
77 dict.clear(self)
78 self.update(self._savedict)
78 self.update(self._savedict)
79
79
80 def _checkpoint(self):
80 def _checkpoint(self):
81 self._savedict.clear()
81 self._savedict.clear()
82 self._savedict.update(self)
82 self._savedict.update(self)
83
83
84 def update(self,other):
84 def update(self,other):
85 self._checkpoint()
85 self._checkpoint()
86 dict.update(self,other)
86 dict.update(self,other)
87
87
88 # If '_' is in the namespace, python won't set it when executing code,
88 # If '_' is in the namespace, python won't set it when executing code,
89 # and we have examples that test it. So we ensure that the namespace
89 # and we have examples that test it. So we ensure that the namespace
90 # is always 'clean' of it before it's used for test code execution.
90 # is always 'clean' of it before it's used for test code execution.
91 self.pop('_',None)
91 self.pop('_',None)
92
92
93 # The builtins namespace must *always* be the real __builtin__ module,
93 # The builtins namespace must *always* be the real __builtin__ module,
94 # else weird stuff happens. The main ipython code does have provisions
94 # else weird stuff happens. The main ipython code does have provisions
95 # to ensure this after %run, but since in this class we do some
95 # to ensure this after %run, but since in this class we do some
96 # aggressive low-level cleaning of the execution namespace, we need to
96 # aggressive low-level cleaning of the execution namespace, we need to
97 # correct for that ourselves, to ensure consitency with the 'real'
97 # correct for that ourselves, to ensure consitency with the 'real'
98 # ipython.
98 # ipython.
99 self['__builtins__'] = __builtin__
99 self['__builtins__'] = __builtin__
100
100
101
101
102 def get_ipython():
102 def get_ipython():
103 # This will get replaced by the real thing once we start IPython below
103 # This will get replaced by the real thing once we start IPython below
104 return start_ipython()
104 return start_ipython()
105
105
106
106
107 def start_ipython():
107 def start_ipython():
108 """Start a global IPython shell, which we need for IPython-specific syntax.
108 """Start a global IPython shell, which we need for IPython-specific syntax.
109 """
109 """
110 global get_ipython
110 global get_ipython
111
111
112 # This function should only ever run once!
112 # This function should only ever run once!
113 if hasattr(start_ipython, 'already_called'):
113 if hasattr(start_ipython, 'already_called'):
114 return
114 return
115 start_ipython.already_called = True
115 start_ipython.already_called = True
116
116
117 # Ok, first time we're called, go ahead
117 # Ok, first time we're called, go ahead
118 from IPython.core import iplib
118 from IPython.core import iplib
119
119
120 def xsys(cmd):
120 def xsys(cmd):
121 """Execute a command and print its output.
121 """Execute a command and print its output.
122
122
123 This is just a convenience function to replace the IPython system call
123 This is just a convenience function to replace the IPython system call
124 with one that is more doctest-friendly.
124 with one that is more doctest-friendly.
125 """
125 """
126 cmd = _ip.var_expand(cmd,depth=1)
126 cmd = _ip.var_expand(cmd,depth=1)
127 sys.stdout.write(commands.getoutput(cmd))
127 sys.stdout.write(commands.getoutput(cmd))
128 sys.stdout.flush()
128 sys.stdout.flush()
129
129
130 # Store certain global objects that IPython modifies
130 # Store certain global objects that IPython modifies
131 _displayhook = sys.displayhook
131 _displayhook = sys.displayhook
132 _excepthook = sys.excepthook
132 _excepthook = sys.excepthook
133 _main = sys.modules.get('__main__')
133 _main = sys.modules.get('__main__')
134
134
135 # Create custom argv and namespaces for our IPython to be test-friendly
135 # Create custom argv and namespaces for our IPython to be test-friendly
136 config = tools.default_config()
136 config = tools.default_config()
137
137
138 # Create and initialize our test-friendly IPython instance.
138 # Create and initialize our test-friendly IPython instance.
139 shell = iplib.InteractiveShell(
139 shell = iplib.InteractiveShell.instance(
140 config=config,
140 config=config,
141 user_ns=ipnsdict(), user_global_ns={}
141 user_ns=ipnsdict(), user_global_ns={}
142 )
142 )
143
143
144 # A few more tweaks needed for playing nicely with doctests...
144 # A few more tweaks needed for playing nicely with doctests...
145
145
146 # These traps are normally only active for interactive use, set them
146 # These traps are normally only active for interactive use, set them
147 # permanently since we'll be mocking interactive sessions.
147 # permanently since we'll be mocking interactive sessions.
148 shell.builtin_trap.set()
148 shell.builtin_trap.set()
149
149
150 # Set error printing to stdout so nose can doctest exceptions
150 # Set error printing to stdout so nose can doctest exceptions
151 shell.InteractiveTB.out_stream = 'stdout'
151 shell.InteractiveTB.out_stream = 'stdout'
152
152
153 # Modify the IPython system call with one that uses getoutput, so that we
153 # Modify the IPython system call with one that uses getoutput, so that we
154 # can capture subcommands and print them to Python's stdout, otherwise the
154 # can capture subcommands and print them to Python's stdout, otherwise the
155 # doctest machinery would miss them.
155 # doctest machinery would miss them.
156 shell.system = xsys
156 shell.system = xsys
157
157
158 # IPython is ready, now clean up some global state...
158 # IPython is ready, now clean up some global state...
159
159
160 # Deactivate the various python system hooks added by ipython for
160 # Deactivate the various python system hooks added by ipython for
161 # interactive convenience so we don't confuse the doctest system
161 # interactive convenience so we don't confuse the doctest system
162 sys.modules['__main__'] = _main
162 sys.modules['__main__'] = _main
163 sys.displayhook = _displayhook
163 sys.displayhook = _displayhook
164 sys.excepthook = _excepthook
164 sys.excepthook = _excepthook
165
165
166 # So that ipython magics and aliases can be doctested (they work by making
166 # So that ipython magics and aliases can be doctested (they work by making
167 # a call into a global _ip object). Also make the top-level get_ipython
167 # a call into a global _ip object). Also make the top-level get_ipython
168 # now return this without recursively calling here again.
168 # now return this without recursively calling here again.
169 _ip = shell
169 _ip = shell
170 get_ipython = _ip.get_ipython
170 get_ipython = _ip.get_ipython
171 __builtin__._ip = _ip
171 __builtin__._ip = _ip
172 __builtin__.get_ipython = get_ipython
172 __builtin__.get_ipython = get_ipython
173
173
174 return _ip
174 return _ip
@@ -1,1048 +1,1050 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight Traits like module.
4 A lightweight Traits like module.
5
5
6 This is designed to provide a lightweight, simple, pure Python version of
6 This is designed to provide a lightweight, simple, pure Python version of
7 many of the capabilities of enthought.traits. This includes:
7 many of the capabilities of enthought.traits. This includes:
8
8
9 * Validation
9 * Validation
10 * Type specification with defaults
10 * Type specification with defaults
11 * Static and dynamic notification
11 * Static and dynamic notification
12 * Basic predefined types
12 * Basic predefined types
13 * An API that is similar to enthought.traits
13 * An API that is similar to enthought.traits
14
14
15 We don't support:
15 We don't support:
16
16
17 * Delegation
17 * Delegation
18 * Automatic GUI generation
18 * Automatic GUI generation
19 * A full set of trait types. Most importantly, we don't provide container
19 * A full set of trait types. Most importantly, we don't provide container
20 traits (list, dict, tuple) that can trigger notifications if their
20 traits (list, dict, tuple) that can trigger notifications if their
21 contents change.
21 contents change.
22 * API compatibility with enthought.traits
22 * API compatibility with enthought.traits
23
23
24 There are also some important difference in our design:
24 There are also some important difference in our design:
25
25
26 * enthought.traits does not validate default values. We do.
26 * enthought.traits does not validate default values. We do.
27
27
28 We choose to create this module because we need these capabilities, but
28 We choose to create this module because we need these capabilities, but
29 we need them to be pure Python so they work in all Python implementations,
29 we need them to be pure Python so they work in all Python implementations,
30 including Jython and IronPython.
30 including Jython and IronPython.
31
31
32 Authors:
32 Authors:
33
33
34 * Brian Granger
34 * Brian Granger
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
36 and is licensed under the BSD license. Also, many of the ideas also come
36 and is licensed under the BSD license. Also, many of the ideas also come
37 from enthought.traits even though our implementation is very different.
37 from enthought.traits even though our implementation is very different.
38 """
38 """
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Copyright (C) 2008-2009 The IPython Development Team
41 # Copyright (C) 2008-2009 The IPython Development Team
42 #
42 #
43 # Distributed under the terms of the BSD License. The full license is in
43 # Distributed under the terms of the BSD License. The full license is in
44 # the file COPYING, distributed as part of this software.
44 # the file COPYING, distributed as part of this software.
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Imports
48 # Imports
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 import inspect
52 import inspect
53 import sys
53 import sys
54 import types
54 import types
55 from types import (
55 from types import (
56 InstanceType, ClassType, FunctionType,
56 InstanceType, ClassType, FunctionType,
57 ListType, TupleType
57 ListType, TupleType
58 )
58 )
59
59
60 def import_item(name):
60 def import_item(name):
61 """Import and return bar given the string foo.bar."""
61 """Import and return bar given the string foo.bar."""
62 package = '.'.join(name.split('.')[0:-1])
62 package = '.'.join(name.split('.')[0:-1])
63 obj = name.split('.')[-1]
63 obj = name.split('.')[-1]
64 execString = 'from %s import %s' % (package, obj)
64 execString = 'from %s import %s' % (package, obj)
65 try:
65 try:
66 exec execString
66 exec execString
67 except SyntaxError:
67 except SyntaxError:
68 raise ImportError("Invalid class specification: %s" % name)
68 raise ImportError("Invalid class specification: %s" % name)
69 exec 'temp = %s' % obj
69 exec 'temp = %s' % obj
70 return temp
70 return temp
71
71
72
72
73 ClassTypes = (ClassType, type)
73 ClassTypes = (ClassType, type)
74
74
75 SequenceTypes = (ListType, TupleType)
75 SequenceTypes = (ListType, TupleType)
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Basic classes
78 # Basic classes
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81
81
82 class NoDefaultSpecified ( object ): pass
82 class NoDefaultSpecified ( object ): pass
83 NoDefaultSpecified = NoDefaultSpecified()
83 NoDefaultSpecified = NoDefaultSpecified()
84
84
85
85
86 class Undefined ( object ): pass
86 class Undefined ( object ): pass
87 Undefined = Undefined()
87 Undefined = Undefined()
88
88
89 class TraitError(Exception):
89 class TraitError(Exception):
90 pass
90 pass
91
91
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93 # Utilities
93 # Utilities
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95
95
96
96
97 def class_of ( object ):
97 def class_of ( object ):
98 """ Returns a string containing the class name of an object with the
98 """ Returns a string containing the class name of an object with the
99 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
99 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
100 'a PlotValue').
100 'a PlotValue').
101 """
101 """
102 if isinstance( object, basestring ):
102 if isinstance( object, basestring ):
103 return add_article( object )
103 return add_article( object )
104
104
105 return add_article( object.__class__.__name__ )
105 return add_article( object.__class__.__name__ )
106
106
107
107
108 def add_article ( name ):
108 def add_article ( name ):
109 """ Returns a string containing the correct indefinite article ('a' or 'an')
109 """ Returns a string containing the correct indefinite article ('a' or 'an')
110 prefixed to the specified string.
110 prefixed to the specified string.
111 """
111 """
112 if name[:1].lower() in 'aeiou':
112 if name[:1].lower() in 'aeiou':
113 return 'an ' + name
113 return 'an ' + name
114
114
115 return 'a ' + name
115 return 'a ' + name
116
116
117
117
118 def repr_type(obj):
118 def repr_type(obj):
119 """ Return a string representation of a value and its type for readable
119 """ Return a string representation of a value and its type for readable
120 error messages.
120 error messages.
121 """
121 """
122 the_type = type(obj)
122 the_type = type(obj)
123 if the_type is InstanceType:
123 if the_type is InstanceType:
124 # Old-style class.
124 # Old-style class.
125 the_type = obj.__class__
125 the_type = obj.__class__
126 msg = '%r %r' % (obj, the_type)
126 msg = '%r %r' % (obj, the_type)
127 return msg
127 return msg
128
128
129
129
130 def parse_notifier_name(name):
130 def parse_notifier_name(name):
131 """Convert the name argument to a list of names.
131 """Convert the name argument to a list of names.
132
132
133 Examples
133 Examples
134 --------
134 --------
135
135
136 >>> parse_notifier_name('a')
136 >>> parse_notifier_name('a')
137 ['a']
137 ['a']
138 >>> parse_notifier_name(['a','b'])
138 >>> parse_notifier_name(['a','b'])
139 ['a', 'b']
139 ['a', 'b']
140 >>> parse_notifier_name(None)
140 >>> parse_notifier_name(None)
141 ['anytrait']
141 ['anytrait']
142 """
142 """
143 if isinstance(name, str):
143 if isinstance(name, str):
144 return [name]
144 return [name]
145 elif name is None:
145 elif name is None:
146 return ['anytrait']
146 return ['anytrait']
147 elif isinstance(name, (list, tuple)):
147 elif isinstance(name, (list, tuple)):
148 for n in name:
148 for n in name:
149 assert isinstance(n, str), "names must be strings"
149 assert isinstance(n, str), "names must be strings"
150 return name
150 return name
151
151
152
152
153 class _SimpleTest:
153 class _SimpleTest:
154 def __init__ ( self, value ): self.value = value
154 def __init__ ( self, value ): self.value = value
155 def __call__ ( self, test ):
155 def __call__ ( self, test ):
156 return test == self.value
156 return test == self.value
157 def __repr__(self):
157 def __repr__(self):
158 return "<SimpleTest(%r)" % self.value
158 return "<SimpleTest(%r)" % self.value
159 def __str__(self):
159 def __str__(self):
160 return self.__repr__()
160 return self.__repr__()
161
161
162
162
163 def getmembers(object, predicate=None):
163 def getmembers(object, predicate=None):
164 """A safe version of inspect.getmembers that handles missing attributes.
164 """A safe version of inspect.getmembers that handles missing attributes.
165
165
166 This is useful when there are descriptor based attributes that for
166 This is useful when there are descriptor based attributes that for
167 some reason raise AttributeError even though they exist. This happens
167 some reason raise AttributeError even though they exist. This happens
168 in zope.inteface with the __provides__ attribute.
168 in zope.inteface with the __provides__ attribute.
169 """
169 """
170 results = []
170 results = []
171 for key in dir(object):
171 for key in dir(object):
172 try:
172 try:
173 value = getattr(object, key)
173 value = getattr(object, key)
174 except AttributeError:
174 except AttributeError:
175 pass
175 pass
176 else:
176 else:
177 if not predicate or predicate(value):
177 if not predicate or predicate(value):
178 results.append((key, value))
178 results.append((key, value))
179 results.sort()
179 results.sort()
180 return results
180 return results
181
181
182
182
183 #-----------------------------------------------------------------------------
183 #-----------------------------------------------------------------------------
184 # Base TraitType for all traits
184 # Base TraitType for all traits
185 #-----------------------------------------------------------------------------
185 #-----------------------------------------------------------------------------
186
186
187
187
188 class TraitType(object):
188 class TraitType(object):
189 """A base class for all trait descriptors.
189 """A base class for all trait descriptors.
190
190
191 Notes
191 Notes
192 -----
192 -----
193 Our implementation of traits is based on Python's descriptor
193 Our implementation of traits is based on Python's descriptor
194 prototol. This class is the base class for all such descriptors. The
194 prototol. This class is the base class for all such descriptors. The
195 only magic we use is a custom metaclass for the main :class:`HasTraits`
195 only magic we use is a custom metaclass for the main :class:`HasTraits`
196 class that does the following:
196 class that does the following:
197
197
198 1. Sets the :attr:`name` attribute of every :class:`TraitType`
198 1. Sets the :attr:`name` attribute of every :class:`TraitType`
199 instance in the class dict to the name of the attribute.
199 instance in the class dict to the name of the attribute.
200 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
200 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
201 instance in the class dict to the *class* that declared the trait.
201 instance in the class dict to the *class* that declared the trait.
202 This is used by the :class:`This` trait to allow subclasses to
202 This is used by the :class:`This` trait to allow subclasses to
203 accept superclasses for :class:`This` values.
203 accept superclasses for :class:`This` values.
204 """
204 """
205
205
206
206
207 metadata = {}
207 metadata = {}
208 default_value = Undefined
208 default_value = Undefined
209 info_text = 'any value'
209 info_text = 'any value'
210
210
211 def __init__(self, default_value=NoDefaultSpecified, **metadata):
211 def __init__(self, default_value=NoDefaultSpecified, **metadata):
212 """Create a TraitType.
212 """Create a TraitType.
213 """
213 """
214 if default_value is not NoDefaultSpecified:
214 if default_value is not NoDefaultSpecified:
215 self.default_value = default_value
215 self.default_value = default_value
216
216
217 if len(metadata) > 0:
217 if len(metadata) > 0:
218 if len(self.metadata) > 0:
218 if len(self.metadata) > 0:
219 self._metadata = self.metadata.copy()
219 self._metadata = self.metadata.copy()
220 self._metadata.update(metadata)
220 self._metadata.update(metadata)
221 else:
221 else:
222 self._metadata = metadata
222 self._metadata = metadata
223 else:
223 else:
224 self._metadata = self.metadata
224 self._metadata = self.metadata
225
225
226 self.init()
226 self.init()
227
227
228 def init(self):
228 def init(self):
229 pass
229 pass
230
230
231 def get_default_value(self):
231 def get_default_value(self):
232 """Create a new instance of the default value."""
232 """Create a new instance of the default value."""
233 dv = self.default_value
233 dv = self.default_value
234 return dv
234 return dv
235
235
236 def instance_init(self, obj):
236 def instance_init(self, obj):
237 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
237 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
238
238
239 Some stages of initialization must be delayed until the parent
239 Some stages of initialization must be delayed until the parent
240 :class:`HasTraits` instance has been created. This method is
240 :class:`HasTraits` instance has been created. This method is
241 called in :meth:`HasTraits.__new__` after the instance has been
241 called in :meth:`HasTraits.__new__` after the instance has been
242 created.
242 created.
243
243
244 This method trigger the creation and validation of default values
244 This method trigger the creation and validation of default values
245 and also things like the resolution of str given class names in
245 and also things like the resolution of str given class names in
246 :class:`Type` and :class`Instance`.
246 :class:`Type` and :class`Instance`.
247
247
248 Parameters
248 Parameters
249 ----------
249 ----------
250 obj : :class:`HasTraits` instance
250 obj : :class:`HasTraits` instance
251 The parent :class:`HasTraits` instance that has just been
251 The parent :class:`HasTraits` instance that has just been
252 created.
252 created.
253 """
253 """
254 self.set_default_value(obj)
254 self.set_default_value(obj)
255
255
256 def set_default_value(self, obj):
256 def set_default_value(self, obj):
257 """Set the default value on a per instance basis.
257 """Set the default value on a per instance basis.
258
258
259 This method is called by :meth:`instance_init` to create and
259 This method is called by :meth:`instance_init` to create and
260 validate the default value. The creation and validation of
260 validate the default value. The creation and validation of
261 default values must be delayed until the parent :class:`HasTraits`
261 default values must be delayed until the parent :class:`HasTraits`
262 class has been instantiated.
262 class has been instantiated.
263 """
263 """
264 dv = self.get_default_value()
264 dv = self.get_default_value()
265 newdv = self._validate(obj, dv)
265 newdv = self._validate(obj, dv)
266 obj._trait_values[self.name] = newdv
266 obj._trait_values[self.name] = newdv
267
267
268 def __get__(self, obj, cls=None):
268 def __get__(self, obj, cls=None):
269 """Get the value of the trait by self.name for the instance.
269 """Get the value of the trait by self.name for the instance.
270
270
271 Default values are instantiated when :meth:`HasTraits.__new__`
271 Default values are instantiated when :meth:`HasTraits.__new__`
272 is called. Thus by the time this method gets called either the
272 is called. Thus by the time this method gets called either the
273 default value or a user defined value (they called :meth:`__set__`)
273 default value or a user defined value (they called :meth:`__set__`)
274 is in the :class:`HasTraits` instance.
274 is in the :class:`HasTraits` instance.
275 """
275 """
276 if obj is None:
276 if obj is None:
277 return self
277 return self
278 else:
278 else:
279 try:
279 try:
280 value = obj._trait_values[self.name]
280 value = obj._trait_values[self.name]
281 except:
281 except:
282 # HasTraits should call set_default_value to populate
282 # HasTraits should call set_default_value to populate
283 # this. So this should never be reached.
283 # this. So this should never be reached.
284 raise TraitError('Unexpected error in TraitType: '
284 raise TraitError('Unexpected error in TraitType: '
285 'default value not set properly')
285 'default value not set properly')
286 else:
286 else:
287 return value
287 return value
288
288
289 def __set__(self, obj, value):
289 def __set__(self, obj, value):
290 new_value = self._validate(obj, value)
290 new_value = self._validate(obj, value)
291 old_value = self.__get__(obj)
291 old_value = self.__get__(obj)
292 if old_value != new_value:
292 if old_value != new_value:
293 obj._trait_values[self.name] = new_value
293 obj._trait_values[self.name] = new_value
294 obj._notify_trait(self.name, old_value, new_value)
294 obj._notify_trait(self.name, old_value, new_value)
295
295
296 def _validate(self, obj, value):
296 def _validate(self, obj, value):
297 if hasattr(self, 'validate'):
297 if hasattr(self, 'validate'):
298 return self.validate(obj, value)
298 return self.validate(obj, value)
299 elif hasattr(self, 'is_valid_for'):
299 elif hasattr(self, 'is_valid_for'):
300 valid = self.is_valid_for(value)
300 valid = self.is_valid_for(value)
301 if valid:
301 if valid:
302 return value
302 return value
303 else:
303 else:
304 raise TraitError('invalid value for type: %r' % value)
304 raise TraitError('invalid value for type: %r' % value)
305 elif hasattr(self, 'value_for'):
305 elif hasattr(self, 'value_for'):
306 return self.value_for(value)
306 return self.value_for(value)
307 else:
307 else:
308 return value
308 return value
309
309
310 def info(self):
310 def info(self):
311 return self.info_text
311 return self.info_text
312
312
313 def error(self, obj, value):
313 def error(self, obj, value):
314 if obj is not None:
314 if obj is not None:
315 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
315 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
316 % (self.name, class_of(obj),
316 % (self.name, class_of(obj),
317 self.info(), repr_type(value))
317 self.info(), repr_type(value))
318 else:
318 else:
319 e = "The '%s' trait must be %s, but a value of %r was specified." \
319 e = "The '%s' trait must be %s, but a value of %r was specified." \
320 % (self.name, self.info(), repr_type(value))
320 % (self.name, self.info(), repr_type(value))
321 raise TraitError(e)
321 raise TraitError(e)
322
322
323 def get_metadata(self, key):
323 def get_metadata(self, key):
324 return getattr(self, '_metadata', {}).get(key, None)
324 return getattr(self, '_metadata', {}).get(key, None)
325
325
326 def set_metadata(self, key, value):
326 def set_metadata(self, key, value):
327 getattr(self, '_metadata', {})[key] = value
327 getattr(self, '_metadata', {})[key] = value
328
328
329
329
330 #-----------------------------------------------------------------------------
330 #-----------------------------------------------------------------------------
331 # The HasTraits implementation
331 # The HasTraits implementation
332 #-----------------------------------------------------------------------------
332 #-----------------------------------------------------------------------------
333
333
334
334
335 class MetaHasTraits(type):
335 class MetaHasTraits(type):
336 """A metaclass for HasTraits.
336 """A metaclass for HasTraits.
337
337
338 This metaclass makes sure that any TraitType class attributes are
338 This metaclass makes sure that any TraitType class attributes are
339 instantiated and sets their name attribute.
339 instantiated and sets their name attribute.
340 """
340 """
341
341
342 def __new__(mcls, name, bases, classdict):
342 def __new__(mcls, name, bases, classdict):
343 """Create the HasTraits class.
343 """Create the HasTraits class.
344
344
345 This instantiates all TraitTypes in the class dict and sets their
345 This instantiates all TraitTypes in the class dict and sets their
346 :attr:`name` attribute.
346 :attr:`name` attribute.
347 """
347 """
348 # print "MetaHasTraitlets (mcls, name): ", mcls, name
348 # print "MetaHasTraitlets (mcls, name): ", mcls, name
349 # print "MetaHasTraitlets (bases): ", bases
349 # print "MetaHasTraitlets (bases): ", bases
350 # print "MetaHasTraitlets (classdict): ", classdict
350 # print "MetaHasTraitlets (classdict): ", classdict
351 for k,v in classdict.iteritems():
351 for k,v in classdict.iteritems():
352 if isinstance(v, TraitType):
352 if isinstance(v, TraitType):
353 v.name = k
353 v.name = k
354 elif inspect.isclass(v):
354 elif inspect.isclass(v):
355 if issubclass(v, TraitType):
355 if issubclass(v, TraitType):
356 vinst = v()
356 vinst = v()
357 vinst.name = k
357 vinst.name = k
358 classdict[k] = vinst
358 classdict[k] = vinst
359 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
359 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
360
360
361 def __init__(cls, name, bases, classdict):
361 def __init__(cls, name, bases, classdict):
362 """Finish initializing the HasTraits class.
362 """Finish initializing the HasTraits class.
363
363
364 This sets the :attr:`this_class` attribute of each TraitType in the
364 This sets the :attr:`this_class` attribute of each TraitType in the
365 class dict to the newly created class ``cls``.
365 class dict to the newly created class ``cls``.
366 """
366 """
367 for k, v in classdict.iteritems():
367 for k, v in classdict.iteritems():
368 if isinstance(v, TraitType):
368 if isinstance(v, TraitType):
369 v.this_class = cls
369 v.this_class = cls
370 super(MetaHasTraits, cls).__init__(name, bases, classdict)
370 super(MetaHasTraits, cls).__init__(name, bases, classdict)
371
371
372 class HasTraits(object):
372 class HasTraits(object):
373
373
374 __metaclass__ = MetaHasTraits
374 __metaclass__ = MetaHasTraits
375
375
376 def __new__(cls, **kw):
376 def __new__(cls, **kw):
377 # This is needed because in Python 2.6 object.__new__ only accepts
377 # This is needed because in Python 2.6 object.__new__ only accepts
378 # the cls argument.
378 # the cls argument.
379 new_meth = super(HasTraits, cls).__new__
379 new_meth = super(HasTraits, cls).__new__
380 if new_meth is object.__new__:
380 if new_meth is object.__new__:
381 inst = new_meth(cls)
381 inst = new_meth(cls)
382 else:
382 else:
383 inst = new_meth(cls, **kw)
383 inst = new_meth(cls, **kw)
384 inst._trait_values = {}
384 inst._trait_values = {}
385 inst._trait_notifiers = {}
385 inst._trait_notifiers = {}
386 # Here we tell all the TraitType instances to set their default
386 # Here we tell all the TraitType instances to set their default
387 # values on the instance.
387 # values on the instance.
388 for key in dir(cls):
388 for key in dir(cls):
389 # Some descriptors raise AttributeError like zope.interface's
389 # Some descriptors raise AttributeError like zope.interface's
390 # __provides__ attributes even though they exist. This causes
390 # __provides__ attributes even though they exist. This causes
391 # AttributeErrors even though they are listed in dir(cls).
391 # AttributeErrors even though they are listed in dir(cls).
392 try:
392 try:
393 value = getattr(cls, key)
393 value = getattr(cls, key)
394 except AttributeError:
394 except AttributeError:
395 pass
395 pass
396 else:
396 else:
397 if isinstance(value, TraitType):
397 if isinstance(value, TraitType):
398 value.instance_init(inst)
398 value.instance_init(inst)
399
399
400 return inst
400 return inst
401
401
402 def __init__(self, **kw):
402 def __init__(self, **kw):
403 # Allow trait values to be set using keyword arguments.
403 # Allow trait values to be set using keyword arguments.
404 # We need to use setattr for this to trigger validation and
405 # notifications.
404 for key, value in kw.iteritems():
406 for key, value in kw.iteritems():
405 setattr(self, key, value)
407 setattr(self, key, value)
406
408
407 def _notify_trait(self, name, old_value, new_value):
409 def _notify_trait(self, name, old_value, new_value):
408
410
409 # First dynamic ones
411 # First dynamic ones
410 callables = self._trait_notifiers.get(name,[])
412 callables = self._trait_notifiers.get(name,[])
411 more_callables = self._trait_notifiers.get('anytrait',[])
413 more_callables = self._trait_notifiers.get('anytrait',[])
412 callables.extend(more_callables)
414 callables.extend(more_callables)
413
415
414 # Now static ones
416 # Now static ones
415 try:
417 try:
416 cb = getattr(self, '_%s_changed' % name)
418 cb = getattr(self, '_%s_changed' % name)
417 except:
419 except:
418 pass
420 pass
419 else:
421 else:
420 callables.append(cb)
422 callables.append(cb)
421
423
422 # Call them all now
424 # Call them all now
423 for c in callables:
425 for c in callables:
424 # Traits catches and logs errors here. I allow them to raise
426 # Traits catches and logs errors here. I allow them to raise
425 if callable(c):
427 if callable(c):
426 argspec = inspect.getargspec(c)
428 argspec = inspect.getargspec(c)
427 nargs = len(argspec[0])
429 nargs = len(argspec[0])
428 # Bound methods have an additional 'self' argument
430 # Bound methods have an additional 'self' argument
429 # I don't know how to treat unbound methods, but they
431 # I don't know how to treat unbound methods, but they
430 # can't really be used for callbacks.
432 # can't really be used for callbacks.
431 if isinstance(c, types.MethodType):
433 if isinstance(c, types.MethodType):
432 offset = -1
434 offset = -1
433 else:
435 else:
434 offset = 0
436 offset = 0
435 if nargs + offset == 0:
437 if nargs + offset == 0:
436 c()
438 c()
437 elif nargs + offset == 1:
439 elif nargs + offset == 1:
438 c(name)
440 c(name)
439 elif nargs + offset == 2:
441 elif nargs + offset == 2:
440 c(name, new_value)
442 c(name, new_value)
441 elif nargs + offset == 3:
443 elif nargs + offset == 3:
442 c(name, old_value, new_value)
444 c(name, old_value, new_value)
443 else:
445 else:
444 raise TraitError('a trait changed callback '
446 raise TraitError('a trait changed callback '
445 'must have 0-3 arguments.')
447 'must have 0-3 arguments.')
446 else:
448 else:
447 raise TraitError('a trait changed callback '
449 raise TraitError('a trait changed callback '
448 'must be callable.')
450 'must be callable.')
449
451
450
452
451 def _add_notifiers(self, handler, name):
453 def _add_notifiers(self, handler, name):
452 if not self._trait_notifiers.has_key(name):
454 if not self._trait_notifiers.has_key(name):
453 nlist = []
455 nlist = []
454 self._trait_notifiers[name] = nlist
456 self._trait_notifiers[name] = nlist
455 else:
457 else:
456 nlist = self._trait_notifiers[name]
458 nlist = self._trait_notifiers[name]
457 if handler not in nlist:
459 if handler not in nlist:
458 nlist.append(handler)
460 nlist.append(handler)
459
461
460 def _remove_notifiers(self, handler, name):
462 def _remove_notifiers(self, handler, name):
461 if self._trait_notifiers.has_key(name):
463 if self._trait_notifiers.has_key(name):
462 nlist = self._trait_notifiers[name]
464 nlist = self._trait_notifiers[name]
463 try:
465 try:
464 index = nlist.index(handler)
466 index = nlist.index(handler)
465 except ValueError:
467 except ValueError:
466 pass
468 pass
467 else:
469 else:
468 del nlist[index]
470 del nlist[index]
469
471
470 def on_trait_change(self, handler, name=None, remove=False):
472 def on_trait_change(self, handler, name=None, remove=False):
471 """Setup a handler to be called when a trait changes.
473 """Setup a handler to be called when a trait changes.
472
474
473 This is used to setup dynamic notifications of trait changes.
475 This is used to setup dynamic notifications of trait changes.
474
476
475 Static handlers can be created by creating methods on a HasTraits
477 Static handlers can be created by creating methods on a HasTraits
476 subclass with the naming convention '_[traitname]_changed'. Thus,
478 subclass with the naming convention '_[traitname]_changed'. Thus,
477 to create static handler for the trait 'a', create the method
479 to create static handler for the trait 'a', create the method
478 _a_changed(self, name, old, new) (fewer arguments can be used, see
480 _a_changed(self, name, old, new) (fewer arguments can be used, see
479 below).
481 below).
480
482
481 Parameters
483 Parameters
482 ----------
484 ----------
483 handler : callable
485 handler : callable
484 A callable that is called when a trait changes. Its
486 A callable that is called when a trait changes. Its
485 signature can be handler(), handler(name), handler(name, new)
487 signature can be handler(), handler(name), handler(name, new)
486 or handler(name, old, new).
488 or handler(name, old, new).
487 name : list, str, None
489 name : list, str, None
488 If None, the handler will apply to all traits. If a list
490 If None, the handler will apply to all traits. If a list
489 of str, handler will apply to all names in the list. If a
491 of str, handler will apply to all names in the list. If a
490 str, the handler will apply just to that name.
492 str, the handler will apply just to that name.
491 remove : bool
493 remove : bool
492 If False (the default), then install the handler. If True
494 If False (the default), then install the handler. If True
493 then unintall it.
495 then unintall it.
494 """
496 """
495 if remove:
497 if remove:
496 names = parse_notifier_name(name)
498 names = parse_notifier_name(name)
497 for n in names:
499 for n in names:
498 self._remove_notifiers(handler, n)
500 self._remove_notifiers(handler, n)
499 else:
501 else:
500 names = parse_notifier_name(name)
502 names = parse_notifier_name(name)
501 for n in names:
503 for n in names:
502 self._add_notifiers(handler, n)
504 self._add_notifiers(handler, n)
503
505
504 def trait_names(self, **metadata):
506 def trait_names(self, **metadata):
505 """Get a list of all the names of this classes traits."""
507 """Get a list of all the names of this classes traits."""
506 return self.traits(**metadata).keys()
508 return self.traits(**metadata).keys()
507
509
508 def traits(self, **metadata):
510 def traits(self, **metadata):
509 """Get a list of all the traits of this class.
511 """Get a list of all the traits of this class.
510
512
511 The TraitTypes returned don't know anything about the values
513 The TraitTypes returned don't know anything about the values
512 that the various HasTrait's instances are holding.
514 that the various HasTrait's instances are holding.
513
515
514 This follows the same algorithm as traits does and does not allow
516 This follows the same algorithm as traits does and does not allow
515 for any simple way of specifying merely that a metadata name
517 for any simple way of specifying merely that a metadata name
516 exists, but has any value. This is because get_metadata returns
518 exists, but has any value. This is because get_metadata returns
517 None if a metadata key doesn't exist.
519 None if a metadata key doesn't exist.
518 """
520 """
519 traits = dict([memb for memb in getmembers(self.__class__) if \
521 traits = dict([memb for memb in getmembers(self.__class__) if \
520 isinstance(memb[1], TraitType)])
522 isinstance(memb[1], TraitType)])
521
523
522 if len(metadata) == 0:
524 if len(metadata) == 0:
523 return traits
525 return traits
524
526
525 for meta_name, meta_eval in metadata.items():
527 for meta_name, meta_eval in metadata.items():
526 if type(meta_eval) is not FunctionType:
528 if type(meta_eval) is not FunctionType:
527 metadata[meta_name] = _SimpleTest(meta_eval)
529 metadata[meta_name] = _SimpleTest(meta_eval)
528
530
529 result = {}
531 result = {}
530 for name, trait in traits.items():
532 for name, trait in traits.items():
531 for meta_name, meta_eval in metadata.items():
533 for meta_name, meta_eval in metadata.items():
532 if not meta_eval(trait.get_metadata(meta_name)):
534 if not meta_eval(trait.get_metadata(meta_name)):
533 break
535 break
534 else:
536 else:
535 result[name] = trait
537 result[name] = trait
536
538
537 return result
539 return result
538
540
539 def trait_metadata(self, traitname, key):
541 def trait_metadata(self, traitname, key):
540 """Get metadata values for trait by key."""
542 """Get metadata values for trait by key."""
541 try:
543 try:
542 trait = getattr(self.__class__, traitname)
544 trait = getattr(self.__class__, traitname)
543 except AttributeError:
545 except AttributeError:
544 raise TraitError("Class %s does not have a trait named %s" %
546 raise TraitError("Class %s does not have a trait named %s" %
545 (self.__class__.__name__, traitname))
547 (self.__class__.__name__, traitname))
546 else:
548 else:
547 return trait.get_metadata(key)
549 return trait.get_metadata(key)
548
550
549 #-----------------------------------------------------------------------------
551 #-----------------------------------------------------------------------------
550 # Actual TraitTypes implementations/subclasses
552 # Actual TraitTypes implementations/subclasses
551 #-----------------------------------------------------------------------------
553 #-----------------------------------------------------------------------------
552
554
553 #-----------------------------------------------------------------------------
555 #-----------------------------------------------------------------------------
554 # TraitTypes subclasses for handling classes and instances of classes
556 # TraitTypes subclasses for handling classes and instances of classes
555 #-----------------------------------------------------------------------------
557 #-----------------------------------------------------------------------------
556
558
557
559
558 class ClassBasedTraitType(TraitType):
560 class ClassBasedTraitType(TraitType):
559 """A trait with error reporting for Type, Instance and This."""
561 """A trait with error reporting for Type, Instance and This."""
560
562
561 def error(self, obj, value):
563 def error(self, obj, value):
562 kind = type(value)
564 kind = type(value)
563 if kind is InstanceType:
565 if kind is InstanceType:
564 msg = 'class %s' % value.__class__.__name__
566 msg = 'class %s' % value.__class__.__name__
565 else:
567 else:
566 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
568 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
567
569
568 super(ClassBasedTraitType, self).error(obj, msg)
570 super(ClassBasedTraitType, self).error(obj, msg)
569
571
570
572
571 class Type(ClassBasedTraitType):
573 class Type(ClassBasedTraitType):
572 """A trait whose value must be a subclass of a specified class."""
574 """A trait whose value must be a subclass of a specified class."""
573
575
574 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
576 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
575 """Construct a Type trait
577 """Construct a Type trait
576
578
577 A Type trait specifies that its values must be subclasses of
579 A Type trait specifies that its values must be subclasses of
578 a particular class.
580 a particular class.
579
581
580 If only ``default_value`` is given, it is used for the ``klass`` as
582 If only ``default_value`` is given, it is used for the ``klass`` as
581 well.
583 well.
582
584
583 Parameters
585 Parameters
584 ----------
586 ----------
585 default_value : class, str or None
587 default_value : class, str or None
586 The default value must be a subclass of klass. If an str,
588 The default value must be a subclass of klass. If an str,
587 the str must be a fully specified class name, like 'foo.bar.Bah'.
589 the str must be a fully specified class name, like 'foo.bar.Bah'.
588 The string is resolved into real class, when the parent
590 The string is resolved into real class, when the parent
589 :class:`HasTraits` class is instantiated.
591 :class:`HasTraits` class is instantiated.
590 klass : class, str, None
592 klass : class, str, None
591 Values of this trait must be a subclass of klass. The klass
593 Values of this trait must be a subclass of klass. The klass
592 may be specified in a string like: 'foo.bar.MyClass'.
594 may be specified in a string like: 'foo.bar.MyClass'.
593 The string is resolved into real class, when the parent
595 The string is resolved into real class, when the parent
594 :class:`HasTraits` class is instantiated.
596 :class:`HasTraits` class is instantiated.
595 allow_none : boolean
597 allow_none : boolean
596 Indicates whether None is allowed as an assignable value. Even if
598 Indicates whether None is allowed as an assignable value. Even if
597 ``False``, the default value may be ``None``.
599 ``False``, the default value may be ``None``.
598 """
600 """
599 if default_value is None:
601 if default_value is None:
600 if klass is None:
602 if klass is None:
601 klass = object
603 klass = object
602 elif klass is None:
604 elif klass is None:
603 klass = default_value
605 klass = default_value
604
606
605 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
607 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
606 raise TraitError("A Type trait must specify a class.")
608 raise TraitError("A Type trait must specify a class.")
607
609
608 self.klass = klass
610 self.klass = klass
609 self._allow_none = allow_none
611 self._allow_none = allow_none
610
612
611 super(Type, self).__init__(default_value, **metadata)
613 super(Type, self).__init__(default_value, **metadata)
612
614
613 def validate(self, obj, value):
615 def validate(self, obj, value):
614 """Validates that the value is a valid object instance."""
616 """Validates that the value is a valid object instance."""
615 try:
617 try:
616 if issubclass(value, self.klass):
618 if issubclass(value, self.klass):
617 return value
619 return value
618 except:
620 except:
619 if (value is None) and (self._allow_none):
621 if (value is None) and (self._allow_none):
620 return value
622 return value
621
623
622 self.error(obj, value)
624 self.error(obj, value)
623
625
624 def info(self):
626 def info(self):
625 """ Returns a description of the trait."""
627 """ Returns a description of the trait."""
626 if isinstance(self.klass, basestring):
628 if isinstance(self.klass, basestring):
627 klass = self.klass
629 klass = self.klass
628 else:
630 else:
629 klass = self.klass.__name__
631 klass = self.klass.__name__
630 result = 'a subclass of ' + klass
632 result = 'a subclass of ' + klass
631 if self._allow_none:
633 if self._allow_none:
632 return result + ' or None'
634 return result + ' or None'
633 return result
635 return result
634
636
635 def instance_init(self, obj):
637 def instance_init(self, obj):
636 self._resolve_classes()
638 self._resolve_classes()
637 super(Type, self).instance_init(obj)
639 super(Type, self).instance_init(obj)
638
640
639 def _resolve_classes(self):
641 def _resolve_classes(self):
640 if isinstance(self.klass, basestring):
642 if isinstance(self.klass, basestring):
641 self.klass = import_item(self.klass)
643 self.klass = import_item(self.klass)
642 if isinstance(self.default_value, basestring):
644 if isinstance(self.default_value, basestring):
643 self.default_value = import_item(self.default_value)
645 self.default_value = import_item(self.default_value)
644
646
645 def get_default_value(self):
647 def get_default_value(self):
646 return self.default_value
648 return self.default_value
647
649
648
650
649 class DefaultValueGenerator(object):
651 class DefaultValueGenerator(object):
650 """A class for generating new default value instances."""
652 """A class for generating new default value instances."""
651
653
652 def __init__(self, *args, **kw):
654 def __init__(self, *args, **kw):
653 self.args = args
655 self.args = args
654 self.kw = kw
656 self.kw = kw
655
657
656 def generate(self, klass):
658 def generate(self, klass):
657 return klass(*self.args, **self.kw)
659 return klass(*self.args, **self.kw)
658
660
659
661
660 class Instance(ClassBasedTraitType):
662 class Instance(ClassBasedTraitType):
661 """A trait whose value must be an instance of a specified class.
663 """A trait whose value must be an instance of a specified class.
662
664
663 The value can also be an instance of a subclass of the specified class.
665 The value can also be an instance of a subclass of the specified class.
664 """
666 """
665
667
666 def __init__(self, klass=None, args=None, kw=None,
668 def __init__(self, klass=None, args=None, kw=None,
667 allow_none=True, **metadata ):
669 allow_none=True, **metadata ):
668 """Construct an Instance trait.
670 """Construct an Instance trait.
669
671
670 This trait allows values that are instances of a particular
672 This trait allows values that are instances of a particular
671 class or its sublclasses. Our implementation is quite different
673 class or its sublclasses. Our implementation is quite different
672 from that of enthough.traits as we don't allow instances to be used
674 from that of enthough.traits as we don't allow instances to be used
673 for klass and we handle the ``args`` and ``kw`` arguments differently.
675 for klass and we handle the ``args`` and ``kw`` arguments differently.
674
676
675 Parameters
677 Parameters
676 ----------
678 ----------
677 klass : class, str
679 klass : class, str
678 The class that forms the basis for the trait. Class names
680 The class that forms the basis for the trait. Class names
679 can also be specified as strings, like 'foo.bar.Bar'.
681 can also be specified as strings, like 'foo.bar.Bar'.
680 args : tuple
682 args : tuple
681 Positional arguments for generating the default value.
683 Positional arguments for generating the default value.
682 kw : dict
684 kw : dict
683 Keyword arguments for generating the default value.
685 Keyword arguments for generating the default value.
684 allow_none : bool
686 allow_none : bool
685 Indicates whether None is allowed as a value.
687 Indicates whether None is allowed as a value.
686
688
687 Default Value
689 Default Value
688 -------------
690 -------------
689 If both ``args`` and ``kw`` are None, then the default value is None.
691 If both ``args`` and ``kw`` are None, then the default value is None.
690 If ``args`` is a tuple and ``kw`` is a dict, then the default is
692 If ``args`` is a tuple and ``kw`` is a dict, then the default is
691 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
693 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
692 not (but not both), None is replace by ``()`` or ``{}``.
694 not (but not both), None is replace by ``()`` or ``{}``.
693 """
695 """
694
696
695 self._allow_none = allow_none
697 self._allow_none = allow_none
696
698
697 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
699 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
698 raise TraitError('The klass argument must be a class'
700 raise TraitError('The klass argument must be a class'
699 ' you gave: %r' % klass)
701 ' you gave: %r' % klass)
700 self.klass = klass
702 self.klass = klass
701
703
702 # self.klass is a class, so handle default_value
704 # self.klass is a class, so handle default_value
703 if args is None and kw is None:
705 if args is None and kw is None:
704 default_value = None
706 default_value = None
705 else:
707 else:
706 if args is None:
708 if args is None:
707 # kw is not None
709 # kw is not None
708 args = ()
710 args = ()
709 elif kw is None:
711 elif kw is None:
710 # args is not None
712 # args is not None
711 kw = {}
713 kw = {}
712
714
713 if not isinstance(kw, dict):
715 if not isinstance(kw, dict):
714 raise TraitError("The 'kw' argument must be a dict or None.")
716 raise TraitError("The 'kw' argument must be a dict or None.")
715 if not isinstance(args, tuple):
717 if not isinstance(args, tuple):
716 raise TraitError("The 'args' argument must be a tuple or None.")
718 raise TraitError("The 'args' argument must be a tuple or None.")
717
719
718 default_value = DefaultValueGenerator(*args, **kw)
720 default_value = DefaultValueGenerator(*args, **kw)
719
721
720 super(Instance, self).__init__(default_value, **metadata)
722 super(Instance, self).__init__(default_value, **metadata)
721
723
722 def validate(self, obj, value):
724 def validate(self, obj, value):
723 if value is None:
725 if value is None:
724 if self._allow_none:
726 if self._allow_none:
725 return value
727 return value
726 self.error(obj, value)
728 self.error(obj, value)
727
729
728 if isinstance(value, self.klass):
730 if isinstance(value, self.klass):
729 return value
731 return value
730 else:
732 else:
731 self.error(obj, value)
733 self.error(obj, value)
732
734
733 def info(self):
735 def info(self):
734 if isinstance(self.klass, basestring):
736 if isinstance(self.klass, basestring):
735 klass = self.klass
737 klass = self.klass
736 else:
738 else:
737 klass = self.klass.__name__
739 klass = self.klass.__name__
738 result = class_of(klass)
740 result = class_of(klass)
739 if self._allow_none:
741 if self._allow_none:
740 return result + ' or None'
742 return result + ' or None'
741
743
742 return result
744 return result
743
745
744 def instance_init(self, obj):
746 def instance_init(self, obj):
745 self._resolve_classes()
747 self._resolve_classes()
746 super(Instance, self).instance_init(obj)
748 super(Instance, self).instance_init(obj)
747
749
748 def _resolve_classes(self):
750 def _resolve_classes(self):
749 if isinstance(self.klass, basestring):
751 if isinstance(self.klass, basestring):
750 self.klass = import_item(self.klass)
752 self.klass = import_item(self.klass)
751
753
752 def get_default_value(self):
754 def get_default_value(self):
753 """Instantiate a default value instance.
755 """Instantiate a default value instance.
754
756
755 This is called when the containing HasTraits classes'
757 This is called when the containing HasTraits classes'
756 :meth:`__new__` method is called to ensure that a unique instance
758 :meth:`__new__` method is called to ensure that a unique instance
757 is created for each HasTraits instance.
759 is created for each HasTraits instance.
758 """
760 """
759 dv = self.default_value
761 dv = self.default_value
760 if isinstance(dv, DefaultValueGenerator):
762 if isinstance(dv, DefaultValueGenerator):
761 return dv.generate(self.klass)
763 return dv.generate(self.klass)
762 else:
764 else:
763 return dv
765 return dv
764
766
765
767
766 class This(ClassBasedTraitType):
768 class This(ClassBasedTraitType):
767 """A trait for instances of the class containing this trait.
769 """A trait for instances of the class containing this trait.
768
770
769 Because how how and when class bodies are executed, the ``This``
771 Because how how and when class bodies are executed, the ``This``
770 trait can only have a default value of None. This, and because we
772 trait can only have a default value of None. This, and because we
771 always validate default values, ``allow_none`` is *always* true.
773 always validate default values, ``allow_none`` is *always* true.
772 """
774 """
773
775
774 info_text = 'an instance of the same type as the receiver or None'
776 info_text = 'an instance of the same type as the receiver or None'
775
777
776 def __init__(self, **metadata):
778 def __init__(self, **metadata):
777 super(This, self).__init__(None, **metadata)
779 super(This, self).__init__(None, **metadata)
778
780
779 def validate(self, obj, value):
781 def validate(self, obj, value):
780 # What if value is a superclass of obj.__class__? This is
782 # What if value is a superclass of obj.__class__? This is
781 # complicated if it was the superclass that defined the This
783 # complicated if it was the superclass that defined the This
782 # trait.
784 # trait.
783 if isinstance(value, self.this_class) or (value is None):
785 if isinstance(value, self.this_class) or (value is None):
784 return value
786 return value
785 else:
787 else:
786 self.error(obj, value)
788 self.error(obj, value)
787
789
788
790
789 #-----------------------------------------------------------------------------
791 #-----------------------------------------------------------------------------
790 # Basic TraitTypes implementations/subclasses
792 # Basic TraitTypes implementations/subclasses
791 #-----------------------------------------------------------------------------
793 #-----------------------------------------------------------------------------
792
794
793
795
794 class Any(TraitType):
796 class Any(TraitType):
795 default_value = None
797 default_value = None
796 info_text = 'any value'
798 info_text = 'any value'
797
799
798
800
799 class Int(TraitType):
801 class Int(TraitType):
800 """A integer trait."""
802 """A integer trait."""
801
803
802 evaluate = int
804 evaluate = int
803 default_value = 0
805 default_value = 0
804 info_text = 'an integer'
806 info_text = 'an integer'
805
807
806 def validate(self, obj, value):
808 def validate(self, obj, value):
807 if isinstance(value, int):
809 if isinstance(value, int):
808 return value
810 return value
809 self.error(obj, value)
811 self.error(obj, value)
810
812
811 class CInt(Int):
813 class CInt(Int):
812 """A casting version of the int trait."""
814 """A casting version of the int trait."""
813
815
814 def validate(self, obj, value):
816 def validate(self, obj, value):
815 try:
817 try:
816 return int(value)
818 return int(value)
817 except:
819 except:
818 self.error(obj, value)
820 self.error(obj, value)
819
821
820
822
821 class Long(TraitType):
823 class Long(TraitType):
822 """A long integer trait."""
824 """A long integer trait."""
823
825
824 evaluate = long
826 evaluate = long
825 default_value = 0L
827 default_value = 0L
826 info_text = 'a long'
828 info_text = 'a long'
827
829
828 def validate(self, obj, value):
830 def validate(self, obj, value):
829 if isinstance(value, long):
831 if isinstance(value, long):
830 return value
832 return value
831 if isinstance(value, int):
833 if isinstance(value, int):
832 return long(value)
834 return long(value)
833 self.error(obj, value)
835 self.error(obj, value)
834
836
835
837
836 class CLong(Long):
838 class CLong(Long):
837 """A casting version of the long integer trait."""
839 """A casting version of the long integer trait."""
838
840
839 def validate(self, obj, value):
841 def validate(self, obj, value):
840 try:
842 try:
841 return long(value)
843 return long(value)
842 except:
844 except:
843 self.error(obj, value)
845 self.error(obj, value)
844
846
845
847
846 class Float(TraitType):
848 class Float(TraitType):
847 """A float trait."""
849 """A float trait."""
848
850
849 evaluate = float
851 evaluate = float
850 default_value = 0.0
852 default_value = 0.0
851 info_text = 'a float'
853 info_text = 'a float'
852
854
853 def validate(self, obj, value):
855 def validate(self, obj, value):
854 if isinstance(value, float):
856 if isinstance(value, float):
855 return value
857 return value
856 if isinstance(value, int):
858 if isinstance(value, int):
857 return float(value)
859 return float(value)
858 self.error(obj, value)
860 self.error(obj, value)
859
861
860
862
861 class CFloat(Float):
863 class CFloat(Float):
862 """A casting version of the float trait."""
864 """A casting version of the float trait."""
863
865
864 def validate(self, obj, value):
866 def validate(self, obj, value):
865 try:
867 try:
866 return float(value)
868 return float(value)
867 except:
869 except:
868 self.error(obj, value)
870 self.error(obj, value)
869
871
870 class Complex(TraitType):
872 class Complex(TraitType):
871 """A trait for complex numbers."""
873 """A trait for complex numbers."""
872
874
873 evaluate = complex
875 evaluate = complex
874 default_value = 0.0 + 0.0j
876 default_value = 0.0 + 0.0j
875 info_text = 'a complex number'
877 info_text = 'a complex number'
876
878
877 def validate(self, obj, value):
879 def validate(self, obj, value):
878 if isinstance(value, complex):
880 if isinstance(value, complex):
879 return value
881 return value
880 if isinstance(value, (float, int)):
882 if isinstance(value, (float, int)):
881 return complex(value)
883 return complex(value)
882 self.error(obj, value)
884 self.error(obj, value)
883
885
884
886
885 class CComplex(Complex):
887 class CComplex(Complex):
886 """A casting version of the complex number trait."""
888 """A casting version of the complex number trait."""
887
889
888 def validate (self, obj, value):
890 def validate (self, obj, value):
889 try:
891 try:
890 return complex(value)
892 return complex(value)
891 except:
893 except:
892 self.error(obj, value)
894 self.error(obj, value)
893
895
894
896
895 class Str(TraitType):
897 class Str(TraitType):
896 """A trait for strings."""
898 """A trait for strings."""
897
899
898 evaluate = lambda x: x
900 evaluate = lambda x: x
899 default_value = ''
901 default_value = ''
900 info_text = 'a string'
902 info_text = 'a string'
901
903
902 def validate(self, obj, value):
904 def validate(self, obj, value):
903 if isinstance(value, str):
905 if isinstance(value, str):
904 return value
906 return value
905 self.error(obj, value)
907 self.error(obj, value)
906
908
907
909
908 class CStr(Str):
910 class CStr(Str):
909 """A casting version of the string trait."""
911 """A casting version of the string trait."""
910
912
911 def validate(self, obj, value):
913 def validate(self, obj, value):
912 try:
914 try:
913 return str(value)
915 return str(value)
914 except:
916 except:
915 try:
917 try:
916 return unicode(value)
918 return unicode(value)
917 except:
919 except:
918 self.error(obj, value)
920 self.error(obj, value)
919
921
920
922
921 class Unicode(TraitType):
923 class Unicode(TraitType):
922 """A trait for unicode strings."""
924 """A trait for unicode strings."""
923
925
924 evaluate = unicode
926 evaluate = unicode
925 default_value = u''
927 default_value = u''
926 info_text = 'a unicode string'
928 info_text = 'a unicode string'
927
929
928 def validate(self, obj, value):
930 def validate(self, obj, value):
929 if isinstance(value, unicode):
931 if isinstance(value, unicode):
930 return value
932 return value
931 if isinstance(value, str):
933 if isinstance(value, str):
932 return unicode(value)
934 return unicode(value)
933 self.error(obj, value)
935 self.error(obj, value)
934
936
935
937
936 class CUnicode(Unicode):
938 class CUnicode(Unicode):
937 """A casting version of the unicode trait."""
939 """A casting version of the unicode trait."""
938
940
939 def validate(self, obj, value):
941 def validate(self, obj, value):
940 try:
942 try:
941 return unicode(value)
943 return unicode(value)
942 except:
944 except:
943 self.error(obj, value)
945 self.error(obj, value)
944
946
945
947
946 class Bool(TraitType):
948 class Bool(TraitType):
947 """A boolean (True, False) trait."""
949 """A boolean (True, False) trait."""
948 evaluate = bool
950 evaluate = bool
949 default_value = False
951 default_value = False
950 info_text = 'a boolean'
952 info_text = 'a boolean'
951
953
952 def validate(self, obj, value):
954 def validate(self, obj, value):
953 if isinstance(value, bool):
955 if isinstance(value, bool):
954 return value
956 return value
955 self.error(obj, value)
957 self.error(obj, value)
956
958
957
959
958 class CBool(Bool):
960 class CBool(Bool):
959 """A casting version of the boolean trait."""
961 """A casting version of the boolean trait."""
960
962
961 def validate(self, obj, value):
963 def validate(self, obj, value):
962 try:
964 try:
963 return bool(value)
965 return bool(value)
964 except:
966 except:
965 self.error(obj, value)
967 self.error(obj, value)
966
968
967
969
968 class Enum(TraitType):
970 class Enum(TraitType):
969 """An enum that whose value must be in a given sequence."""
971 """An enum that whose value must be in a given sequence."""
970
972
971 def __init__(self, values, default_value=None, allow_none=True, **metadata):
973 def __init__(self, values, default_value=None, allow_none=True, **metadata):
972 self.values = values
974 self.values = values
973 self._allow_none = allow_none
975 self._allow_none = allow_none
974 super(Enum, self).__init__(default_value, **metadata)
976 super(Enum, self).__init__(default_value, **metadata)
975
977
976 def validate(self, obj, value):
978 def validate(self, obj, value):
977 if value is None:
979 if value is None:
978 if self._allow_none:
980 if self._allow_none:
979 return value
981 return value
980
982
981 if value in self.values:
983 if value in self.values:
982 return value
984 return value
983 self.error(obj, value)
985 self.error(obj, value)
984
986
985 def info(self):
987 def info(self):
986 """ Returns a description of the trait."""
988 """ Returns a description of the trait."""
987 result = 'any of ' + repr(self.values)
989 result = 'any of ' + repr(self.values)
988 if self._allow_none:
990 if self._allow_none:
989 return result + ' or None'
991 return result + ' or None'
990 return result
992 return result
991
993
992 class CaselessStrEnum(Enum):
994 class CaselessStrEnum(Enum):
993 """An enum of strings that are caseless in validate."""
995 """An enum of strings that are caseless in validate."""
994
996
995 def validate(self, obj, value):
997 def validate(self, obj, value):
996 if value is None:
998 if value is None:
997 if self._allow_none:
999 if self._allow_none:
998 return value
1000 return value
999
1001
1000 if not isinstance(value, str):
1002 if not isinstance(value, str):
1001 self.error(obj, value)
1003 self.error(obj, value)
1002
1004
1003 for v in self.values:
1005 for v in self.values:
1004 if v.lower() == value.lower():
1006 if v.lower() == value.lower():
1005 return v
1007 return v
1006 self.error(obj, value)
1008 self.error(obj, value)
1007
1009
1008
1010
1009 class List(Instance):
1011 class List(Instance):
1010 """An instance of a Python list."""
1012 """An instance of a Python list."""
1011
1013
1012 def __init__(self, default_value=None, allow_none=True, **metadata):
1014 def __init__(self, default_value=None, allow_none=True, **metadata):
1013 """Create a list trait type from a list or tuple.
1015 """Create a list trait type from a list or tuple.
1014
1016
1015 The default value is created by doing ``list(default_value)``,
1017 The default value is created by doing ``list(default_value)``,
1016 which creates a copy of the ``default_value``.
1018 which creates a copy of the ``default_value``.
1017 """
1019 """
1018 if default_value is None:
1020 if default_value is None:
1019 args = ((),)
1021 args = ((),)
1020 elif isinstance(default_value, SequenceTypes):
1022 elif isinstance(default_value, SequenceTypes):
1021 args = (default_value,)
1023 args = (default_value,)
1022 else:
1024 else:
1023 raise TypeError('default value of List was %s' % default_value)
1025 raise TypeError('default value of List was %s' % default_value)
1024
1026
1025 super(List,self).__init__(klass=list, args=args,
1027 super(List,self).__init__(klass=list, args=args,
1026 allow_none=allow_none, **metadata)
1028 allow_none=allow_none, **metadata)
1027
1029
1028
1030
1029 class Dict(Instance):
1031 class Dict(Instance):
1030 """An instance of a Python dict."""
1032 """An instance of a Python dict."""
1031
1033
1032 def __init__(self, default_value=None, allow_none=True, **metadata):
1034 def __init__(self, default_value=None, allow_none=True, **metadata):
1033 """Create a dict trait type from a dict.
1035 """Create a dict trait type from a dict.
1034
1036
1035 The default value is created by doing ``dict(default_value)``,
1037 The default value is created by doing ``dict(default_value)``,
1036 which creates a copy of the ``default_value``.
1038 which creates a copy of the ``default_value``.
1037 """
1039 """
1038 if default_value is None:
1040 if default_value is None:
1039 args = ((),)
1041 args = ((),)
1040 elif isinstance(default_value, dict):
1042 elif isinstance(default_value, dict):
1041 args = (default_value,)
1043 args = (default_value,)
1042 elif isinstance(default_value, SequenceTypes):
1044 elif isinstance(default_value, SequenceTypes):
1043 args = (default_value,)
1045 args = (default_value,)
1044 else:
1046 else:
1045 raise TypeError('default value of Dict was %s' % default_value)
1047 raise TypeError('default value of Dict was %s' % default_value)
1046
1048
1047 super(Dict,self).__init__(klass=dict, args=args,
1049 super(Dict,self).__init__(klass=dict, args=args,
1048 allow_none=allow_none, **metadata)
1050 allow_none=allow_none, **metadata)
General Comments 0
You need to be logged in to leave comments. Login now