You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/contrib/spyne/model/_base.py

1071 lines
37 KiB

#
# spyne - Copyright (C) Spyne contributors.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#
"""This module contains the ModelBase class and other building blocks for
defining models.
"""
from __future__ import print_function
import logging
logger = logging.getLogger(__name__)
import re
import decimal
import threading
import spyne.const.xml
from copy import deepcopy
from collections import OrderedDict
from spyne import const
from spyne.util import Break, six
from spyne.util.cdict import cdict
from spyne.util.odict import odict
from spyne.const.xml import DEFAULT_NS
def _decode_pa_dict(d):
"""Decodes dict passed to prot_attrs.
>>> _decode_pa_dict({})
cdict({})
>>> _decode_pa_dict({1: 2)})
cdict({1: 2})
>>> _decode_pa_dict({(1,2): 3)})
cdict({1: 3, 2: 3})
"""
retval = cdict()
for k, v in d.items():
if isinstance(k, (frozenset, tuple)):
for subk in k:
retval[subk] = v
for k, v in d.items():
if not isinstance(k, (frozenset, tuple)):
retval[k] = v
return retval
class AttributesMeta(type(object)):
NULLABLE_DEFAULT = True
def __new__(cls, cls_name, cls_bases, cls_dict):
# Mapper args should not be inherited.
if not 'sqla_mapper_args' in cls_dict:
cls_dict['sqla_mapper_args'] = None
rd = {}
for k in list(cls_dict.keys()):
if k in ('parser', 'cast'):
rd['parser'] = cls_dict.pop(k)
continue
if k in ('sanitize', 'sanitizer'):
rd['sanitizer'] = cls_dict.pop(k)
continue
if k == 'logged':
rd['logged'] = cls_dict.pop(k)
continue
retval = super(AttributesMeta, cls).__new__(cls, cls_name, cls_bases,
cls_dict)
for k, v in rd.items():
if v is None:
setattr(retval, k, None)
else:
setattr(retval, k, staticmethod(v))
return retval
def __init__(self, cls_name, cls_bases, cls_dict):
# you will probably want to look at ModelBase._s_customize as well.
if not hasattr(self, '_method_config_do'):
self._method_config_do = None
nullable = cls_dict.get('nullable', None)
nillable = cls_dict.get('nillable', None)
if nullable is not None:
assert nillable is None or nullable == nillable
self._nullable = nullable
elif nillable is not None:
assert nullable is None or nullable == nillable
self._nullable = nillable
if not hasattr(self, '_nullable'):
self._nullable = None
if not hasattr(self, '_default_factory'):
self._default_factory = None
if not hasattr(self, '_html_cloth'):
self._html_cloth = None
if not hasattr(self, '_html_root_cloth'):
self._html_root_cloth = None
if 'html_cloth' in cls_dict:
self.set_html_cloth(cls_dict.pop('html_cloth'))
if 'html_root_cloth' in cls_dict:
self.set_html_cloth(cls_dict.pop('html_root_cloth'))
if not hasattr(self, '_xml_cloth'):
self._xml_cloth = None
if not hasattr(self, '_xml_root_cloth'):
self._xml_root_cloth = None
if 'xml_cloth' in cls_dict:
self.set_xml_cloth(cls_dict.pop('xml_cloth'))
if 'xml_root_cloth' in cls_dict:
self.set_xml_cloth(cls_dict.pop('xml_root_cloth'))
if 'method_config_do' in cls_dict and \
cls_dict['method_config_do'] is not None:
cls_dict['method_config_do'] = \
staticmethod(cls_dict['method_config_do'])
super(AttributesMeta, self).__init__(cls_name, cls_bases, cls_dict)
def get_nullable(self):
return (self._nullable if self._nullable is not None else
self.NULLABLE_DEFAULT)
def set_nullable(self, what):
self._nullable = what
nullable = property(get_nullable, set_nullable)
def get_nillable(self):
return self.nullable
def set_nillable(self, what):
self.nullable = what
nillable = property(get_nillable, set_nillable)
def get_default_factory(self):
return self._default_factory
def set_default_factory(self, what):
self._default_factory = staticmethod(what)
default_factory = property(get_default_factory, set_default_factory)
def get_html_cloth(self):
return self._html_cloth
def set_html_cloth(self, what):
from spyne.protocol.cloth.to_cloth import ClothParserMixin
cm = ClothParserMixin.from_html_cloth(what)
if cm._root_cloth is not None:
self._html_root_cloth = cm._root_cloth
elif cm._cloth is not None:
self._html_cloth = cm._cloth
else:
raise Exception("%r is not a suitable cloth", what)
html_cloth = property(get_html_cloth, set_html_cloth)
def get_html_root_cloth(self):
return self._html_root_cloth
html_root_cloth = property(get_html_root_cloth)
def get_xml_cloth(self):
return self._xml_cloth
def set_xml_cloth(self, what):
from spyne.protocol.cloth.to_cloth import ClothParserMixin
cm = ClothParserMixin.from_xml_cloth(what)
if cm._root_cloth is not None:
self._xml_root_cloth = cm._root_cloth
elif cm._cloth is not None:
self._xml_cloth = cm._cloth
else:
raise Exception("%r is not a suitable cloth", what)
xml_cloth = property(get_xml_cloth, set_xml_cloth)
def get_xml_root_cloth(self):
return self._xml_root_cloth
xml_root_cloth = property(get_xml_root_cloth)
def get_method_config_do(self):
return self._method_config_do
def set_method_config_do(self, what):
if what is None:
self._method_config_do = None
else:
self._method_config_do = staticmethod(what)
method_config_do = property(get_method_config_do, set_method_config_do)
class ModelBaseMeta(type(object)):
def __getitem__(self, item):
return self.customize(**item)
def customize(self, **kwargs):
"""Duplicates cls and overwrites the values in ``cls.Attributes`` with
``**kwargs`` and returns the new class."""
cls_name, cls_bases, cls_dict = self._s_customize(**kwargs)
return type(cls_name, cls_bases, cls_dict)
@six.add_metaclass(ModelBaseMeta)
class ModelBase(object):
"""The base class for type markers. It defines the model interface for the
interface generators to use and also manages class customizations that are
mainly used for defining constraints on input values.
"""
__orig__ = None
"""This holds the original class the class .customize()d from. Ie if this is
None, the class is not a customize()d one."""
__extends__ = None
"""This holds the original class the class inherited or .customize()d from.
This is different from __orig__ because it's only set when
``cls.is_default(cls) == False``"""
__namespace__ = None
"""The public namespace of this class. Use ``get_namespace()`` instead of
accessing it directly."""
__type_name__ = None
"""The public type name of the class. Use ``get_type_name()`` instead of
accessing it directly."""
Value = type(None)
"""The value of this type is an instance of this class"""
# These are not the xml schema defaults. The xml schema defaults are
# considered in XmlSchema's add() method. the defaults here are to reflect
# what people seem to want most.
#
# Please note that min_occurs and max_occurs must be validated in the
# ComplexModelBase deserializer.
@six.add_metaclass(AttributesMeta)
class Attributes(object):
"""The class that holds the constraints for the given type."""
_wrapper = False
# when skip_wrappers=True is passed to a protocol, these objects
# are skipped. just for internal use.
_explicit_type_name = False
# set to true when type_name is passed to customize() call.
out_type = None
"""Override serialization type. Usually, this designates the return type
of the callable in the `sanitizer` attribute. If this is a two-way type,
it may be a good idea to also use the `parser` attribute to perform
reverse conversion."""
default = None
"""The default value if the input is None.
Please note that this default is UNCONDITIONALLY applied in class
initializer. It's recommended to at least make an effort to use this
only in customized classes and not in original models.
"""
default_factory = None
"""The callable that produces a default value if the value is None.
The warnings in ``default`` apply here as well."""
db_default = None
"""The default value used only when persisting the value if it is
``None``.
Only works for primitives. Unlike ``default`` this can also be set to a
callable that takes no arguments according to SQLAlchemy docs."""
nillable = None
"""Set this to false to reject null values. Synonyms with
``nullable``. True by default. The default value can be changed by
setting ``AttributesMeta.NULLABLE_DEFAULT``."""
min_occurs = 0
"""Set this to 1 to make this object mandatory. Can be set to any
positive integer. Note that an object can still be null or empty, even
if it's there."""
max_occurs = 1
"""Can be set to any strictly positive integer. Values greater than 1
will imply an iterable of objects as native python type. Can be set to
``decimal.Decimal("inf")`` for arbitrary number of arguments."""
schema_tag = spyne.const.xml.XSD('element')
"""The tag used to add a primitives as child to a complex type in the
xml schema."""
translations = None
"""A dict that contains locale codes as keys and translations of field
names to that language as values.
"""
sub_ns = None
"""An Xml-specific attribute that specifies which namespace should be
used for field names in classes.
"""
sub_name = None
"""This specifies which string should be used as field name when this
type is seriazed under a ComplexModel.
"""
wsdl_part_name = None
"""This specifies which string should be used as wsdl message part name when this
type is serialized under a ComplexModel ie."parameters".
"""
sqla_column_args = None
"""A dict that will be passed to SQLAlchemy's ``Column`` constructor as
``**kwargs``.
"""
exc_mapper = False
"""If true, this field will be excluded from the table mapper of the
parent class.
"""
exc_table = False
"""DEPRECATED !!! Use ``exc_db`` instead."""
exc_db = False
"""If ``True``, this field will not be persisted to the database. This
attribute only makes sense in a subfield of a ``ComplexModel`` subclass.
"""
exc_interface = False
"""If `True`, this field will be excluded from the interface
document."""
exc = False
"""If `True`, this field will be excluded from all serialization or
deserialization operations. See `prot_attrs` to make this only apply to
a specific protocol class or instance."""
logged = True
"""If `False`, this object will be ignored in ``log_repr``, mostly used
for logging purposes.
* Primitives can have logger=``'...'`` which will
always log the value as ``(...)``.
* ``AnyDict`` can have one of
``('keys', 'keys-full', 'values', 'values-full, 'full')`` as logger
value where for ``'keys'`` and ``'values'`` the output of ``keys()``
and ``values()`` will be logged up to MAX_DICT_ELEMENT_NUM number of
elements and for ``'full'`` variants, all of the contents of the dict
will be logged will be logged
* ``Array`` can also have ``logger='full'`` where all of the value
will be logged where as for simple ``logger=True`` only
MAX_ARRAY_ELEMENT_NUM elements will be logged.
* For ``ComplexModel`` subclasses sent as first value to log_repr,
``logger=False`` means a string of form ``ClassName(...)`` will be
logged.
"""
sanitizer = None
"""A callable that takes the associated native type and returns the
parsed value. Only called during serialization."""
parser = None
"""A callable that takes the associated native type and returns the
parsed value. Only called during deserialization."""
unique = None
"""If True, this object will be set as unique in the database schema
with default indexing options. If the value is a string, it will be
used as the indexing method to create the unique index. See sqlalchemy
documentation on how to create multi-column unique constraints.
"""
db_type = None
"""When not None, it overrides Spyne's own mapping from Spyne types to
SQLAlchemy types. It's a standard SQLAlchemy type marker, e.g.
``sqlalchemy.Integer``.
"""
table_name = None
"""Database table name."""
xml_choice_group = None
"""When not None, shares the same <choice> tag with fields with the same
xml_choice_group value.
"""
index = None
"""Can be ``True``, a string, or a tuple of two strings.
* If True, this object will be set as indexed in the database schema
with default options.
* If the value is a string, the value will denote the indexing method
used by the database. Should be one of:
('btree', 'gin', 'gist', 'hash', 'spgist')
See: http://www.postgresql.org/docs/9.2/static/indexes-types.html
* If the value is a tuple of two strings, the first value will denote
the index name and the second value will denote the indexing method as
above.
"""
read_only = False
"""If True, the attribute won't be initialized from outside values.
Set this to ``True`` for e.g. read-only properties."""
prot_attrs = None
"""Customize child attributes for protocols. It's a dict of dicts.
The key is either a ProtocolBase subclass or a ProtocolBase instance.
Instances override classes."""
pa = None
"""Alias for prot_attrs."""
empty_is_none = False
"""When the incoming object is empty (e.g. '' for strings) treat it as
None. No effect (yet) for outgoing values."""
order = None
"""An integer that's passed to ``_type_info.insert()`` as first argument
when not None. ``.append()`` is used otherwise."""
validate_on_assignment = False
"""Perform validation on assignment (i.e. all the time) instead of on
just serialization"""
polymap = {}
"""A dict of classes that override polymorphic substitions for classes
given as keys to classes given as values."""
class Annotations(object):
"""The class that holds the annotations for the given type."""
__use_parent_doc__ = False
"""If equal to True and doc is empty, Annotations will use __doc__
from parent. Set it to False to avoid this mechanism. This is a
convenience option"""
doc = ""
"""The public documentation for the given type."""
appinfo = None
"""Any object that carries app-specific info."""
class Empty(object):
pass
_force_own_namespace = None
@classmethod
def ancestors(cls):
"""Returns a list of parent classes in child-to-parent order."""
retval = []
extends = cls.__extends__
while extends is not None:
retval.append(extends)
extends = extends.__extends__
return retval
@staticmethod
def is_default(cls):
return True
@classmethod
def get_namespace_prefix(cls, interface):
"""Returns the namespace prefix for the given interface. The
get_namespace_prefix of the interface class generates a prefix if none
is defined.
"""
ns = cls.get_namespace()
retval = interface.get_namespace_prefix(ns)
return retval
@classmethod
def get_namespace(cls):
"""Returns the namespace of the class. Defaults to the python module
name."""
return cls.__namespace__
@classmethod
def _fill_empty_type_name(cls, parent_ns, parent_tn, k):
cls.__namespace__ = parent_ns
cls.__type_name__ = "%s_%s%s" % (parent_tn, k, const.TYPE_SUFFIX)
extends = cls.__extends__
while extends is not None and extends.__type_name__ is ModelBase.Empty:
cls.__extends__._fill_empty_type_name(cls.get_namespace(),
cls.get_type_name(), k + const.PARENT_SUFFIX)
extends = extends.__extends__
# TODO: rename to "resolve_identifier"
@staticmethod
def resolve_namespace(cls, default_ns, tags=None):
"""This call finalizes the namespace assignment. The default namespace
is not available until the application calls populate_interface method
of the interface generator.
"""
if tags is None:
tags = set()
elif cls in tags:
return False
tags.add(cls)
if cls.__namespace__ is spyne.const.xml.DEFAULT_NS:
cls.__namespace__ = default_ns
if (cls.__namespace__ in spyne.const.xml.PREFMAP and
not cls.is_default(cls)):
cls.__namespace__ = default_ns
if cls.__namespace__ is None:
ret = []
for f in cls.__module__.split('.'):
if f.startswith('_'):
break
else:
ret.append(f)
cls.__namespace__ = '.'.join(ret)
if cls.__namespace__ is None or len(cls.__namespace__) == 0:
cls.__namespace__ = default_ns
if cls.__namespace__ is None or len(cls.__namespace__) == 0:
raise ValueError("You need to explicitly set %r.__namespace__" % cls)
# print(" resolve ns for %r to %r" % (cls, cls.__namespace__))
if getattr(cls, '__extends__', None) != None:
cls.__extends__.resolve_namespace(cls.__extends__, default_ns, tags)
return True
@classmethod
def get_type_name(cls):
"""Returns the class name unless the __type_name__ attribute is defined.
"""
retval = cls.__type_name__
if retval is None:
retval = cls.__name__
return retval
# FIXME: Rename this to get_type_name_with_ns_pref
@classmethod
def get_type_name_ns(cls, interface):
"""Returns the type name with a namespace prefix, separated by a column.
"""
if cls.get_namespace() != None:
return "%s:%s" % (cls.get_namespace_prefix(interface),
cls.get_type_name())
@classmethod
def get_element_name(cls):
return cls.Attributes.sub_name or cls.get_type_name()
@classmethod
def get_wsdl_part_name(cls):
return cls.Attributes.wsdl_part_name or cls.get_element_name()
@classmethod
def get_element_name_ns(cls, interface):
ns = cls.Attributes.sub_ns or cls.get_namespace()
if ns is DEFAULT_NS:
ns = interface.get_tns()
if ns is not None:
pref = interface.get_namespace_prefix(ns)
return "%s:%s" % (pref, cls.get_element_name())
@classmethod
def to_bytes(cls, value):
"""
Returns str(value). This should be overridden if this is not enough.
"""
return six.binary_type(value)
@classmethod
def to_unicode(cls, value):
"""
Returns unicode(value). This should be overridden if this is not enough.
"""
return six.text_type(value)
@classmethod
def get_documentation(cls):
if cls.Annotations.doc:
return cls.Annotations.doc
elif cls.Annotations.__use_parent_doc__:
return cls.__doc__
else:
return ''
@classmethod
def _s_customize(cls, **kwargs):
"""Sanitizes customization parameters of the class it belongs to.
Doesn't perform any actual customization.
"""
def _log_debug(s, *args):
logger.debug("\t%s: %s" % (cls.get_type_name(), s), *args)
cls_dict = odict({'__module__': cls.__module__, '__doc__': cls.__doc__})
if getattr(cls, '__orig__', None) is None:
cls_dict['__orig__'] = cls
else:
cls_dict['__orig__'] = cls.__orig__
class Attributes(cls.Attributes):
_explicit_type_name = False
if cls.Attributes.translations is None:
Attributes.translations = {}
if cls.Attributes.sqla_column_args is None:
Attributes.sqla_column_args = (), {}
else:
Attributes.sqla_column_args = deepcopy(
cls.Attributes.sqla_column_args)
cls_dict['Attributes'] = Attributes
# properties get reset every time a new class is defined. So we need
# to reinitialize them explicitly.
for k in ('nillable', '_xml_cloth', '_xml_root_cloth', '_html_cloth',
'_html_root_cloth'):
v = getattr(cls.Attributes, k)
if v is not None:
setattr(Attributes, k, v)
class Annotations(cls.Annotations):
pass
cls_dict['Annotations'] = Annotations
# get protocol attrs
prot = kwargs.get('protocol', None)
if prot is None:
prot = kwargs.get('prot', None)
if prot is None:
prot = kwargs.get('p', None)
if prot is not None and len(prot.type_attrs) > 0:
# if there is a class customization from protocol, do it
type_attrs = prot.type_attrs.copy()
type_attrs.update(kwargs)
_log_debug("kwargs %r => %r from prot typeattr %r",
kwargs, type_attrs, prot.type_attrs)
kwargs = type_attrs
# the ones that wrap values in staticmethod() should be added to
# AttributesMeta initializer
for k, v in kwargs.items():
if k.startswith('_'):
_log_debug("ignoring '%s' because of leading underscore", k)
continue
if k in ('protocol', 'prot', 'p'):
Attributes.prot = v
_log_debug("setting prot=%r", v)
elif k in ('voa', 'validate_on_assignment'):
Attributes.validate_on_assignment = v
_log_debug("setting voa=%r", v)
elif k in ('parser', 'in_cast'):
setattr(Attributes, 'parser', staticmethod(v))
_log_debug("setting %s=%r", k, v)
elif k in ('sanitize', 'sanitizer', 'out_cast'):
setattr(Attributes, 'sanitizer', staticmethod(v))
_log_debug("setting %s=%r as sanitizer", k, v)
elif k == 'logged':
setattr(Attributes, 'logged', staticmethod(v))
_log_debug("setting %s=%r as log sanitizer", k, v)
elif k in ("doc", "appinfo"):
setattr(Annotations, k, v)
_log_debug("setting Annotations.%s=%r", k, v)
elif k in ('primary_key', 'pk'):
setattr(Attributes, 'primary_key', v)
Attributes.sqla_column_args[-1]['primary_key'] = v
_log_debug("setting primary_key=%r", v)
elif k in ('protocol_attrs', 'prot_attrs', 'pa'):
setattr(Attributes, 'prot_attrs', _decode_pa_dict(v))
_log_debug("setting prot_attrs=%r", v)
elif k in ('foreign_key', 'fk'):
from sqlalchemy.schema import ForeignKey
t, d = Attributes.sqla_column_args
fkt = (ForeignKey(v),)
new_v = (t + fkt, d)
Attributes.sqla_column_args = new_v
_log_debug("setting sqla_column_args=%r", new_v)
elif k in ('autoincrement', 'onupdate', 'server_default'):
Attributes.sqla_column_args[-1][k] = v
_log_debug("adding %s=%r to Attributes.sqla_column_args", k, v)
elif k == 'values_dict':
assert not 'values' in v, "`values` and `values_dict` can't be" \
"specified at the same time"
if not isinstance(v, dict):
# our odict has one nasty implicit behaviour: setitem on
# int keys is treated as array indexes, not dict keys. so
# dicts with int indexes can't work with odict. so we use
# the one from stdlib
v = OrderedDict(v)
Attributes.values = list(v.keys())
Attributes.values_dict = v
_log_debug("setting values=%r, values_dict=%r",
Attributes.values, Attributes.values_dict)
elif k == 'exc_table':
Attributes.exc_table = v
Attributes.exc_db = v
_log_debug("setting exc_table=%r, exc_db=%r", v, v)
elif k == 'max_occurs' and v in ('unbounded', 'inf', float('inf')):
new_v = decimal.Decimal('inf')
setattr(Attributes, k, new_v)
_log_debug("setting max_occurs=%r", new_v)
elif k == 'type_name':
Attributes._explicit_type_name = True
_log_debug("setting _explicit_type_name=True because "
"we have 'type_name'")
else:
setattr(Attributes, k, v)
_log_debug("setting %s=%r", k, v)
return (cls.__name__, (cls,), cls_dict)
@staticmethod
def validate_string(cls, value):
"""Override this method to do your own input validation on the input
string. This is called before converting the incoming string to the
native python value."""
return (cls.Attributes.nillable or value is not None)
@staticmethod
def validate_native(cls, value):
"""Override this method to do your own input validation on the native
value. This is called after converting the incoming string to the
native python value."""
return (cls.Attributes.nullable or value is not None)
class Null(ModelBase):
pass
class SimpleModelAttributesMeta(AttributesMeta):
def __init__(self, cls_name, cls_bases, cls_dict):
super(SimpleModelAttributesMeta, self).__init__(cls_name, cls_bases,
cls_dict)
if getattr(self, '_pattern', None) is None:
self._pattern = None
def get_pattern(self):
return self._pattern
def set_pattern(self, pattern):
self._pattern = pattern
if pattern is not None:
self._pattern_re = re.compile(pattern)
pattern = property(get_pattern, set_pattern)
def get_unicode_pattern(self):
return self._pattern
def set_unicode_pattern(self, pattern):
self._pattern = pattern
if pattern is not None:
self._pattern_re = re.compile(pattern, re.UNICODE)
unicode_pattern = property(get_unicode_pattern, set_unicode_pattern)
upattern = property(get_unicode_pattern, set_unicode_pattern)
class SimpleModel(ModelBase):
"""The base class for primitives."""
__namespace__ = "http://www.w3.org/2001/XMLSchema"
@six.add_metaclass(SimpleModelAttributesMeta)
class Attributes(ModelBase.Attributes):
"""The class that holds the constraints for the given type."""
values = set()
"""The set of possible values for this type."""
# some hacks are done in _s_customize to make `values_dict`
# behave like `values`
values_dict = dict()
"""The dict of possible values for this type. Dict keys are values and
dict values are either a single string or a translation dict."""
_pattern_re = None
def __new__(cls, **kwargs):
"""Overriden so that any attempt to instantiate a primitive will return
a customized class instead of an instance.
See spyne.model.base.ModelBase for more information.
"""
return cls.customize(**kwargs)
@classmethod
def customize(cls, **kwargs):
"""Duplicates cls and overwrites the values in ``cls.Attributes`` with
``**kwargs`` and returns the new class."""
cls_name, cls_bases, cls_dict = cls._s_customize(**kwargs)
retval = type(cls_name, cls_bases, cls_dict)
if not retval.is_default(retval):
retval.__extends__ = cls
retval.__type_name__ = kwargs.get("type_name", ModelBase.Empty)
if 'type_name' in kwargs:
logger.debug("Type name for %r was overridden as '%s'",
retval, retval.__type_name__)
retval.resolve_namespace(retval, kwargs.get('__namespace__'))
return retval
@staticmethod
def is_default(cls):
return (cls.Attributes.values == SimpleModel.Attributes.values)
@staticmethod
def validate_native(cls, value):
return (ModelBase.validate_native(cls, value)
and (
cls.Attributes.values is None or
len(cls.Attributes.values) == 0 or (
(value is None and cls.Attributes.nillable) or
(value is not None and value in cls.Attributes.values)
)
)
)
class PushBase(object):
def __init__(self, callback=None, errback=None):
self.orig_thread = threading.current_thread()
self._cb = callback
self._eb = errback
self.length = 0
self.ctx = None
self.app = None
self.gen = None
self._cb_finish = None
self._eb_finish = None
self.interim = False
def _init(self, ctx, gen, _cb_finish, _eb_finish, interim):
self.length = 0
self.ctx = ctx
self.app = ctx.app
self.gen = gen
self._cb_finish = _cb_finish
self._eb_finish = _eb_finish
self.interim = interim
def init(self, ctx, gen, _cb_finish, _eb_finish, interim):
self._init(ctx, gen, _cb_finish, _eb_finish, interim)
if self._cb is not None:
return self._cb(self)
def __len__(self):
return self.length
def append(self, inst):
self.gen.send(inst)
self.length += 1
def extend(self, insts):
for inst in insts:
self.gen.send(inst)
self.length += 1
def close(self):
try:
self.gen.throw(Break())
except (Break, StopIteration, GeneratorExit):
pass
self._cb_finish()
class xml:
"""Compound option object for xml serialization. It's meant to be passed to
:func:`ComplexModelBase.Attributes.store_as`.
:param root_tag: Root tag of the xml element that contains the field values.
:param no_ns: When true, the xml document is stripped from namespace
information. This is generally a stupid thing to do. Use with caution.
"""
def __init__(self, root_tag=None, no_ns=False, pretty_print=False):
self.root_tag = root_tag
self.no_ns = no_ns
self.pretty_print = pretty_print
class table:
"""Compound option object for for storing the class instance as in row in a
table in a relational database. It's meant to be passed to
:func:`ComplexModelBase.Attributes.store_as`.
:param multi: When False, configures a one-to-many relationship where the
child table has a foreign key to the parent. When not ``False``,
configures a many-to-many relationship by creating an intermediate
relation table that has foreign keys to both parent and child classes
and generates a table name automatically. When ``True``, the table name
is generated automatically. Otherwise, it should be a string, as the
value is used as the name of the intermediate table.
:param left: Name of the left join column.
:param right: Name of the right join column.
:param backref: See https://docs.sqlalchemy.org/en/13/orm/relationship_api.html?highlight=lazy#sqlalchemy.orm.relationship.params.backref
:param cascade: https://docs.sqlalchemy.org/en/13/orm/relationship_api.html?highlight=lazy#sqlalchemy.orm.relationship.params.cascade
:param lazy: See https://docs.sqlalchemy.org/en/13/orm/relationship_api.html?highlight=lazy#sqlalchemy.orm.relationship.params.lazy
:param back_populates: See https://docs.sqlalchemy.org/en/13/orm/relationship_api.html?highlight=lazy#sqlalchemy.orm.relationship.params.back_populates
"""
def __init__(self, multi=False, left=None, right=None, backref=None,
id_backref=None, cascade=False, lazy='select', back_populates=None,
fk_left_deferrable=None, fk_left_initially=None,
fk_right_deferrable=None, fk_right_initially=None,
fk_left_ondelete=None, fk_left_onupdate=None,
fk_right_ondelete=None, fk_right_onupdate=None,
explicit_join=False, order_by=False, single_parent=None):
self.multi = multi
self.left = left
self.right = right
self.backref = backref
self.id_backref = id_backref
self.cascade = cascade
self.lazy = lazy
self.back_populates = back_populates
self.fk_left_deferrable = fk_left_deferrable
self.fk_left_initially = fk_left_initially
self.fk_right_deferrable = fk_right_deferrable
self.fk_right_initially = fk_right_initially
self.fk_left_ondelete = fk_left_ondelete
self.fk_left_onupdate = fk_left_onupdate
self.fk_right_ondelete = fk_right_ondelete
self.fk_right_onupdate = fk_right_onupdate
self.explicit_join = explicit_join
self.order_by = order_by
self.single_parent = single_parent
class json:
"""Compound option object for json serialization. It's meant to be passed to
:func:`ComplexModelBase.Attributes.store_as`.
Make sure you don't mix this with the json package when importing.
"""
def __init__(self, ignore_wrappers=True, complex_as=dict):
if ignore_wrappers != True:
raise NotImplementedError("ignore_wrappers != True")
self.ignore_wrappers = ignore_wrappers
self.complex_as = complex_as
class jsonb:
"""Compound option object for jsonb serialization. It's meant to be passed
to :func:`ComplexModelBase.Attributes.store_as`.
"""
def __init__(self, ignore_wrappers=True, complex_as=dict):
if ignore_wrappers != True:
raise NotImplementedError("ignore_wrappers != True")
self.ignore_wrappers = ignore_wrappers
self.complex_as = complex_as
class msgpack:
"""Compound option object for msgpack serialization. It's meant to be passed
to :func:`ComplexModelBase.Attributes.store_as`.
Make sure you don't mix this with the msgpack package when importing.
"""
def __init__(self):
pass
PSSM_VALUES = {'json': json, 'jsonb': jsonb, 'xml': xml,
'msgpack': msgpack, 'table': table}
def apply_pssm(val):
if val is not None:
val_c = PSSM_VALUES.get(val, None)
if val_c is None:
assert isinstance(val, tuple(PSSM_VALUES.values())), \
"'store_as' should be one of: %r or an instance of %r not %r" \
% (tuple(PSSM_VALUES.keys()), tuple(PSSM_VALUES.values()), val)
return val
return val_c()