##// END OF EJS Templates
Copy IPython-configurable with the same hierarchy
Matthias BUSSONNIER -
Show More
@@ -0,0 +1,356 b''
1 # encoding: utf-8
2 """
3 A base class for objects that are configurable.
4
5 Inheritance diagram:
6
7 .. inheritance-diagram:: IPython.config.configurable
8 :parts: 3
9
10 Authors:
11
12 * Brian Granger
13 * Fernando Perez
14 * Min RK
15 """
16
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2008-2011 The IPython Development Team
19 #
20 # Distributed under the terms of the BSD License. The full license is in
21 # the file COPYING, distributed as part of this software.
22 #-----------------------------------------------------------------------------
23
24 #-----------------------------------------------------------------------------
25 # Imports
26 #-----------------------------------------------------------------------------
27
28 import datetime
29 from copy import deepcopy
30
31 from loader import Config
32 from IPython.utils.traitlets import HasTraits, Instance
33 from IPython.utils.text import indent, wrap_paragraphs
34
35
36 #-----------------------------------------------------------------------------
37 # Helper classes for Configurables
38 #-----------------------------------------------------------------------------
39
40
41 class ConfigurableError(Exception):
42 pass
43
44
45 class MultipleInstanceError(ConfigurableError):
46 pass
47
48 #-----------------------------------------------------------------------------
49 # Configurable implementation
50 #-----------------------------------------------------------------------------
51
52 class Configurable(HasTraits):
53
54 config = Instance(Config,(),{})
55 created = None
56
57 def __init__(self, **kwargs):
58 """Create a configurable given a config config.
59
60 Parameters
61 ----------
62 config : Config
63 If this is empty, default values are used. If config is a
64 :class:`Config` instance, it will be used to configure the
65 instance.
66
67 Notes
68 -----
69 Subclasses of Configurable must call the :meth:`__init__` method of
70 :class:`Configurable` *before* doing anything else and using
71 :func:`super`::
72
73 class MyConfigurable(Configurable):
74 def __init__(self, config=None):
75 super(MyConfigurable, self).__init__(config)
76 # Then any other code you need to finish initialization.
77
78 This ensures that instances will be configured properly.
79 """
80 config = kwargs.pop('config', None)
81 if config is not None:
82 # We used to deepcopy, but for now we are trying to just save
83 # by reference. This *could* have side effects as all components
84 # will share config. In fact, I did find such a side effect in
85 # _config_changed below. If a config attribute value was a mutable type
86 # all instances of a component were getting the same copy, effectively
87 # making that a class attribute.
88 # self.config = deepcopy(config)
89 self.config = config
90 # This should go second so individual keyword arguments override
91 # the values in config.
92 super(Configurable, self).__init__(**kwargs)
93 self.created = datetime.datetime.now()
94
95 #-------------------------------------------------------------------------
96 # Static trait notifiations
97 #-------------------------------------------------------------------------
98
99 def _config_changed(self, name, old, new):
100 """Update all the class traits having ``config=True`` as metadata.
101
102 For any class trait with a ``config`` metadata attribute that is
103 ``True``, we update the trait with the value of the corresponding
104 config entry.
105 """
106 # Get all traits with a config metadata entry that is True
107 traits = self.traits(config=True)
108
109 # We auto-load config section for this class as well as any parent
110 # classes that are Configurable subclasses. This starts with Configurable
111 # and works down the mro loading the config for each section.
112 section_names = [cls.__name__ for cls in \
113 reversed(self.__class__.__mro__) if
114 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
115
116 for sname in section_names:
117 # Don't do a blind getattr as that would cause the config to
118 # dynamically create the section with name self.__class__.__name__.
119 if new._has_section(sname):
120 my_config = new[sname]
121 for k, v in traits.iteritems():
122 # Don't allow traitlets with config=True to start with
123 # uppercase. Otherwise, they are confused with Config
124 # subsections. But, developers shouldn't have uppercase
125 # attributes anyways! (PEP 6)
126 if k[0].upper()==k[0] and not k.startswith('_'):
127 raise ConfigurableError('Configurable traitlets with '
128 'config=True must start with a lowercase so they are '
129 'not confused with Config subsections: %s.%s' % \
130 (self.__class__.__name__, k))
131 try:
132 # Here we grab the value from the config
133 # If k has the naming convention of a config
134 # section, it will be auto created.
135 config_value = my_config[k]
136 except KeyError:
137 pass
138 else:
139 # print "Setting %s.%s from %s.%s=%r" % \
140 # (self.__class__.__name__,k,sname,k,config_value)
141 # We have to do a deepcopy here if we don't deepcopy the entire
142 # config object. If we don't, a mutable config_value will be
143 # shared by all instances, effectively making it a class attribute.
144 setattr(self, k, deepcopy(config_value))
145
146 def update_config(self, config):
147 """Fire the traits events when the config is updated."""
148 # Save a copy of the current config.
149 newconfig = deepcopy(self.config)
150 # Merge the new config into the current one.
151 newconfig._merge(config)
152 # Save the combined config as self.config, which triggers the traits
153 # events.
154 self.config = newconfig
155
156 @classmethod
157 def class_get_help(cls, inst=None):
158 """Get the help string for this class in ReST format.
159
160 If `inst` is given, it's current trait values will be used in place of
161 class defaults.
162 """
163 assert inst is None or isinstance(inst, cls)
164 cls_traits = cls.class_traits(config=True)
165 final_help = []
166 final_help.append(u'%s options' % cls.__name__)
167 final_help.append(len(final_help[0])*u'-')
168 for k,v in sorted(cls.class_traits(config=True).iteritems()):
169 help = cls.class_get_trait_help(v, inst)
170 final_help.append(help)
171 return '\n'.join(final_help)
172
173 @classmethod
174 def class_get_trait_help(cls, trait, inst=None):
175 """Get the help string for a single trait.
176
177 If `inst` is given, it's current trait values will be used in place of
178 the class default.
179 """
180 assert inst is None or isinstance(inst, cls)
181 lines = []
182 header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
183 lines.append(header)
184 if inst is not None:
185 lines.append(indent('Current: %r' % getattr(inst, trait.name), 4))
186 else:
187 try:
188 dvr = repr(trait.get_default_value())
189 except Exception:
190 dvr = None # ignore defaults we can't construct
191 if dvr is not None:
192 if len(dvr) > 64:
193 dvr = dvr[:61]+'...'
194 lines.append(indent('Default: %s' % dvr, 4))
195 if 'Enum' in trait.__class__.__name__:
196 # include Enum choices
197 lines.append(indent('Choices: %r' % (trait.values,)))
198
199 help = trait.get_metadata('help')
200 if help is not None:
201 help = '\n'.join(wrap_paragraphs(help, 76))
202 lines.append(indent(help, 4))
203 return '\n'.join(lines)
204
205 @classmethod
206 def class_print_help(cls, inst=None):
207 """Get the help string for a single trait and print it."""
208 print cls.class_get_help(inst)
209
210 @classmethod
211 def class_config_section(cls):
212 """Get the config class config section"""
213 def c(s):
214 """return a commented, wrapped block."""
215 s = '\n\n'.join(wrap_paragraphs(s, 78))
216
217 return '# ' + s.replace('\n', '\n# ')
218
219 # section header
220 breaker = '#' + '-'*78
221 s = "# %s configuration"%cls.__name__
222 lines = [breaker, s, breaker, '']
223 # get the description trait
224 desc = cls.class_traits().get('description')
225 if desc:
226 desc = desc.default_value
227 else:
228 # no description trait, use __doc__
229 desc = getattr(cls, '__doc__', '')
230 if desc:
231 lines.append(c(desc))
232 lines.append('')
233
234 parents = []
235 for parent in cls.mro():
236 # only include parents that are not base classes
237 # and are not the class itself
238 # and have some configurable traits to inherit
239 if parent is not cls and issubclass(parent, Configurable) and \
240 parent.class_traits(config=True):
241 parents.append(parent)
242
243 if parents:
244 pstr = ', '.join([ p.__name__ for p in parents ])
245 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
246 lines.append('')
247
248 for name,trait in cls.class_traits(config=True).iteritems():
249 help = trait.get_metadata('help') or ''
250 lines.append(c(help))
251 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
252 lines.append('')
253 return '\n'.join(lines)
254
255
256
257 class SingletonConfigurable(Configurable):
258 """A configurable that only allows one instance.
259
260 This class is for classes that should only have one instance of itself
261 or *any* subclass. To create and retrieve such a class use the
262 :meth:`SingletonConfigurable.instance` method.
263 """
264
265 _instance = None
266
267 @classmethod
268 def _walk_mro(cls):
269 """Walk the cls.mro() for parent classes that are also singletons
270
271 For use in instance()
272 """
273
274 for subclass in cls.mro():
275 if issubclass(cls, subclass) and \
276 issubclass(subclass, SingletonConfigurable) and \
277 subclass != SingletonConfigurable:
278 yield subclass
279
280 @classmethod
281 def clear_instance(cls):
282 """unset _instance for this class and singleton parents.
283 """
284 if not cls.initialized():
285 return
286 for subclass in cls._walk_mro():
287 if isinstance(subclass._instance, cls):
288 # only clear instances that are instances
289 # of the calling class
290 subclass._instance = None
291
292 @classmethod
293 def instance(cls, *args, **kwargs):
294 """Returns a global instance of this class.
295
296 This method create a new instance if none have previously been created
297 and returns a previously created instance is one already exists.
298
299 The arguments and keyword arguments passed to this method are passed
300 on to the :meth:`__init__` method of the class upon instantiation.
301
302 Examples
303 --------
304
305 Create a singleton class using instance, and retrieve it::
306
307 >>> from IPython.config.configurable import SingletonConfigurable
308 >>> class Foo(SingletonConfigurable): pass
309 >>> foo = Foo.instance()
310 >>> foo == Foo.instance()
311 True
312
313 Create a subclass that is retrived using the base class instance::
314
315 >>> class Bar(SingletonConfigurable): pass
316 >>> class Bam(Bar): pass
317 >>> bam = Bam.instance()
318 >>> bam == Bar.instance()
319 True
320 """
321 # Create and save the instance
322 if cls._instance is None:
323 inst = cls(*args, **kwargs)
324 # Now make sure that the instance will also be returned by
325 # parent classes' _instance attribute.
326 for subclass in cls._walk_mro():
327 subclass._instance = inst
328
329 if isinstance(cls._instance, cls):
330 return cls._instance
331 else:
332 raise MultipleInstanceError(
333 'Multiple incompatible subclass instances of '
334 '%s are being created.' % cls.__name__
335 )
336
337 @classmethod
338 def initialized(cls):
339 """Has an instance been created?"""
340 return hasattr(cls, "_instance") and cls._instance is not None
341
342
343 class LoggingConfigurable(Configurable):
344 """A parent class for Configurables that log.
345
346 Subclasses have a log trait, and the default behavior
347 is to get the logger from the currently running Application
348 via Application.instance().log.
349 """
350
351 log = Instance('logging.Logger')
352 def _log_default(self):
353 from IPython.config.application import Application
354 return Application.instance().log
355
356
This diff has been collapsed as it changes many lines, (850 lines changed) Show them Hide them
@@ -0,0 +1,850 b''
1 # encoding: utf-8
2 """
3 Utilities for working with strings and text.
4
5 Inheritance diagram:
6
7 .. inheritance-diagram:: IPython.utils.text
8 :parts: 3
9 """
10
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
17
18 #-----------------------------------------------------------------------------
19 # Imports
20 #-----------------------------------------------------------------------------
21
22 import __main__
23
24 import os
25 import re
26 import shutil
27 import sys
28 import textwrap
29 from string import Formatter
30
31 from IPython.external.path import path
32 from IPython.testing.skipdoctest import skip_doctest_py3, skip_doctest
33 from IPython.utils import py3compat
34 from IPython.utils.io import nlprint
35 from IPython.utils.data import flatten
36
37 #-----------------------------------------------------------------------------
38 # Code
39 #-----------------------------------------------------------------------------
40
41 def unquote_ends(istr):
42 """Remove a single pair of quotes from the endpoints of a string."""
43
44 if not istr:
45 return istr
46 if (istr[0]=="'" and istr[-1]=="'") or \
47 (istr[0]=='"' and istr[-1]=='"'):
48 return istr[1:-1]
49 else:
50 return istr
51
52
53 class LSString(str):
54 """String derivative with a special access attributes.
55
56 These are normal strings, but with the special attributes:
57
58 .l (or .list) : value as list (split on newlines).
59 .n (or .nlstr): original value (the string itself).
60 .s (or .spstr): value as whitespace-separated string.
61 .p (or .paths): list of path objects
62
63 Any values which require transformations are computed only once and
64 cached.
65
66 Such strings are very useful to efficiently interact with the shell, which
67 typically only understands whitespace-separated options for commands."""
68
69 def get_list(self):
70 try:
71 return self.__list
72 except AttributeError:
73 self.__list = self.split('\n')
74 return self.__list
75
76 l = list = property(get_list)
77
78 def get_spstr(self):
79 try:
80 return self.__spstr
81 except AttributeError:
82 self.__spstr = self.replace('\n',' ')
83 return self.__spstr
84
85 s = spstr = property(get_spstr)
86
87 def get_nlstr(self):
88 return self
89
90 n = nlstr = property(get_nlstr)
91
92 def get_paths(self):
93 try:
94 return self.__paths
95 except AttributeError:
96 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
97 return self.__paths
98
99 p = paths = property(get_paths)
100
101 # FIXME: We need to reimplement type specific displayhook and then add this
102 # back as a custom printer. This should also be moved outside utils into the
103 # core.
104
105 # def print_lsstring(arg):
106 # """ Prettier (non-repr-like) and more informative printer for LSString """
107 # print "LSString (.p, .n, .l, .s available). Value:"
108 # print arg
109 #
110 #
111 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
112
113
114 class SList(list):
115 """List derivative with a special access attributes.
116
117 These are normal lists, but with the special attributes:
118
119 .l (or .list) : value as list (the list itself).
120 .n (or .nlstr): value as a string, joined on newlines.
121 .s (or .spstr): value as a string, joined on spaces.
122 .p (or .paths): list of path objects
123
124 Any values which require transformations are computed only once and
125 cached."""
126
127 def get_list(self):
128 return self
129
130 l = list = property(get_list)
131
132 def get_spstr(self):
133 try:
134 return self.__spstr
135 except AttributeError:
136 self.__spstr = ' '.join(self)
137 return self.__spstr
138
139 s = spstr = property(get_spstr)
140
141 def get_nlstr(self):
142 try:
143 return self.__nlstr
144 except AttributeError:
145 self.__nlstr = '\n'.join(self)
146 return self.__nlstr
147
148 n = nlstr = property(get_nlstr)
149
150 def get_paths(self):
151 try:
152 return self.__paths
153 except AttributeError:
154 self.__paths = [path(p) for p in self if os.path.exists(p)]
155 return self.__paths
156
157 p = paths = property(get_paths)
158
159 def grep(self, pattern, prune = False, field = None):
160 """ Return all strings matching 'pattern' (a regex or callable)
161
162 This is case-insensitive. If prune is true, return all items
163 NOT matching the pattern.
164
165 If field is specified, the match must occur in the specified
166 whitespace-separated field.
167
168 Examples::
169
170 a.grep( lambda x: x.startswith('C') )
171 a.grep('Cha.*log', prune=1)
172 a.grep('chm', field=-1)
173 """
174
175 def match_target(s):
176 if field is None:
177 return s
178 parts = s.split()
179 try:
180 tgt = parts[field]
181 return tgt
182 except IndexError:
183 return ""
184
185 if isinstance(pattern, basestring):
186 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
187 else:
188 pred = pattern
189 if not prune:
190 return SList([el for el in self if pred(match_target(el))])
191 else:
192 return SList([el for el in self if not pred(match_target(el))])
193
194 def fields(self, *fields):
195 """ Collect whitespace-separated fields from string list
196
197 Allows quick awk-like usage of string lists.
198
199 Example data (in var a, created by 'a = !ls -l')::
200 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
201 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
202
203 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
204 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
205 (note the joining by space).
206 a.fields(-1) is ['ChangeLog', 'IPython']
207
208 IndexErrors are ignored.
209
210 Without args, fields() just split()'s the strings.
211 """
212 if len(fields) == 0:
213 return [el.split() for el in self]
214
215 res = SList()
216 for el in [f.split() for f in self]:
217 lineparts = []
218
219 for fd in fields:
220 try:
221 lineparts.append(el[fd])
222 except IndexError:
223 pass
224 if lineparts:
225 res.append(" ".join(lineparts))
226
227 return res
228
229 def sort(self,field= None, nums = False):
230 """ sort by specified fields (see fields())
231
232 Example::
233 a.sort(1, nums = True)
234
235 Sorts a by second field, in numerical order (so that 21 > 3)
236
237 """
238
239 #decorate, sort, undecorate
240 if field is not None:
241 dsu = [[SList([line]).fields(field), line] for line in self]
242 else:
243 dsu = [[line, line] for line in self]
244 if nums:
245 for i in range(len(dsu)):
246 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
247 try:
248 n = int(numstr)
249 except ValueError:
250 n = 0;
251 dsu[i][0] = n
252
253
254 dsu.sort()
255 return SList([t[1] for t in dsu])
256
257
258 # FIXME: We need to reimplement type specific displayhook and then add this
259 # back as a custom printer. This should also be moved outside utils into the
260 # core.
261
262 # def print_slist(arg):
263 # """ Prettier (non-repr-like) and more informative printer for SList """
264 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
265 # if hasattr(arg, 'hideonce') and arg.hideonce:
266 # arg.hideonce = False
267 # return
268 #
269 # nlprint(arg)
270 #
271 # print_slist = result_display.when_type(SList)(print_slist)
272
273
274 def esc_quotes(strng):
275 """Return the input string with single and double quotes escaped out"""
276
277 return strng.replace('"','\\"').replace("'","\\'")
278
279
280 def qw(words,flat=0,sep=None,maxsplit=-1):
281 """Similar to Perl's qw() operator, but with some more options.
282
283 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
284
285 words can also be a list itself, and with flat=1, the output will be
286 recursively flattened.
287
288 Examples:
289
290 >>> qw('1 2')
291 ['1', '2']
292
293 >>> qw(['a b','1 2',['m n','p q']])
294 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
295
296 >>> qw(['a b','1 2',['m n','p q']],flat=1)
297 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
298 """
299
300 if isinstance(words, basestring):
301 return [word.strip() for word in words.split(sep,maxsplit)
302 if word and not word.isspace() ]
303 if flat:
304 return flatten(map(qw,words,[1]*len(words)))
305 return map(qw,words)
306
307
308 def qwflat(words,sep=None,maxsplit=-1):
309 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
310 return qw(words,1,sep,maxsplit)
311
312
313 def qw_lol(indata):
314 """qw_lol('a b') -> [['a','b']],
315 otherwise it's just a call to qw().
316
317 We need this to make sure the modules_some keys *always* end up as a
318 list of lists."""
319
320 if isinstance(indata, basestring):
321 return [qw(indata)]
322 else:
323 return qw(indata)
324
325
326 def grep(pat,list,case=1):
327 """Simple minded grep-like function.
328 grep(pat,list) returns occurrences of pat in list, None on failure.
329
330 It only does simple string matching, with no support for regexps. Use the
331 option case=0 for case-insensitive matching."""
332
333 # This is pretty crude. At least it should implement copying only references
334 # to the original data in case it's big. Now it copies the data for output.
335 out=[]
336 if case:
337 for term in list:
338 if term.find(pat)>-1: out.append(term)
339 else:
340 lpat=pat.lower()
341 for term in list:
342 if term.lower().find(lpat)>-1: out.append(term)
343
344 if len(out): return out
345 else: return None
346
347
348 def dgrep(pat,*opts):
349 """Return grep() on dir()+dir(__builtins__).
350
351 A very common use of grep() when working interactively."""
352
353 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
354
355
356 def idgrep(pat):
357 """Case-insensitive dgrep()"""
358
359 return dgrep(pat,0)
360
361
362 def igrep(pat,list):
363 """Synonym for case-insensitive grep."""
364
365 return grep(pat,list,case=0)
366
367
368 def indent(instr,nspaces=4, ntabs=0, flatten=False):
369 """Indent a string a given number of spaces or tabstops.
370
371 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
372
373 Parameters
374 ----------
375
376 instr : basestring
377 The string to be indented.
378 nspaces : int (default: 4)
379 The number of spaces to be indented.
380 ntabs : int (default: 0)
381 The number of tabs to be indented.
382 flatten : bool (default: False)
383 Whether to scrub existing indentation. If True, all lines will be
384 aligned to the same indentation. If False, existing indentation will
385 be strictly increased.
386
387 Returns
388 -------
389
390 str|unicode : string indented by ntabs and nspaces.
391
392 """
393 if instr is None:
394 return
395 ind = '\t'*ntabs+' '*nspaces
396 if flatten:
397 pat = re.compile(r'^\s*', re.MULTILINE)
398 else:
399 pat = re.compile(r'^', re.MULTILINE)
400 outstr = re.sub(pat, ind, instr)
401 if outstr.endswith(os.linesep+ind):
402 return outstr[:-len(ind)]
403 else:
404 return outstr
405
406 def native_line_ends(filename,backup=1):
407 """Convert (in-place) a file to line-ends native to the current OS.
408
409 If the optional backup argument is given as false, no backup of the
410 original file is left. """
411
412 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
413
414 bak_filename = filename + backup_suffixes[os.name]
415
416 original = open(filename).read()
417 shutil.copy2(filename,bak_filename)
418 try:
419 new = open(filename,'wb')
420 new.write(os.linesep.join(original.splitlines()))
421 new.write(os.linesep) # ALWAYS put an eol at the end of the file
422 new.close()
423 except:
424 os.rename(bak_filename,filename)
425 if not backup:
426 try:
427 os.remove(bak_filename)
428 except:
429 pass
430
431
432 def list_strings(arg):
433 """Always return a list of strings, given a string or list of strings
434 as input.
435
436 :Examples:
437
438 In [7]: list_strings('A single string')
439 Out[7]: ['A single string']
440
441 In [8]: list_strings(['A single string in a list'])
442 Out[8]: ['A single string in a list']
443
444 In [9]: list_strings(['A','list','of','strings'])
445 Out[9]: ['A', 'list', 'of', 'strings']
446 """
447
448 if isinstance(arg,basestring): return [arg]
449 else: return arg
450
451
452 def marquee(txt='',width=78,mark='*'):
453 """Return the input string centered in a 'marquee'.
454
455 :Examples:
456
457 In [16]: marquee('A test',40)
458 Out[16]: '**************** A test ****************'
459
460 In [17]: marquee('A test',40,'-')
461 Out[17]: '---------------- A test ----------------'
462
463 In [18]: marquee('A test',40,' ')
464 Out[18]: ' A test '
465
466 """
467 if not txt:
468 return (mark*width)[:width]
469 nmark = (width-len(txt)-2)//len(mark)//2
470 if nmark < 0: nmark =0
471 marks = mark*nmark
472 return '%s %s %s' % (marks,txt,marks)
473
474
475 ini_spaces_re = re.compile(r'^(\s+)')
476
477 def num_ini_spaces(strng):
478 """Return the number of initial spaces in a string"""
479
480 ini_spaces = ini_spaces_re.match(strng)
481 if ini_spaces:
482 return ini_spaces.end()
483 else:
484 return 0
485
486
487 def format_screen(strng):
488 """Format a string for screen printing.
489
490 This removes some latex-type format codes."""
491 # Paragraph continue
492 par_re = re.compile(r'\\$',re.MULTILINE)
493 strng = par_re.sub('',strng)
494 return strng
495
496
497 def dedent(text):
498 """Equivalent of textwrap.dedent that ignores unindented first line.
499
500 This means it will still dedent strings like:
501 '''foo
502 is a bar
503 '''
504
505 For use in wrap_paragraphs.
506 """
507
508 if text.startswith('\n'):
509 # text starts with blank line, don't ignore the first line
510 return textwrap.dedent(text)
511
512 # split first line
513 splits = text.split('\n',1)
514 if len(splits) == 1:
515 # only one line
516 return textwrap.dedent(text)
517
518 first, rest = splits
519 # dedent everything but the first line
520 rest = textwrap.dedent(rest)
521 return '\n'.join([first, rest])
522
523
524 def wrap_paragraphs(text, ncols=80):
525 """Wrap multiple paragraphs to fit a specified width.
526
527 This is equivalent to textwrap.wrap, but with support for multiple
528 paragraphs, as separated by empty lines.
529
530 Returns
531 -------
532
533 list of complete paragraphs, wrapped to fill `ncols` columns.
534 """
535 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
536 text = dedent(text).strip()
537 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
538 out_ps = []
539 indent_re = re.compile(r'\n\s+', re.MULTILINE)
540 for p in paragraphs:
541 # presume indentation that survives dedent is meaningful formatting,
542 # so don't fill unless text is flush.
543 if indent_re.search(p) is None:
544 # wrap paragraph
545 p = textwrap.fill(p, ncols)
546 out_ps.append(p)
547 return out_ps
548
549
550 def long_substr(data):
551 """Return the longest common substring in a list of strings.
552
553 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
554 """
555 substr = ''
556 if len(data) > 1 and len(data[0]) > 0:
557 for i in range(len(data[0])):
558 for j in range(len(data[0])-i+1):
559 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
560 substr = data[0][i:i+j]
561 elif len(data) == 1:
562 substr = data[0]
563 return substr
564
565
566 def strip_email_quotes(text):
567 """Strip leading email quotation characters ('>').
568
569 Removes any combination of leading '>' interspersed with whitespace that
570 appears *identically* in all lines of the input text.
571
572 Parameters
573 ----------
574 text : str
575
576 Examples
577 --------
578
579 Simple uses::
580
581 In [2]: strip_email_quotes('> > text')
582 Out[2]: 'text'
583
584 In [3]: strip_email_quotes('> > text\\n> > more')
585 Out[3]: 'text\\nmore'
586
587 Note how only the common prefix that appears in all lines is stripped::
588
589 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
590 Out[4]: '> text\\n> more\\nmore...'
591
592 So if any line has no quote marks ('>') , then none are stripped from any
593 of them ::
594
595 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
596 Out[5]: '> > text\\n> > more\\nlast different'
597 """
598 lines = text.splitlines()
599 matches = set()
600 for line in lines:
601 prefix = re.match(r'^(\s*>[ >]*)', line)
602 if prefix:
603 matches.add(prefix.group(1))
604 else:
605 break
606 else:
607 prefix = long_substr(list(matches))
608 if prefix:
609 strip = len(prefix)
610 text = '\n'.join([ ln[strip:] for ln in lines])
611 return text
612
613
614 class EvalFormatter(Formatter):
615 """A String Formatter that allows evaluation of simple expressions.
616
617 Note that this version interprets a : as specifying a format string (as per
618 standard string formatting), so if slicing is required, you must explicitly
619 create a slice.
620
621 This is to be used in templating cases, such as the parallel batch
622 script templates, where simple arithmetic on arguments is useful.
623
624 Examples
625 --------
626
627 In [1]: f = EvalFormatter()
628 In [2]: f.format('{n//4}', n=8)
629 Out [2]: '2'
630
631 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
632 Out [3]: 'll'
633 """
634 def get_field(self, name, args, kwargs):
635 v = eval(name, kwargs)
636 return v, name
637
638
639 @skip_doctest_py3
640 class FullEvalFormatter(Formatter):
641 """A String Formatter that allows evaluation of simple expressions.
642
643 Any time a format key is not found in the kwargs,
644 it will be tried as an expression in the kwargs namespace.
645
646 Note that this version allows slicing using [1:2], so you cannot specify
647 a format string. Use :class:`EvalFormatter` to permit format strings.
648
649 Examples
650 --------
651
652 In [1]: f = FullEvalFormatter()
653 In [2]: f.format('{n//4}', n=8)
654 Out[2]: u'2'
655
656 In [3]: f.format('{list(range(5))[2:4]}')
657 Out[3]: u'[2, 3]'
658
659 In [4]: f.format('{3*2}')
660 Out[4]: u'6'
661 """
662 # copied from Formatter._vformat with minor changes to allow eval
663 # and replace the format_spec code with slicing
664 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
665 if recursion_depth < 0:
666 raise ValueError('Max string recursion exceeded')
667 result = []
668 for literal_text, field_name, format_spec, conversion in \
669 self.parse(format_string):
670
671 # output the literal text
672 if literal_text:
673 result.append(literal_text)
674
675 # if there's a field, output it
676 if field_name is not None:
677 # this is some markup, find the object and do
678 # the formatting
679
680 if format_spec:
681 # override format spec, to allow slicing:
682 field_name = ':'.join([field_name, format_spec])
683
684 # eval the contents of the field for the object
685 # to be formatted
686 obj = eval(field_name, kwargs)
687
688 # do any conversion on the resulting object
689 obj = self.convert_field(obj, conversion)
690
691 # format the object and append to the result
692 result.append(self.format_field(obj, ''))
693
694 return u''.join(py3compat.cast_unicode(s) for s in result)
695
696
697 @skip_doctest_py3
698 class DollarFormatter(FullEvalFormatter):
699 """Formatter allowing Itpl style $foo replacement, for names and attribute
700 access only. Standard {foo} replacement also works, and allows full
701 evaluation of its arguments.
702
703 Examples
704 --------
705 In [1]: f = DollarFormatter()
706 In [2]: f.format('{n//4}', n=8)
707 Out[2]: u'2'
708
709 In [3]: f.format('23 * 76 is $result', result=23*76)
710 Out[3]: u'23 * 76 is 1748'
711
712 In [4]: f.format('$a or {b}', a=1, b=2)
713 Out[4]: u'1 or 2'
714 """
715 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
716 def parse(self, fmt_string):
717 for literal_txt, field_name, format_spec, conversion \
718 in Formatter.parse(self, fmt_string):
719
720 # Find $foo patterns in the literal text.
721 continue_from = 0
722 txt = ""
723 for m in self._dollar_pattern.finditer(literal_txt):
724 new_txt, new_field = m.group(1,2)
725 # $$foo --> $foo
726 if new_field.startswith("$"):
727 txt += new_txt + new_field
728 else:
729 yield (txt + new_txt, new_field, "", None)
730 txt = ""
731 continue_from = m.end()
732
733 # Re-yield the {foo} style pattern
734 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
735
736 #-----------------------------------------------------------------------------
737 # Utils to columnize a list of string
738 #-----------------------------------------------------------------------------
739
740 def _chunks(l, n):
741 """Yield successive n-sized chunks from l."""
742 for i in xrange(0, len(l), n):
743 yield l[i:i+n]
744
745
746 def _find_optimal(rlist , separator_size=2 , displaywidth=80):
747 """Calculate optimal info to columnize a list of string"""
748 for nrow in range(1, len(rlist)+1) :
749 chk = map(max,_chunks(rlist, nrow))
750 sumlength = sum(chk)
751 ncols = len(chk)
752 if sumlength+separator_size*(ncols-1) <= displaywidth :
753 break;
754 return {'columns_numbers' : ncols,
755 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0,
756 'rows_numbers' : nrow,
757 'columns_width' : chk
758 }
759
760
761 def _get_or_default(mylist, i, default=None):
762 """return list item number, or default if don't exist"""
763 if i >= len(mylist):
764 return default
765 else :
766 return mylist[i]
767
768
769 @skip_doctest
770 def compute_item_matrix(items, empty=None, *args, **kwargs) :
771 """Returns a nested list, and info to columnize items
772
773 Parameters :
774 ------------
775
776 items :
777 list of strings to columize
778 empty : (default None)
779 default value to fill list if needed
780 separator_size : int (default=2)
781 How much caracters will be used as a separation between each columns.
782 displaywidth : int (default=80)
783 The width of the area onto wich the columns should enter
784
785 Returns :
786 ---------
787
788 Returns a tuple of (strings_matrix, dict_info)
789
790 strings_matrix :
791
792 nested list of string, the outer most list contains as many list as
793 rows, the innermost lists have each as many element as colums. If the
794 total number of elements in `items` does not equal the product of
795 rows*columns, the last element of some lists are filled with `None`.
796
797 dict_info :
798 some info to make columnize easier:
799
800 columns_numbers : number of columns
801 rows_numbers : number of rows
802 columns_width : list of with of each columns
803 optimal_separator_width : best separator width between columns
804
805 Exemple :
806 ---------
807
808 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
809 ...: compute_item_matrix(l,displaywidth=12)
810 Out[1]:
811 ([['aaa', 'f', 'k'],
812 ['b', 'g', 'l'],
813 ['cc', 'h', None],
814 ['d', 'i', None],
815 ['eeeee', 'j', None]],
816 {'columns_numbers': 3,
817 'columns_width': [5, 1, 1],
818 'optimal_separator_width': 2,
819 'rows_numbers': 5})
820
821 """
822 info = _find_optimal(map(len, items), *args, **kwargs)
823 nrow, ncol = info['rows_numbers'], info['columns_numbers']
824 return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info)
825
826
827 def columnize(items, separator=' ', displaywidth=80):
828 """ Transform a list of strings into a single string with columns.
829
830 Parameters
831 ----------
832 items : sequence of strings
833 The strings to process.
834
835 separator : str, optional [default is two spaces]
836 The string that separates columns.
837
838 displaywidth : int, optional [default is 80]
839 Width of the display in number of characters.
840
841 Returns
842 -------
843 The formatted string.
844 """
845 if not items :
846 return '\n'
847 matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth)
848 fmatrix = [filter(None, x) for x in matrix]
849 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])])
850 return '\n'.join(map(sjoin, fmatrix))+'\n'
This diff has been collapsed as it changes many lines, (1439 lines changed) Show them Hide them
@@ -0,0 +1,1439 b''
1 # encoding: utf-8
2 """
3 A lightweight Traits like module.
4
5 This is designed to provide a lightweight, simple, pure Python version of
6 many of the capabilities of enthought.traits. This includes:
7
8 * Validation
9 * Type specification with defaults
10 * Static and dynamic notification
11 * Basic predefined types
12 * An API that is similar to enthought.traits
13
14 We don't support:
15
16 * Delegation
17 * Automatic GUI generation
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
20 contents change.
21 * API compatibility with enthought.traits
22
23 There are also some important difference in our design:
24
25 * enthought.traits does not validate default values. We do.
26
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,
29 including Jython and IronPython.
30
31 Inheritance diagram:
32
33 .. inheritance-diagram:: IPython.utils.traitlets
34 :parts: 3
35
36 Authors:
37
38 * Brian Granger
39 * Enthought, Inc. Some of the code in this file comes from enthought.traits
40 and is licensed under the BSD license. Also, many of the ideas also come
41 from enthought.traits even though our implementation is very different.
42 """
43
44 #-----------------------------------------------------------------------------
45 # Copyright (C) 2008-2011 The IPython Development Team
46 #
47 # Distributed under the terms of the BSD License. The full license is in
48 # the file COPYING, distributed as part of this software.
49 #-----------------------------------------------------------------------------
50
51 #-----------------------------------------------------------------------------
52 # Imports
53 #-----------------------------------------------------------------------------
54
55
56 import inspect
57 import re
58 import sys
59 import types
60 from types import FunctionType
61 try:
62 from types import ClassType, InstanceType
63 ClassTypes = (ClassType, type)
64 except:
65 ClassTypes = (type,)
66
67 from .importstring import import_item
68 from IPython.utils import py3compat
69
70 SequenceTypes = (list, tuple, set, frozenset)
71
72 #-----------------------------------------------------------------------------
73 # Basic classes
74 #-----------------------------------------------------------------------------
75
76
77 class NoDefaultSpecified ( object ): pass
78 NoDefaultSpecified = NoDefaultSpecified()
79
80
81 class Undefined ( object ): pass
82 Undefined = Undefined()
83
84 class TraitError(Exception):
85 pass
86
87 #-----------------------------------------------------------------------------
88 # Utilities
89 #-----------------------------------------------------------------------------
90
91
92 def class_of ( object ):
93 """ Returns a string containing the class name of an object with the
94 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
95 'a PlotValue').
96 """
97 if isinstance( object, basestring ):
98 return add_article( object )
99
100 return add_article( object.__class__.__name__ )
101
102
103 def add_article ( name ):
104 """ Returns a string containing the correct indefinite article ('a' or 'an')
105 prefixed to the specified string.
106 """
107 if name[:1].lower() in 'aeiou':
108 return 'an ' + name
109
110 return 'a ' + name
111
112
113 def repr_type(obj):
114 """ Return a string representation of a value and its type for readable
115 error messages.
116 """
117 the_type = type(obj)
118 if (not py3compat.PY3) and the_type is InstanceType:
119 # Old-style class.
120 the_type = obj.__class__
121 msg = '%r %r' % (obj, the_type)
122 return msg
123
124
125 def is_trait(t):
126 """ Returns whether the given value is an instance or subclass of TraitType.
127 """
128 return (isinstance(t, TraitType) or
129 (isinstance(t, type) and issubclass(t, TraitType)))
130
131
132 def parse_notifier_name(name):
133 """Convert the name argument to a list of names.
134
135 Examples
136 --------
137
138 >>> parse_notifier_name('a')
139 ['a']
140 >>> parse_notifier_name(['a','b'])
141 ['a', 'b']
142 >>> parse_notifier_name(None)
143 ['anytrait']
144 """
145 if isinstance(name, str):
146 return [name]
147 elif name is None:
148 return ['anytrait']
149 elif isinstance(name, (list, tuple)):
150 for n in name:
151 assert isinstance(n, str), "names must be strings"
152 return name
153
154
155 class _SimpleTest:
156 def __init__ ( self, value ): self.value = value
157 def __call__ ( self, test ):
158 return test == self.value
159 def __repr__(self):
160 return "<SimpleTest(%r)" % self.value
161 def __str__(self):
162 return self.__repr__()
163
164
165 def getmembers(object, predicate=None):
166 """A safe version of inspect.getmembers that handles missing attributes.
167
168 This is useful when there are descriptor based attributes that for
169 some reason raise AttributeError even though they exist. This happens
170 in zope.inteface with the __provides__ attribute.
171 """
172 results = []
173 for key in dir(object):
174 try:
175 value = getattr(object, key)
176 except AttributeError:
177 pass
178 else:
179 if not predicate or predicate(value):
180 results.append((key, value))
181 results.sort()
182 return results
183
184
185 #-----------------------------------------------------------------------------
186 # Base TraitType for all traits
187 #-----------------------------------------------------------------------------
188
189
190 class TraitType(object):
191 """A base class for all trait descriptors.
192
193 Notes
194 -----
195 Our implementation of traits is based on Python's descriptor
196 prototol. This class is the base class for all such descriptors. The
197 only magic we use is a custom metaclass for the main :class:`HasTraits`
198 class that does the following:
199
200 1. Sets the :attr:`name` attribute of every :class:`TraitType`
201 instance in the class dict to the name of the attribute.
202 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
203 instance in the class dict to the *class* that declared the trait.
204 This is used by the :class:`This` trait to allow subclasses to
205 accept superclasses for :class:`This` values.
206 """
207
208
209 metadata = {}
210 default_value = Undefined
211 info_text = 'any value'
212
213 def __init__(self, default_value=NoDefaultSpecified, **metadata):
214 """Create a TraitType.
215 """
216 if default_value is not NoDefaultSpecified:
217 self.default_value = default_value
218
219 if len(metadata) > 0:
220 if len(self.metadata) > 0:
221 self._metadata = self.metadata.copy()
222 self._metadata.update(metadata)
223 else:
224 self._metadata = metadata
225 else:
226 self._metadata = self.metadata
227
228 self.init()
229
230 def init(self):
231 pass
232
233 def get_default_value(self):
234 """Create a new instance of the default value."""
235 return self.default_value
236
237 def instance_init(self, obj):
238 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
239
240 Some stages of initialization must be delayed until the parent
241 :class:`HasTraits` instance has been created. This method is
242 called in :meth:`HasTraits.__new__` after the instance has been
243 created.
244
245 This method trigger the creation and validation of default values
246 and also things like the resolution of str given class names in
247 :class:`Type` and :class`Instance`.
248
249 Parameters
250 ----------
251 obj : :class:`HasTraits` instance
252 The parent :class:`HasTraits` instance that has just been
253 created.
254 """
255 self.set_default_value(obj)
256
257 def set_default_value(self, obj):
258 """Set the default value on a per instance basis.
259
260 This method is called by :meth:`instance_init` to create and
261 validate the default value. The creation and validation of
262 default values must be delayed until the parent :class:`HasTraits`
263 class has been instantiated.
264 """
265 # Check for a deferred initializer defined in the same class as the
266 # trait declaration or above.
267 mro = type(obj).mro()
268 meth_name = '_%s_default' % self.name
269 for cls in mro[:mro.index(self.this_class)+1]:
270 if meth_name in cls.__dict__:
271 break
272 else:
273 # We didn't find one. Do static initialization.
274 dv = self.get_default_value()
275 newdv = self._validate(obj, dv)
276 obj._trait_values[self.name] = newdv
277 return
278 # Complete the dynamic initialization.
279 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
280
281 def __get__(self, obj, cls=None):
282 """Get the value of the trait by self.name for the instance.
283
284 Default values are instantiated when :meth:`HasTraits.__new__`
285 is called. Thus by the time this method gets called either the
286 default value or a user defined value (they called :meth:`__set__`)
287 is in the :class:`HasTraits` instance.
288 """
289 if obj is None:
290 return self
291 else:
292 try:
293 value = obj._trait_values[self.name]
294 except KeyError:
295 # Check for a dynamic initializer.
296 if self.name in obj._trait_dyn_inits:
297 value = obj._trait_dyn_inits[self.name](obj)
298 # FIXME: Do we really validate here?
299 value = self._validate(obj, value)
300 obj._trait_values[self.name] = value
301 return value
302 else:
303 raise TraitError('Unexpected error in TraitType: '
304 'both default value and dynamic initializer are '
305 'absent.')
306 except Exception:
307 # HasTraits should call set_default_value to populate
308 # this. So this should never be reached.
309 raise TraitError('Unexpected error in TraitType: '
310 'default value not set properly')
311 else:
312 return value
313
314 def __set__(self, obj, value):
315 new_value = self._validate(obj, value)
316 old_value = self.__get__(obj)
317 obj._trait_values[self.name] = new_value
318 if old_value != new_value:
319 obj._notify_trait(self.name, old_value, new_value)
320
321 def _validate(self, obj, value):
322 if hasattr(self, 'validate'):
323 return self.validate(obj, value)
324 elif hasattr(self, 'is_valid_for'):
325 valid = self.is_valid_for(value)
326 if valid:
327 return value
328 else:
329 raise TraitError('invalid value for type: %r' % value)
330 elif hasattr(self, 'value_for'):
331 return self.value_for(value)
332 else:
333 return value
334
335 def info(self):
336 return self.info_text
337
338 def error(self, obj, value):
339 if obj is not None:
340 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
341 % (self.name, class_of(obj),
342 self.info(), repr_type(value))
343 else:
344 e = "The '%s' trait must be %s, but a value of %r was specified." \
345 % (self.name, self.info(), repr_type(value))
346 raise TraitError(e)
347
348 def get_metadata(self, key):
349 return getattr(self, '_metadata', {}).get(key, None)
350
351 def set_metadata(self, key, value):
352 getattr(self, '_metadata', {})[key] = value
353
354
355 #-----------------------------------------------------------------------------
356 # The HasTraits implementation
357 #-----------------------------------------------------------------------------
358
359
360 class MetaHasTraits(type):
361 """A metaclass for HasTraits.
362
363 This metaclass makes sure that any TraitType class attributes are
364 instantiated and sets their name attribute.
365 """
366
367 def __new__(mcls, name, bases, classdict):
368 """Create the HasTraits class.
369
370 This instantiates all TraitTypes in the class dict and sets their
371 :attr:`name` attribute.
372 """
373 # print "MetaHasTraitlets (mcls, name): ", mcls, name
374 # print "MetaHasTraitlets (bases): ", bases
375 # print "MetaHasTraitlets (classdict): ", classdict
376 for k,v in classdict.iteritems():
377 if isinstance(v, TraitType):
378 v.name = k
379 elif inspect.isclass(v):
380 if issubclass(v, TraitType):
381 vinst = v()
382 vinst.name = k
383 classdict[k] = vinst
384 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
385
386 def __init__(cls, name, bases, classdict):
387 """Finish initializing the HasTraits class.
388
389 This sets the :attr:`this_class` attribute of each TraitType in the
390 class dict to the newly created class ``cls``.
391 """
392 for k, v in classdict.iteritems():
393 if isinstance(v, TraitType):
394 v.this_class = cls
395 super(MetaHasTraits, cls).__init__(name, bases, classdict)
396
397 class HasTraits(object):
398
399 __metaclass__ = MetaHasTraits
400
401 def __new__(cls, **kw):
402 # This is needed because in Python 2.6 object.__new__ only accepts
403 # the cls argument.
404 new_meth = super(HasTraits, cls).__new__
405 if new_meth is object.__new__:
406 inst = new_meth(cls)
407 else:
408 inst = new_meth(cls, **kw)
409 inst._trait_values = {}
410 inst._trait_notifiers = {}
411 inst._trait_dyn_inits = {}
412 # Here we tell all the TraitType instances to set their default
413 # values on the instance.
414 for key in dir(cls):
415 # Some descriptors raise AttributeError like zope.interface's
416 # __provides__ attributes even though they exist. This causes
417 # AttributeErrors even though they are listed in dir(cls).
418 try:
419 value = getattr(cls, key)
420 except AttributeError:
421 pass
422 else:
423 if isinstance(value, TraitType):
424 value.instance_init(inst)
425
426 return inst
427
428 def __init__(self, **kw):
429 # Allow trait values to be set using keyword arguments.
430 # We need to use setattr for this to trigger validation and
431 # notifications.
432 for key, value in kw.iteritems():
433 setattr(self, key, value)
434
435 def _notify_trait(self, name, old_value, new_value):
436
437 # First dynamic ones
438 callables = self._trait_notifiers.get(name,[])
439 more_callables = self._trait_notifiers.get('anytrait',[])
440 callables.extend(more_callables)
441
442 # Now static ones
443 try:
444 cb = getattr(self, '_%s_changed' % name)
445 except:
446 pass
447 else:
448 callables.append(cb)
449
450 # Call them all now
451 for c in callables:
452 # Traits catches and logs errors here. I allow them to raise
453 if callable(c):
454 argspec = inspect.getargspec(c)
455 nargs = len(argspec[0])
456 # Bound methods have an additional 'self' argument
457 # I don't know how to treat unbound methods, but they
458 # can't really be used for callbacks.
459 if isinstance(c, types.MethodType):
460 offset = -1
461 else:
462 offset = 0
463 if nargs + offset == 0:
464 c()
465 elif nargs + offset == 1:
466 c(name)
467 elif nargs + offset == 2:
468 c(name, new_value)
469 elif nargs + offset == 3:
470 c(name, old_value, new_value)
471 else:
472 raise TraitError('a trait changed callback '
473 'must have 0-3 arguments.')
474 else:
475 raise TraitError('a trait changed callback '
476 'must be callable.')
477
478
479 def _add_notifiers(self, handler, name):
480 if name not in self._trait_notifiers:
481 nlist = []
482 self._trait_notifiers[name] = nlist
483 else:
484 nlist = self._trait_notifiers[name]
485 if handler not in nlist:
486 nlist.append(handler)
487
488 def _remove_notifiers(self, handler, name):
489 if name in self._trait_notifiers:
490 nlist = self._trait_notifiers[name]
491 try:
492 index = nlist.index(handler)
493 except ValueError:
494 pass
495 else:
496 del nlist[index]
497
498 def on_trait_change(self, handler, name=None, remove=False):
499 """Setup a handler to be called when a trait changes.
500
501 This is used to setup dynamic notifications of trait changes.
502
503 Static handlers can be created by creating methods on a HasTraits
504 subclass with the naming convention '_[traitname]_changed'. Thus,
505 to create static handler for the trait 'a', create the method
506 _a_changed(self, name, old, new) (fewer arguments can be used, see
507 below).
508
509 Parameters
510 ----------
511 handler : callable
512 A callable that is called when a trait changes. Its
513 signature can be handler(), handler(name), handler(name, new)
514 or handler(name, old, new).
515 name : list, str, None
516 If None, the handler will apply to all traits. If a list
517 of str, handler will apply to all names in the list. If a
518 str, the handler will apply just to that name.
519 remove : bool
520 If False (the default), then install the handler. If True
521 then unintall it.
522 """
523 if remove:
524 names = parse_notifier_name(name)
525 for n in names:
526 self._remove_notifiers(handler, n)
527 else:
528 names = parse_notifier_name(name)
529 for n in names:
530 self._add_notifiers(handler, n)
531
532 @classmethod
533 def class_trait_names(cls, **metadata):
534 """Get a list of all the names of this classes traits.
535
536 This method is just like the :meth:`trait_names` method, but is unbound.
537 """
538 return cls.class_traits(**metadata).keys()
539
540 @classmethod
541 def class_traits(cls, **metadata):
542 """Get a list of all the traits of this class.
543
544 This method is just like the :meth:`traits` method, but is unbound.
545
546 The TraitTypes returned don't know anything about the values
547 that the various HasTrait's instances are holding.
548
549 This follows the same algorithm as traits does and does not allow
550 for any simple way of specifying merely that a metadata name
551 exists, but has any value. This is because get_metadata returns
552 None if a metadata key doesn't exist.
553 """
554 traits = dict([memb for memb in getmembers(cls) if \
555 isinstance(memb[1], TraitType)])
556
557 if len(metadata) == 0:
558 return traits
559
560 for meta_name, meta_eval in metadata.items():
561 if type(meta_eval) is not FunctionType:
562 metadata[meta_name] = _SimpleTest(meta_eval)
563
564 result = {}
565 for name, trait in traits.items():
566 for meta_name, meta_eval in metadata.items():
567 if not meta_eval(trait.get_metadata(meta_name)):
568 break
569 else:
570 result[name] = trait
571
572 return result
573
574 def trait_names(self, **metadata):
575 """Get a list of all the names of this classes traits."""
576 return self.traits(**metadata).keys()
577
578 def traits(self, **metadata):
579 """Get a list of all the traits of this class.
580
581 The TraitTypes returned don't know anything about the values
582 that the various HasTrait's instances are holding.
583
584 This follows the same algorithm as traits does and does not allow
585 for any simple way of specifying merely that a metadata name
586 exists, but has any value. This is because get_metadata returns
587 None if a metadata key doesn't exist.
588 """
589 traits = dict([memb for memb in getmembers(self.__class__) if \
590 isinstance(memb[1], TraitType)])
591
592 if len(metadata) == 0:
593 return traits
594
595 for meta_name, meta_eval in metadata.items():
596 if type(meta_eval) is not FunctionType:
597 metadata[meta_name] = _SimpleTest(meta_eval)
598
599 result = {}
600 for name, trait in traits.items():
601 for meta_name, meta_eval in metadata.items():
602 if not meta_eval(trait.get_metadata(meta_name)):
603 break
604 else:
605 result[name] = trait
606
607 return result
608
609 def trait_metadata(self, traitname, key):
610 """Get metadata values for trait by key."""
611 try:
612 trait = getattr(self.__class__, traitname)
613 except AttributeError:
614 raise TraitError("Class %s does not have a trait named %s" %
615 (self.__class__.__name__, traitname))
616 else:
617 return trait.get_metadata(key)
618
619 #-----------------------------------------------------------------------------
620 # Actual TraitTypes implementations/subclasses
621 #-----------------------------------------------------------------------------
622
623 #-----------------------------------------------------------------------------
624 # TraitTypes subclasses for handling classes and instances of classes
625 #-----------------------------------------------------------------------------
626
627
628 class ClassBasedTraitType(TraitType):
629 """A trait with error reporting for Type, Instance and This."""
630
631 def error(self, obj, value):
632 kind = type(value)
633 if (not py3compat.PY3) and kind is InstanceType:
634 msg = 'class %s' % value.__class__.__name__
635 else:
636 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
637
638 if obj is not None:
639 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
640 % (self.name, class_of(obj),
641 self.info(), msg)
642 else:
643 e = "The '%s' trait must be %s, but a value of %r was specified." \
644 % (self.name, self.info(), msg)
645
646 raise TraitError(e)
647
648
649 class Type(ClassBasedTraitType):
650 """A trait whose value must be a subclass of a specified class."""
651
652 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
653 """Construct a Type trait
654
655 A Type trait specifies that its values must be subclasses of
656 a particular class.
657
658 If only ``default_value`` is given, it is used for the ``klass`` as
659 well.
660
661 Parameters
662 ----------
663 default_value : class, str or None
664 The default value must be a subclass of klass. If an str,
665 the str must be a fully specified class name, like 'foo.bar.Bah'.
666 The string is resolved into real class, when the parent
667 :class:`HasTraits` class is instantiated.
668 klass : class, str, None
669 Values of this trait must be a subclass of klass. The klass
670 may be specified in a string like: 'foo.bar.MyClass'.
671 The string is resolved into real class, when the parent
672 :class:`HasTraits` class is instantiated.
673 allow_none : boolean
674 Indicates whether None is allowed as an assignable value. Even if
675 ``False``, the default value may be ``None``.
676 """
677 if default_value is None:
678 if klass is None:
679 klass = object
680 elif klass is None:
681 klass = default_value
682
683 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
684 raise TraitError("A Type trait must specify a class.")
685
686 self.klass = klass
687 self._allow_none = allow_none
688
689 super(Type, self).__init__(default_value, **metadata)
690
691 def validate(self, obj, value):
692 """Validates that the value is a valid object instance."""
693 try:
694 if issubclass(value, self.klass):
695 return value
696 except:
697 if (value is None) and (self._allow_none):
698 return value
699
700 self.error(obj, value)
701
702 def info(self):
703 """ Returns a description of the trait."""
704 if isinstance(self.klass, basestring):
705 klass = self.klass
706 else:
707 klass = self.klass.__name__
708 result = 'a subclass of ' + klass
709 if self._allow_none:
710 return result + ' or None'
711 return result
712
713 def instance_init(self, obj):
714 self._resolve_classes()
715 super(Type, self).instance_init(obj)
716
717 def _resolve_classes(self):
718 if isinstance(self.klass, basestring):
719 self.klass = import_item(self.klass)
720 if isinstance(self.default_value, basestring):
721 self.default_value = import_item(self.default_value)
722
723 def get_default_value(self):
724 return self.default_value
725
726
727 class DefaultValueGenerator(object):
728 """A class for generating new default value instances."""
729
730 def __init__(self, *args, **kw):
731 self.args = args
732 self.kw = kw
733
734 def generate(self, klass):
735 return klass(*self.args, **self.kw)
736
737
738 class Instance(ClassBasedTraitType):
739 """A trait whose value must be an instance of a specified class.
740
741 The value can also be an instance of a subclass of the specified class.
742 """
743
744 def __init__(self, klass=None, args=None, kw=None,
745 allow_none=True, **metadata ):
746 """Construct an Instance trait.
747
748 This trait allows values that are instances of a particular
749 class or its sublclasses. Our implementation is quite different
750 from that of enthough.traits as we don't allow instances to be used
751 for klass and we handle the ``args`` and ``kw`` arguments differently.
752
753 Parameters
754 ----------
755 klass : class, str
756 The class that forms the basis for the trait. Class names
757 can also be specified as strings, like 'foo.bar.Bar'.
758 args : tuple
759 Positional arguments for generating the default value.
760 kw : dict
761 Keyword arguments for generating the default value.
762 allow_none : bool
763 Indicates whether None is allowed as a value.
764
765 Default Value
766 -------------
767 If both ``args`` and ``kw`` are None, then the default value is None.
768 If ``args`` is a tuple and ``kw`` is a dict, then the default is
769 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
770 not (but not both), None is replace by ``()`` or ``{}``.
771 """
772
773 self._allow_none = allow_none
774
775 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
776 raise TraitError('The klass argument must be a class'
777 ' you gave: %r' % klass)
778 self.klass = klass
779
780 # self.klass is a class, so handle default_value
781 if args is None and kw is None:
782 default_value = None
783 else:
784 if args is None:
785 # kw is not None
786 args = ()
787 elif kw is None:
788 # args is not None
789 kw = {}
790
791 if not isinstance(kw, dict):
792 raise TraitError("The 'kw' argument must be a dict or None.")
793 if not isinstance(args, tuple):
794 raise TraitError("The 'args' argument must be a tuple or None.")
795
796 default_value = DefaultValueGenerator(*args, **kw)
797
798 super(Instance, self).__init__(default_value, **metadata)
799
800 def validate(self, obj, value):
801 if value is None:
802 if self._allow_none:
803 return value
804 self.error(obj, value)
805
806 if isinstance(value, self.klass):
807 return value
808 else:
809 self.error(obj, value)
810
811 def info(self):
812 if isinstance(self.klass, basestring):
813 klass = self.klass
814 else:
815 klass = self.klass.__name__
816 result = class_of(klass)
817 if self._allow_none:
818 return result + ' or None'
819
820 return result
821
822 def instance_init(self, obj):
823 self._resolve_classes()
824 super(Instance, self).instance_init(obj)
825
826 def _resolve_classes(self):
827 if isinstance(self.klass, basestring):
828 self.klass = import_item(self.klass)
829
830 def get_default_value(self):
831 """Instantiate a default value instance.
832
833 This is called when the containing HasTraits classes'
834 :meth:`__new__` method is called to ensure that a unique instance
835 is created for each HasTraits instance.
836 """
837 dv = self.default_value
838 if isinstance(dv, DefaultValueGenerator):
839 return dv.generate(self.klass)
840 else:
841 return dv
842
843
844 class This(ClassBasedTraitType):
845 """A trait for instances of the class containing this trait.
846
847 Because how how and when class bodies are executed, the ``This``
848 trait can only have a default value of None. This, and because we
849 always validate default values, ``allow_none`` is *always* true.
850 """
851
852 info_text = 'an instance of the same type as the receiver or None'
853
854 def __init__(self, **metadata):
855 super(This, self).__init__(None, **metadata)
856
857 def validate(self, obj, value):
858 # What if value is a superclass of obj.__class__? This is
859 # complicated if it was the superclass that defined the This
860 # trait.
861 if isinstance(value, self.this_class) or (value is None):
862 return value
863 else:
864 self.error(obj, value)
865
866
867 #-----------------------------------------------------------------------------
868 # Basic TraitTypes implementations/subclasses
869 #-----------------------------------------------------------------------------
870
871
872 class Any(TraitType):
873 default_value = None
874 info_text = 'any value'
875
876
877 class Int(TraitType):
878 """An int trait."""
879
880 default_value = 0
881 info_text = 'an int'
882
883 def validate(self, obj, value):
884 if isinstance(value, int):
885 return value
886 self.error(obj, value)
887
888 class CInt(Int):
889 """A casting version of the int trait."""
890
891 def validate(self, obj, value):
892 try:
893 return int(value)
894 except:
895 self.error(obj, value)
896
897 if py3compat.PY3:
898 Long, CLong = Int, CInt
899 Integer = Int
900 else:
901 class Long(TraitType):
902 """A long integer trait."""
903
904 default_value = 0L
905 info_text = 'a long'
906
907 def validate(self, obj, value):
908 if isinstance(value, long):
909 return value
910 if isinstance(value, int):
911 return long(value)
912 self.error(obj, value)
913
914
915 class CLong(Long):
916 """A casting version of the long integer trait."""
917
918 def validate(self, obj, value):
919 try:
920 return long(value)
921 except:
922 self.error(obj, value)
923
924 class Integer(TraitType):
925 """An integer trait.
926
927 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
928
929 default_value = 0
930 info_text = 'an integer'
931
932 def validate(self, obj, value):
933 if isinstance(value, int):
934 return value
935 if isinstance(value, long):
936 # downcast longs that fit in int:
937 # note that int(n > sys.maxint) returns a long, so
938 # we don't need a condition on this cast
939 return int(value)
940 if sys.platform == "cli":
941 from System import Int64
942 if isinstance(value, Int64):
943 return int(value)
944 self.error(obj, value)
945
946
947 class Float(TraitType):
948 """A float trait."""
949
950 default_value = 0.0
951 info_text = 'a float'
952
953 def validate(self, obj, value):
954 if isinstance(value, float):
955 return value
956 if isinstance(value, int):
957 return float(value)
958 self.error(obj, value)
959
960
961 class CFloat(Float):
962 """A casting version of the float trait."""
963
964 def validate(self, obj, value):
965 try:
966 return float(value)
967 except:
968 self.error(obj, value)
969
970 class Complex(TraitType):
971 """A trait for complex numbers."""
972
973 default_value = 0.0 + 0.0j
974 info_text = 'a complex number'
975
976 def validate(self, obj, value):
977 if isinstance(value, complex):
978 return value
979 if isinstance(value, (float, int)):
980 return complex(value)
981 self.error(obj, value)
982
983
984 class CComplex(Complex):
985 """A casting version of the complex number trait."""
986
987 def validate (self, obj, value):
988 try:
989 return complex(value)
990 except:
991 self.error(obj, value)
992
993 # We should always be explicit about whether we're using bytes or unicode, both
994 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
995 # we don't have a Str type.
996 class Bytes(TraitType):
997 """A trait for byte strings."""
998
999 default_value = b''
1000 info_text = 'a string'
1001
1002 def validate(self, obj, value):
1003 if isinstance(value, bytes):
1004 return value
1005 self.error(obj, value)
1006
1007
1008 class CBytes(Bytes):
1009 """A casting version of the byte string trait."""
1010
1011 def validate(self, obj, value):
1012 try:
1013 return bytes(value)
1014 except:
1015 self.error(obj, value)
1016
1017
1018 class Unicode(TraitType):
1019 """A trait for unicode strings."""
1020
1021 default_value = u''
1022 info_text = 'a unicode string'
1023
1024 def validate(self, obj, value):
1025 if isinstance(value, unicode):
1026 return value
1027 if isinstance(value, bytes):
1028 return unicode(value)
1029 self.error(obj, value)
1030
1031
1032 class CUnicode(Unicode):
1033 """A casting version of the unicode trait."""
1034
1035 def validate(self, obj, value):
1036 try:
1037 return unicode(value)
1038 except:
1039 self.error(obj, value)
1040
1041
1042 class ObjectName(TraitType):
1043 """A string holding a valid object name in this version of Python.
1044
1045 This does not check that the name exists in any scope."""
1046 info_text = "a valid object identifier in Python"
1047
1048 if py3compat.PY3:
1049 # Python 3:
1050 coerce_str = staticmethod(lambda _,s: s)
1051
1052 else:
1053 # Python 2:
1054 def coerce_str(self, obj, value):
1055 "In Python 2, coerce ascii-only unicode to str"
1056 if isinstance(value, unicode):
1057 try:
1058 return str(value)
1059 except UnicodeEncodeError:
1060 self.error(obj, value)
1061 return value
1062
1063 def validate(self, obj, value):
1064 value = self.coerce_str(obj, value)
1065
1066 if isinstance(value, str) and py3compat.isidentifier(value):
1067 return value
1068 self.error(obj, value)
1069
1070 class DottedObjectName(ObjectName):
1071 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1072 def validate(self, obj, value):
1073 value = self.coerce_str(obj, value)
1074
1075 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1076 return value
1077 self.error(obj, value)
1078
1079
1080 class Bool(TraitType):
1081 """A boolean (True, False) trait."""
1082
1083 default_value = False
1084 info_text = 'a boolean'
1085
1086 def validate(self, obj, value):
1087 if isinstance(value, bool):
1088 return value
1089 self.error(obj, value)
1090
1091
1092 class CBool(Bool):
1093 """A casting version of the boolean trait."""
1094
1095 def validate(self, obj, value):
1096 try:
1097 return bool(value)
1098 except:
1099 self.error(obj, value)
1100
1101
1102 class Enum(TraitType):
1103 """An enum that whose value must be in a given sequence."""
1104
1105 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1106 self.values = values
1107 self._allow_none = allow_none
1108 super(Enum, self).__init__(default_value, **metadata)
1109
1110 def validate(self, obj, value):
1111 if value is None:
1112 if self._allow_none:
1113 return value
1114
1115 if value in self.values:
1116 return value
1117 self.error(obj, value)
1118
1119 def info(self):
1120 """ Returns a description of the trait."""
1121 result = 'any of ' + repr(self.values)
1122 if self._allow_none:
1123 return result + ' or None'
1124 return result
1125
1126 class CaselessStrEnum(Enum):
1127 """An enum of strings that are caseless in validate."""
1128
1129 def validate(self, obj, value):
1130 if value is None:
1131 if self._allow_none:
1132 return value
1133
1134 if not isinstance(value, basestring):
1135 self.error(obj, value)
1136
1137 for v in self.values:
1138 if v.lower() == value.lower():
1139 return v
1140 self.error(obj, value)
1141
1142 class Container(Instance):
1143 """An instance of a container (list, set, etc.)
1144
1145 To be subclassed by overriding klass.
1146 """
1147 klass = None
1148 _valid_defaults = SequenceTypes
1149 _trait = None
1150
1151 def __init__(self, trait=None, default_value=None, allow_none=True,
1152 **metadata):
1153 """Create a container trait type from a list, set, or tuple.
1154
1155 The default value is created by doing ``List(default_value)``,
1156 which creates a copy of the ``default_value``.
1157
1158 ``trait`` can be specified, which restricts the type of elements
1159 in the container to that TraitType.
1160
1161 If only one arg is given and it is not a Trait, it is taken as
1162 ``default_value``:
1163
1164 ``c = List([1,2,3])``
1165
1166 Parameters
1167 ----------
1168
1169 trait : TraitType [ optional ]
1170 the type for restricting the contents of the Container. If unspecified,
1171 types are not checked.
1172
1173 default_value : SequenceType [ optional ]
1174 The default value for the Trait. Must be list/tuple/set, and
1175 will be cast to the container type.
1176
1177 allow_none : Bool [ default True ]
1178 Whether to allow the value to be None
1179
1180 **metadata : any
1181 further keys for extensions to the Trait (e.g. config)
1182
1183 """
1184 # allow List([values]):
1185 if default_value is None and not is_trait(trait):
1186 default_value = trait
1187 trait = None
1188
1189 if default_value is None:
1190 args = ()
1191 elif isinstance(default_value, self._valid_defaults):
1192 args = (default_value,)
1193 else:
1194 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1195
1196 if is_trait(trait):
1197 self._trait = trait() if isinstance(trait, type) else trait
1198 self._trait.name = 'element'
1199 elif trait is not None:
1200 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1201
1202 super(Container,self).__init__(klass=self.klass, args=args,
1203 allow_none=allow_none, **metadata)
1204
1205 def element_error(self, obj, element, validator):
1206 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1207 % (self.name, class_of(obj), validator.info(), repr_type(element))
1208 raise TraitError(e)
1209
1210 def validate(self, obj, value):
1211 value = super(Container, self).validate(obj, value)
1212 if value is None:
1213 return value
1214
1215 value = self.validate_elements(obj, value)
1216
1217 return value
1218
1219 def validate_elements(self, obj, value):
1220 validated = []
1221 if self._trait is None or isinstance(self._trait, Any):
1222 return value
1223 for v in value:
1224 try:
1225 v = self._trait.validate(obj, v)
1226 except TraitError:
1227 self.element_error(obj, v, self._trait)
1228 else:
1229 validated.append(v)
1230 return self.klass(validated)
1231
1232
1233 class List(Container):
1234 """An instance of a Python list."""
1235 klass = list
1236
1237 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1238 allow_none=True, **metadata):
1239 """Create a List trait type from a list, set, or tuple.
1240
1241 The default value is created by doing ``List(default_value)``,
1242 which creates a copy of the ``default_value``.
1243
1244 ``trait`` can be specified, which restricts the type of elements
1245 in the container to that TraitType.
1246
1247 If only one arg is given and it is not a Trait, it is taken as
1248 ``default_value``:
1249
1250 ``c = List([1,2,3])``
1251
1252 Parameters
1253 ----------
1254
1255 trait : TraitType [ optional ]
1256 the type for restricting the contents of the Container. If unspecified,
1257 types are not checked.
1258
1259 default_value : SequenceType [ optional ]
1260 The default value for the Trait. Must be list/tuple/set, and
1261 will be cast to the container type.
1262
1263 minlen : Int [ default 0 ]
1264 The minimum length of the input list
1265
1266 maxlen : Int [ default sys.maxsize ]
1267 The maximum length of the input list
1268
1269 allow_none : Bool [ default True ]
1270 Whether to allow the value to be None
1271
1272 **metadata : any
1273 further keys for extensions to the Trait (e.g. config)
1274
1275 """
1276 self._minlen = minlen
1277 self._maxlen = maxlen
1278 super(List, self).__init__(trait=trait, default_value=default_value,
1279 allow_none=allow_none, **metadata)
1280
1281 def length_error(self, obj, value):
1282 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1283 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1284 raise TraitError(e)
1285
1286 def validate_elements(self, obj, value):
1287 length = len(value)
1288 if length < self._minlen or length > self._maxlen:
1289 self.length_error(obj, value)
1290
1291 return super(List, self).validate_elements(obj, value)
1292
1293
1294 class Set(Container):
1295 """An instance of a Python set."""
1296 klass = set
1297
1298 class Tuple(Container):
1299 """An instance of a Python tuple."""
1300 klass = tuple
1301
1302 def __init__(self, *traits, **metadata):
1303 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1304
1305 Create a tuple from a list, set, or tuple.
1306
1307 Create a fixed-type tuple with Traits:
1308
1309 ``t = Tuple(Int, Str, CStr)``
1310
1311 would be length 3, with Int,Str,CStr for each element.
1312
1313 If only one arg is given and it is not a Trait, it is taken as
1314 default_value:
1315
1316 ``t = Tuple((1,2,3))``
1317
1318 Otherwise, ``default_value`` *must* be specified by keyword.
1319
1320 Parameters
1321 ----------
1322
1323 *traits : TraitTypes [ optional ]
1324 the tsype for restricting the contents of the Tuple. If unspecified,
1325 types are not checked. If specified, then each positional argument
1326 corresponds to an element of the tuple. Tuples defined with traits
1327 are of fixed length.
1328
1329 default_value : SequenceType [ optional ]
1330 The default value for the Tuple. Must be list/tuple/set, and
1331 will be cast to a tuple. If `traits` are specified, the
1332 `default_value` must conform to the shape and type they specify.
1333
1334 allow_none : Bool [ default True ]
1335 Whether to allow the value to be None
1336
1337 **metadata : any
1338 further keys for extensions to the Trait (e.g. config)
1339
1340 """
1341 default_value = metadata.pop('default_value', None)
1342 allow_none = metadata.pop('allow_none', True)
1343
1344 # allow Tuple((values,)):
1345 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1346 default_value = traits[0]
1347 traits = ()
1348
1349 if default_value is None:
1350 args = ()
1351 elif isinstance(default_value, self._valid_defaults):
1352 args = (default_value,)
1353 else:
1354 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1355
1356 self._traits = []
1357 for trait in traits:
1358 t = trait() if isinstance(trait, type) else trait
1359 t.name = 'element'
1360 self._traits.append(t)
1361
1362 if self._traits and default_value is None:
1363 # don't allow default to be an empty container if length is specified
1364 args = None
1365 super(Container,self).__init__(klass=self.klass, args=args,
1366 allow_none=allow_none, **metadata)
1367
1368 def validate_elements(self, obj, value):
1369 if not self._traits:
1370 # nothing to validate
1371 return value
1372 if len(value) != len(self._traits):
1373 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1374 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1375 raise TraitError(e)
1376
1377 validated = []
1378 for t,v in zip(self._traits, value):
1379 try:
1380 v = t.validate(obj, v)
1381 except TraitError:
1382 self.element_error(obj, v, t)
1383 else:
1384 validated.append(v)
1385 return tuple(validated)
1386
1387
1388 class Dict(Instance):
1389 """An instance of a Python dict."""
1390
1391 def __init__(self, default_value=None, allow_none=True, **metadata):
1392 """Create a dict trait type from a dict.
1393
1394 The default value is created by doing ``dict(default_value)``,
1395 which creates a copy of the ``default_value``.
1396 """
1397 if default_value is None:
1398 args = ((),)
1399 elif isinstance(default_value, dict):
1400 args = (default_value,)
1401 elif isinstance(default_value, SequenceTypes):
1402 args = (default_value,)
1403 else:
1404 raise TypeError('default value of Dict was %s' % default_value)
1405
1406 super(Dict,self).__init__(klass=dict, args=args,
1407 allow_none=allow_none, **metadata)
1408
1409 class TCPAddress(TraitType):
1410 """A trait for an (ip, port) tuple.
1411
1412 This allows for both IPv4 IP addresses as well as hostnames.
1413 """
1414
1415 default_value = ('127.0.0.1', 0)
1416 info_text = 'an (ip, port) tuple'
1417
1418 def validate(self, obj, value):
1419 if isinstance(value, tuple):
1420 if len(value) == 2:
1421 if isinstance(value[0], basestring) and isinstance(value[1], int):
1422 port = value[1]
1423 if port >= 0 and port <= 65535:
1424 return value
1425 self.error(obj, value)
1426
1427 class CRegExp(TraitType):
1428 """A casting compiled regular expression trait.
1429
1430 Accepts both strings and compiled regular expressions. The resulting
1431 attribute will be a compiled regular expression."""
1432
1433 info_text = 'a regular expression'
1434
1435 def validate(self, obj, value):
1436 try:
1437 return re.compile(value)
1438 except:
1439 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now