##// END OF EJS Templates
Don't calculate default value when setting traitlet for the first time
Thomas Kluyver -
Show More
@@ -1,379 +1,380 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for IPython.
3 An application for IPython.
4
4
5 All top-level applications should use the classes in this module for
5 All top-level applications should use the classes in this module for
6 handling configuration and creating configurables.
6 handling configuration and creating configurables.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the configurable objects, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 import atexit
15 import atexit
16 import glob
16 import glob
17 import logging
17 import logging
18 import os
18 import os
19 import shutil
19 import shutil
20 import sys
20 import sys
21
21
22 from IPython.config.application import Application, catch_config_error
22 from IPython.config.application import Application, catch_config_error
23 from IPython.config.loader import ConfigFileNotFound
23 from IPython.config.loader import ConfigFileNotFound
24 from IPython.core import release, crashhandler
24 from IPython.core import release, crashhandler
25 from IPython.core.profiledir import ProfileDir, ProfileDirError
25 from IPython.core.profiledir import ProfileDir, ProfileDirError
26 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_dir_exists
26 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_dir_exists
27 from IPython.utils import py3compat
27 from IPython.utils import py3compat
28 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance
28 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance
29
29
30 if os.name == 'nt':
30 if os.name == 'nt':
31 programdata = os.environ.get('PROGRAMDATA', None)
31 programdata = os.environ.get('PROGRAMDATA', None)
32 if programdata:
32 if programdata:
33 SYSTEM_CONFIG_DIRS = [os.path.join(programdata, 'ipython')]
33 SYSTEM_CONFIG_DIRS = [os.path.join(programdata, 'ipython')]
34 else: # PROGRAMDATA is not defined by default on XP.
34 else: # PROGRAMDATA is not defined by default on XP.
35 SYSTEM_CONFIG_DIRS = []
35 SYSTEM_CONFIG_DIRS = []
36 else:
36 else:
37 SYSTEM_CONFIG_DIRS = [
37 SYSTEM_CONFIG_DIRS = [
38 "/usr/local/etc/ipython",
38 "/usr/local/etc/ipython",
39 "/etc/ipython",
39 "/etc/ipython",
40 ]
40 ]
41
41
42
42
43 # aliases and flags
43 # aliases and flags
44
44
45 base_aliases = {
45 base_aliases = {
46 'profile-dir' : 'ProfileDir.location',
46 'profile-dir' : 'ProfileDir.location',
47 'profile' : 'BaseIPythonApplication.profile',
47 'profile' : 'BaseIPythonApplication.profile',
48 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
48 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
49 'log-level' : 'Application.log_level',
49 'log-level' : 'Application.log_level',
50 'config' : 'BaseIPythonApplication.extra_config_file',
50 'config' : 'BaseIPythonApplication.extra_config_file',
51 }
51 }
52
52
53 base_flags = dict(
53 base_flags = dict(
54 debug = ({'Application' : {'log_level' : logging.DEBUG}},
54 debug = ({'Application' : {'log_level' : logging.DEBUG}},
55 "set log level to logging.DEBUG (maximize logging output)"),
55 "set log level to logging.DEBUG (maximize logging output)"),
56 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
56 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
57 "set log level to logging.CRITICAL (minimize logging output)"),
57 "set log level to logging.CRITICAL (minimize logging output)"),
58 init = ({'BaseIPythonApplication' : {
58 init = ({'BaseIPythonApplication' : {
59 'copy_config_files' : True,
59 'copy_config_files' : True,
60 'auto_create' : True}
60 'auto_create' : True}
61 }, """Initialize profile with default config files. This is equivalent
61 }, """Initialize profile with default config files. This is equivalent
62 to running `ipython profile create <profile>` prior to startup.
62 to running `ipython profile create <profile>` prior to startup.
63 """)
63 """)
64 )
64 )
65
65
66
66
67 class BaseIPythonApplication(Application):
67 class BaseIPythonApplication(Application):
68
68
69 name = Unicode(u'ipython')
69 name = Unicode(u'ipython')
70 description = Unicode(u'IPython: an enhanced interactive Python shell.')
70 description = Unicode(u'IPython: an enhanced interactive Python shell.')
71 version = Unicode(release.version)
71 version = Unicode(release.version)
72
72
73 aliases = Dict(base_aliases)
73 aliases = Dict(base_aliases)
74 flags = Dict(base_flags)
74 flags = Dict(base_flags)
75 classes = List([ProfileDir])
75 classes = List([ProfileDir])
76
76
77 # Track whether the config_file has changed,
77 # Track whether the config_file has changed,
78 # because some logic happens only if we aren't using the default.
78 # because some logic happens only if we aren't using the default.
79 config_file_specified = Set()
79 config_file_specified = Set()
80
80
81 config_file_name = Unicode()
81 config_file_name = Unicode()
82 def _config_file_name_default(self):
82 def _config_file_name_default(self):
83 return self.name.replace('-','_') + u'_config.py'
83 return self.name.replace('-','_') + u'_config.py'
84 def _config_file_name_changed(self, name, old, new):
84 def _config_file_name_changed(self, name, old, new):
85 if new != old:
85 if new != old:
86 self.config_file_specified.add(new)
86 self.config_file_specified.add(new)
87
87
88 # The directory that contains IPython's builtin profiles.
88 # The directory that contains IPython's builtin profiles.
89 builtin_profile_dir = Unicode(
89 builtin_profile_dir = Unicode(
90 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
90 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
91 )
91 )
92
92
93 config_file_paths = List(Unicode)
93 config_file_paths = List(Unicode)
94 def _config_file_paths_default(self):
94 def _config_file_paths_default(self):
95 return [py3compat.getcwd()]
95 return [py3compat.getcwd()]
96
96
97 extra_config_file = Unicode(config=True,
97 extra_config_file = Unicode(config=True,
98 help="""Path to an extra config file to load.
98 help="""Path to an extra config file to load.
99
99
100 If specified, load this config file in addition to any other IPython config.
100 If specified, load this config file in addition to any other IPython config.
101 """)
101 """)
102 def _extra_config_file_changed(self, name, old, new):
102 def _extra_config_file_changed(self, name, old, new):
103 try:
103 try:
104 self.config_files.remove(old)
104 self.config_files.remove(old)
105 except ValueError:
105 except ValueError:
106 pass
106 pass
107 self.config_file_specified.add(new)
107 self.config_file_specified.add(new)
108 self.config_files.append(new)
108 self.config_files.append(new)
109
109
110 profile = Unicode(u'default', config=True,
110 profile = Unicode(u'default', config=True,
111 help="""The IPython profile to use."""
111 help="""The IPython profile to use."""
112 )
112 )
113
113
114 def _profile_changed(self, name, old, new):
114 def _profile_changed(self, name, old, new):
115 self.builtin_profile_dir = os.path.join(
115 self.builtin_profile_dir = os.path.join(
116 get_ipython_package_dir(), u'config', u'profile', new
116 get_ipython_package_dir(), u'config', u'profile', new
117 )
117 )
118
118
119 ipython_dir = Unicode(config=True,
119 ipython_dir = Unicode(config=True,
120 help="""
120 help="""
121 The name of the IPython directory. This directory is used for logging
121 The name of the IPython directory. This directory is used for logging
122 configuration (through profiles), history storage, etc. The default
122 configuration (through profiles), history storage, etc. The default
123 is usually $HOME/.ipython. This option can also be specified through
123 is usually $HOME/.ipython. This option can also be specified through
124 the environment variable IPYTHONDIR.
124 the environment variable IPYTHONDIR.
125 """
125 """
126 )
126 )
127 def _ipython_dir_default(self):
127 def _ipython_dir_default(self):
128 d = get_ipython_dir()
128 d = get_ipython_dir()
129 self._ipython_dir_changed('ipython_dir', d, d)
129 self._ipython_dir_changed('ipython_dir', d, d)
130 return d
130 return d
131
131
132 _in_init_profile_dir = False
132 _in_init_profile_dir = False
133 profile_dir = Instance(ProfileDir)
133 profile_dir = Instance(ProfileDir)
134 def _profile_dir_default(self):
134 def _profile_dir_default(self):
135 # avoid recursion
135 # avoid recursion
136 if self._in_init_profile_dir:
136 if self._in_init_profile_dir:
137 return
137 return
138 # profile_dir requested early, force initialization
138 # profile_dir requested early, force initialization
139 self.init_profile_dir()
139 self.init_profile_dir()
140 return self.profile_dir
140 return self.profile_dir
141
141
142 overwrite = Bool(False, config=True,
142 overwrite = Bool(False, config=True,
143 help="""Whether to overwrite existing config files when copying""")
143 help="""Whether to overwrite existing config files when copying""")
144 auto_create = Bool(False, config=True,
144 auto_create = Bool(False, config=True,
145 help="""Whether to create profile dir if it doesn't exist""")
145 help="""Whether to create profile dir if it doesn't exist""")
146
146
147 config_files = List(Unicode)
147 config_files = List(Unicode)
148 def _config_files_default(self):
148 def _config_files_default(self):
149 return [self.config_file_name]
149 return [self.config_file_name]
150
150
151 copy_config_files = Bool(False, config=True,
151 copy_config_files = Bool(False, config=True,
152 help="""Whether to install the default config files into the profile dir.
152 help="""Whether to install the default config files into the profile dir.
153 If a new profile is being created, and IPython contains config files for that
153 If a new profile is being created, and IPython contains config files for that
154 profile, then they will be staged into the new directory. Otherwise,
154 profile, then they will be staged into the new directory. Otherwise,
155 default config files will be automatically generated.
155 default config files will be automatically generated.
156 """)
156 """)
157
157
158 verbose_crash = Bool(False, config=True,
158 verbose_crash = Bool(False, config=True,
159 help="""Create a massive crash report when IPython encounters what may be an
159 help="""Create a massive crash report when IPython encounters what may be an
160 internal error. The default is to append a short message to the
160 internal error. The default is to append a short message to the
161 usual traceback""")
161 usual traceback""")
162
162
163 # The class to use as the crash handler.
163 # The class to use as the crash handler.
164 crash_handler_class = Type(crashhandler.CrashHandler)
164 crash_handler_class = Type(crashhandler.CrashHandler)
165
165
166 @catch_config_error
166 @catch_config_error
167 def __init__(self, **kwargs):
167 def __init__(self, **kwargs):
168 super(BaseIPythonApplication, self).__init__(**kwargs)
168 super(BaseIPythonApplication, self).__init__(**kwargs)
169 # ensure current working directory exists
169 # ensure current working directory exists
170 try:
170 try:
171 directory = py3compat.getcwd()
171 directory = py3compat.getcwd()
172 except:
172 except:
173 # raise exception
173 # raise exception
174 self.log.error("Current working directory doesn't exist.")
174 self.log.error("Current working directory doesn't exist.")
175 raise
175 raise
176
176
177 #-------------------------------------------------------------------------
177 #-------------------------------------------------------------------------
178 # Various stages of Application creation
178 # Various stages of Application creation
179 #-------------------------------------------------------------------------
179 #-------------------------------------------------------------------------
180
180
181 def init_crash_handler(self):
181 def init_crash_handler(self):
182 """Create a crash handler, typically setting sys.excepthook to it."""
182 """Create a crash handler, typically setting sys.excepthook to it."""
183 self.crash_handler = self.crash_handler_class(self)
183 self.crash_handler = self.crash_handler_class(self)
184 sys.excepthook = self.excepthook
184 sys.excepthook = self.excepthook
185 def unset_crashhandler():
185 def unset_crashhandler():
186 sys.excepthook = sys.__excepthook__
186 sys.excepthook = sys.__excepthook__
187 atexit.register(unset_crashhandler)
187 atexit.register(unset_crashhandler)
188
188
189 def excepthook(self, etype, evalue, tb):
189 def excepthook(self, etype, evalue, tb):
190 """this is sys.excepthook after init_crashhandler
190 """this is sys.excepthook after init_crashhandler
191
191
192 set self.verbose_crash=True to use our full crashhandler, instead of
192 set self.verbose_crash=True to use our full crashhandler, instead of
193 a regular traceback with a short message (crash_handler_lite)
193 a regular traceback with a short message (crash_handler_lite)
194 """
194 """
195
195
196 if self.verbose_crash:
196 if self.verbose_crash:
197 return self.crash_handler(etype, evalue, tb)
197 return self.crash_handler(etype, evalue, tb)
198 else:
198 else:
199 return crashhandler.crash_handler_lite(etype, evalue, tb)
199 return crashhandler.crash_handler_lite(etype, evalue, tb)
200
200
201 def _ipython_dir_changed(self, name, old, new):
201 def _ipython_dir_changed(self, name, old, new):
202 str_old = py3compat.cast_bytes_py2(os.path.abspath(old),
202 if old is not None:
203 sys.getfilesystemencoding()
203 str_old = py3compat.cast_bytes_py2(os.path.abspath(old),
204 )
204 sys.getfilesystemencoding()
205 if str_old in sys.path:
205 )
206 sys.path.remove(str_old)
206 if str_old in sys.path:
207 sys.path.remove(str_old)
207 str_path = py3compat.cast_bytes_py2(os.path.abspath(new),
208 str_path = py3compat.cast_bytes_py2(os.path.abspath(new),
208 sys.getfilesystemencoding()
209 sys.getfilesystemencoding()
209 )
210 )
210 sys.path.append(str_path)
211 sys.path.append(str_path)
211 ensure_dir_exists(new)
212 ensure_dir_exists(new)
212 readme = os.path.join(new, 'README')
213 readme = os.path.join(new, 'README')
213 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
214 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
214 if not os.path.exists(readme) and os.path.exists(readme_src):
215 if not os.path.exists(readme) and os.path.exists(readme_src):
215 shutil.copy(readme_src, readme)
216 shutil.copy(readme_src, readme)
216 for d in ('extensions', 'nbextensions'):
217 for d in ('extensions', 'nbextensions'):
217 path = os.path.join(new, d)
218 path = os.path.join(new, d)
218 try:
219 try:
219 ensure_dir_exists(path)
220 ensure_dir_exists(path)
220 except OSError:
221 except OSError:
221 # this will not be EEXIST
222 # this will not be EEXIST
222 self.log.error("couldn't create path %s: %s", path, e)
223 self.log.error("couldn't create path %s: %s", path, e)
223 self.log.debug("IPYTHONDIR set to: %s" % new)
224 self.log.debug("IPYTHONDIR set to: %s" % new)
224
225
225 def load_config_file(self, suppress_errors=True):
226 def load_config_file(self, suppress_errors=True):
226 """Load the config file.
227 """Load the config file.
227
228
228 By default, errors in loading config are handled, and a warning
229 By default, errors in loading config are handled, and a warning
229 printed on screen. For testing, the suppress_errors option is set
230 printed on screen. For testing, the suppress_errors option is set
230 to False, so errors will make tests fail.
231 to False, so errors will make tests fail.
231 """
232 """
232 self.log.debug("Searching path %s for config files", self.config_file_paths)
233 self.log.debug("Searching path %s for config files", self.config_file_paths)
233 base_config = 'ipython_config.py'
234 base_config = 'ipython_config.py'
234 self.log.debug("Attempting to load config file: %s" %
235 self.log.debug("Attempting to load config file: %s" %
235 base_config)
236 base_config)
236 try:
237 try:
237 Application.load_config_file(
238 Application.load_config_file(
238 self,
239 self,
239 base_config,
240 base_config,
240 path=self.config_file_paths
241 path=self.config_file_paths
241 )
242 )
242 except ConfigFileNotFound:
243 except ConfigFileNotFound:
243 # ignore errors loading parent
244 # ignore errors loading parent
244 self.log.debug("Config file %s not found", base_config)
245 self.log.debug("Config file %s not found", base_config)
245 pass
246 pass
246
247
247 for config_file_name in self.config_files:
248 for config_file_name in self.config_files:
248 if not config_file_name or config_file_name == base_config:
249 if not config_file_name or config_file_name == base_config:
249 continue
250 continue
250 self.log.debug("Attempting to load config file: %s" %
251 self.log.debug("Attempting to load config file: %s" %
251 self.config_file_name)
252 self.config_file_name)
252 try:
253 try:
253 Application.load_config_file(
254 Application.load_config_file(
254 self,
255 self,
255 config_file_name,
256 config_file_name,
256 path=self.config_file_paths
257 path=self.config_file_paths
257 )
258 )
258 except ConfigFileNotFound:
259 except ConfigFileNotFound:
259 # Only warn if the default config file was NOT being used.
260 # Only warn if the default config file was NOT being used.
260 if config_file_name in self.config_file_specified:
261 if config_file_name in self.config_file_specified:
261 msg = self.log.warn
262 msg = self.log.warn
262 else:
263 else:
263 msg = self.log.debug
264 msg = self.log.debug
264 msg("Config file not found, skipping: %s", config_file_name)
265 msg("Config file not found, skipping: %s", config_file_name)
265 except:
266 except:
266 # For testing purposes.
267 # For testing purposes.
267 if not suppress_errors:
268 if not suppress_errors:
268 raise
269 raise
269 self.log.warn("Error loading config file: %s" %
270 self.log.warn("Error loading config file: %s" %
270 self.config_file_name, exc_info=True)
271 self.config_file_name, exc_info=True)
271
272
272 def init_profile_dir(self):
273 def init_profile_dir(self):
273 """initialize the profile dir"""
274 """initialize the profile dir"""
274 self._in_init_profile_dir = True
275 self._in_init_profile_dir = True
275 if self.profile_dir is not None:
276 if self.profile_dir is not None:
276 # already ran
277 # already ran
277 return
278 return
278 if 'ProfileDir.location' not in self.config:
279 if 'ProfileDir.location' not in self.config:
279 # location not specified, find by profile name
280 # location not specified, find by profile name
280 try:
281 try:
281 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
282 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
282 except ProfileDirError:
283 except ProfileDirError:
283 # not found, maybe create it (always create default profile)
284 # not found, maybe create it (always create default profile)
284 if self.auto_create or self.profile == 'default':
285 if self.auto_create or self.profile == 'default':
285 try:
286 try:
286 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
287 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
287 except ProfileDirError:
288 except ProfileDirError:
288 self.log.fatal("Could not create profile: %r"%self.profile)
289 self.log.fatal("Could not create profile: %r"%self.profile)
289 self.exit(1)
290 self.exit(1)
290 else:
291 else:
291 self.log.info("Created profile dir: %r"%p.location)
292 self.log.info("Created profile dir: %r"%p.location)
292 else:
293 else:
293 self.log.fatal("Profile %r not found."%self.profile)
294 self.log.fatal("Profile %r not found."%self.profile)
294 self.exit(1)
295 self.exit(1)
295 else:
296 else:
296 self.log.info("Using existing profile dir: %r"%p.location)
297 self.log.info("Using existing profile dir: %r"%p.location)
297 else:
298 else:
298 location = self.config.ProfileDir.location
299 location = self.config.ProfileDir.location
299 # location is fully specified
300 # location is fully specified
300 try:
301 try:
301 p = ProfileDir.find_profile_dir(location, self.config)
302 p = ProfileDir.find_profile_dir(location, self.config)
302 except ProfileDirError:
303 except ProfileDirError:
303 # not found, maybe create it
304 # not found, maybe create it
304 if self.auto_create:
305 if self.auto_create:
305 try:
306 try:
306 p = ProfileDir.create_profile_dir(location, self.config)
307 p = ProfileDir.create_profile_dir(location, self.config)
307 except ProfileDirError:
308 except ProfileDirError:
308 self.log.fatal("Could not create profile directory: %r"%location)
309 self.log.fatal("Could not create profile directory: %r"%location)
309 self.exit(1)
310 self.exit(1)
310 else:
311 else:
311 self.log.info("Creating new profile dir: %r"%location)
312 self.log.info("Creating new profile dir: %r"%location)
312 else:
313 else:
313 self.log.fatal("Profile directory %r not found."%location)
314 self.log.fatal("Profile directory %r not found."%location)
314 self.exit(1)
315 self.exit(1)
315 else:
316 else:
316 self.log.info("Using existing profile dir: %r"%location)
317 self.log.info("Using existing profile dir: %r"%location)
317 # if profile_dir is specified explicitly, set profile name
318 # if profile_dir is specified explicitly, set profile name
318 dir_name = os.path.basename(p.location)
319 dir_name = os.path.basename(p.location)
319 if dir_name.startswith('profile_'):
320 if dir_name.startswith('profile_'):
320 self.profile = dir_name[8:]
321 self.profile = dir_name[8:]
321
322
322 self.profile_dir = p
323 self.profile_dir = p
323 self.config_file_paths.append(p.location)
324 self.config_file_paths.append(p.location)
324 self._in_init_profile_dir = False
325 self._in_init_profile_dir = False
325
326
326 def init_config_files(self):
327 def init_config_files(self):
327 """[optionally] copy default config files into profile dir."""
328 """[optionally] copy default config files into profile dir."""
328 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
329 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
329 # copy config files
330 # copy config files
330 path = self.builtin_profile_dir
331 path = self.builtin_profile_dir
331 if self.copy_config_files:
332 if self.copy_config_files:
332 src = self.profile
333 src = self.profile
333
334
334 cfg = self.config_file_name
335 cfg = self.config_file_name
335 if path and os.path.exists(os.path.join(path, cfg)):
336 if path and os.path.exists(os.path.join(path, cfg)):
336 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
337 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
337 cfg, src, self.profile_dir.location, self.overwrite)
338 cfg, src, self.profile_dir.location, self.overwrite)
338 )
339 )
339 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
340 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
340 else:
341 else:
341 self.stage_default_config_file()
342 self.stage_default_config_file()
342 else:
343 else:
343 # Still stage *bundled* config files, but not generated ones
344 # Still stage *bundled* config files, but not generated ones
344 # This is necessary for `ipython profile=sympy` to load the profile
345 # This is necessary for `ipython profile=sympy` to load the profile
345 # on the first go
346 # on the first go
346 files = glob.glob(os.path.join(path, '*.py'))
347 files = glob.glob(os.path.join(path, '*.py'))
347 for fullpath in files:
348 for fullpath in files:
348 cfg = os.path.basename(fullpath)
349 cfg = os.path.basename(fullpath)
349 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
350 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
350 # file was copied
351 # file was copied
351 self.log.warn("Staging bundled %s from %s into %r"%(
352 self.log.warn("Staging bundled %s from %s into %r"%(
352 cfg, self.profile, self.profile_dir.location)
353 cfg, self.profile, self.profile_dir.location)
353 )
354 )
354
355
355
356
356 def stage_default_config_file(self):
357 def stage_default_config_file(self):
357 """auto generate default config file, and stage it into the profile."""
358 """auto generate default config file, and stage it into the profile."""
358 s = self.generate_config_file()
359 s = self.generate_config_file()
359 fname = os.path.join(self.profile_dir.location, self.config_file_name)
360 fname = os.path.join(self.profile_dir.location, self.config_file_name)
360 if self.overwrite or not os.path.exists(fname):
361 if self.overwrite or not os.path.exists(fname):
361 self.log.warn("Generating default config file: %r"%(fname))
362 self.log.warn("Generating default config file: %r"%(fname))
362 with open(fname, 'w') as f:
363 with open(fname, 'w') as f:
363 f.write(s)
364 f.write(s)
364
365
365 @catch_config_error
366 @catch_config_error
366 def initialize(self, argv=None):
367 def initialize(self, argv=None):
367 # don't hook up crash handler before parsing command-line
368 # don't hook up crash handler before parsing command-line
368 self.parse_command_line(argv)
369 self.parse_command_line(argv)
369 self.init_crash_handler()
370 self.init_crash_handler()
370 if self.subapp is not None:
371 if self.subapp is not None:
371 # stop here if subapp is taking over
372 # stop here if subapp is taking over
372 return
373 return
373 cl_config = self.config
374 cl_config = self.config
374 self.init_profile_dir()
375 self.init_profile_dir()
375 self.init_config_files()
376 self.init_config_files()
376 self.load_config_file()
377 self.load_config_file()
377 # enforce cl-opts override configfile opts:
378 # enforce cl-opts override configfile opts:
378 self.update_config(cl_config)
379 self.update_config(cl_config)
379
380
@@ -1,118 +1,119 b''
1 """Configurable for configuring the IPython inline backend
1 """Configurable for configuring the IPython inline backend
2
2
3 This module does not import anything from matplotlib.
3 This module does not import anything from matplotlib.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2011 The IPython Development Team
6 # Copyright (C) 2011 The IPython Development Team
7 #
7 #
8 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from IPython.config import Config
16 from IPython.config.configurable import SingletonConfigurable
17 from IPython.config.configurable import SingletonConfigurable
17 from IPython.utils.traitlets import (
18 from IPython.utils.traitlets import (
18 Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode
19 Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode
19 )
20 )
20 from IPython.utils.warn import warn
21 from IPython.utils.warn import warn
21
22
22 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
23 # Configurable for inline backend options
24 # Configurable for inline backend options
24 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25
26
26 def pil_available():
27 def pil_available():
27 """Test if PIL/Pillow is available"""
28 """Test if PIL/Pillow is available"""
28 out = False
29 out = False
29 try:
30 try:
30 from PIL import Image
31 from PIL import Image
31 out = True
32 out = True
32 except:
33 except:
33 pass
34 pass
34 return out
35 return out
35
36
36 # inherit from InlineBackendConfig for deprecation purposes
37 # inherit from InlineBackendConfig for deprecation purposes
37 class InlineBackendConfig(SingletonConfigurable):
38 class InlineBackendConfig(SingletonConfigurable):
38 pass
39 pass
39
40
40 class InlineBackend(InlineBackendConfig):
41 class InlineBackend(InlineBackendConfig):
41 """An object to store configuration of the inline backend."""
42 """An object to store configuration of the inline backend."""
42
43
43 def _config_changed(self, name, old, new):
44 def _config_changed(self, name, old, new):
44 # warn on change of renamed config section
45 # warn on change of renamed config section
45 if new.InlineBackendConfig != old.InlineBackendConfig:
46 if new.InlineBackendConfig != getattr(old, 'InlineBackendConfig', Config()):
46 warn("InlineBackendConfig has been renamed to InlineBackend")
47 warn("InlineBackendConfig has been renamed to InlineBackend")
47 super(InlineBackend, self)._config_changed(name, old, new)
48 super(InlineBackend, self)._config_changed(name, old, new)
48
49
49 # The typical default figure size is too large for inline use,
50 # The typical default figure size is too large for inline use,
50 # so we shrink the figure size to 6x4, and tweak fonts to
51 # so we shrink the figure size to 6x4, and tweak fonts to
51 # make that fit.
52 # make that fit.
52 rc = Dict({'figure.figsize': (6.0,4.0),
53 rc = Dict({'figure.figsize': (6.0,4.0),
53 # play nicely with white background in the Qt and notebook frontend
54 # play nicely with white background in the Qt and notebook frontend
54 'figure.facecolor': (1,1,1,0),
55 'figure.facecolor': (1,1,1,0),
55 'figure.edgecolor': (1,1,1,0),
56 'figure.edgecolor': (1,1,1,0),
56 'axes.facecolor': (1,1,1,0),
57 'axes.facecolor': (1,1,1,0),
57 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
58 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
58 'font.size': 10,
59 'font.size': 10,
59 # 72 dpi matches SVG/qtconsole
60 # 72 dpi matches SVG/qtconsole
60 # this only affects PNG export, as SVG has no dpi setting
61 # this only affects PNG export, as SVG has no dpi setting
61 'savefig.dpi': 72,
62 'savefig.dpi': 72,
62 # 10pt still needs a little more room on the xlabel:
63 # 10pt still needs a little more room on the xlabel:
63 'figure.subplot.bottom' : .125
64 'figure.subplot.bottom' : .125
64 }, config=True,
65 }, config=True,
65 help="""Subset of matplotlib rcParams that should be different for the
66 help="""Subset of matplotlib rcParams that should be different for the
66 inline backend."""
67 inline backend."""
67 )
68 )
68
69
69 figure_formats = Set({'png'}, config=True,
70 figure_formats = Set({'png'}, config=True,
70 help="""A set of figure formats to enable: 'png',
71 help="""A set of figure formats to enable: 'png',
71 'retina', 'jpeg', 'svg', 'pdf'.""")
72 'retina', 'jpeg', 'svg', 'pdf'.""")
72
73
73 def _update_figure_formatters(self):
74 def _update_figure_formatters(self):
74 if self.shell is not None:
75 if self.shell is not None:
75 from IPython.core.pylabtools import select_figure_formats
76 from IPython.core.pylabtools import select_figure_formats
76 select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs)
77 select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs)
77
78
78 def _figure_formats_changed(self, name, old, new):
79 def _figure_formats_changed(self, name, old, new):
79 if 'jpg' in new or 'jpeg' in new:
80 if 'jpg' in new or 'jpeg' in new:
80 if not pil_available():
81 if not pil_available():
81 raise TraitError("Requires PIL/Pillow for JPG figures")
82 raise TraitError("Requires PIL/Pillow for JPG figures")
82 self._update_figure_formatters()
83 self._update_figure_formatters()
83
84
84 figure_format = Unicode(config=True, help="""The figure format to enable (deprecated
85 figure_format = Unicode(config=True, help="""The figure format to enable (deprecated
85 use `figure_formats` instead)""")
86 use `figure_formats` instead)""")
86
87
87 def _figure_format_changed(self, name, old, new):
88 def _figure_format_changed(self, name, old, new):
88 if new:
89 if new:
89 self.figure_formats = {new}
90 self.figure_formats = {new}
90
91
91 print_figure_kwargs = Dict({'bbox_inches' : 'tight'}, config=True,
92 print_figure_kwargs = Dict({'bbox_inches' : 'tight'}, config=True,
92 help="""Extra kwargs to be passed to fig.canvas.print_figure.
93 help="""Extra kwargs to be passed to fig.canvas.print_figure.
93
94
94 Logical examples include: bbox_inches, quality (for jpeg figures), etc.
95 Logical examples include: bbox_inches, quality (for jpeg figures), etc.
95 """
96 """
96 )
97 )
97 _print_figure_kwargs_changed = _update_figure_formatters
98 _print_figure_kwargs_changed = _update_figure_formatters
98
99
99 close_figures = Bool(True, config=True,
100 close_figures = Bool(True, config=True,
100 help="""Close all figures at the end of each cell.
101 help="""Close all figures at the end of each cell.
101
102
102 When True, ensures that each cell starts with no active figures, but it
103 When True, ensures that each cell starts with no active figures, but it
103 also means that one must keep track of references in order to edit or
104 also means that one must keep track of references in order to edit or
104 redraw figures in subsequent cells. This mode is ideal for the notebook,
105 redraw figures in subsequent cells. This mode is ideal for the notebook,
105 where residual plots from other cells might be surprising.
106 where residual plots from other cells might be surprising.
106
107
107 When False, one must call figure() to create new figures. This means
108 When False, one must call figure() to create new figures. This means
108 that gcf() and getfigs() can reference figures created in other cells,
109 that gcf() and getfigs() can reference figures created in other cells,
109 and the active figure can continue to be edited with pylab/pyplot
110 and the active figure can continue to be edited with pylab/pyplot
110 methods that reference the current active figure. This mode facilitates
111 methods that reference the current active figure. This mode facilitates
111 iterative editing of figures, and behaves most consistently with
112 iterative editing of figures, and behaves most consistently with
112 other matplotlib backends, but figure barriers between cells must
113 other matplotlib backends, but figure barriers between cells must
113 be explicit.
114 be explicit.
114 """)
115 """)
115
116
116 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
117 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
117
118
118
119
@@ -1,1710 +1,1714 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A lightweight Traits like module.
3 A lightweight Traits like module.
4
4
5 This is designed to provide a lightweight, simple, pure Python version of
5 This is designed to provide a lightweight, simple, pure Python version of
6 many of the capabilities of enthought.traits. This includes:
6 many of the capabilities of enthought.traits. This includes:
7
7
8 * Validation
8 * Validation
9 * Type specification with defaults
9 * Type specification with defaults
10 * Static and dynamic notification
10 * Static and dynamic notification
11 * Basic predefined types
11 * Basic predefined types
12 * An API that is similar to enthought.traits
12 * An API that is similar to enthought.traits
13
13
14 We don't support:
14 We don't support:
15
15
16 * Delegation
16 * Delegation
17 * Automatic GUI generation
17 * Automatic GUI generation
18 * A full set of trait types. Most importantly, we don't provide container
18 * A full set of trait types. Most importantly, we don't provide container
19 traits (list, dict, tuple) that can trigger notifications if their
19 traits (list, dict, tuple) that can trigger notifications if their
20 contents change.
20 contents change.
21 * API compatibility with enthought.traits
21 * API compatibility with enthought.traits
22
22
23 There are also some important difference in our design:
23 There are also some important difference in our design:
24
24
25 * enthought.traits does not validate default values. We do.
25 * enthought.traits does not validate default values. We do.
26
26
27 We choose to create this module because we need these capabilities, but
27 We choose to create this module because we need these capabilities, but
28 we need them to be pure Python so they work in all Python implementations,
28 we need them to be pure Python so they work in all Python implementations,
29 including Jython and IronPython.
29 including Jython and IronPython.
30
30
31 Inheritance diagram:
31 Inheritance diagram:
32
32
33 .. inheritance-diagram:: IPython.utils.traitlets
33 .. inheritance-diagram:: IPython.utils.traitlets
34 :parts: 3
34 :parts: 3
35 """
35 """
36
36
37 # Copyright (c) IPython Development Team.
37 # Copyright (c) IPython Development Team.
38 # Distributed under the terms of the Modified BSD License.
38 # Distributed under the terms of the Modified BSD License.
39 #
39 #
40 # Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
40 # Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
41 # also under the terms of the Modified BSD License.
41 # also under the terms of the Modified BSD License.
42
42
43 import contextlib
43 import contextlib
44 import inspect
44 import inspect
45 import re
45 import re
46 import sys
46 import sys
47 import types
47 import types
48 from types import FunctionType
48 from types import FunctionType
49 try:
49 try:
50 from types import ClassType, InstanceType
50 from types import ClassType, InstanceType
51 ClassTypes = (ClassType, type)
51 ClassTypes = (ClassType, type)
52 except:
52 except:
53 ClassTypes = (type,)
53 ClassTypes = (type,)
54
54
55 from .importstring import import_item
55 from .importstring import import_item
56 from IPython.utils import py3compat
56 from IPython.utils import py3compat
57 from IPython.utils import eventful
57 from IPython.utils import eventful
58 from IPython.utils.py3compat import iteritems, string_types
58 from IPython.utils.py3compat import iteritems, string_types
59 from IPython.testing.skipdoctest import skip_doctest
59 from IPython.testing.skipdoctest import skip_doctest
60
60
61 SequenceTypes = (list, tuple, set, frozenset)
61 SequenceTypes = (list, tuple, set, frozenset)
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Basic classes
64 # Basic classes
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67
67
68 class NoDefaultSpecified ( object ): pass
68 class NoDefaultSpecified ( object ): pass
69 NoDefaultSpecified = NoDefaultSpecified()
69 NoDefaultSpecified = NoDefaultSpecified()
70
70
71
71
72 class Undefined ( object ): pass
72 class Undefined ( object ): pass
73 Undefined = Undefined()
73 Undefined = Undefined()
74
74
75 class TraitError(Exception):
75 class TraitError(Exception):
76 pass
76 pass
77
77
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79 # Utilities
79 # Utilities
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81
81
82
82
83 def class_of ( object ):
83 def class_of ( object ):
84 """ Returns a string containing the class name of an object with the
84 """ Returns a string containing the class name of an object with the
85 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
85 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
86 'a PlotValue').
86 'a PlotValue').
87 """
87 """
88 if isinstance( object, py3compat.string_types ):
88 if isinstance( object, py3compat.string_types ):
89 return add_article( object )
89 return add_article( object )
90
90
91 return add_article( object.__class__.__name__ )
91 return add_article( object.__class__.__name__ )
92
92
93
93
94 def add_article ( name ):
94 def add_article ( name ):
95 """ Returns a string containing the correct indefinite article ('a' or 'an')
95 """ Returns a string containing the correct indefinite article ('a' or 'an')
96 prefixed to the specified string.
96 prefixed to the specified string.
97 """
97 """
98 if name[:1].lower() in 'aeiou':
98 if name[:1].lower() in 'aeiou':
99 return 'an ' + name
99 return 'an ' + name
100
100
101 return 'a ' + name
101 return 'a ' + name
102
102
103
103
104 def repr_type(obj):
104 def repr_type(obj):
105 """ Return a string representation of a value and its type for readable
105 """ Return a string representation of a value and its type for readable
106 error messages.
106 error messages.
107 """
107 """
108 the_type = type(obj)
108 the_type = type(obj)
109 if (not py3compat.PY3) and the_type is InstanceType:
109 if (not py3compat.PY3) and the_type is InstanceType:
110 # Old-style class.
110 # Old-style class.
111 the_type = obj.__class__
111 the_type = obj.__class__
112 msg = '%r %r' % (obj, the_type)
112 msg = '%r %r' % (obj, the_type)
113 return msg
113 return msg
114
114
115
115
116 def is_trait(t):
116 def is_trait(t):
117 """ Returns whether the given value is an instance or subclass of TraitType.
117 """ Returns whether the given value is an instance or subclass of TraitType.
118 """
118 """
119 return (isinstance(t, TraitType) or
119 return (isinstance(t, TraitType) or
120 (isinstance(t, type) and issubclass(t, TraitType)))
120 (isinstance(t, type) and issubclass(t, TraitType)))
121
121
122
122
123 def parse_notifier_name(name):
123 def parse_notifier_name(name):
124 """Convert the name argument to a list of names.
124 """Convert the name argument to a list of names.
125
125
126 Examples
126 Examples
127 --------
127 --------
128
128
129 >>> parse_notifier_name('a')
129 >>> parse_notifier_name('a')
130 ['a']
130 ['a']
131 >>> parse_notifier_name(['a','b'])
131 >>> parse_notifier_name(['a','b'])
132 ['a', 'b']
132 ['a', 'b']
133 >>> parse_notifier_name(None)
133 >>> parse_notifier_name(None)
134 ['anytrait']
134 ['anytrait']
135 """
135 """
136 if isinstance(name, string_types):
136 if isinstance(name, string_types):
137 return [name]
137 return [name]
138 elif name is None:
138 elif name is None:
139 return ['anytrait']
139 return ['anytrait']
140 elif isinstance(name, (list, tuple)):
140 elif isinstance(name, (list, tuple)):
141 for n in name:
141 for n in name:
142 assert isinstance(n, string_types), "names must be strings"
142 assert isinstance(n, string_types), "names must be strings"
143 return name
143 return name
144
144
145
145
146 class _SimpleTest:
146 class _SimpleTest:
147 def __init__ ( self, value ): self.value = value
147 def __init__ ( self, value ): self.value = value
148 def __call__ ( self, test ):
148 def __call__ ( self, test ):
149 return test == self.value
149 return test == self.value
150 def __repr__(self):
150 def __repr__(self):
151 return "<SimpleTest(%r)" % self.value
151 return "<SimpleTest(%r)" % self.value
152 def __str__(self):
152 def __str__(self):
153 return self.__repr__()
153 return self.__repr__()
154
154
155
155
156 def getmembers(object, predicate=None):
156 def getmembers(object, predicate=None):
157 """A safe version of inspect.getmembers that handles missing attributes.
157 """A safe version of inspect.getmembers that handles missing attributes.
158
158
159 This is useful when there are descriptor based attributes that for
159 This is useful when there are descriptor based attributes that for
160 some reason raise AttributeError even though they exist. This happens
160 some reason raise AttributeError even though they exist. This happens
161 in zope.inteface with the __provides__ attribute.
161 in zope.inteface with the __provides__ attribute.
162 """
162 """
163 results = []
163 results = []
164 for key in dir(object):
164 for key in dir(object):
165 try:
165 try:
166 value = getattr(object, key)
166 value = getattr(object, key)
167 except AttributeError:
167 except AttributeError:
168 pass
168 pass
169 else:
169 else:
170 if not predicate or predicate(value):
170 if not predicate or predicate(value):
171 results.append((key, value))
171 results.append((key, value))
172 results.sort()
172 results.sort()
173 return results
173 return results
174
174
175 @skip_doctest
175 @skip_doctest
176 class link(object):
176 class link(object):
177 """Link traits from different objects together so they remain in sync.
177 """Link traits from different objects together so they remain in sync.
178
178
179 Parameters
179 Parameters
180 ----------
180 ----------
181 obj : pairs of objects/attributes
181 obj : pairs of objects/attributes
182
182
183 Examples
183 Examples
184 --------
184 --------
185
185
186 >>> c = link((obj1, 'value'), (obj2, 'value'), (obj3, 'value'))
186 >>> c = link((obj1, 'value'), (obj2, 'value'), (obj3, 'value'))
187 >>> obj1.value = 5 # updates other objects as well
187 >>> obj1.value = 5 # updates other objects as well
188 """
188 """
189 updating = False
189 updating = False
190 def __init__(self, *args):
190 def __init__(self, *args):
191 if len(args) < 2:
191 if len(args) < 2:
192 raise TypeError('At least two traitlets must be provided.')
192 raise TypeError('At least two traitlets must be provided.')
193
193
194 self.objects = {}
194 self.objects = {}
195
195
196 initial = getattr(args[0][0], args[0][1])
196 initial = getattr(args[0][0], args[0][1])
197 for obj, attr in args:
197 for obj, attr in args:
198 setattr(obj, attr, initial)
198 setattr(obj, attr, initial)
199
199
200 callback = self._make_closure(obj, attr)
200 callback = self._make_closure(obj, attr)
201 obj.on_trait_change(callback, attr)
201 obj.on_trait_change(callback, attr)
202 self.objects[(obj, attr)] = callback
202 self.objects[(obj, attr)] = callback
203
203
204 @contextlib.contextmanager
204 @contextlib.contextmanager
205 def _busy_updating(self):
205 def _busy_updating(self):
206 self.updating = True
206 self.updating = True
207 try:
207 try:
208 yield
208 yield
209 finally:
209 finally:
210 self.updating = False
210 self.updating = False
211
211
212 def _make_closure(self, sending_obj, sending_attr):
212 def _make_closure(self, sending_obj, sending_attr):
213 def update(name, old, new):
213 def update(name, old, new):
214 self._update(sending_obj, sending_attr, new)
214 self._update(sending_obj, sending_attr, new)
215 return update
215 return update
216
216
217 def _update(self, sending_obj, sending_attr, new):
217 def _update(self, sending_obj, sending_attr, new):
218 if self.updating:
218 if self.updating:
219 return
219 return
220 with self._busy_updating():
220 with self._busy_updating():
221 for obj, attr in self.objects.keys():
221 for obj, attr in self.objects.keys():
222 setattr(obj, attr, new)
222 setattr(obj, attr, new)
223
223
224 def unlink(self):
224 def unlink(self):
225 for key, callback in self.objects.items():
225 for key, callback in self.objects.items():
226 (obj, attr) = key
226 (obj, attr) = key
227 obj.on_trait_change(callback, attr, remove=True)
227 obj.on_trait_change(callback, attr, remove=True)
228
228
229 @skip_doctest
229 @skip_doctest
230 class directional_link(object):
230 class directional_link(object):
231 """Link the trait of a source object with traits of target objects.
231 """Link the trait of a source object with traits of target objects.
232
232
233 Parameters
233 Parameters
234 ----------
234 ----------
235 source : pair of object, name
235 source : pair of object, name
236 targets : pairs of objects/attributes
236 targets : pairs of objects/attributes
237
237
238 Examples
238 Examples
239 --------
239 --------
240
240
241 >>> c = directional_link((src, 'value'), (tgt1, 'value'), (tgt2, 'value'))
241 >>> c = directional_link((src, 'value'), (tgt1, 'value'), (tgt2, 'value'))
242 >>> src.value = 5 # updates target objects
242 >>> src.value = 5 # updates target objects
243 >>> tgt1.value = 6 # does not update other objects
243 >>> tgt1.value = 6 # does not update other objects
244 """
244 """
245 updating = False
245 updating = False
246
246
247 def __init__(self, source, *targets):
247 def __init__(self, source, *targets):
248 self.source = source
248 self.source = source
249 self.targets = targets
249 self.targets = targets
250
250
251 # Update current value
251 # Update current value
252 src_attr_value = getattr(source[0], source[1])
252 src_attr_value = getattr(source[0], source[1])
253 for obj, attr in targets:
253 for obj, attr in targets:
254 setattr(obj, attr, src_attr_value)
254 setattr(obj, attr, src_attr_value)
255
255
256 # Wire
256 # Wire
257 self.source[0].on_trait_change(self._update, self.source[1])
257 self.source[0].on_trait_change(self._update, self.source[1])
258
258
259 @contextlib.contextmanager
259 @contextlib.contextmanager
260 def _busy_updating(self):
260 def _busy_updating(self):
261 self.updating = True
261 self.updating = True
262 try:
262 try:
263 yield
263 yield
264 finally:
264 finally:
265 self.updating = False
265 self.updating = False
266
266
267 def _update(self, name, old, new):
267 def _update(self, name, old, new):
268 if self.updating:
268 if self.updating:
269 return
269 return
270 with self._busy_updating():
270 with self._busy_updating():
271 for obj, attr in self.targets:
271 for obj, attr in self.targets:
272 setattr(obj, attr, new)
272 setattr(obj, attr, new)
273
273
274 def unlink(self):
274 def unlink(self):
275 self.source[0].on_trait_change(self._update, self.source[1], remove=True)
275 self.source[0].on_trait_change(self._update, self.source[1], remove=True)
276 self.source = None
276 self.source = None
277 self.targets = []
277 self.targets = []
278
278
279 def dlink(source, *targets):
279 def dlink(source, *targets):
280 """Shorter helper function returning a directional_link object"""
280 """Shorter helper function returning a directional_link object"""
281 return directional_link(source, *targets)
281 return directional_link(source, *targets)
282
282
283 #-----------------------------------------------------------------------------
283 #-----------------------------------------------------------------------------
284 # Base TraitType for all traits
284 # Base TraitType for all traits
285 #-----------------------------------------------------------------------------
285 #-----------------------------------------------------------------------------
286
286
287
287
288 class TraitType(object):
288 class TraitType(object):
289 """A base class for all trait descriptors.
289 """A base class for all trait descriptors.
290
290
291 Notes
291 Notes
292 -----
292 -----
293 Our implementation of traits is based on Python's descriptor
293 Our implementation of traits is based on Python's descriptor
294 prototol. This class is the base class for all such descriptors. The
294 prototol. This class is the base class for all such descriptors. The
295 only magic we use is a custom metaclass for the main :class:`HasTraits`
295 only magic we use is a custom metaclass for the main :class:`HasTraits`
296 class that does the following:
296 class that does the following:
297
297
298 1. Sets the :attr:`name` attribute of every :class:`TraitType`
298 1. Sets the :attr:`name` attribute of every :class:`TraitType`
299 instance in the class dict to the name of the attribute.
299 instance in the class dict to the name of the attribute.
300 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
300 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
301 instance in the class dict to the *class* that declared the trait.
301 instance in the class dict to the *class* that declared the trait.
302 This is used by the :class:`This` trait to allow subclasses to
302 This is used by the :class:`This` trait to allow subclasses to
303 accept superclasses for :class:`This` values.
303 accept superclasses for :class:`This` values.
304 """
304 """
305
305
306
306
307 metadata = {}
307 metadata = {}
308 default_value = Undefined
308 default_value = Undefined
309 allow_none = False
309 allow_none = False
310 info_text = 'any value'
310 info_text = 'any value'
311
311
312 def __init__(self, default_value=NoDefaultSpecified, allow_none=None, **metadata):
312 def __init__(self, default_value=NoDefaultSpecified, allow_none=None, **metadata):
313 """Create a TraitType.
313 """Create a TraitType.
314 """
314 """
315 if default_value is not NoDefaultSpecified:
315 if default_value is not NoDefaultSpecified:
316 self.default_value = default_value
316 self.default_value = default_value
317 if allow_none is not None:
317 if allow_none is not None:
318 self.allow_none = allow_none
318 self.allow_none = allow_none
319
319
320 if len(metadata) > 0:
320 if len(metadata) > 0:
321 if len(self.metadata) > 0:
321 if len(self.metadata) > 0:
322 self._metadata = self.metadata.copy()
322 self._metadata = self.metadata.copy()
323 self._metadata.update(metadata)
323 self._metadata.update(metadata)
324 else:
324 else:
325 self._metadata = metadata
325 self._metadata = metadata
326 else:
326 else:
327 self._metadata = self.metadata
327 self._metadata = self.metadata
328
328
329 self.init()
329 self.init()
330
330
331 def init(self):
331 def init(self):
332 pass
332 pass
333
333
334 def get_default_value(self):
334 def get_default_value(self):
335 """Create a new instance of the default value."""
335 """Create a new instance of the default value."""
336 return self.default_value
336 return self.default_value
337
337
338 def instance_init(self, obj):
338 def instance_init(self, obj):
339 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
339 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
340
340
341 Some stages of initialization must be delayed until the parent
341 Some stages of initialization must be delayed until the parent
342 :class:`HasTraits` instance has been created. This method is
342 :class:`HasTraits` instance has been created. This method is
343 called in :meth:`HasTraits.__new__` after the instance has been
343 called in :meth:`HasTraits.__new__` after the instance has been
344 created.
344 created.
345
345
346 This method trigger the creation and validation of default values
346 This method trigger the creation and validation of default values
347 and also things like the resolution of str given class names in
347 and also things like the resolution of str given class names in
348 :class:`Type` and :class`Instance`.
348 :class:`Type` and :class`Instance`.
349
349
350 Parameters
350 Parameters
351 ----------
351 ----------
352 obj : :class:`HasTraits` instance
352 obj : :class:`HasTraits` instance
353 The parent :class:`HasTraits` instance that has just been
353 The parent :class:`HasTraits` instance that has just been
354 created.
354 created.
355 """
355 """
356 self.set_default_value(obj)
356 self.set_default_value(obj)
357
357
358 def set_default_value(self, obj):
358 def set_default_value(self, obj):
359 """Set the default value on a per instance basis.
359 """Set the default value on a per instance basis.
360
360
361 This method is called by :meth:`instance_init` to create and
361 This method is called by :meth:`instance_init` to create and
362 validate the default value. The creation and validation of
362 validate the default value. The creation and validation of
363 default values must be delayed until the parent :class:`HasTraits`
363 default values must be delayed until the parent :class:`HasTraits`
364 class has been instantiated.
364 class has been instantiated.
365 """
365 """
366 # Check for a deferred initializer defined in the same class as the
366 # Check for a deferred initializer defined in the same class as the
367 # trait declaration or above.
367 # trait declaration or above.
368 mro = type(obj).mro()
368 mro = type(obj).mro()
369 meth_name = '_%s_default' % self.name
369 meth_name = '_%s_default' % self.name
370 for cls in mro[:mro.index(self.this_class)+1]:
370 for cls in mro[:mro.index(self.this_class)+1]:
371 if meth_name in cls.__dict__:
371 if meth_name in cls.__dict__:
372 break
372 break
373 else:
373 else:
374 # We didn't find one. Do static initialization.
374 # We didn't find one. Do static initialization.
375 dv = self.get_default_value()
375 dv = self.get_default_value()
376 newdv = self._validate(obj, dv)
376 newdv = self._validate(obj, dv)
377 obj._trait_values[self.name] = newdv
377 obj._trait_values[self.name] = newdv
378 return
378 return
379 # Complete the dynamic initialization.
379 # Complete the dynamic initialization.
380 obj._trait_dyn_inits[self.name] = meth_name
380 obj._trait_dyn_inits[self.name] = meth_name
381
381
382 def __get__(self, obj, cls=None):
382 def __get__(self, obj, cls=None):
383 """Get the value of the trait by self.name for the instance.
383 """Get the value of the trait by self.name for the instance.
384
384
385 Default values are instantiated when :meth:`HasTraits.__new__`
385 Default values are instantiated when :meth:`HasTraits.__new__`
386 is called. Thus by the time this method gets called either the
386 is called. Thus by the time this method gets called either the
387 default value or a user defined value (they called :meth:`__set__`)
387 default value or a user defined value (they called :meth:`__set__`)
388 is in the :class:`HasTraits` instance.
388 is in the :class:`HasTraits` instance.
389 """
389 """
390 if obj is None:
390 if obj is None:
391 return self
391 return self
392 else:
392 else:
393 try:
393 try:
394 value = obj._trait_values[self.name]
394 value = obj._trait_values[self.name]
395 except KeyError:
395 except KeyError:
396 # Check for a dynamic initializer.
396 # Check for a dynamic initializer.
397 if self.name in obj._trait_dyn_inits:
397 if self.name in obj._trait_dyn_inits:
398 method = getattr(obj, obj._trait_dyn_inits[self.name])
398 method = getattr(obj, obj._trait_dyn_inits[self.name])
399 value = method()
399 value = method()
400 # FIXME: Do we really validate here?
400 # FIXME: Do we really validate here?
401 value = self._validate(obj, value)
401 value = self._validate(obj, value)
402 obj._trait_values[self.name] = value
402 obj._trait_values[self.name] = value
403 return value
403 return value
404 else:
404 else:
405 raise TraitError('Unexpected error in TraitType: '
405 raise TraitError('Unexpected error in TraitType: '
406 'both default value and dynamic initializer are '
406 'both default value and dynamic initializer are '
407 'absent.')
407 'absent.')
408 except Exception:
408 except Exception:
409 # HasTraits should call set_default_value to populate
409 # HasTraits should call set_default_value to populate
410 # this. So this should never be reached.
410 # this. So this should never be reached.
411 raise TraitError('Unexpected error in TraitType: '
411 raise TraitError('Unexpected error in TraitType: '
412 'default value not set properly')
412 'default value not set properly')
413 else:
413 else:
414 return value
414 return value
415
415
416 def __set__(self, obj, value):
416 def __set__(self, obj, value):
417 new_value = self._validate(obj, value)
417 new_value = self._validate(obj, value)
418 old_value = self.__get__(obj)
418 try:
419 old_value = obj._trait_values[self.name]
420 except KeyError:
421 old_value = None
422
419 obj._trait_values[self.name] = new_value
423 obj._trait_values[self.name] = new_value
420 try:
424 try:
421 silent = bool(old_value == new_value)
425 silent = bool(old_value == new_value)
422 except:
426 except:
423 # if there is an error in comparing, default to notify
427 # if there is an error in comparing, default to notify
424 silent = False
428 silent = False
425 if silent is not True:
429 if silent is not True:
426 # we explicitly compare silent to True just in case the equality
430 # we explicitly compare silent to True just in case the equality
427 # comparison above returns something other than True/False
431 # comparison above returns something other than True/False
428 obj._notify_trait(self.name, old_value, new_value)
432 obj._notify_trait(self.name, old_value, new_value)
429
433
430 def _validate(self, obj, value):
434 def _validate(self, obj, value):
431 if value is None and self.allow_none:
435 if value is None and self.allow_none:
432 return value
436 return value
433 if hasattr(self, 'validate'):
437 if hasattr(self, 'validate'):
434 return self.validate(obj, value)
438 return self.validate(obj, value)
435 elif hasattr(self, 'is_valid_for'):
439 elif hasattr(self, 'is_valid_for'):
436 valid = self.is_valid_for(value)
440 valid = self.is_valid_for(value)
437 if valid:
441 if valid:
438 return value
442 return value
439 else:
443 else:
440 raise TraitError('invalid value for type: %r' % value)
444 raise TraitError('invalid value for type: %r' % value)
441 elif hasattr(self, 'value_for'):
445 elif hasattr(self, 'value_for'):
442 return self.value_for(value)
446 return self.value_for(value)
443 else:
447 else:
444 return value
448 return value
445
449
446 def __or__(self, other):
450 def __or__(self, other):
447 if isinstance(other, Union):
451 if isinstance(other, Union):
448 return Union([self] + other.trait_types)
452 return Union([self] + other.trait_types)
449 else:
453 else:
450 return Union([self, other])
454 return Union([self, other])
451
455
452 def info(self):
456 def info(self):
453 return self.info_text
457 return self.info_text
454
458
455 def error(self, obj, value):
459 def error(self, obj, value):
456 if obj is not None:
460 if obj is not None:
457 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
461 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
458 % (self.name, class_of(obj),
462 % (self.name, class_of(obj),
459 self.info(), repr_type(value))
463 self.info(), repr_type(value))
460 else:
464 else:
461 e = "The '%s' trait must be %s, but a value of %r was specified." \
465 e = "The '%s' trait must be %s, but a value of %r was specified." \
462 % (self.name, self.info(), repr_type(value))
466 % (self.name, self.info(), repr_type(value))
463 raise TraitError(e)
467 raise TraitError(e)
464
468
465 def get_metadata(self, key, default=None):
469 def get_metadata(self, key, default=None):
466 return getattr(self, '_metadata', {}).get(key, default)
470 return getattr(self, '_metadata', {}).get(key, default)
467
471
468 def set_metadata(self, key, value):
472 def set_metadata(self, key, value):
469 getattr(self, '_metadata', {})[key] = value
473 getattr(self, '_metadata', {})[key] = value
470
474
471
475
472 #-----------------------------------------------------------------------------
476 #-----------------------------------------------------------------------------
473 # The HasTraits implementation
477 # The HasTraits implementation
474 #-----------------------------------------------------------------------------
478 #-----------------------------------------------------------------------------
475
479
476
480
477 class MetaHasTraits(type):
481 class MetaHasTraits(type):
478 """A metaclass for HasTraits.
482 """A metaclass for HasTraits.
479
483
480 This metaclass makes sure that any TraitType class attributes are
484 This metaclass makes sure that any TraitType class attributes are
481 instantiated and sets their name attribute.
485 instantiated and sets their name attribute.
482 """
486 """
483
487
484 def __new__(mcls, name, bases, classdict):
488 def __new__(mcls, name, bases, classdict):
485 """Create the HasTraits class.
489 """Create the HasTraits class.
486
490
487 This instantiates all TraitTypes in the class dict and sets their
491 This instantiates all TraitTypes in the class dict and sets their
488 :attr:`name` attribute.
492 :attr:`name` attribute.
489 """
493 """
490 # print "MetaHasTraitlets (mcls, name): ", mcls, name
494 # print "MetaHasTraitlets (mcls, name): ", mcls, name
491 # print "MetaHasTraitlets (bases): ", bases
495 # print "MetaHasTraitlets (bases): ", bases
492 # print "MetaHasTraitlets (classdict): ", classdict
496 # print "MetaHasTraitlets (classdict): ", classdict
493 for k,v in iteritems(classdict):
497 for k,v in iteritems(classdict):
494 if isinstance(v, TraitType):
498 if isinstance(v, TraitType):
495 v.name = k
499 v.name = k
496 elif inspect.isclass(v):
500 elif inspect.isclass(v):
497 if issubclass(v, TraitType):
501 if issubclass(v, TraitType):
498 vinst = v()
502 vinst = v()
499 vinst.name = k
503 vinst.name = k
500 classdict[k] = vinst
504 classdict[k] = vinst
501 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
505 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
502
506
503 def __init__(cls, name, bases, classdict):
507 def __init__(cls, name, bases, classdict):
504 """Finish initializing the HasTraits class.
508 """Finish initializing the HasTraits class.
505
509
506 This sets the :attr:`this_class` attribute of each TraitType in the
510 This sets the :attr:`this_class` attribute of each TraitType in the
507 class dict to the newly created class ``cls``.
511 class dict to the newly created class ``cls``.
508 """
512 """
509 for k, v in iteritems(classdict):
513 for k, v in iteritems(classdict):
510 if isinstance(v, TraitType):
514 if isinstance(v, TraitType):
511 v.this_class = cls
515 v.this_class = cls
512 super(MetaHasTraits, cls).__init__(name, bases, classdict)
516 super(MetaHasTraits, cls).__init__(name, bases, classdict)
513
517
514 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
518 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
515
519
516 def __new__(cls, *args, **kw):
520 def __new__(cls, *args, **kw):
517 # This is needed because object.__new__ only accepts
521 # This is needed because object.__new__ only accepts
518 # the cls argument.
522 # the cls argument.
519 new_meth = super(HasTraits, cls).__new__
523 new_meth = super(HasTraits, cls).__new__
520 if new_meth is object.__new__:
524 if new_meth is object.__new__:
521 inst = new_meth(cls)
525 inst = new_meth(cls)
522 else:
526 else:
523 inst = new_meth(cls, **kw)
527 inst = new_meth(cls, **kw)
524 inst._trait_values = {}
528 inst._trait_values = {}
525 inst._trait_notifiers = {}
529 inst._trait_notifiers = {}
526 inst._trait_dyn_inits = {}
530 inst._trait_dyn_inits = {}
527 # Here we tell all the TraitType instances to set their default
531 # Here we tell all the TraitType instances to set their default
528 # values on the instance.
532 # values on the instance.
529 for key in dir(cls):
533 for key in dir(cls):
530 # Some descriptors raise AttributeError like zope.interface's
534 # Some descriptors raise AttributeError like zope.interface's
531 # __provides__ attributes even though they exist. This causes
535 # __provides__ attributes even though they exist. This causes
532 # AttributeErrors even though they are listed in dir(cls).
536 # AttributeErrors even though they are listed in dir(cls).
533 try:
537 try:
534 value = getattr(cls, key)
538 value = getattr(cls, key)
535 except AttributeError:
539 except AttributeError:
536 pass
540 pass
537 else:
541 else:
538 if isinstance(value, TraitType):
542 if isinstance(value, TraitType):
539 value.instance_init(inst)
543 value.instance_init(inst)
540
544
541 return inst
545 return inst
542
546
543 def __init__(self, *args, **kw):
547 def __init__(self, *args, **kw):
544 # Allow trait values to be set using keyword arguments.
548 # Allow trait values to be set using keyword arguments.
545 # We need to use setattr for this to trigger validation and
549 # We need to use setattr for this to trigger validation and
546 # notifications.
550 # notifications.
547 for key, value in iteritems(kw):
551 for key, value in iteritems(kw):
548 setattr(self, key, value)
552 setattr(self, key, value)
549
553
550 def _notify_trait(self, name, old_value, new_value):
554 def _notify_trait(self, name, old_value, new_value):
551
555
552 # First dynamic ones
556 # First dynamic ones
553 callables = []
557 callables = []
554 callables.extend(self._trait_notifiers.get(name,[]))
558 callables.extend(self._trait_notifiers.get(name,[]))
555 callables.extend(self._trait_notifiers.get('anytrait',[]))
559 callables.extend(self._trait_notifiers.get('anytrait',[]))
556
560
557 # Now static ones
561 # Now static ones
558 try:
562 try:
559 cb = getattr(self, '_%s_changed' % name)
563 cb = getattr(self, '_%s_changed' % name)
560 except:
564 except:
561 pass
565 pass
562 else:
566 else:
563 callables.append(cb)
567 callables.append(cb)
564
568
565 # Call them all now
569 # Call them all now
566 for c in callables:
570 for c in callables:
567 # Traits catches and logs errors here. I allow them to raise
571 # Traits catches and logs errors here. I allow them to raise
568 if callable(c):
572 if callable(c):
569 argspec = inspect.getargspec(c)
573 argspec = inspect.getargspec(c)
570 nargs = len(argspec[0])
574 nargs = len(argspec[0])
571 # Bound methods have an additional 'self' argument
575 # Bound methods have an additional 'self' argument
572 # I don't know how to treat unbound methods, but they
576 # I don't know how to treat unbound methods, but they
573 # can't really be used for callbacks.
577 # can't really be used for callbacks.
574 if isinstance(c, types.MethodType):
578 if isinstance(c, types.MethodType):
575 offset = -1
579 offset = -1
576 else:
580 else:
577 offset = 0
581 offset = 0
578 if nargs + offset == 0:
582 if nargs + offset == 0:
579 c()
583 c()
580 elif nargs + offset == 1:
584 elif nargs + offset == 1:
581 c(name)
585 c(name)
582 elif nargs + offset == 2:
586 elif nargs + offset == 2:
583 c(name, new_value)
587 c(name, new_value)
584 elif nargs + offset == 3:
588 elif nargs + offset == 3:
585 c(name, old_value, new_value)
589 c(name, old_value, new_value)
586 else:
590 else:
587 raise TraitError('a trait changed callback '
591 raise TraitError('a trait changed callback '
588 'must have 0-3 arguments.')
592 'must have 0-3 arguments.')
589 else:
593 else:
590 raise TraitError('a trait changed callback '
594 raise TraitError('a trait changed callback '
591 'must be callable.')
595 'must be callable.')
592
596
593
597
594 def _add_notifiers(self, handler, name):
598 def _add_notifiers(self, handler, name):
595 if name not in self._trait_notifiers:
599 if name not in self._trait_notifiers:
596 nlist = []
600 nlist = []
597 self._trait_notifiers[name] = nlist
601 self._trait_notifiers[name] = nlist
598 else:
602 else:
599 nlist = self._trait_notifiers[name]
603 nlist = self._trait_notifiers[name]
600 if handler not in nlist:
604 if handler not in nlist:
601 nlist.append(handler)
605 nlist.append(handler)
602
606
603 def _remove_notifiers(self, handler, name):
607 def _remove_notifiers(self, handler, name):
604 if name in self._trait_notifiers:
608 if name in self._trait_notifiers:
605 nlist = self._trait_notifiers[name]
609 nlist = self._trait_notifiers[name]
606 try:
610 try:
607 index = nlist.index(handler)
611 index = nlist.index(handler)
608 except ValueError:
612 except ValueError:
609 pass
613 pass
610 else:
614 else:
611 del nlist[index]
615 del nlist[index]
612
616
613 def on_trait_change(self, handler, name=None, remove=False):
617 def on_trait_change(self, handler, name=None, remove=False):
614 """Setup a handler to be called when a trait changes.
618 """Setup a handler to be called when a trait changes.
615
619
616 This is used to setup dynamic notifications of trait changes.
620 This is used to setup dynamic notifications of trait changes.
617
621
618 Static handlers can be created by creating methods on a HasTraits
622 Static handlers can be created by creating methods on a HasTraits
619 subclass with the naming convention '_[traitname]_changed'. Thus,
623 subclass with the naming convention '_[traitname]_changed'. Thus,
620 to create static handler for the trait 'a', create the method
624 to create static handler for the trait 'a', create the method
621 _a_changed(self, name, old, new) (fewer arguments can be used, see
625 _a_changed(self, name, old, new) (fewer arguments can be used, see
622 below).
626 below).
623
627
624 Parameters
628 Parameters
625 ----------
629 ----------
626 handler : callable
630 handler : callable
627 A callable that is called when a trait changes. Its
631 A callable that is called when a trait changes. Its
628 signature can be handler(), handler(name), handler(name, new)
632 signature can be handler(), handler(name), handler(name, new)
629 or handler(name, old, new).
633 or handler(name, old, new).
630 name : list, str, None
634 name : list, str, None
631 If None, the handler will apply to all traits. If a list
635 If None, the handler will apply to all traits. If a list
632 of str, handler will apply to all names in the list. If a
636 of str, handler will apply to all names in the list. If a
633 str, the handler will apply just to that name.
637 str, the handler will apply just to that name.
634 remove : bool
638 remove : bool
635 If False (the default), then install the handler. If True
639 If False (the default), then install the handler. If True
636 then unintall it.
640 then unintall it.
637 """
641 """
638 if remove:
642 if remove:
639 names = parse_notifier_name(name)
643 names = parse_notifier_name(name)
640 for n in names:
644 for n in names:
641 self._remove_notifiers(handler, n)
645 self._remove_notifiers(handler, n)
642 else:
646 else:
643 names = parse_notifier_name(name)
647 names = parse_notifier_name(name)
644 for n in names:
648 for n in names:
645 self._add_notifiers(handler, n)
649 self._add_notifiers(handler, n)
646
650
647 @classmethod
651 @classmethod
648 def class_trait_names(cls, **metadata):
652 def class_trait_names(cls, **metadata):
649 """Get a list of all the names of this class' traits.
653 """Get a list of all the names of this class' traits.
650
654
651 This method is just like the :meth:`trait_names` method,
655 This method is just like the :meth:`trait_names` method,
652 but is unbound.
656 but is unbound.
653 """
657 """
654 return cls.class_traits(**metadata).keys()
658 return cls.class_traits(**metadata).keys()
655
659
656 @classmethod
660 @classmethod
657 def class_traits(cls, **metadata):
661 def class_traits(cls, **metadata):
658 """Get a `dict` of all the traits of this class. The dictionary
662 """Get a `dict` of all the traits of this class. The dictionary
659 is keyed on the name and the values are the TraitType objects.
663 is keyed on the name and the values are the TraitType objects.
660
664
661 This method is just like the :meth:`traits` method, but is unbound.
665 This method is just like the :meth:`traits` method, but is unbound.
662
666
663 The TraitTypes returned don't know anything about the values
667 The TraitTypes returned don't know anything about the values
664 that the various HasTrait's instances are holding.
668 that the various HasTrait's instances are holding.
665
669
666 The metadata kwargs allow functions to be passed in which
670 The metadata kwargs allow functions to be passed in which
667 filter traits based on metadata values. The functions should
671 filter traits based on metadata values. The functions should
668 take a single value as an argument and return a boolean. If
672 take a single value as an argument and return a boolean. If
669 any function returns False, then the trait is not included in
673 any function returns False, then the trait is not included in
670 the output. This does not allow for any simple way of
674 the output. This does not allow for any simple way of
671 testing that a metadata name exists and has any
675 testing that a metadata name exists and has any
672 value because get_metadata returns None if a metadata key
676 value because get_metadata returns None if a metadata key
673 doesn't exist.
677 doesn't exist.
674 """
678 """
675 traits = dict([memb for memb in getmembers(cls) if
679 traits = dict([memb for memb in getmembers(cls) if
676 isinstance(memb[1], TraitType)])
680 isinstance(memb[1], TraitType)])
677
681
678 if len(metadata) == 0:
682 if len(metadata) == 0:
679 return traits
683 return traits
680
684
681 for meta_name, meta_eval in metadata.items():
685 for meta_name, meta_eval in metadata.items():
682 if type(meta_eval) is not FunctionType:
686 if type(meta_eval) is not FunctionType:
683 metadata[meta_name] = _SimpleTest(meta_eval)
687 metadata[meta_name] = _SimpleTest(meta_eval)
684
688
685 result = {}
689 result = {}
686 for name, trait in traits.items():
690 for name, trait in traits.items():
687 for meta_name, meta_eval in metadata.items():
691 for meta_name, meta_eval in metadata.items():
688 if not meta_eval(trait.get_metadata(meta_name)):
692 if not meta_eval(trait.get_metadata(meta_name)):
689 break
693 break
690 else:
694 else:
691 result[name] = trait
695 result[name] = trait
692
696
693 return result
697 return result
694
698
695 def trait_names(self, **metadata):
699 def trait_names(self, **metadata):
696 """Get a list of all the names of this class' traits."""
700 """Get a list of all the names of this class' traits."""
697 return self.traits(**metadata).keys()
701 return self.traits(**metadata).keys()
698
702
699 def traits(self, **metadata):
703 def traits(self, **metadata):
700 """Get a `dict` of all the traits of this class. The dictionary
704 """Get a `dict` of all the traits of this class. The dictionary
701 is keyed on the name and the values are the TraitType objects.
705 is keyed on the name and the values are the TraitType objects.
702
706
703 The TraitTypes returned don't know anything about the values
707 The TraitTypes returned don't know anything about the values
704 that the various HasTrait's instances are holding.
708 that the various HasTrait's instances are holding.
705
709
706 The metadata kwargs allow functions to be passed in which
710 The metadata kwargs allow functions to be passed in which
707 filter traits based on metadata values. The functions should
711 filter traits based on metadata values. The functions should
708 take a single value as an argument and return a boolean. If
712 take a single value as an argument and return a boolean. If
709 any function returns False, then the trait is not included in
713 any function returns False, then the trait is not included in
710 the output. This does not allow for any simple way of
714 the output. This does not allow for any simple way of
711 testing that a metadata name exists and has any
715 testing that a metadata name exists and has any
712 value because get_metadata returns None if a metadata key
716 value because get_metadata returns None if a metadata key
713 doesn't exist.
717 doesn't exist.
714 """
718 """
715 traits = dict([memb for memb in getmembers(self.__class__) if
719 traits = dict([memb for memb in getmembers(self.__class__) if
716 isinstance(memb[1], TraitType)])
720 isinstance(memb[1], TraitType)])
717
721
718 if len(metadata) == 0:
722 if len(metadata) == 0:
719 return traits
723 return traits
720
724
721 for meta_name, meta_eval in metadata.items():
725 for meta_name, meta_eval in metadata.items():
722 if type(meta_eval) is not FunctionType:
726 if type(meta_eval) is not FunctionType:
723 metadata[meta_name] = _SimpleTest(meta_eval)
727 metadata[meta_name] = _SimpleTest(meta_eval)
724
728
725 result = {}
729 result = {}
726 for name, trait in traits.items():
730 for name, trait in traits.items():
727 for meta_name, meta_eval in metadata.items():
731 for meta_name, meta_eval in metadata.items():
728 if not meta_eval(trait.get_metadata(meta_name)):
732 if not meta_eval(trait.get_metadata(meta_name)):
729 break
733 break
730 else:
734 else:
731 result[name] = trait
735 result[name] = trait
732
736
733 return result
737 return result
734
738
735 def trait_metadata(self, traitname, key, default=None):
739 def trait_metadata(self, traitname, key, default=None):
736 """Get metadata values for trait by key."""
740 """Get metadata values for trait by key."""
737 try:
741 try:
738 trait = getattr(self.__class__, traitname)
742 trait = getattr(self.__class__, traitname)
739 except AttributeError:
743 except AttributeError:
740 raise TraitError("Class %s does not have a trait named %s" %
744 raise TraitError("Class %s does not have a trait named %s" %
741 (self.__class__.__name__, traitname))
745 (self.__class__.__name__, traitname))
742 else:
746 else:
743 return trait.get_metadata(key, default)
747 return trait.get_metadata(key, default)
744
748
745 #-----------------------------------------------------------------------------
749 #-----------------------------------------------------------------------------
746 # Actual TraitTypes implementations/subclasses
750 # Actual TraitTypes implementations/subclasses
747 #-----------------------------------------------------------------------------
751 #-----------------------------------------------------------------------------
748
752
749 #-----------------------------------------------------------------------------
753 #-----------------------------------------------------------------------------
750 # TraitTypes subclasses for handling classes and instances of classes
754 # TraitTypes subclasses for handling classes and instances of classes
751 #-----------------------------------------------------------------------------
755 #-----------------------------------------------------------------------------
752
756
753
757
754 class ClassBasedTraitType(TraitType):
758 class ClassBasedTraitType(TraitType):
755 """
759 """
756 A trait with error reporting and string -> type resolution for Type,
760 A trait with error reporting and string -> type resolution for Type,
757 Instance and This.
761 Instance and This.
758 """
762 """
759
763
760 def _resolve_string(self, string):
764 def _resolve_string(self, string):
761 """
765 """
762 Resolve a string supplied for a type into an actual object.
766 Resolve a string supplied for a type into an actual object.
763 """
767 """
764 return import_item(string)
768 return import_item(string)
765
769
766 def error(self, obj, value):
770 def error(self, obj, value):
767 kind = type(value)
771 kind = type(value)
768 if (not py3compat.PY3) and kind is InstanceType:
772 if (not py3compat.PY3) and kind is InstanceType:
769 msg = 'class %s' % value.__class__.__name__
773 msg = 'class %s' % value.__class__.__name__
770 else:
774 else:
771 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
775 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
772
776
773 if obj is not None:
777 if obj is not None:
774 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
778 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
775 % (self.name, class_of(obj),
779 % (self.name, class_of(obj),
776 self.info(), msg)
780 self.info(), msg)
777 else:
781 else:
778 e = "The '%s' trait must be %s, but a value of %r was specified." \
782 e = "The '%s' trait must be %s, but a value of %r was specified." \
779 % (self.name, self.info(), msg)
783 % (self.name, self.info(), msg)
780
784
781 raise TraitError(e)
785 raise TraitError(e)
782
786
783
787
784 class Type(ClassBasedTraitType):
788 class Type(ClassBasedTraitType):
785 """A trait whose value must be a subclass of a specified class."""
789 """A trait whose value must be a subclass of a specified class."""
786
790
787 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
791 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
788 """Construct a Type trait
792 """Construct a Type trait
789
793
790 A Type trait specifies that its values must be subclasses of
794 A Type trait specifies that its values must be subclasses of
791 a particular class.
795 a particular class.
792
796
793 If only ``default_value`` is given, it is used for the ``klass`` as
797 If only ``default_value`` is given, it is used for the ``klass`` as
794 well.
798 well.
795
799
796 Parameters
800 Parameters
797 ----------
801 ----------
798 default_value : class, str or None
802 default_value : class, str or None
799 The default value must be a subclass of klass. If an str,
803 The default value must be a subclass of klass. If an str,
800 the str must be a fully specified class name, like 'foo.bar.Bah'.
804 the str must be a fully specified class name, like 'foo.bar.Bah'.
801 The string is resolved into real class, when the parent
805 The string is resolved into real class, when the parent
802 :class:`HasTraits` class is instantiated.
806 :class:`HasTraits` class is instantiated.
803 klass : class, str, None
807 klass : class, str, None
804 Values of this trait must be a subclass of klass. The klass
808 Values of this trait must be a subclass of klass. The klass
805 may be specified in a string like: 'foo.bar.MyClass'.
809 may be specified in a string like: 'foo.bar.MyClass'.
806 The string is resolved into real class, when the parent
810 The string is resolved into real class, when the parent
807 :class:`HasTraits` class is instantiated.
811 :class:`HasTraits` class is instantiated.
808 allow_none : boolean
812 allow_none : boolean
809 Indicates whether None is allowed as an assignable value. Even if
813 Indicates whether None is allowed as an assignable value. Even if
810 ``False``, the default value may be ``None``.
814 ``False``, the default value may be ``None``.
811 """
815 """
812 if default_value is None:
816 if default_value is None:
813 if klass is None:
817 if klass is None:
814 klass = object
818 klass = object
815 elif klass is None:
819 elif klass is None:
816 klass = default_value
820 klass = default_value
817
821
818 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
822 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
819 raise TraitError("A Type trait must specify a class.")
823 raise TraitError("A Type trait must specify a class.")
820
824
821 self.klass = klass
825 self.klass = klass
822
826
823 super(Type, self).__init__(default_value, allow_none=allow_none, **metadata)
827 super(Type, self).__init__(default_value, allow_none=allow_none, **metadata)
824
828
825 def validate(self, obj, value):
829 def validate(self, obj, value):
826 """Validates that the value is a valid object instance."""
830 """Validates that the value is a valid object instance."""
827 if isinstance(value, py3compat.string_types):
831 if isinstance(value, py3compat.string_types):
828 try:
832 try:
829 value = self._resolve_string(value)
833 value = self._resolve_string(value)
830 except ImportError:
834 except ImportError:
831 raise TraitError("The '%s' trait of %s instance must be a type, but "
835 raise TraitError("The '%s' trait of %s instance must be a type, but "
832 "%r could not be imported" % (self.name, obj, value))
836 "%r could not be imported" % (self.name, obj, value))
833 try:
837 try:
834 if issubclass(value, self.klass):
838 if issubclass(value, self.klass):
835 return value
839 return value
836 except:
840 except:
837 pass
841 pass
838
842
839 self.error(obj, value)
843 self.error(obj, value)
840
844
841 def info(self):
845 def info(self):
842 """ Returns a description of the trait."""
846 """ Returns a description of the trait."""
843 if isinstance(self.klass, py3compat.string_types):
847 if isinstance(self.klass, py3compat.string_types):
844 klass = self.klass
848 klass = self.klass
845 else:
849 else:
846 klass = self.klass.__name__
850 klass = self.klass.__name__
847 result = 'a subclass of ' + klass
851 result = 'a subclass of ' + klass
848 if self.allow_none:
852 if self.allow_none:
849 return result + ' or None'
853 return result + ' or None'
850 return result
854 return result
851
855
852 def instance_init(self, obj):
856 def instance_init(self, obj):
853 self._resolve_classes()
857 self._resolve_classes()
854 super(Type, self).instance_init(obj)
858 super(Type, self).instance_init(obj)
855
859
856 def _resolve_classes(self):
860 def _resolve_classes(self):
857 if isinstance(self.klass, py3compat.string_types):
861 if isinstance(self.klass, py3compat.string_types):
858 self.klass = self._resolve_string(self.klass)
862 self.klass = self._resolve_string(self.klass)
859 if isinstance(self.default_value, py3compat.string_types):
863 if isinstance(self.default_value, py3compat.string_types):
860 self.default_value = self._resolve_string(self.default_value)
864 self.default_value = self._resolve_string(self.default_value)
861
865
862 def get_default_value(self):
866 def get_default_value(self):
863 return self.default_value
867 return self.default_value
864
868
865
869
866 class DefaultValueGenerator(object):
870 class DefaultValueGenerator(object):
867 """A class for generating new default value instances."""
871 """A class for generating new default value instances."""
868
872
869 def __init__(self, *args, **kw):
873 def __init__(self, *args, **kw):
870 self.args = args
874 self.args = args
871 self.kw = kw
875 self.kw = kw
872
876
873 def generate(self, klass):
877 def generate(self, klass):
874 return klass(*self.args, **self.kw)
878 return klass(*self.args, **self.kw)
875
879
876
880
877 class Instance(ClassBasedTraitType):
881 class Instance(ClassBasedTraitType):
878 """A trait whose value must be an instance of a specified class.
882 """A trait whose value must be an instance of a specified class.
879
883
880 The value can also be an instance of a subclass of the specified class.
884 The value can also be an instance of a subclass of the specified class.
881
885
882 Subclasses can declare default classes by overriding the klass attribute
886 Subclasses can declare default classes by overriding the klass attribute
883 """
887 """
884
888
885 klass = None
889 klass = None
886
890
887 def __init__(self, klass=None, args=None, kw=None,
891 def __init__(self, klass=None, args=None, kw=None,
888 allow_none=True, **metadata ):
892 allow_none=True, **metadata ):
889 """Construct an Instance trait.
893 """Construct an Instance trait.
890
894
891 This trait allows values that are instances of a particular
895 This trait allows values that are instances of a particular
892 class or its subclasses. Our implementation is quite different
896 class or its subclasses. Our implementation is quite different
893 from that of enthough.traits as we don't allow instances to be used
897 from that of enthough.traits as we don't allow instances to be used
894 for klass and we handle the ``args`` and ``kw`` arguments differently.
898 for klass and we handle the ``args`` and ``kw`` arguments differently.
895
899
896 Parameters
900 Parameters
897 ----------
901 ----------
898 klass : class, str
902 klass : class, str
899 The class that forms the basis for the trait. Class names
903 The class that forms the basis for the trait. Class names
900 can also be specified as strings, like 'foo.bar.Bar'.
904 can also be specified as strings, like 'foo.bar.Bar'.
901 args : tuple
905 args : tuple
902 Positional arguments for generating the default value.
906 Positional arguments for generating the default value.
903 kw : dict
907 kw : dict
904 Keyword arguments for generating the default value.
908 Keyword arguments for generating the default value.
905 allow_none : bool
909 allow_none : bool
906 Indicates whether None is allowed as a value.
910 Indicates whether None is allowed as a value.
907
911
908 Notes
912 Notes
909 -----
913 -----
910 If both ``args`` and ``kw`` are None, then the default value is None.
914 If both ``args`` and ``kw`` are None, then the default value is None.
911 If ``args`` is a tuple and ``kw`` is a dict, then the default is
915 If ``args`` is a tuple and ``kw`` is a dict, then the default is
912 created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
916 created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
913 None, the None is replaced by ``()`` or ``{}``, respectively.
917 None, the None is replaced by ``()`` or ``{}``, respectively.
914 """
918 """
915 if klass is None:
919 if klass is None:
916 klass = self.klass
920 klass = self.klass
917
921
918 if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
922 if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
919 self.klass = klass
923 self.klass = klass
920 else:
924 else:
921 raise TraitError('The klass attribute must be a class'
925 raise TraitError('The klass attribute must be a class'
922 ' not: %r' % klass)
926 ' not: %r' % klass)
923
927
924 # self.klass is a class, so handle default_value
928 # self.klass is a class, so handle default_value
925 if args is None and kw is None:
929 if args is None and kw is None:
926 default_value = None
930 default_value = None
927 else:
931 else:
928 if args is None:
932 if args is None:
929 # kw is not None
933 # kw is not None
930 args = ()
934 args = ()
931 elif kw is None:
935 elif kw is None:
932 # args is not None
936 # args is not None
933 kw = {}
937 kw = {}
934
938
935 if not isinstance(kw, dict):
939 if not isinstance(kw, dict):
936 raise TraitError("The 'kw' argument must be a dict or None.")
940 raise TraitError("The 'kw' argument must be a dict or None.")
937 if not isinstance(args, tuple):
941 if not isinstance(args, tuple):
938 raise TraitError("The 'args' argument must be a tuple or None.")
942 raise TraitError("The 'args' argument must be a tuple or None.")
939
943
940 default_value = DefaultValueGenerator(*args, **kw)
944 default_value = DefaultValueGenerator(*args, **kw)
941
945
942 super(Instance, self).__init__(default_value, allow_none=allow_none, **metadata)
946 super(Instance, self).__init__(default_value, allow_none=allow_none, **metadata)
943
947
944 def validate(self, obj, value):
948 def validate(self, obj, value):
945 if isinstance(value, self.klass):
949 if isinstance(value, self.klass):
946 return value
950 return value
947 else:
951 else:
948 self.error(obj, value)
952 self.error(obj, value)
949
953
950 def info(self):
954 def info(self):
951 if isinstance(self.klass, py3compat.string_types):
955 if isinstance(self.klass, py3compat.string_types):
952 klass = self.klass
956 klass = self.klass
953 else:
957 else:
954 klass = self.klass.__name__
958 klass = self.klass.__name__
955 result = class_of(klass)
959 result = class_of(klass)
956 if self.allow_none:
960 if self.allow_none:
957 return result + ' or None'
961 return result + ' or None'
958
962
959 return result
963 return result
960
964
961 def instance_init(self, obj):
965 def instance_init(self, obj):
962 self._resolve_classes()
966 self._resolve_classes()
963 super(Instance, self).instance_init(obj)
967 super(Instance, self).instance_init(obj)
964
968
965 def _resolve_classes(self):
969 def _resolve_classes(self):
966 if isinstance(self.klass, py3compat.string_types):
970 if isinstance(self.klass, py3compat.string_types):
967 self.klass = self._resolve_string(self.klass)
971 self.klass = self._resolve_string(self.klass)
968
972
969 def get_default_value(self):
973 def get_default_value(self):
970 """Instantiate a default value instance.
974 """Instantiate a default value instance.
971
975
972 This is called when the containing HasTraits classes'
976 This is called when the containing HasTraits classes'
973 :meth:`__new__` method is called to ensure that a unique instance
977 :meth:`__new__` method is called to ensure that a unique instance
974 is created for each HasTraits instance.
978 is created for each HasTraits instance.
975 """
979 """
976 dv = self.default_value
980 dv = self.default_value
977 if isinstance(dv, DefaultValueGenerator):
981 if isinstance(dv, DefaultValueGenerator):
978 return dv.generate(self.klass)
982 return dv.generate(self.klass)
979 else:
983 else:
980 return dv
984 return dv
981
985
982
986
983 class ForwardDeclaredMixin(object):
987 class ForwardDeclaredMixin(object):
984 """
988 """
985 Mixin for forward-declared versions of Instance and Type.
989 Mixin for forward-declared versions of Instance and Type.
986 """
990 """
987 def _resolve_string(self, string):
991 def _resolve_string(self, string):
988 """
992 """
989 Find the specified class name by looking for it in the module in which
993 Find the specified class name by looking for it in the module in which
990 our this_class attribute was defined.
994 our this_class attribute was defined.
991 """
995 """
992 modname = self.this_class.__module__
996 modname = self.this_class.__module__
993 return import_item('.'.join([modname, string]))
997 return import_item('.'.join([modname, string]))
994
998
995
999
996 class ForwardDeclaredType(ForwardDeclaredMixin, Type):
1000 class ForwardDeclaredType(ForwardDeclaredMixin, Type):
997 """
1001 """
998 Forward-declared version of Type.
1002 Forward-declared version of Type.
999 """
1003 """
1000 pass
1004 pass
1001
1005
1002
1006
1003 class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
1007 class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
1004 """
1008 """
1005 Forward-declared version of Instance.
1009 Forward-declared version of Instance.
1006 """
1010 """
1007 pass
1011 pass
1008
1012
1009
1013
1010 class This(ClassBasedTraitType):
1014 class This(ClassBasedTraitType):
1011 """A trait for instances of the class containing this trait.
1015 """A trait for instances of the class containing this trait.
1012
1016
1013 Because how how and when class bodies are executed, the ``This``
1017 Because how how and when class bodies are executed, the ``This``
1014 trait can only have a default value of None. This, and because we
1018 trait can only have a default value of None. This, and because we
1015 always validate default values, ``allow_none`` is *always* true.
1019 always validate default values, ``allow_none`` is *always* true.
1016 """
1020 """
1017
1021
1018 info_text = 'an instance of the same type as the receiver or None'
1022 info_text = 'an instance of the same type as the receiver or None'
1019
1023
1020 def __init__(self, **metadata):
1024 def __init__(self, **metadata):
1021 super(This, self).__init__(None, **metadata)
1025 super(This, self).__init__(None, **metadata)
1022
1026
1023 def validate(self, obj, value):
1027 def validate(self, obj, value):
1024 # What if value is a superclass of obj.__class__? This is
1028 # What if value is a superclass of obj.__class__? This is
1025 # complicated if it was the superclass that defined the This
1029 # complicated if it was the superclass that defined the This
1026 # trait.
1030 # trait.
1027 if isinstance(value, self.this_class) or (value is None):
1031 if isinstance(value, self.this_class) or (value is None):
1028 return value
1032 return value
1029 else:
1033 else:
1030 self.error(obj, value)
1034 self.error(obj, value)
1031
1035
1032
1036
1033 class Union(TraitType):
1037 class Union(TraitType):
1034 """A trait type representing a Union type."""
1038 """A trait type representing a Union type."""
1035
1039
1036 def __init__(self, trait_types, **metadata):
1040 def __init__(self, trait_types, **metadata):
1037 """Construct a Union trait.
1041 """Construct a Union trait.
1038
1042
1039 This trait allows values that are allowed by at least one of the
1043 This trait allows values that are allowed by at least one of the
1040 specified trait types.
1044 specified trait types.
1041
1045
1042 Parameters
1046 Parameters
1043 ----------
1047 ----------
1044 trait_types: sequence
1048 trait_types: sequence
1045 The list of trait types of length at least 1.
1049 The list of trait types of length at least 1.
1046
1050
1047 Notes
1051 Notes
1048 -----
1052 -----
1049 Union([Float(), Bool(), Int()]) attempts to validate the provided values
1053 Union([Float(), Bool(), Int()]) attempts to validate the provided values
1050 with the validation function of Float, then Bool, and finally Int.
1054 with the validation function of Float, then Bool, and finally Int.
1051 """
1055 """
1052 self.trait_types = trait_types
1056 self.trait_types = trait_types
1053 self.info_text = " or ".join([tt.info_text for tt in self.trait_types])
1057 self.info_text = " or ".join([tt.info_text for tt in self.trait_types])
1054 self.default_value = self.trait_types[0].get_default_value()
1058 self.default_value = self.trait_types[0].get_default_value()
1055 super(Union, self).__init__(**metadata)
1059 super(Union, self).__init__(**metadata)
1056
1060
1057 def instance_init(self, obj):
1061 def instance_init(self, obj):
1058 for trait_type in self.trait_types:
1062 for trait_type in self.trait_types:
1059 trait_type.name = self.name
1063 trait_type.name = self.name
1060 trait_type.this_class = self.this_class
1064 trait_type.this_class = self.this_class
1061 if hasattr(trait_type, '_resolve_classes'):
1065 if hasattr(trait_type, '_resolve_classes'):
1062 trait_type._resolve_classes()
1066 trait_type._resolve_classes()
1063 super(Union, self).instance_init(obj)
1067 super(Union, self).instance_init(obj)
1064
1068
1065 def validate(self, obj, value):
1069 def validate(self, obj, value):
1066 for trait_type in self.trait_types:
1070 for trait_type in self.trait_types:
1067 try:
1071 try:
1068 return trait_type._validate(obj, value)
1072 return trait_type._validate(obj, value)
1069 except TraitError:
1073 except TraitError:
1070 continue
1074 continue
1071 self.error(obj, value)
1075 self.error(obj, value)
1072
1076
1073 def __or__(self, other):
1077 def __or__(self, other):
1074 if isinstance(other, Union):
1078 if isinstance(other, Union):
1075 return Union(self.trait_types + other.trait_types)
1079 return Union(self.trait_types + other.trait_types)
1076 else:
1080 else:
1077 return Union(self.trait_types + [other])
1081 return Union(self.trait_types + [other])
1078
1082
1079 #-----------------------------------------------------------------------------
1083 #-----------------------------------------------------------------------------
1080 # Basic TraitTypes implementations/subclasses
1084 # Basic TraitTypes implementations/subclasses
1081 #-----------------------------------------------------------------------------
1085 #-----------------------------------------------------------------------------
1082
1086
1083
1087
1084 class Any(TraitType):
1088 class Any(TraitType):
1085 default_value = None
1089 default_value = None
1086 info_text = 'any value'
1090 info_text = 'any value'
1087
1091
1088
1092
1089 class Int(TraitType):
1093 class Int(TraitType):
1090 """An int trait."""
1094 """An int trait."""
1091
1095
1092 default_value = 0
1096 default_value = 0
1093 info_text = 'an int'
1097 info_text = 'an int'
1094
1098
1095 def validate(self, obj, value):
1099 def validate(self, obj, value):
1096 if isinstance(value, int):
1100 if isinstance(value, int):
1097 return value
1101 return value
1098 self.error(obj, value)
1102 self.error(obj, value)
1099
1103
1100 class CInt(Int):
1104 class CInt(Int):
1101 """A casting version of the int trait."""
1105 """A casting version of the int trait."""
1102
1106
1103 def validate(self, obj, value):
1107 def validate(self, obj, value):
1104 try:
1108 try:
1105 return int(value)
1109 return int(value)
1106 except:
1110 except:
1107 self.error(obj, value)
1111 self.error(obj, value)
1108
1112
1109 if py3compat.PY3:
1113 if py3compat.PY3:
1110 Long, CLong = Int, CInt
1114 Long, CLong = Int, CInt
1111 Integer = Int
1115 Integer = Int
1112 else:
1116 else:
1113 class Long(TraitType):
1117 class Long(TraitType):
1114 """A long integer trait."""
1118 """A long integer trait."""
1115
1119
1116 default_value = 0
1120 default_value = 0
1117 info_text = 'a long'
1121 info_text = 'a long'
1118
1122
1119 def validate(self, obj, value):
1123 def validate(self, obj, value):
1120 if isinstance(value, long):
1124 if isinstance(value, long):
1121 return value
1125 return value
1122 if isinstance(value, int):
1126 if isinstance(value, int):
1123 return long(value)
1127 return long(value)
1124 self.error(obj, value)
1128 self.error(obj, value)
1125
1129
1126
1130
1127 class CLong(Long):
1131 class CLong(Long):
1128 """A casting version of the long integer trait."""
1132 """A casting version of the long integer trait."""
1129
1133
1130 def validate(self, obj, value):
1134 def validate(self, obj, value):
1131 try:
1135 try:
1132 return long(value)
1136 return long(value)
1133 except:
1137 except:
1134 self.error(obj, value)
1138 self.error(obj, value)
1135
1139
1136 class Integer(TraitType):
1140 class Integer(TraitType):
1137 """An integer trait.
1141 """An integer trait.
1138
1142
1139 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
1143 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
1140
1144
1141 default_value = 0
1145 default_value = 0
1142 info_text = 'an integer'
1146 info_text = 'an integer'
1143
1147
1144 def validate(self, obj, value):
1148 def validate(self, obj, value):
1145 if isinstance(value, int):
1149 if isinstance(value, int):
1146 return value
1150 return value
1147 if isinstance(value, long):
1151 if isinstance(value, long):
1148 # downcast longs that fit in int:
1152 # downcast longs that fit in int:
1149 # note that int(n > sys.maxint) returns a long, so
1153 # note that int(n > sys.maxint) returns a long, so
1150 # we don't need a condition on this cast
1154 # we don't need a condition on this cast
1151 return int(value)
1155 return int(value)
1152 if sys.platform == "cli":
1156 if sys.platform == "cli":
1153 from System import Int64
1157 from System import Int64
1154 if isinstance(value, Int64):
1158 if isinstance(value, Int64):
1155 return int(value)
1159 return int(value)
1156 self.error(obj, value)
1160 self.error(obj, value)
1157
1161
1158
1162
1159 class Float(TraitType):
1163 class Float(TraitType):
1160 """A float trait."""
1164 """A float trait."""
1161
1165
1162 default_value = 0.0
1166 default_value = 0.0
1163 info_text = 'a float'
1167 info_text = 'a float'
1164
1168
1165 def validate(self, obj, value):
1169 def validate(self, obj, value):
1166 if isinstance(value, float):
1170 if isinstance(value, float):
1167 return value
1171 return value
1168 if isinstance(value, int):
1172 if isinstance(value, int):
1169 return float(value)
1173 return float(value)
1170 self.error(obj, value)
1174 self.error(obj, value)
1171
1175
1172
1176
1173 class CFloat(Float):
1177 class CFloat(Float):
1174 """A casting version of the float trait."""
1178 """A casting version of the float trait."""
1175
1179
1176 def validate(self, obj, value):
1180 def validate(self, obj, value):
1177 try:
1181 try:
1178 return float(value)
1182 return float(value)
1179 except:
1183 except:
1180 self.error(obj, value)
1184 self.error(obj, value)
1181
1185
1182 class Complex(TraitType):
1186 class Complex(TraitType):
1183 """A trait for complex numbers."""
1187 """A trait for complex numbers."""
1184
1188
1185 default_value = 0.0 + 0.0j
1189 default_value = 0.0 + 0.0j
1186 info_text = 'a complex number'
1190 info_text = 'a complex number'
1187
1191
1188 def validate(self, obj, value):
1192 def validate(self, obj, value):
1189 if isinstance(value, complex):
1193 if isinstance(value, complex):
1190 return value
1194 return value
1191 if isinstance(value, (float, int)):
1195 if isinstance(value, (float, int)):
1192 return complex(value)
1196 return complex(value)
1193 self.error(obj, value)
1197 self.error(obj, value)
1194
1198
1195
1199
1196 class CComplex(Complex):
1200 class CComplex(Complex):
1197 """A casting version of the complex number trait."""
1201 """A casting version of the complex number trait."""
1198
1202
1199 def validate (self, obj, value):
1203 def validate (self, obj, value):
1200 try:
1204 try:
1201 return complex(value)
1205 return complex(value)
1202 except:
1206 except:
1203 self.error(obj, value)
1207 self.error(obj, value)
1204
1208
1205 # We should always be explicit about whether we're using bytes or unicode, both
1209 # We should always be explicit about whether we're using bytes or unicode, both
1206 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1210 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1207 # we don't have a Str type.
1211 # we don't have a Str type.
1208 class Bytes(TraitType):
1212 class Bytes(TraitType):
1209 """A trait for byte strings."""
1213 """A trait for byte strings."""
1210
1214
1211 default_value = b''
1215 default_value = b''
1212 info_text = 'a bytes object'
1216 info_text = 'a bytes object'
1213
1217
1214 def validate(self, obj, value):
1218 def validate(self, obj, value):
1215 if isinstance(value, bytes):
1219 if isinstance(value, bytes):
1216 return value
1220 return value
1217 self.error(obj, value)
1221 self.error(obj, value)
1218
1222
1219
1223
1220 class CBytes(Bytes):
1224 class CBytes(Bytes):
1221 """A casting version of the byte string trait."""
1225 """A casting version of the byte string trait."""
1222
1226
1223 def validate(self, obj, value):
1227 def validate(self, obj, value):
1224 try:
1228 try:
1225 return bytes(value)
1229 return bytes(value)
1226 except:
1230 except:
1227 self.error(obj, value)
1231 self.error(obj, value)
1228
1232
1229
1233
1230 class Unicode(TraitType):
1234 class Unicode(TraitType):
1231 """A trait for unicode strings."""
1235 """A trait for unicode strings."""
1232
1236
1233 default_value = u''
1237 default_value = u''
1234 info_text = 'a unicode string'
1238 info_text = 'a unicode string'
1235
1239
1236 def validate(self, obj, value):
1240 def validate(self, obj, value):
1237 if isinstance(value, py3compat.unicode_type):
1241 if isinstance(value, py3compat.unicode_type):
1238 return value
1242 return value
1239 if isinstance(value, bytes):
1243 if isinstance(value, bytes):
1240 try:
1244 try:
1241 return value.decode('ascii', 'strict')
1245 return value.decode('ascii', 'strict')
1242 except UnicodeDecodeError:
1246 except UnicodeDecodeError:
1243 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1247 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1244 raise TraitError(msg.format(value, self.name, class_of(obj)))
1248 raise TraitError(msg.format(value, self.name, class_of(obj)))
1245 self.error(obj, value)
1249 self.error(obj, value)
1246
1250
1247
1251
1248 class CUnicode(Unicode):
1252 class CUnicode(Unicode):
1249 """A casting version of the unicode trait."""
1253 """A casting version of the unicode trait."""
1250
1254
1251 def validate(self, obj, value):
1255 def validate(self, obj, value):
1252 try:
1256 try:
1253 return py3compat.unicode_type(value)
1257 return py3compat.unicode_type(value)
1254 except:
1258 except:
1255 self.error(obj, value)
1259 self.error(obj, value)
1256
1260
1257
1261
1258 class ObjectName(TraitType):
1262 class ObjectName(TraitType):
1259 """A string holding a valid object name in this version of Python.
1263 """A string holding a valid object name in this version of Python.
1260
1264
1261 This does not check that the name exists in any scope."""
1265 This does not check that the name exists in any scope."""
1262 info_text = "a valid object identifier in Python"
1266 info_text = "a valid object identifier in Python"
1263
1267
1264 if py3compat.PY3:
1268 if py3compat.PY3:
1265 # Python 3:
1269 # Python 3:
1266 coerce_str = staticmethod(lambda _,s: s)
1270 coerce_str = staticmethod(lambda _,s: s)
1267
1271
1268 else:
1272 else:
1269 # Python 2:
1273 # Python 2:
1270 def coerce_str(self, obj, value):
1274 def coerce_str(self, obj, value):
1271 "In Python 2, coerce ascii-only unicode to str"
1275 "In Python 2, coerce ascii-only unicode to str"
1272 if isinstance(value, unicode):
1276 if isinstance(value, unicode):
1273 try:
1277 try:
1274 return str(value)
1278 return str(value)
1275 except UnicodeEncodeError:
1279 except UnicodeEncodeError:
1276 self.error(obj, value)
1280 self.error(obj, value)
1277 return value
1281 return value
1278
1282
1279 def validate(self, obj, value):
1283 def validate(self, obj, value):
1280 value = self.coerce_str(obj, value)
1284 value = self.coerce_str(obj, value)
1281
1285
1282 if isinstance(value, string_types) and py3compat.isidentifier(value):
1286 if isinstance(value, string_types) and py3compat.isidentifier(value):
1283 return value
1287 return value
1284 self.error(obj, value)
1288 self.error(obj, value)
1285
1289
1286 class DottedObjectName(ObjectName):
1290 class DottedObjectName(ObjectName):
1287 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1291 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1288 def validate(self, obj, value):
1292 def validate(self, obj, value):
1289 value = self.coerce_str(obj, value)
1293 value = self.coerce_str(obj, value)
1290
1294
1291 if isinstance(value, string_types) and py3compat.isidentifier(value, dotted=True):
1295 if isinstance(value, string_types) and py3compat.isidentifier(value, dotted=True):
1292 return value
1296 return value
1293 self.error(obj, value)
1297 self.error(obj, value)
1294
1298
1295
1299
1296 class Bool(TraitType):
1300 class Bool(TraitType):
1297 """A boolean (True, False) trait."""
1301 """A boolean (True, False) trait."""
1298
1302
1299 default_value = False
1303 default_value = False
1300 info_text = 'a boolean'
1304 info_text = 'a boolean'
1301
1305
1302 def validate(self, obj, value):
1306 def validate(self, obj, value):
1303 if isinstance(value, bool):
1307 if isinstance(value, bool):
1304 return value
1308 return value
1305 self.error(obj, value)
1309 self.error(obj, value)
1306
1310
1307
1311
1308 class CBool(Bool):
1312 class CBool(Bool):
1309 """A casting version of the boolean trait."""
1313 """A casting version of the boolean trait."""
1310
1314
1311 def validate(self, obj, value):
1315 def validate(self, obj, value):
1312 try:
1316 try:
1313 return bool(value)
1317 return bool(value)
1314 except:
1318 except:
1315 self.error(obj, value)
1319 self.error(obj, value)
1316
1320
1317
1321
1318 class Enum(TraitType):
1322 class Enum(TraitType):
1319 """An enum that whose value must be in a given sequence."""
1323 """An enum that whose value must be in a given sequence."""
1320
1324
1321 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1325 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1322 self.values = values
1326 self.values = values
1323 super(Enum, self).__init__(default_value, allow_none=allow_none, **metadata)
1327 super(Enum, self).__init__(default_value, allow_none=allow_none, **metadata)
1324
1328
1325 def validate(self, obj, value):
1329 def validate(self, obj, value):
1326 if value in self.values:
1330 if value in self.values:
1327 return value
1331 return value
1328 self.error(obj, value)
1332 self.error(obj, value)
1329
1333
1330 def info(self):
1334 def info(self):
1331 """ Returns a description of the trait."""
1335 """ Returns a description of the trait."""
1332 result = 'any of ' + repr(self.values)
1336 result = 'any of ' + repr(self.values)
1333 if self.allow_none:
1337 if self.allow_none:
1334 return result + ' or None'
1338 return result + ' or None'
1335 return result
1339 return result
1336
1340
1337 class CaselessStrEnum(Enum):
1341 class CaselessStrEnum(Enum):
1338 """An enum of strings that are caseless in validate."""
1342 """An enum of strings that are caseless in validate."""
1339
1343
1340 def validate(self, obj, value):
1344 def validate(self, obj, value):
1341 if not isinstance(value, py3compat.string_types):
1345 if not isinstance(value, py3compat.string_types):
1342 self.error(obj, value)
1346 self.error(obj, value)
1343
1347
1344 for v in self.values:
1348 for v in self.values:
1345 if v.lower() == value.lower():
1349 if v.lower() == value.lower():
1346 return v
1350 return v
1347 self.error(obj, value)
1351 self.error(obj, value)
1348
1352
1349 class Container(Instance):
1353 class Container(Instance):
1350 """An instance of a container (list, set, etc.)
1354 """An instance of a container (list, set, etc.)
1351
1355
1352 To be subclassed by overriding klass.
1356 To be subclassed by overriding klass.
1353 """
1357 """
1354 klass = None
1358 klass = None
1355 _cast_types = ()
1359 _cast_types = ()
1356 _valid_defaults = SequenceTypes
1360 _valid_defaults = SequenceTypes
1357 _trait = None
1361 _trait = None
1358
1362
1359 def __init__(self, trait=None, default_value=None, allow_none=True,
1363 def __init__(self, trait=None, default_value=None, allow_none=True,
1360 **metadata):
1364 **metadata):
1361 """Create a container trait type from a list, set, or tuple.
1365 """Create a container trait type from a list, set, or tuple.
1362
1366
1363 The default value is created by doing ``List(default_value)``,
1367 The default value is created by doing ``List(default_value)``,
1364 which creates a copy of the ``default_value``.
1368 which creates a copy of the ``default_value``.
1365
1369
1366 ``trait`` can be specified, which restricts the type of elements
1370 ``trait`` can be specified, which restricts the type of elements
1367 in the container to that TraitType.
1371 in the container to that TraitType.
1368
1372
1369 If only one arg is given and it is not a Trait, it is taken as
1373 If only one arg is given and it is not a Trait, it is taken as
1370 ``default_value``:
1374 ``default_value``:
1371
1375
1372 ``c = List([1,2,3])``
1376 ``c = List([1,2,3])``
1373
1377
1374 Parameters
1378 Parameters
1375 ----------
1379 ----------
1376
1380
1377 trait : TraitType [ optional ]
1381 trait : TraitType [ optional ]
1378 the type for restricting the contents of the Container. If unspecified,
1382 the type for restricting the contents of the Container. If unspecified,
1379 types are not checked.
1383 types are not checked.
1380
1384
1381 default_value : SequenceType [ optional ]
1385 default_value : SequenceType [ optional ]
1382 The default value for the Trait. Must be list/tuple/set, and
1386 The default value for the Trait. Must be list/tuple/set, and
1383 will be cast to the container type.
1387 will be cast to the container type.
1384
1388
1385 allow_none : Bool [ default True ]
1389 allow_none : Bool [ default True ]
1386 Whether to allow the value to be None
1390 Whether to allow the value to be None
1387
1391
1388 **metadata : any
1392 **metadata : any
1389 further keys for extensions to the Trait (e.g. config)
1393 further keys for extensions to the Trait (e.g. config)
1390
1394
1391 """
1395 """
1392 # allow List([values]):
1396 # allow List([values]):
1393 if default_value is None and not is_trait(trait):
1397 if default_value is None and not is_trait(trait):
1394 default_value = trait
1398 default_value = trait
1395 trait = None
1399 trait = None
1396
1400
1397 if default_value is None:
1401 if default_value is None:
1398 args = ()
1402 args = ()
1399 elif isinstance(default_value, self._valid_defaults):
1403 elif isinstance(default_value, self._valid_defaults):
1400 args = (default_value,)
1404 args = (default_value,)
1401 else:
1405 else:
1402 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1406 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1403
1407
1404 if is_trait(trait):
1408 if is_trait(trait):
1405 self._trait = trait() if isinstance(trait, type) else trait
1409 self._trait = trait() if isinstance(trait, type) else trait
1406 self._trait.name = 'element'
1410 self._trait.name = 'element'
1407 elif trait is not None:
1411 elif trait is not None:
1408 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1412 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1409
1413
1410 super(Container,self).__init__(klass=self.klass, args=args,
1414 super(Container,self).__init__(klass=self.klass, args=args,
1411 allow_none=allow_none, **metadata)
1415 allow_none=allow_none, **metadata)
1412
1416
1413 def element_error(self, obj, element, validator):
1417 def element_error(self, obj, element, validator):
1414 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1418 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1415 % (self.name, class_of(obj), validator.info(), repr_type(element))
1419 % (self.name, class_of(obj), validator.info(), repr_type(element))
1416 raise TraitError(e)
1420 raise TraitError(e)
1417
1421
1418 def validate(self, obj, value):
1422 def validate(self, obj, value):
1419 if isinstance(value, self._cast_types):
1423 if isinstance(value, self._cast_types):
1420 value = self.klass(value)
1424 value = self.klass(value)
1421 value = super(Container, self).validate(obj, value)
1425 value = super(Container, self).validate(obj, value)
1422 if value is None:
1426 if value is None:
1423 return value
1427 return value
1424
1428
1425 value = self.validate_elements(obj, value)
1429 value = self.validate_elements(obj, value)
1426
1430
1427 return value
1431 return value
1428
1432
1429 def validate_elements(self, obj, value):
1433 def validate_elements(self, obj, value):
1430 validated = []
1434 validated = []
1431 if self._trait is None or isinstance(self._trait, Any):
1435 if self._trait is None or isinstance(self._trait, Any):
1432 return value
1436 return value
1433 for v in value:
1437 for v in value:
1434 try:
1438 try:
1435 v = self._trait._validate(obj, v)
1439 v = self._trait._validate(obj, v)
1436 except TraitError:
1440 except TraitError:
1437 self.element_error(obj, v, self._trait)
1441 self.element_error(obj, v, self._trait)
1438 else:
1442 else:
1439 validated.append(v)
1443 validated.append(v)
1440 return self.klass(validated)
1444 return self.klass(validated)
1441
1445
1442 def instance_init(self, obj):
1446 def instance_init(self, obj):
1443 if isinstance(self._trait, TraitType):
1447 if isinstance(self._trait, TraitType):
1444 self._trait.this_class = self.this_class
1448 self._trait.this_class = self.this_class
1445 if hasattr(self._trait, 'instance_init'):
1449 if hasattr(self._trait, 'instance_init'):
1446 self._trait.instance_init(obj)
1450 self._trait.instance_init(obj)
1447 super(Container, self).instance_init(obj)
1451 super(Container, self).instance_init(obj)
1448
1452
1449
1453
1450 class List(Container):
1454 class List(Container):
1451 """An instance of a Python list."""
1455 """An instance of a Python list."""
1452 klass = list
1456 klass = list
1453 _cast_types = (tuple,)
1457 _cast_types = (tuple,)
1454
1458
1455 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1459 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1456 allow_none=True, **metadata):
1460 allow_none=True, **metadata):
1457 """Create a List trait type from a list, set, or tuple.
1461 """Create a List trait type from a list, set, or tuple.
1458
1462
1459 The default value is created by doing ``List(default_value)``,
1463 The default value is created by doing ``List(default_value)``,
1460 which creates a copy of the ``default_value``.
1464 which creates a copy of the ``default_value``.
1461
1465
1462 ``trait`` can be specified, which restricts the type of elements
1466 ``trait`` can be specified, which restricts the type of elements
1463 in the container to that TraitType.
1467 in the container to that TraitType.
1464
1468
1465 If only one arg is given and it is not a Trait, it is taken as
1469 If only one arg is given and it is not a Trait, it is taken as
1466 ``default_value``:
1470 ``default_value``:
1467
1471
1468 ``c = List([1,2,3])``
1472 ``c = List([1,2,3])``
1469
1473
1470 Parameters
1474 Parameters
1471 ----------
1475 ----------
1472
1476
1473 trait : TraitType [ optional ]
1477 trait : TraitType [ optional ]
1474 the type for restricting the contents of the Container. If unspecified,
1478 the type for restricting the contents of the Container. If unspecified,
1475 types are not checked.
1479 types are not checked.
1476
1480
1477 default_value : SequenceType [ optional ]
1481 default_value : SequenceType [ optional ]
1478 The default value for the Trait. Must be list/tuple/set, and
1482 The default value for the Trait. Must be list/tuple/set, and
1479 will be cast to the container type.
1483 will be cast to the container type.
1480
1484
1481 minlen : Int [ default 0 ]
1485 minlen : Int [ default 0 ]
1482 The minimum length of the input list
1486 The minimum length of the input list
1483
1487
1484 maxlen : Int [ default sys.maxsize ]
1488 maxlen : Int [ default sys.maxsize ]
1485 The maximum length of the input list
1489 The maximum length of the input list
1486
1490
1487 allow_none : Bool [ default True ]
1491 allow_none : Bool [ default True ]
1488 Whether to allow the value to be None
1492 Whether to allow the value to be None
1489
1493
1490 **metadata : any
1494 **metadata : any
1491 further keys for extensions to the Trait (e.g. config)
1495 further keys for extensions to the Trait (e.g. config)
1492
1496
1493 """
1497 """
1494 self._minlen = minlen
1498 self._minlen = minlen
1495 self._maxlen = maxlen
1499 self._maxlen = maxlen
1496 super(List, self).__init__(trait=trait, default_value=default_value,
1500 super(List, self).__init__(trait=trait, default_value=default_value,
1497 allow_none=allow_none, **metadata)
1501 allow_none=allow_none, **metadata)
1498
1502
1499 def length_error(self, obj, value):
1503 def length_error(self, obj, value):
1500 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1504 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1501 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1505 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1502 raise TraitError(e)
1506 raise TraitError(e)
1503
1507
1504 def validate_elements(self, obj, value):
1508 def validate_elements(self, obj, value):
1505 length = len(value)
1509 length = len(value)
1506 if length < self._minlen or length > self._maxlen:
1510 if length < self._minlen or length > self._maxlen:
1507 self.length_error(obj, value)
1511 self.length_error(obj, value)
1508
1512
1509 return super(List, self).validate_elements(obj, value)
1513 return super(List, self).validate_elements(obj, value)
1510
1514
1511 def validate(self, obj, value):
1515 def validate(self, obj, value):
1512 value = super(List, self).validate(obj, value)
1516 value = super(List, self).validate(obj, value)
1513
1517
1514 value = self.validate_elements(obj, value)
1518 value = self.validate_elements(obj, value)
1515
1519
1516 return value
1520 return value
1517
1521
1518
1522
1519
1523
1520 class Set(List):
1524 class Set(List):
1521 """An instance of a Python set."""
1525 """An instance of a Python set."""
1522 klass = set
1526 klass = set
1523 _cast_types = (tuple, list)
1527 _cast_types = (tuple, list)
1524
1528
1525 class Tuple(Container):
1529 class Tuple(Container):
1526 """An instance of a Python tuple."""
1530 """An instance of a Python tuple."""
1527 klass = tuple
1531 klass = tuple
1528 _cast_types = (list,)
1532 _cast_types = (list,)
1529
1533
1530 def __init__(self, *traits, **metadata):
1534 def __init__(self, *traits, **metadata):
1531 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1535 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1532
1536
1533 Create a tuple from a list, set, or tuple.
1537 Create a tuple from a list, set, or tuple.
1534
1538
1535 Create a fixed-type tuple with Traits:
1539 Create a fixed-type tuple with Traits:
1536
1540
1537 ``t = Tuple(Int, Str, CStr)``
1541 ``t = Tuple(Int, Str, CStr)``
1538
1542
1539 would be length 3, with Int,Str,CStr for each element.
1543 would be length 3, with Int,Str,CStr for each element.
1540
1544
1541 If only one arg is given and it is not a Trait, it is taken as
1545 If only one arg is given and it is not a Trait, it is taken as
1542 default_value:
1546 default_value:
1543
1547
1544 ``t = Tuple((1,2,3))``
1548 ``t = Tuple((1,2,3))``
1545
1549
1546 Otherwise, ``default_value`` *must* be specified by keyword.
1550 Otherwise, ``default_value`` *must* be specified by keyword.
1547
1551
1548 Parameters
1552 Parameters
1549 ----------
1553 ----------
1550
1554
1551 *traits : TraitTypes [ optional ]
1555 *traits : TraitTypes [ optional ]
1552 the tsype for restricting the contents of the Tuple. If unspecified,
1556 the tsype for restricting the contents of the Tuple. If unspecified,
1553 types are not checked. If specified, then each positional argument
1557 types are not checked. If specified, then each positional argument
1554 corresponds to an element of the tuple. Tuples defined with traits
1558 corresponds to an element of the tuple. Tuples defined with traits
1555 are of fixed length.
1559 are of fixed length.
1556
1560
1557 default_value : SequenceType [ optional ]
1561 default_value : SequenceType [ optional ]
1558 The default value for the Tuple. Must be list/tuple/set, and
1562 The default value for the Tuple. Must be list/tuple/set, and
1559 will be cast to a tuple. If `traits` are specified, the
1563 will be cast to a tuple. If `traits` are specified, the
1560 `default_value` must conform to the shape and type they specify.
1564 `default_value` must conform to the shape and type they specify.
1561
1565
1562 allow_none : Bool [ default True ]
1566 allow_none : Bool [ default True ]
1563 Whether to allow the value to be None
1567 Whether to allow the value to be None
1564
1568
1565 **metadata : any
1569 **metadata : any
1566 further keys for extensions to the Trait (e.g. config)
1570 further keys for extensions to the Trait (e.g. config)
1567
1571
1568 """
1572 """
1569 default_value = metadata.pop('default_value', None)
1573 default_value = metadata.pop('default_value', None)
1570 allow_none = metadata.pop('allow_none', True)
1574 allow_none = metadata.pop('allow_none', True)
1571
1575
1572 # allow Tuple((values,)):
1576 # allow Tuple((values,)):
1573 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1577 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1574 default_value = traits[0]
1578 default_value = traits[0]
1575 traits = ()
1579 traits = ()
1576
1580
1577 if default_value is None:
1581 if default_value is None:
1578 args = ()
1582 args = ()
1579 elif isinstance(default_value, self._valid_defaults):
1583 elif isinstance(default_value, self._valid_defaults):
1580 args = (default_value,)
1584 args = (default_value,)
1581 else:
1585 else:
1582 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1586 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1583
1587
1584 self._traits = []
1588 self._traits = []
1585 for trait in traits:
1589 for trait in traits:
1586 t = trait() if isinstance(trait, type) else trait
1590 t = trait() if isinstance(trait, type) else trait
1587 t.name = 'element'
1591 t.name = 'element'
1588 self._traits.append(t)
1592 self._traits.append(t)
1589
1593
1590 if self._traits and default_value is None:
1594 if self._traits and default_value is None:
1591 # don't allow default to be an empty container if length is specified
1595 # don't allow default to be an empty container if length is specified
1592 args = None
1596 args = None
1593 super(Container,self).__init__(klass=self.klass, args=args,
1597 super(Container,self).__init__(klass=self.klass, args=args,
1594 allow_none=allow_none, **metadata)
1598 allow_none=allow_none, **metadata)
1595
1599
1596 def validate_elements(self, obj, value):
1600 def validate_elements(self, obj, value):
1597 if not self._traits:
1601 if not self._traits:
1598 # nothing to validate
1602 # nothing to validate
1599 return value
1603 return value
1600 if len(value) != len(self._traits):
1604 if len(value) != len(self._traits):
1601 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1605 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1602 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1606 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1603 raise TraitError(e)
1607 raise TraitError(e)
1604
1608
1605 validated = []
1609 validated = []
1606 for t,v in zip(self._traits, value):
1610 for t,v in zip(self._traits, value):
1607 try:
1611 try:
1608 v = t._validate(obj, v)
1612 v = t._validate(obj, v)
1609 except TraitError:
1613 except TraitError:
1610 self.element_error(obj, v, t)
1614 self.element_error(obj, v, t)
1611 else:
1615 else:
1612 validated.append(v)
1616 validated.append(v)
1613 return tuple(validated)
1617 return tuple(validated)
1614
1618
1615
1619
1616 class Dict(Instance):
1620 class Dict(Instance):
1617 """An instance of a Python dict."""
1621 """An instance of a Python dict."""
1618
1622
1619 def __init__(self, default_value=None, allow_none=True, **metadata):
1623 def __init__(self, default_value=None, allow_none=True, **metadata):
1620 """Create a dict trait type from a dict.
1624 """Create a dict trait type from a dict.
1621
1625
1622 The default value is created by doing ``dict(default_value)``,
1626 The default value is created by doing ``dict(default_value)``,
1623 which creates a copy of the ``default_value``.
1627 which creates a copy of the ``default_value``.
1624 """
1628 """
1625 if default_value is None:
1629 if default_value is None:
1626 args = ((),)
1630 args = ((),)
1627 elif isinstance(default_value, dict):
1631 elif isinstance(default_value, dict):
1628 args = (default_value,)
1632 args = (default_value,)
1629 elif isinstance(default_value, SequenceTypes):
1633 elif isinstance(default_value, SequenceTypes):
1630 args = (default_value,)
1634 args = (default_value,)
1631 else:
1635 else:
1632 raise TypeError('default value of Dict was %s' % default_value)
1636 raise TypeError('default value of Dict was %s' % default_value)
1633
1637
1634 super(Dict,self).__init__(klass=dict, args=args,
1638 super(Dict,self).__init__(klass=dict, args=args,
1635 allow_none=allow_none, **metadata)
1639 allow_none=allow_none, **metadata)
1636
1640
1637
1641
1638 class EventfulDict(Instance):
1642 class EventfulDict(Instance):
1639 """An instance of an EventfulDict."""
1643 """An instance of an EventfulDict."""
1640
1644
1641 def __init__(self, default_value=None, allow_none=True, **metadata):
1645 def __init__(self, default_value=None, allow_none=True, **metadata):
1642 """Create a EventfulDict trait type from a dict.
1646 """Create a EventfulDict trait type from a dict.
1643
1647
1644 The default value is created by doing
1648 The default value is created by doing
1645 ``eventful.EvenfulDict(default_value)``, which creates a copy of the
1649 ``eventful.EvenfulDict(default_value)``, which creates a copy of the
1646 ``default_value``.
1650 ``default_value``.
1647 """
1651 """
1648 if default_value is None:
1652 if default_value is None:
1649 args = ((),)
1653 args = ((),)
1650 elif isinstance(default_value, dict):
1654 elif isinstance(default_value, dict):
1651 args = (default_value,)
1655 args = (default_value,)
1652 elif isinstance(default_value, SequenceTypes):
1656 elif isinstance(default_value, SequenceTypes):
1653 args = (default_value,)
1657 args = (default_value,)
1654 else:
1658 else:
1655 raise TypeError('default value of EventfulDict was %s' % default_value)
1659 raise TypeError('default value of EventfulDict was %s' % default_value)
1656
1660
1657 super(EventfulDict, self).__init__(klass=eventful.EventfulDict, args=args,
1661 super(EventfulDict, self).__init__(klass=eventful.EventfulDict, args=args,
1658 allow_none=allow_none, **metadata)
1662 allow_none=allow_none, **metadata)
1659
1663
1660
1664
1661 class EventfulList(Instance):
1665 class EventfulList(Instance):
1662 """An instance of an EventfulList."""
1666 """An instance of an EventfulList."""
1663
1667
1664 def __init__(self, default_value=None, allow_none=True, **metadata):
1668 def __init__(self, default_value=None, allow_none=True, **metadata):
1665 """Create a EventfulList trait type from a dict.
1669 """Create a EventfulList trait type from a dict.
1666
1670
1667 The default value is created by doing
1671 The default value is created by doing
1668 ``eventful.EvenfulList(default_value)``, which creates a copy of the
1672 ``eventful.EvenfulList(default_value)``, which creates a copy of the
1669 ``default_value``.
1673 ``default_value``.
1670 """
1674 """
1671 if default_value is None:
1675 if default_value is None:
1672 args = ((),)
1676 args = ((),)
1673 else:
1677 else:
1674 args = (default_value,)
1678 args = (default_value,)
1675
1679
1676 super(EventfulList, self).__init__(klass=eventful.EventfulList, args=args,
1680 super(EventfulList, self).__init__(klass=eventful.EventfulList, args=args,
1677 allow_none=allow_none, **metadata)
1681 allow_none=allow_none, **metadata)
1678
1682
1679
1683
1680 class TCPAddress(TraitType):
1684 class TCPAddress(TraitType):
1681 """A trait for an (ip, port) tuple.
1685 """A trait for an (ip, port) tuple.
1682
1686
1683 This allows for both IPv4 IP addresses as well as hostnames.
1687 This allows for both IPv4 IP addresses as well as hostnames.
1684 """
1688 """
1685
1689
1686 default_value = ('127.0.0.1', 0)
1690 default_value = ('127.0.0.1', 0)
1687 info_text = 'an (ip, port) tuple'
1691 info_text = 'an (ip, port) tuple'
1688
1692
1689 def validate(self, obj, value):
1693 def validate(self, obj, value):
1690 if isinstance(value, tuple):
1694 if isinstance(value, tuple):
1691 if len(value) == 2:
1695 if len(value) == 2:
1692 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1696 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1693 port = value[1]
1697 port = value[1]
1694 if port >= 0 and port <= 65535:
1698 if port >= 0 and port <= 65535:
1695 return value
1699 return value
1696 self.error(obj, value)
1700 self.error(obj, value)
1697
1701
1698 class CRegExp(TraitType):
1702 class CRegExp(TraitType):
1699 """A casting compiled regular expression trait.
1703 """A casting compiled regular expression trait.
1700
1704
1701 Accepts both strings and compiled regular expressions. The resulting
1705 Accepts both strings and compiled regular expressions. The resulting
1702 attribute will be a compiled regular expression."""
1706 attribute will be a compiled regular expression."""
1703
1707
1704 info_text = 'a regular expression'
1708 info_text = 'a regular expression'
1705
1709
1706 def validate(self, obj, value):
1710 def validate(self, obj, value):
1707 try:
1711 try:
1708 return re.compile(value)
1712 return re.compile(value)
1709 except:
1713 except:
1710 self.error(obj, value)
1714 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now