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