##// END OF EJS Templates
Complete first pass on testing system. All tests pass on my box. Whew....
Complete first pass on testing system. All tests pass on my box. Whew. We now have a first cut of a functioning testing system. Many examples had to be disabled (with a proper decorator), but now we can begin building a robust test suite for all of IPython. The big remaining todo is to fix the handling of the ipython/test namespace synchronization, which is still not 100% correct. Once that is done, some of the currently skipped tests will be reactivated.

File last commit:

r1253:9ce3a358
r1435:da07a079
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