##// END OF EJS Templates
Merge update.
Merge update.

File last commit:

r1253:9ce3a358
r1615:5fb7caae merge
Show More
traitlets.py
322 lines | 6.7 KiB | text/x-python | PythonLexer
Stefan van der Walt
Merge sconfig branch.
r1253 """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