Files
deb-python-wsme/wsme/types.py
Christophe de Vienne 22f9ea3d67 Documented most of the api
2011-10-11 16:15:13 +02:00

159 lines
4.2 KiB
Python

import datetime
import decimal
import weakref
import inspect
binary = object()
pod_types = [str, unicode, int, float, bool]
dt_types = [datetime.date, datetime.time, datetime.datetime]
extra_types = [binary, decimal.Decimal]
native_types = pod_types + dt_types + extra_types
complex_types = []
array_types = []
def iscomplex(datatype):
return hasattr(datatype, '_wsme_attributes')
class wsproperty(property):
"""
A specialised :class:`property` to define typed-property on complex types.
Example::
class MyComplexType(object):
def get_aint(self):
return self._aint
def set_aint(self, value):
assert avalue < 10 # Dummy input validation
self._aint = value
aint = wsproperty(int, get_aint, set_aint, mandatory=True)
"""
def __init__(self, datatype, fget, fset=None,
mandatory=False, doc=None):
property.__init__(self, fget, fset)
self.datatype = datatype
self.mandatory = mandatory
class wsattr(object):
"""
Complex type attribute definition when the datatype only is not
enough.
Example::
class MyComplexType(object):
optionalvalue = int
mandatoryvalue = wsattr(int, mandatory=True)
"""
def __init__(self, datatype, mandatory=False):
self.datatype = datatype
self.mandatory = mandatory
def iswsattr(attr):
if inspect.isfunction(attr) or inspect.ismethod(attr):
return False
return True
def sort_attributes(class_, attributes):
"""Sort a class attributes list.
3 mechanisms are attempted :
#. Look for a _wsme_attr_order attribute on the class_. This allow
to define an arbitrary order of the attributes (usefull for
generated types).
#. Access the object source code to find the declaration order.
#. Sort by alphabetically"""
if not len(attributes):
return
attrs = dict(attributes)
if hasattr(class_, '_wsme_attr_order'):
names_order = class_._wsme_attr_order
else:
names = attrs.keys()
names_order = []
try:
lines = []
for line in inspect.getsourcelines(class_)[0]:
line = line.strip().replace(" ", "")
if '=' in line:
aname = line[:line.index('=')]
if aname in names and aname not in names_order:
names_order.append(aname)
assert len(names_order) == len(names)
except (TypeError, IOError):
names_order = list(names)
names_order.sort()
attributes[:] = [(name, attrs[name]) for name in names_order]
def inspect_class(class_):
"""Extract a list of (name, wsattr|wsproperty) for the given class_"""
attributes = []
for name, attr in inspect.getmembers(class_, iswsattr):
if name.startswith('_'):
continue
if isinstance(attr, wsattr):
attrdef = attr
elif isinstance(attr, wsproperty):
attrdef = attr
else:
if attr not in native_types and inspect.isclass(attr):
print name, attr
register_type(attr)
attrdef = wsattr(attr)
attributes.append((name, attrdef))
sort_attributes(class_, attributes)
return attributes
def register_type(class_):
"""
Make sure a type is registered.
It is automatically called by :class:`expose() <wsme.expose>`
and :class:`validate() <wsme.validate>`.
Unless you want to control when the class inspection is done there
is no need to call it.
"""
if class_ is None or \
class_ in native_types or \
hasattr(class_, '_wsme_attributes'):
return
if isinstance(class_, list):
if len(class_) != 1:
raise ValueError("Cannot register type %s" % repr(class_))
register_type(class_[0])
array_types.append(class_[0])
return
class_._wsme_attributes = inspect_class(class_)
complex_types.append(weakref.ref(class_))
def list_attributes(class_):
"""
Returns a list of a complex type attributes.
"""
if not hasattr(class_, '_wsme_attributes'):
register_type(class_)
return class_._wsme_attributes