Add functions to register/unregister new base types. Closes #124342.

  • "register_base_type" to register into Yams structures a new base type
  • "unregister_base_type" function to unregister it (mainly for test purpose)
  • existing "_make_type" is now public and documented
authorSylvain Thénault <sylvain.thenault@logilab.fr>
changeset2c482979bf58
branchdefault
phasepublic
hiddenno
parent revision#7e4de6a474fd [buildobjs] compute RDEF_PROPERTIES and REL_PROPERTIES at runtime
child revision#7a7e572b5002 [diff cleanups] drop overkill ATTR_CARD_CONVERTER dict
files modified by this revision
__init__.py
buildobjs.py
test/unittest_schema.py
# HG changeset patch
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1366795903 -7200
# Wed Apr 24 11:31:43 2013 +0200
# Node ID 2c482979bf58f3f3f7ec07217d7dc5c91b81a3a3
# Parent 7e4de6a474fd3e89b7faa6932b2642dd3a3283a5
Add functions to register/unregister new base types. Closes #124342.

* "register_base_type" to register into Yams structures a new base type
* "unregister_base_type" function to unregister it (mainly for test purpose)
* existing "_make_type" is now public and documented

diff --git a/__init__.py b/__init__.py
@@ -1,6 +1,6 @@
1 -# copyright 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 +# copyright 2004-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
3  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
4  #
5  # This file is part of yams.
6  #
7  # yams is free software: you can redistribute it and/or modify it under the
@@ -87,5 +87,31 @@
8          msg = 'using string instead of cubicweb.OBJECT'
9          if cls:
10              msg += ' for attribute %s of class %s' % (attr, cls.__name__)
11          warn(DeprecationWarning, msg)
12          return SUBJECT
13 +
14 +def register_base_type(name, parameters=(), check_function=None):
15 +    """register a yams base (final) type. You'll have to call
16 +    base_type_class to generate the class.
17 +    """
18 +    from yams.schema import RelationDefinitionSchema
19 +    from yams.constraints import BASE_CHECKERS, yes
20 +    # Add the datatype to yams base types
21 +    assert name not in BASE_TYPES, '%s alreadt in BASE_TYPES %s' % (name, BASE_TYPES)
22 +    BASE_TYPES.add(name)
23 +    # Add the new datatype to the authorized types of RelationDefinitionSchema
24 +    if not isinstance(parameters, dict):
25 +        # turn tuple/list into dict with None values
26 +        parameters = dict((p, None) for p in parameters)
27 +    RelationDefinitionSchema.BASE_TYPE_PROPERTIES[name] = parameters
28 +    # Add a yams checker or yes is not specified
29 +    BASE_CHECKERS[name] = check_function or yes
30 +
31 +def unregister_base_type(name):
32 +    """Unregister a yams base (final) type"""
33 +    from yams.schema import RelationDefinitionSchema
34 +    from yams.constraints import BASE_CHECKERS
35 +    assert name in BASE_TYPES, '%s not in BASE_TYPES %s' % (name, BASE_TYPES)
36 +    BASE_TYPES.remove(name)
37 +    RelationDefinitionSchema.BASE_TYPE_PROPERTIES.pop(name)
38 +    BASE_CHECKERS.pop(name)
diff --git a/buildobjs.py b/buildobjs.py
@@ -731,28 +731,46 @@
39              _add_constraint(kwargs, SizeConstraint(max=maxsize))
40 
41      def __repr__(self):
42          return '<%(name)s(%(etype)s)>' % self.__dict__
43 
44 -# build a specific class for each base type
45 -def _make_type(etype):
46 +
47 +def make_type(etype):
48 +    """create a python class for a Yams base type.
49 +
50 +    Notice it is now possible to create a specific type with user-defined
51 +    behaviour, e.g.:
52 +
53 +        Geometry = make_type('Geometry') # (c.f. postgis)
54 +
55 +    will allow the use of:
56 +
57 +        Geometry(geom_type='POINT')
58 +
59 +    in a Yams schema, provided in this example that `geom_type` is specified to
60 +    the :func:`yams.register_base_type` function which should be called prior to
61 +    make_type.
62 +    """
63 +    assert etype in BASE_TYPES
64      return type(etype, (AbstractTypedAttribute,), {'etype' : etype})
65 
66 -String = _make_type('String')
67 -Password = _make_type('Password')
68 -Bytes = _make_type('Bytes')
69 -Int = _make_type('Int')
70 -BigInt = _make_type('BigInt')
71 -Float = _make_type('Float')
72 -Boolean = _make_type('Boolean')
73 -Decimal = _make_type('Decimal')
74 -Time = _make_type('Time')
75 -Date = _make_type('Date')
76 -Datetime = _make_type('Datetime')
77 -TZTime = _make_type('TZTime')
78 -TZDatetime = _make_type('TZDatetime')
79 -Interval = _make_type('Interval')
80 +
81 +# build a specific class for each base type
82 +String = make_type('String')
83 +Password = make_type('Password')
84 +Bytes = make_type('Bytes')
85 +Int = make_type('Int')
86 +BigInt = make_type('BigInt')
87 +Float = make_type('Float')
88 +Boolean = make_type('Boolean')
89 +Decimal = make_type('Decimal')
90 +Time = make_type('Time')
91 +Date = make_type('Date')
92 +Datetime = make_type('Datetime')
93 +TZTime = make_type('TZTime')
94 +TZDatetime = make_type('TZDatetime')
95 +Interval = make_type('Interval')
96 
97 
98  # provides a RichString factory for convenience
99 
100 
diff --git a/test/unittest_schema.py b/test/unittest_schema.py
@@ -23,15 +23,18 @@
101  from logilab.common.testlib import TestCase, unittest_main
102 
103  from copy import copy, deepcopy
104  from tempfile import mktemp
105 
106 -from yams import BadSchemaDefinition
107 -from yams.buildobjs import (register_base_types, EntityType, RelationType,
108 -                            RelationDefinition, _add_relation)
109 -from yams.schema import *
110 -from yams.constraints import *
111 +from yams import (BASE_TYPES, ValidationError, BadSchemaDefinition,
112 +                  register_base_type, unregister_base_type)
113 +from yams.buildobjs import (register_base_types, make_type, _add_relation,
114 +                            EntityType, RelationType, RelationDefinition)
115 +from yams.schema import Schema, RelationDefinitionSchema
116 +from yams.interfaces import IVocabularyConstraint
117 +from yams.constraints import (BASE_CHECKERS, SizeConstraint, RegexpConstraint,
118 +                              StaticVocabularyConstraint, IntervalBoundConstraint)
119  from yams.reader import SchemaLoader
120 
121 
122  # build a dummy schema ########################################################
123 
@@ -550,7 +553,33 @@
124          self.assertEqual(subj_types,
125                            [('Bug', ['Bug', 'Project', 'Story']),
126                             ('Project', ['Bug', 'Project', 'Story']),
127                             ('Story', ['Bug', 'Project', 'Story'])])
128 
129 +
130 +class CustomTypeTC(TestCase):
131 +
132 +    def tearDown(self):
133 +        try:
134 +            unregister_base_type('Test')
135 +        except AssertionError:
136 +            pass
137 +
138 +    def test_register_base_type(self):
139 +        register_base_type('Test', ('test1', 'test2'))
140 +        self.assertIn('Test', BASE_TYPES)
141 +        self.assertIn('Test', RelationDefinitionSchema.BASE_TYPE_PROPERTIES)
142 +        self.assertEqual(RelationDefinitionSchema.BASE_TYPE_PROPERTIES['Test'],
143 +                         {'test1': None, 'test2': None})
144 +        self.assertTrue('Test' in BASE_CHECKERS)
145 +
146 +    def test_make_base_type_class(self):
147 +        register_base_type('Test', ('test1', 'test2'))
148 +        Test = make_type('Test')
149 +        self.assertIsInstance(Test, type)
150 +        self.assertEqual(Test.etype, 'Test')
151 +        t = Test(test1=1)
152 +        self.assertEqual(t.test1, 1)
153 +        self.assertFalse(hasattr(t, 'test2'))
154 +
155  if __name__ == '__main__':
156      unittest_main()