6"""A set of utilities to simplify OKS instrospection.
11from .
import ConfigObject
15oks_types[
'bool'] = [
'bool']
16oks_types[
'integer'] = [
's8',
'u8',
's16',
'u16',
's32']
17oks_types[
'long'] = [
'u32',
's64',
'u64']
18oks_types[
'float'] = [
'float',
'double']
19oks_types[
'int-number'] = oks_types[
'integer'] + oks_types[
'long']
20oks_types[
'number'] = \
21 oks_types[
'long'] + oks_types[
'integer'] + oks_types[
'float']
22oks_types[
'time'] = [
'date',
'time']
23oks_types[
'string'] = oks_types[
'time'] + [
'string',
'uid',
'enum',
'class']
25range_regexp = re.compile(
26 r'(?P<s1>-?0?x?[\da-fA-F]+(\.\d+)?)-(?P<s2>-?0?x?[\da-fA-F]+(\.\d+)?)')
30 """Decodes a range string representation, returns a tuple with 2 values.
32 This is the supported format in regexp representation:
33 '([-0x]*\\d+)\\D+-?\\d+'
35 if s.find(
'..') != -1:
38 k = range_regexp.match(s)
41 return (k.group(
's1'), k.group(
's2'))
47 """Converts a value v to integer, irrespectively of its formatting.
49 If the number starts with a '0', we convert it using an octal
50 representation. Else, we try a decimal conversion. If any of these fail,
51 we try an hexa conversion before throwing a ValueError.
55 v -- the value to be converted
56 t -- the python type (int or float) to use in the conversion
62 if isinstance(v, tuple)
or isinstance(v, list):
64 if isinstance(v, str):
81def to_int(v):
return str2integer(v, int, sys.maxsize)
84def to_long(v):
return str2integer(v, int, 0xffffffffffffffff)
88 """Checks the range of the value 'v' to make sure it is inside."""
89 if isinstance(v, list):
95 if type(v) == str
and range_re
is not None:
100 if isinstance(k, tuple):
101 if v >= k[0]
and v <= k[1]:
108 raise ValueError(
'Value %s is not in range %s' % (v, range))
112 """Checks the value v against the relationship parameters in 'rel'."""
113 from conffwk.dal
import DalBase
115 if not isinstance(v, DalBase):
116 raise ValueError(
'Relationships should be DAL objects, but %s is not' %
120 if rel[
'type']
not in v.oksTypes():
121 raise ValueError(
'Object %s is not of type or subtype %s' %
122 (repr(v), rel[
'type']))
126 """Checks the cardinality of a certain attribute or relationship."""
129 if prop[
'multivalue']
and not isinstance(v, list):
130 raise ValueError(
'Multivalued properties must be python lists')
131 elif not prop[
'multivalue']
and isinstance(v, list):
133 'Single valued properties cannot be set with python lists')
137 """Coerces the input value 'v' in the way the attribute expects."""
140 if type(v) != attr[
'python_class']:
142 if attr[
'python_class']
in [int, int]:
144 if isinstance(v, str):
147 'Integer number cannot be assigned an empty string')
150 return attr[
'python_class'](v, 8)
152 return attr[
'python_class'](v, 16)
155 return attr[
'python_class'](v)
157 return attr[
'python_class'](v, 16)
159 v = attr[
'python_class'](v)
161 elif attr[
'python_class']
is bool:
162 if isinstance(v, str):
163 if v
in [
'0',
'false']:
168 v = attr[
'python_class'](v)
171 v = attr[
'python_class'](v)
175 check_range(v, attr[
'range'], attr[
'range_re'], attr[
'python_class'])
178 if attr[
'type'] ==
'class':
180 from .dal
import __dal__
182 attr[
'range_re'], attr[
'python_class'])
184 elif attr[
'type'] ==
'date':
187 time.strptime(v,
'%Y-%m-%d')
188 except ValueError
as e:
190 time.strptime(v,
'%d/%m/%y')
191 except ValueError
as e:
193 time.strptime(v,
'%Y-%b-%d')
194 except ValueError
as e:
196 'Date types should have the format '
197 'dd/mm/yy or yyyy-mm-dd or yyyy-mon-dd: %s' % v)
199 elif attr[
'type'] ==
'time':
202 time.strptime(v,
'%Y-%m-%d %H:%M:%S')
203 except ValueError
as e:
205 time.strptime(v,
'%d/%m/%y %H:%M:%S')
206 except ValueError
as e:
208 time.strptime(v,
'%Y-%b-%d %H:%M:%S')
209 except ValueError
as e:
211 'Time types should have the format dd/mm/yy HH:MM:SS '
212 'or yyyy-mm-dd HH:MM:SS or yyyy-mon-dd HH:MM:SS:'
219 """Given a schema of a class, maps coercion functions from libpyconffwk."""
223 schema[
'mapping'] = {}
225 for k, v
in list(schema[
'attribute'].items()):
228 if getname
in oks_types[
'string']:
233 v[
'co_get_method'] = getattr(cls,
'get_' + getname)
234 v[
'co_set_method'] = getattr(cls,
'set_' + typename)
237 v[
'co_get_method'] = getattr(cls,
'get_' + getname)
238 v[
'co_set_method'] = getattr(cls,
'set_' + typename)
240 if v[
'type']
in oks_types[
'string']:
241 v[
'python_class'] = str
242 elif v[
'type']
in oks_types[
'bool']:
243 v[
'python_class'] = bool
244 elif v[
'type']
in oks_types[
'integer']:
245 v[
'python_class'] = to_int
246 elif v[
'type']
in oks_types[
'long']:
247 v[
'python_class'] = to_long
248 elif v[
'type']
in oks_types[
'float']:
249 v[
'python_class'] = float
254 if v[
'type'] ==
'string':
255 v[
'range_re'] = re.compile(v[
'range'])
257 v[
'range'] = [
decode_range(j)
for j
in v[
'range'].split(
',')]
258 for j
in range(len(v[
'range'])):
259 if isinstance(v[
'range'][j], str):
260 v[
'range'][j] = v[
'python_class'](v[
'range'][j])
261 elif len(v[
'range'][j]) == 1:
262 v[
'range'][j] = v[
'python_class'](v[
'range'][j][0])
264 v[
'range'][j] = (v[
'python_class'](v[
'range'][j][0]),
265 v[
'python_class'](v[
'range'][j][1]))
268 elif v[
'type']
in oks_types[
'int-number']:
269 if v[
'type'] ==
's8':
270 v[
'range'] = [(-2**7, (2**7)-1)]
271 if v[
'type'] ==
'u8':
272 v[
'range'] = [(0, (2**8)-1)]
273 if v[
'type'] ==
's16':
274 v[
'range'] = [(-2**15, (2**15)-1)]
275 if v[
'type'] ==
'u16':
276 v[
'range'] = [(0, (2**16)-1)]
277 if v[
'type'] ==
's32':
278 v[
'range'] = [(-2**31, (2**31)-1)]
279 if v[
'type'] ==
'u32':
280 v[
'range'] = [(0, (2**32)-1)]
281 if v[
'type'] ==
's64':
282 v[
'range'] = [(-2**63, (2**63)-1)]
283 if v[
'type'] ==
'u64':
284 v[
'range'] = [(0, (2**64)-1)]
289 if v[
'type']
in oks_types[
'string']:
291 elif v[
'multivalue']:
292 v[
'init-value'] = [
coerce(j, v)
293 for j
in v[
'init-value'].split(
',')]
295 v[
'init-value'] =
coerce(v[
'init-value'], v)
296 except ValueError
as e:
297 logging.warning(
'Initial value of "%s.%s" could not be '
303 if not v[
'init-value']
and v[
'type'] ==
'date':
305 v[
'init-value'] == datetime.date.today().isoformat()
309 if not v[
'init-value']
and v[
'type'] ==
'time':
311 now = datetime.datetime.today()
312 now.replace(microsecond=0)
313 v[
'init-value'] == now.isoformat(sep=
' ')
315 for v
in list(schema[
'relation'].values()):
317 v[
'co_get_method'] = getattr(cls,
'get_objs')
318 v[
'co_set_method'] = getattr(cls,
'set_objs')
321 v[
'co_get_method'] = getattr(cls,
'get_obj')
322 v[
'co_set_method'] = getattr(cls,
'set_obj')
328 """Defines a cache for all known schemas at a certain time.
332 """Initializes the cache with information from the Configuration
335 This method will browse for all declared classes in the Configuration
336 object given as input and will setup the schema for all known classes.
337 After this you can still update the cache using the update() method.
341 conffwk -- The conffwk.Configuration object to use as base for the
344 all -- A boolean indicating if I should store all the attributes and
345 relations from a certain class or just the ones directly associated
353 """Updates this cache with information from the Configuration object.
355 This method will add new classes not yet know to this cache. Classes
356 with existing names will not be added. No warning is generated (this
357 should be done by the OKS layer in any case.
359 for k
in conffwk.classes():
360 if k
in list(self.
data.keys()):
363 self.
data[k][
'attribute'] = conffwk.attributes(k, self.
all)
364 self.
data[k][
'relation'] = conffwk.relations(k, self.
all)
365 self.
data[k][
'superclass'] = conffwk.superclasses(k, self.
all)
366 self.
data[k][
'subclass'] = conffwk.subclasses(k, self.
all)
370 """Updates this cache with information for DAL.
372 This method will add new DAL classes not yet know to this cache.
373 Classes with existing DAL representations will not be touched.
375 from .dal
import generate
379 in list(self.
data.keys())
380 if 'dal' in self.
data[k]])
383 self.
data[k.pyclassName()][
'dal'] = k
386 """Gets the description of a certain class."""
387 return self.
data[key]
390 """Prints a nice display of myself"""
391 return '%s: %d classes loaded' % \
392 (self.__class__.__name__, len(self.
data)) +
'\n' + \
393 str(list(self.
data.keys()))
__init__(self, conffwk, all=True)
update_dal(self, conffwk)
check_range(v, range, range_re, pytype)
check_cardinality(v, prop)
map_coercion(class_name, schema)