##// END OF EJS Templates
- Make ipdoctest a little cleaner by giving it separate option names....
- Make ipdoctest a little cleaner by giving it separate option names. This is in an attempt at better isolating it from the rest of nose, because we are seeing intermittent Twisted errors when it is enabled. However, I still see them sometimes. - Also, make the reference counting tests a little less verbose.

File last commit:

r1715:78d99fed
r1910:8bc770ab
Show More
traitlets.py
322 lines | 6.7 KiB | text/x-python | PythonLexer
"""Traitlets -- a light-weight meta-class free stand-in for Traits.
Traitlet behaviour
==================
- Automatic casting, equivalent to traits.C* classes, e.g. CFloat, CBool etc.
- By default, validation is done by attempting to cast a given value
to the underlying type, e.g. bool for Bool, float for Float etc.
- To set or get a Traitlet value, use the ()-operator. E.g.
>>> b = Bool(False)
>>> b(True)
>>> print b # returns a string representation of the Traitlet
True
>>> print b() # returns the underlying bool object
True
This makes it possible to change values "in-place", unlike an assigment
of the form
>>> c = Bool(False)
>>> c = True
which results in
>>> print type(b), type(c)
<class 'IPython.config.traitlets.Bool'> <type 'bool'>
- Each Traitlet keeps track of its modification state, e.g.
>>> c = Bool(False)
>>> print c.modified
False
>>> c(False)
>>> print c.modified
False
>>> c(True)
>>> print c.modified
True
How to customize Traitlets
==========================
The easiest way to create a new Traitlet is by wrapping an underlying
Python type. This is done by setting the "_type" class attribute. For
example, creating an int-like Traitlet is done as follows:
>>> class MyInt(Traitlet):
... _type = int
>>> i = MyInt(3)
>>> i(4)
>>> print i
4
>>> try:
... i('a')
... except ValidationError:
... pass # this is expected
... else:
... "This should not be reached."
Furthermore, the following methods are provided for finer grained
control of validation and assignment:
- validate(self,value)
Ensure that "value" is valid. If not, raise an exception of any kind
with a suitable error message, which is reported to the user.
- prepare_value(self)
When using the ()-operator to query the underlying Traitlet value,
that value is first passed through prepare_value. For example:
>>> class Eval(Traitlet):
... _type = str
...
... def prepare_value(self):
... return eval(self._value)
>>> x = Eval('1+1')
>>> print x
'1+1'
>>> print x()
2
- __repr__(self)
By default, repr(self._value) is returned. This can be customised
to, for example, first call prepare_value and return the repr of
the resulting object.
"""
import re
import types
class ValidationError(Exception):
pass
class Traitlet(object):
"""Traitlet which knows its modification state.
"""
def __init__(self, value):
"Validate and store the default value. State is 'unmodified'."
self._type = getattr(self,'_type',None)
value = self._parse_validation(value)
self._default_value = value
self.reset()
def reset(self):
self._value = self._default_value
self._changed = False
def validate(self, value):
"Validate the given value."
if self._type is not None:
self._type(value)
def _parse_validation(self, value):
"""Run validation and return a descriptive error if needed.
"""
try:
self.validate(value)
except Exception, e:
err_message = 'Cannot convert "%s" to %s' % \
(value, self.__class__.__name__.lower())
if e.message:
err_message += ': %s' % e.message
raise ValidationError(err_message)
else:
# Cast to appropriate type before storing
if self._type is not None:
value = self._type(value)
return value
def prepare_value(self):
"""Run on value before it is ever returned to the user.
"""
return self._value
def __call__(self,value=None):
"""Query or set value depending on whether `value` is specified.
"""
if value is None:
return self.prepare_value()
self._value = self._parse_validation(value)
self._changed = (self._value != self._default_value)
@property
def modified(self):
"Whether value has changed from original definition."
return self._changed
def __repr__(self):
"""This class is represented by the underlying repr. Used when
dumping value to file.
"""
return repr(self._value)
class Float(Traitlet):
"""
>>> f = Float(0)
>>> print f.modified
False
>>> f(3)
>>> print f()
3.0
>>> print f.modified
True
>>> f(0)
>>> print f()
0.0
>>> print f.modified
False
>>> try:
... f('a')
... except ValidationError:
... pass
"""
_type = float
class Enum(Traitlet):
"""
>>> c = Enum('a','b','c')
>>> print c()
a
>>> try:
... c('unknown')
... except ValidationError:
... pass
>>> print c.modified
False
>>> c('b')
>>> print c()
b
"""
def __init__(self, *options):
self._options = options
super(Enum,self).__init__(options[0])
def validate(self, value):
if not value in self._options:
raise ValueError('must be one of %s' % str(self._options))
class Module(Traitlet):
"""
>>> m = Module('some.unknown.module')
>>> print m
'some.unknown.module'
>>> m = Module('re')
>>> assert type(m()) is types.ModuleType
"""
_type = str
def prepare_value(self):
try:
module = eval(self._value)
except:
module = None
if type(module) is not types.ModuleType:
raise ValueError("Invalid module name: %s" % self._value)
else:
return module
class URI(Traitlet):
"""
>>> u = URI('http://')
>>> try:
... u = URI('something.else')
... except ValidationError:
... pass
>>> u = URI('http://ipython.scipy.org/')
>>> print u
'http://ipython.scipy.org/'
"""
_regexp = re.compile(r'^[a-zA-Z]+:\/\/')
_type = str
def validate(self, uri):
if not self._regexp.match(uri):
raise ValueError()
class Int(Traitlet):
"""
>>> i = Int(3.5)
>>> print i
3
>>> print i()
3
>>> i = Int('4')
>>> print i
4
>>> try:
... i = Int('a')
... except ValidationError:
... pass
... else:
... raise "Should fail"
"""
_type = int
class Bool(Traitlet):
"""
>>> b = Bool(2)
>>> print b
True
>>> print b()
True
>>> b = Bool('True')
>>> print b
True
>>> b(True)
>>> print b.modified
False
>>> print Bool(0)
False
"""
_type = bool
class Unicode(Traitlet):
"""
>>> u = Unicode(123)
>>> print u
u'123'
>>> u = Unicode('123')
>>> print u.modified
False
>>> u('hello world')
>>> print u
u'hello world'
"""
_type = unicode