1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
from copy import copy
from warnings import warn
class SchemaStrategy:
"""
base schema strategy. This contains the common interface for
all subclasses:
* match_schema
* match_object
* __init__
* add_schema
* add_object
* to_schema
* __eq__
"""
KEYWORDS = ('type',)
@classmethod
def match_schema(cls, schema):
raise NotImplementedError("'match_schema' not implemented")
@classmethod
def match_object(cls, obj):
raise NotImplementedError("'match_object' not implemented")
def __init__(self, node_class):
self.node_class = node_class
self._extra_keywords = {}
def add_schema(self, schema):
self._add_extra_keywords(schema)
def _add_extra_keywords(self, schema):
for keyword, value in schema.items():
if keyword in self.KEYWORDS:
continue
elif keyword not in self._extra_keywords:
self._extra_keywords[keyword] = value
elif self._extra_keywords[keyword] != value:
warn(('Schema incompatible. Keyword {0!r} has conflicting '
'values ({1!r} vs. {2!r}). Using {1!r}').format(
keyword, self._extra_keywords[keyword], value))
def add_object(self, obj):
pass
def to_schema(self):
return copy(self._extra_keywords)
def __eq__(self, other):
""" Required for SchemaBuilder.__eq__ to work properly """
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
class TypedSchemaStrategy(SchemaStrategy):
"""
base schema strategy class for scalar types. Subclasses define
these two class constants:
* `JS_TYPE`: a valid value of the `type` keyword
* `PYTHON_TYPE`: Python type objects - can be a tuple of types
"""
@classmethod
def match_schema(cls, schema):
return schema.get('type') == cls.JS_TYPE
@classmethod
def match_object(cls, obj):
return isinstance(obj, cls.PYTHON_TYPE)
def to_schema(self):
schema = super().to_schema()
schema['type'] = self.JS_TYPE
return schema
|