Merge "template() contract function was introduced"
This commit is contained in:
commit
27b94cc243
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import semantic_version
|
import semantic_version
|
||||||
|
|
||||||
|
|
||||||
EXPRESSION_MEMORY_QUOTA = 512 * 1024
|
EXPRESSION_MEMORY_QUOTA = 512 * 1024
|
||||||
ITERATORS_LIMIT = 2000
|
ITERATORS_LIMIT = 2000
|
||||||
|
|
||||||
@ -49,6 +50,9 @@ TL_CONTEXT = '__murano_context'
|
|||||||
TL_ID = '__thread_id'
|
TL_ID = '__thread_id'
|
||||||
TL_OBJECT_STORE = '__murano_object_store'
|
TL_OBJECT_STORE = '__murano_object_store'
|
||||||
TL_SESSION = '__murano_execution_session'
|
TL_SESSION = '__murano_execution_session'
|
||||||
|
TL_CONTRACT_PASSKEY = '__murano_contract_passkey'
|
||||||
|
TL_OBJECTS_DRY_RUN = '__murano_objects_dry_run'
|
||||||
|
|
||||||
|
|
||||||
RUNTIME_VERSION_1_0 = semantic_version.Version('1.0.0')
|
RUNTIME_VERSION_1_0 = semantic_version.Version('1.0.0')
|
||||||
RUNTIME_VERSION_1_1 = semantic_version.Version('1.1.0')
|
RUNTIME_VERSION_1_1 = semantic_version.Version('1.1.0')
|
||||||
|
@ -71,6 +71,13 @@ class MethodArgumentUsages(object):
|
|||||||
All = {Standard, VarArgs, KwArgs}
|
All = {Standard, VarArgs, KwArgs}
|
||||||
|
|
||||||
|
|
||||||
|
class DumpTypes(object):
|
||||||
|
Serializable = 'Serializable'
|
||||||
|
Inline = 'Inline'
|
||||||
|
Mixed = 'Mixed'
|
||||||
|
All = {Serializable, Inline, Mixed}
|
||||||
|
|
||||||
|
|
||||||
class MuranoType(object):
|
class MuranoType(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -223,6 +223,16 @@ def get_class(name, context=None):
|
|||||||
return murano_type.package.find_class(name)
|
return murano_type.package.find_class(name)
|
||||||
|
|
||||||
|
|
||||||
|
def get_contract_passkey():
|
||||||
|
current_thread = eventlet.greenthread.getcurrent()
|
||||||
|
return getattr(current_thread, constants.TL_CONTRACT_PASSKEY, None)
|
||||||
|
|
||||||
|
|
||||||
|
def is_objects_dry_run_mode():
|
||||||
|
current_thread = eventlet.greenthread.getcurrent()
|
||||||
|
return bool(getattr(current_thread, constants.TL_OBJECTS_DRY_RUN, False))
|
||||||
|
|
||||||
|
|
||||||
def get_current_thread_id():
|
def get_current_thread_id():
|
||||||
global _threads_sequencer
|
global _threads_sequencer
|
||||||
|
|
||||||
@ -562,6 +572,34 @@ def parse_object_definition(spec, scope_type, context):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def assemble_object_definition(parsed, model_format=dsl_types.DumpTypes.Mixed):
|
||||||
|
if model_format == dsl_types.DumpTypes.Inline:
|
||||||
|
result = {
|
||||||
|
parsed['type']: parsed['properties'],
|
||||||
|
'id': parsed['id'],
|
||||||
|
'name': parsed['name']
|
||||||
|
}
|
||||||
|
result.update(parsed['extra'])
|
||||||
|
return result
|
||||||
|
result = parsed['properties']
|
||||||
|
header = {
|
||||||
|
'id': parsed['id'],
|
||||||
|
'name': parsed['name']
|
||||||
|
}
|
||||||
|
header.update(parsed['extra'])
|
||||||
|
result['?'] = header
|
||||||
|
if model_format == dsl_types.DumpTypes.Mixed:
|
||||||
|
header['type'] = parsed['type']
|
||||||
|
return result
|
||||||
|
elif model_format == dsl_types.DumpTypes.Serializable:
|
||||||
|
cls = parsed['type']
|
||||||
|
if cls:
|
||||||
|
header['type'] = format_type_string(cls)
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
raise ValueError('Invalid Serialization Type')
|
||||||
|
|
||||||
|
|
||||||
def function(c):
|
def function(c):
|
||||||
if hasattr(c, 'im_func'):
|
if hasattr(c, 'im_func'):
|
||||||
return c.im_func
|
return c.im_func
|
||||||
@ -606,3 +644,17 @@ def format_type_string(type_obj):
|
|||||||
type_obj.name, type_obj.version, type_obj.package.name)
|
type_obj.name, type_obj.version, type_obj.package.name)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Invalid argument')
|
raise ValueError('Invalid argument')
|
||||||
|
|
||||||
|
|
||||||
|
def patch_dict(dct, path, value):
|
||||||
|
parts = path.split('.')
|
||||||
|
for i in range(len(parts) - 1):
|
||||||
|
if not isinstance(dct, dict):
|
||||||
|
dct = None
|
||||||
|
break
|
||||||
|
dct = dct.get(parts[i])
|
||||||
|
if isinstance(dct, dict):
|
||||||
|
if value is yaqlutils.NO_VALUE:
|
||||||
|
dct.pop(parts[-1])
|
||||||
|
else:
|
||||||
|
dct[parts[-1]] = value
|
||||||
|
@ -19,7 +19,6 @@ from murano.dsl import dsl
|
|||||||
from murano.dsl import dsl_types
|
from murano.dsl import dsl_types
|
||||||
from murano.dsl import exceptions
|
from murano.dsl import exceptions
|
||||||
from murano.dsl import helpers
|
from murano.dsl import helpers
|
||||||
from murano.dsl import serializer
|
|
||||||
from murano.dsl import yaql_integration
|
from murano.dsl import yaql_integration
|
||||||
|
|
||||||
|
|
||||||
@ -143,7 +142,8 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||||||
init.invoke(self.real_this, (), init_args,
|
init.invoke(self.real_this, (), init_args,
|
||||||
context.create_child_context())
|
context.create_child_context())
|
||||||
|
|
||||||
if not object_store.initializing and init:
|
if (not object_store.initializing and init
|
||||||
|
and not helpers.is_objects_dry_run_mode()):
|
||||||
yield run_init
|
yield run_init
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -254,13 +254,13 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||||||
self.type.name, self.type.version, self.object_id, id(self))
|
self.type.name, self.type.version, self.object_id, id(self))
|
||||||
|
|
||||||
def to_dictionary(self, include_hidden=False,
|
def to_dictionary(self, include_hidden=False,
|
||||||
serialization_type=serializer.DumpTypes.Serializable,
|
serialization_type=dsl_types.DumpTypes.Serializable,
|
||||||
allow_refs=False):
|
allow_refs=False):
|
||||||
context = helpers.get_context()
|
context = helpers.get_context()
|
||||||
result = {}
|
result = {}
|
||||||
for parent in self.__parents.values():
|
for parent in self.__parents.values():
|
||||||
result.update(parent.to_dictionary(
|
result.update(parent.to_dictionary(
|
||||||
include_hidden, serializer.DumpTypes.Serializable,
|
include_hidden, dsl_types.DumpTypes.Serializable,
|
||||||
allow_refs))
|
allow_refs))
|
||||||
for property_name in self.type.properties:
|
for property_name in self.type.properties:
|
||||||
if property_name in self.__properties:
|
if property_name in self.__properties:
|
||||||
@ -276,14 +276,14 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||||||
'as', context) == 'reference':
|
'as', context) == 'reference':
|
||||||
prop_value = prop_value.object_id
|
prop_value = prop_value.object_id
|
||||||
result[property_name] = prop_value
|
result[property_name] = prop_value
|
||||||
if serialization_type == serializer.DumpTypes.Inline:
|
if serialization_type == dsl_types.DumpTypes.Inline:
|
||||||
result.pop('?')
|
result.pop('?')
|
||||||
result = {
|
result = {
|
||||||
self.type: result,
|
self.type: result,
|
||||||
'id': self.object_id,
|
'id': self.object_id,
|
||||||
'name': self.name
|
'name': self.name
|
||||||
}
|
}
|
||||||
elif serialization_type == serializer.DumpTypes.Mixed:
|
elif serialization_type == dsl_types.DumpTypes.Mixed:
|
||||||
result.update({'?': {
|
result.update({'?': {
|
||||||
'type': self.type,
|
'type': self.type,
|
||||||
'id': self.object_id,
|
'id': self.object_id,
|
||||||
|
@ -58,7 +58,8 @@ class ObjectStore(object):
|
|||||||
scope_type=None, context=None, keep_ids=False):
|
scope_type=None, context=None, keep_ids=False):
|
||||||
# do the object model load in a temporary object store and copy
|
# do the object model load in a temporary object store and copy
|
||||||
# loaded objects here after that
|
# loaded objects here after that
|
||||||
model_store = InitializationObjectStore(owner, self, keep_ids)
|
model_store = InitializationObjectStore(
|
||||||
|
owner, self, keep_ids)
|
||||||
with helpers.with_object_store(model_store):
|
with helpers.with_object_store(model_store):
|
||||||
result = model_store.load(
|
result = model_store.load(
|
||||||
value, owner, scope_type=scope_type,
|
value, owner, scope_type=scope_type,
|
||||||
@ -111,45 +112,44 @@ class InitializationObjectStore(ObjectStore):
|
|||||||
if not parsed:
|
if not parsed:
|
||||||
raise ValueError('Invalid object representation format')
|
raise ValueError('Invalid object representation format')
|
||||||
|
|
||||||
try:
|
if owner is self._root_owner:
|
||||||
if owner is self._root_owner:
|
self._initializing = True
|
||||||
self._initializing = True
|
|
||||||
|
|
||||||
class_obj = parsed['type'] or default_type
|
class_obj = parsed['type'] or default_type
|
||||||
if not class_obj:
|
if not class_obj:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Invalid object representation: '
|
'Invalid object representation: '
|
||||||
'no type information was provided')
|
'no type information was provided')
|
||||||
if isinstance(class_obj, dsl_types.MuranoTypeReference):
|
if isinstance(class_obj, dsl_types.MuranoTypeReference):
|
||||||
class_obj = class_obj.type
|
class_obj = class_obj.type
|
||||||
object_id = parsed['id']
|
object_id = parsed['id']
|
||||||
obj = None if object_id is None else self._store.get(object_id)
|
obj = None if object_id is None else self._store.get(object_id)
|
||||||
if not obj:
|
if not obj:
|
||||||
obj = murano_object.MuranoObject(
|
obj = murano_object.MuranoObject(
|
||||||
class_obj, helpers.weak_proxy(owner),
|
class_obj, helpers.weak_proxy(owner),
|
||||||
name=parsed['name'],
|
name=parsed['name'],
|
||||||
object_id=object_id if self._keep_ids else None)
|
object_id=object_id if self._keep_ids else None)
|
||||||
self.put(obj, object_id or obj.object_id)
|
self.put(obj, object_id or obj.object_id)
|
||||||
|
|
||||||
system_value = ObjectStore._get_designer_attributes(
|
system_value = ObjectStore._get_designer_attributes(
|
||||||
parsed['extra'])
|
parsed['extra'])
|
||||||
self._designer_attributes_store[object_id] = system_value
|
self._designer_attributes_store[object_id] = system_value
|
||||||
|
|
||||||
if context is None:
|
if context is None:
|
||||||
context = self.executor.create_object_context(obj)
|
context = self.executor.create_object_context(obj)
|
||||||
|
|
||||||
def run_initialize():
|
def run_initialize():
|
||||||
self._initializers.extend(
|
self._initializers.extend(
|
||||||
obj.initialize(context, parsed['properties']))
|
obj.initialize(context, parsed['properties']))
|
||||||
|
|
||||||
|
run_initialize()
|
||||||
|
if owner is self._root_owner:
|
||||||
|
self._initializing = False
|
||||||
run_initialize()
|
run_initialize()
|
||||||
if owner is self._root_owner:
|
|
||||||
self._initializing = False
|
if owner is self._root_owner:
|
||||||
run_initialize()
|
with helpers.with_object_store(self.parent_store):
|
||||||
finally:
|
for fn in self._initializers:
|
||||||
if owner is self._root_owner:
|
fn()
|
||||||
with helpers.with_object_store(self.parent_store):
|
|
||||||
for fn in self._initializers:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
@ -128,10 +128,11 @@ def prepare_context(exc, cls):
|
|||||||
context.register_function(bool_)
|
context.register_function(bool_)
|
||||||
context.register_function(not_null)
|
context.register_function(not_null)
|
||||||
context.register_function(check)
|
context.register_function(check)
|
||||||
context.register_function(class_factory(context))
|
|
||||||
context.register_function(owned)
|
context.register_function(owned)
|
||||||
context.register_function(not_owned)
|
context.register_function(not_owned)
|
||||||
context.register_function(finalize)
|
context.register_function(finalize)
|
||||||
|
for fn in class_factory(context):
|
||||||
|
context.register_function(fn)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -295,7 +296,24 @@ def class_factory(context):
|
|||||||
'muranoType': name.type.name
|
'muranoType': name.type.name
|
||||||
})
|
})
|
||||||
|
|
||||||
return class_
|
@specs.parameter('schema', Schema)
|
||||||
|
@specs.parameter('type_', dsl.MuranoTypeParameter(
|
||||||
|
nullable=False, context=context))
|
||||||
|
@specs.parameter('default_type', dsl.MuranoTypeParameter(
|
||||||
|
nullable=True, context=context))
|
||||||
|
@specs.parameter('version_spec', yaqltypes.String(True))
|
||||||
|
@specs.parameter(
|
||||||
|
'exclude_properties', yaqltypes.Sequence(nullable=True))
|
||||||
|
@specs.method
|
||||||
|
def template(schema, type_, exclude_properties=None,
|
||||||
|
default_type=None, version_spec=None):
|
||||||
|
result = class_(schema, type_, default_type, version_spec)
|
||||||
|
result.data['owned'] = True
|
||||||
|
if exclude_properties:
|
||||||
|
result.data['excludedProperties'] = exclude_properties
|
||||||
|
return result
|
||||||
|
|
||||||
|
return class_, template
|
||||||
|
|
||||||
|
|
||||||
@specs.parameter('schema', Schema)
|
@specs.parameter('schema', Schema)
|
||||||
|
@ -21,19 +21,13 @@ from murano.dsl import dsl_types
|
|||||||
from murano.dsl import helpers
|
from murano.dsl import helpers
|
||||||
|
|
||||||
|
|
||||||
class DumpTypes(object):
|
|
||||||
Serializable = 'Serializable'
|
|
||||||
Inline = 'Inline'
|
|
||||||
Mixed = 'Mixed'
|
|
||||||
All = {Serializable, Inline, Mixed}
|
|
||||||
|
|
||||||
|
|
||||||
class ObjRef(object):
|
class ObjRef(object):
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
self.ref_obj = obj
|
self.ref_obj = obj
|
||||||
|
|
||||||
|
|
||||||
def serialize(obj, executor, serialization_type=DumpTypes.Serializable):
|
def serialize(obj, executor,
|
||||||
|
serialization_type=dsl_types.DumpTypes.Serializable):
|
||||||
with helpers.with_object_store(executor.object_store):
|
with helpers.with_object_store(executor.object_store):
|
||||||
return serialize_model(
|
return serialize_model(
|
||||||
obj, executor, True,
|
obj, executor, True,
|
||||||
@ -45,7 +39,7 @@ def serialize(obj, executor, serialization_type=DumpTypes.Serializable):
|
|||||||
|
|
||||||
def _serialize_object(root_object, designer_attributes, allow_refs,
|
def _serialize_object(root_object, designer_attributes, allow_refs,
|
||||||
executor, serialize_actions=True,
|
executor, serialize_actions=True,
|
||||||
serialization_type=DumpTypes.Serializable):
|
serialization_type=dsl_types.DumpTypes.Serializable):
|
||||||
serialized_objects = set()
|
serialized_objects = set()
|
||||||
|
|
||||||
obj = root_object
|
obj = root_object
|
||||||
@ -65,7 +59,7 @@ def serialize_model(root_object, executor,
|
|||||||
make_copy=True,
|
make_copy=True,
|
||||||
serialize_attributes=True,
|
serialize_attributes=True,
|
||||||
serialize_actions=True,
|
serialize_actions=True,
|
||||||
serialization_type=DumpTypes.Serializable):
|
serialization_type=dsl_types.DumpTypes.Serializable):
|
||||||
designer_attributes = executor.object_store.designer_attributes
|
designer_attributes = executor.object_store.designer_attributes
|
||||||
|
|
||||||
if root_object is None:
|
if root_object is None:
|
||||||
@ -138,11 +132,13 @@ def _pass12_serialize(value, parent, serialized_objects,
|
|||||||
if isinstance(value, (dsl_types.MuranoType,
|
if isinstance(value, (dsl_types.MuranoType,
|
||||||
dsl_types.MuranoTypeReference)):
|
dsl_types.MuranoTypeReference)):
|
||||||
return helpers.format_type_string(value), False
|
return helpers.format_type_string(value), False
|
||||||
|
if value is helpers.get_contract_passkey():
|
||||||
|
return value, False
|
||||||
if isinstance(value, dsl_types.MuranoObject):
|
if isinstance(value, dsl_types.MuranoObject):
|
||||||
result = value.to_dictionary(
|
result = value.to_dictionary(
|
||||||
serialization_type=serialization_type, allow_refs=allow_refs)
|
serialization_type=serialization_type, allow_refs=allow_refs)
|
||||||
if designer_attributes_getter is not None:
|
if designer_attributes_getter is not None:
|
||||||
if serialization_type == DumpTypes.Inline:
|
if serialization_type == dsl_types.DumpTypes.Inline:
|
||||||
system_data = result
|
system_data = result
|
||||||
else:
|
else:
|
||||||
system_data = result['?']
|
system_data = result['?']
|
||||||
@ -161,13 +157,13 @@ def _pass12_serialize(value, parent, serialized_objects,
|
|||||||
|
|
||||||
for d_key, d_value in six.iteritems(value):
|
for d_key, d_value in six.iteritems(value):
|
||||||
if (isinstance(d_key, dsl_types.MuranoType) and
|
if (isinstance(d_key, dsl_types.MuranoType) and
|
||||||
serialization_type == DumpTypes.Serializable):
|
serialization_type == dsl_types.DumpTypes.Serializable):
|
||||||
result_key = str(d_key)
|
result_key = str(d_key)
|
||||||
else:
|
else:
|
||||||
result_key = d_key
|
result_key = d_key
|
||||||
if (result_key == 'type' and
|
if (result_key == 'type' and
|
||||||
isinstance(d_value, dsl_types.MuranoType) and
|
isinstance(d_value, dsl_types.MuranoType) and
|
||||||
serialization_type == DumpTypes.Mixed):
|
serialization_type == dsl_types.DumpTypes.Mixed):
|
||||||
result_value = d_value, False
|
result_value = d_value, False
|
||||||
else:
|
else:
|
||||||
result_value = _pass12_serialize(
|
result_value = _pass12_serialize(
|
||||||
|
@ -17,10 +17,12 @@ from yaql.language import specs
|
|||||||
from yaql.language import utils
|
from yaql.language import utils
|
||||||
from yaql.language import yaqltypes
|
from yaql.language import yaqltypes
|
||||||
|
|
||||||
|
from murano.dsl import constants
|
||||||
from murano.dsl import dsl
|
from murano.dsl import dsl
|
||||||
from murano.dsl import dsl_types
|
from murano.dsl import dsl_types
|
||||||
from murano.dsl import exceptions
|
from murano.dsl import exceptions
|
||||||
from murano.dsl import helpers
|
from murano.dsl import helpers
|
||||||
|
from murano.dsl import serializer
|
||||||
|
|
||||||
|
|
||||||
class TypeScheme(object):
|
class TypeScheme(object):
|
||||||
@ -187,9 +189,69 @@ class TypeScheme(object):
|
|||||||
version_spec or helpers.get_type(root_context)):
|
version_spec or helpers.get_type(root_context)):
|
||||||
raise exceptions.ContractViolationException(
|
raise exceptions.ContractViolationException(
|
||||||
'Object of type {0} is not compatible with '
|
'Object of type {0} is not compatible with '
|
||||||
'requested type {1}'.format(obj.type.name, name))
|
'requested type {1}'.format(obj.type.name, murano_class))
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@specs.parameter('type_', dsl.MuranoTypeParameter(
|
||||||
|
nullable=False, context=root_context))
|
||||||
|
@specs.parameter('default_type', dsl.MuranoTypeParameter(
|
||||||
|
nullable=True, context=root_context))
|
||||||
|
@specs.parameter('value', nullable=True)
|
||||||
|
@specs.parameter('version_spec', yaqltypes.String(True))
|
||||||
|
@specs.parameter(
|
||||||
|
'exclude_properties', yaqltypes.Sequence(nullable=True))
|
||||||
|
@specs.method
|
||||||
|
def template(engine, value, type_, exclude_properties=None,
|
||||||
|
default_type=None, version_spec=None):
|
||||||
|
object_store = helpers.get_object_store()
|
||||||
|
passkey = None
|
||||||
|
if not default_type:
|
||||||
|
default_type = type_
|
||||||
|
murano_class = type_.type
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if isinstance(value, dsl_types.MuranoObject):
|
||||||
|
obj = value
|
||||||
|
elif isinstance(value, dsl_types.MuranoObjectInterface):
|
||||||
|
obj = value.object
|
||||||
|
elif isinstance(value, utils.MappingType):
|
||||||
|
passkey = utils.create_marker('<Contract Passkey>')
|
||||||
|
if exclude_properties:
|
||||||
|
parsed = helpers.parse_object_definition(
|
||||||
|
value, calling_type, context)
|
||||||
|
props = dsl.to_mutable(parsed['properties'], engine)
|
||||||
|
for p in exclude_properties:
|
||||||
|
helpers.patch_dict(props, p, passkey)
|
||||||
|
parsed['properties'] = props
|
||||||
|
value = helpers.assemble_object_definition(parsed)
|
||||||
|
with helpers.thread_local_attribute(
|
||||||
|
constants.TL_CONTRACT_PASSKEY, passkey):
|
||||||
|
with helpers.thread_local_attribute(
|
||||||
|
constants.TL_OBJECTS_DRY_RUN, True):
|
||||||
|
obj = object_store.load(
|
||||||
|
value, owner, context=context,
|
||||||
|
default_type=default_type, scope_type=calling_type)
|
||||||
|
else:
|
||||||
|
raise exceptions.ContractViolationException(
|
||||||
|
'Value {0} cannot be represented as class {1}'.format(
|
||||||
|
format_scalar(value), type_))
|
||||||
|
if not helpers.is_instance_of(
|
||||||
|
obj, murano_class.name,
|
||||||
|
version_spec or helpers.get_type(root_context)):
|
||||||
|
raise exceptions.ContractViolationException(
|
||||||
|
'Object of type {0} is not compatible with '
|
||||||
|
'requested type {1}'.format(obj.type.name, type_))
|
||||||
|
|
||||||
|
with helpers.thread_local_attribute(
|
||||||
|
constants.TL_CONTRACT_PASSKEY, passkey):
|
||||||
|
result = serializer.serialize(
|
||||||
|
obj.real_this, object_store.executor,
|
||||||
|
dsl_types.DumpTypes.Mixed)
|
||||||
|
if exclude_properties:
|
||||||
|
for p in exclude_properties:
|
||||||
|
helpers.patch_dict(result, p, utils.NO_VALUE)
|
||||||
|
return result
|
||||||
|
|
||||||
context = root_context.create_child_context()
|
context = root_context.create_child_context()
|
||||||
context.register_function(int_)
|
context.register_function(int_)
|
||||||
context.register_function(string)
|
context.register_function(string)
|
||||||
@ -198,6 +260,7 @@ class TypeScheme(object):
|
|||||||
context.register_function(not_null)
|
context.register_function(not_null)
|
||||||
context.register_function(error)
|
context.register_function(error)
|
||||||
context.register_function(class_)
|
context.register_function(class_)
|
||||||
|
context.register_function(template)
|
||||||
context.register_function(owned_ref)
|
context.register_function(owned_ref)
|
||||||
context.register_function(owned)
|
context.register_function(owned)
|
||||||
context.register_function(not_owned_ref)
|
context.register_function(not_owned_ref)
|
||||||
@ -249,12 +312,33 @@ class TypeScheme(object):
|
|||||||
@specs.parameter('version_spec', yaqltypes.String(True))
|
@specs.parameter('version_spec', yaqltypes.String(True))
|
||||||
@specs.method
|
@specs.method
|
||||||
def class_(value, type, version_spec=None):
|
def class_(value, type, version_spec=None):
|
||||||
if helpers.is_instance_of(
|
if value is None or helpers.is_instance_of(
|
||||||
value, type.type.name,
|
value, type.type.name,
|
||||||
version_spec or helpers.get_names_scope(root_context)):
|
version_spec or helpers.get_names_scope(root_context)):
|
||||||
return value
|
return value
|
||||||
raise exceptions.ContractViolationException()
|
raise exceptions.ContractViolationException()
|
||||||
|
|
||||||
|
@specs.parameter('type_', dsl.MuranoTypeParameter(
|
||||||
|
nullable=False, context=root_context))
|
||||||
|
@specs.parameter('default_type', dsl.MuranoTypeParameter(
|
||||||
|
nullable=True, context=root_context))
|
||||||
|
@specs.parameter('value', nullable=True)
|
||||||
|
@specs.parameter('version_spec', yaqltypes.String(True))
|
||||||
|
@specs.parameter(
|
||||||
|
'exclude_properties', yaqltypes.Sequence(nullable=True))
|
||||||
|
@specs.method
|
||||||
|
def template(value, type_, exclude_properties=None,
|
||||||
|
default_type=None, version_spec=None):
|
||||||
|
|
||||||
|
if value is None or isinstance(value, utils.MappingType):
|
||||||
|
return value
|
||||||
|
|
||||||
|
if helpers.is_instance_of(
|
||||||
|
value, type_.type.name,
|
||||||
|
version_spec or helpers.get_names_scope(root_context)):
|
||||||
|
return value
|
||||||
|
raise exceptions.ContractViolationException()
|
||||||
|
|
||||||
context = root_context.create_child_context()
|
context = root_context.create_child_context()
|
||||||
context.register_function(int_)
|
context.register_function(int_)
|
||||||
context.register_function(string)
|
context.register_function(string)
|
||||||
@ -262,6 +346,7 @@ class TypeScheme(object):
|
|||||||
context.register_function(check)
|
context.register_function(check)
|
||||||
context.register_function(not_null)
|
context.register_function(not_null)
|
||||||
context.register_function(class_)
|
context.register_function(class_)
|
||||||
|
context.register_function(template)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _map_dict(self, data, spec, context, path):
|
def _map_dict(self, data, spec, context, path):
|
||||||
@ -351,6 +436,8 @@ class TypeScheme(object):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def _map(self, data, spec, context, path):
|
def _map(self, data, spec, context, path):
|
||||||
|
if is_passkey(data):
|
||||||
|
return data
|
||||||
child_context = context.create_child_context()
|
child_context = context.create_child_context()
|
||||||
if isinstance(spec, dsl_types.YaqlExpression):
|
if isinstance(spec, dsl_types.YaqlExpression):
|
||||||
child_context[''] = data
|
child_context[''] = data
|
||||||
@ -375,6 +462,9 @@ class TypeScheme(object):
|
|||||||
if data is dsl.NO_VALUE:
|
if data is dsl.NO_VALUE:
|
||||||
data = helpers.evaluate(default, context)
|
data = helpers.evaluate(default, context)
|
||||||
|
|
||||||
|
if is_passkey(data):
|
||||||
|
return data
|
||||||
|
|
||||||
context = self.prepare_transform_context(
|
context = self.prepare_transform_context(
|
||||||
context, this, owner, default, calling_type)
|
context, this, owner, default, calling_type)
|
||||||
return self._map(data, self._spec, context, '')
|
return self._map(data, self._spec, context, '')
|
||||||
@ -383,6 +473,9 @@ class TypeScheme(object):
|
|||||||
if data is dsl.NO_VALUE:
|
if data is dsl.NO_VALUE:
|
||||||
data = helpers.evaluate(default, context)
|
data = helpers.evaluate(default, context)
|
||||||
|
|
||||||
|
if is_passkey(data):
|
||||||
|
return True
|
||||||
|
|
||||||
context = self.prepare_validate_context(context)
|
context = self.prepare_validate_context(context)
|
||||||
try:
|
try:
|
||||||
self._map(data, self._spec, context, '')
|
self._map(data, self._spec, context, '')
|
||||||
@ -395,3 +488,8 @@ def format_scalar(value):
|
|||||||
if isinstance(value, six.string_types):
|
if isinstance(value, six.string_types):
|
||||||
return "'{0}'".format(value)
|
return "'{0}'".format(value)
|
||||||
return six.text_type(value)
|
return six.text_type(value)
|
||||||
|
|
||||||
|
|
||||||
|
def is_passkey(value):
|
||||||
|
passkey = helpers.get_contract_passkey()
|
||||||
|
return passkey is not None and value is passkey
|
||||||
|
@ -245,9 +245,9 @@ def call_func(context, op_dot, base, name, args, kwargs,
|
|||||||
@specs.parameter('obj', dsl.MuranoObjectParameter(decorate=False))
|
@specs.parameter('obj', dsl.MuranoObjectParameter(decorate=False))
|
||||||
@specs.parameter('serialization_type', yaqltypes.String())
|
@specs.parameter('serialization_type', yaqltypes.String())
|
||||||
@specs.parameter('ignore_upcasts', bool)
|
@specs.parameter('ignore_upcasts', bool)
|
||||||
def dump(obj, serialization_type=serializer.DumpTypes.Serializable,
|
def dump(obj, serialization_type=dsl_types.DumpTypes.Serializable,
|
||||||
ignore_upcasts=True):
|
ignore_upcasts=True):
|
||||||
if serialization_type not in serializer.DumpTypes.All:
|
if serialization_type not in dsl_types.DumpTypes.All:
|
||||||
raise ValueError('Invalid Serialization Type')
|
raise ValueError('Invalid Serialization Type')
|
||||||
executor = helpers.get_executor()
|
executor = helpers.get_executor()
|
||||||
if ignore_upcasts:
|
if ignore_upcasts:
|
||||||
|
@ -38,6 +38,28 @@ Methods:
|
|||||||
Body:
|
Body:
|
||||||
Return: $arg
|
Return: $arg
|
||||||
|
|
||||||
|
testTemplateContract:
|
||||||
|
Arguments:
|
||||||
|
arg:
|
||||||
|
Contract: $.template(CreatedClass2)
|
||||||
|
Body:
|
||||||
|
Return: $arg
|
||||||
|
|
||||||
|
testTemplateContractExcludePropertyFromMpl:
|
||||||
|
Body:
|
||||||
|
- $model:
|
||||||
|
:CreatedClass2:
|
||||||
|
property1: qwerty
|
||||||
|
property2: 'not integer'
|
||||||
|
- Return: $.testTemplateContractExcludeProperty($model)
|
||||||
|
|
||||||
|
testTemplateContractExcludeProperty:
|
||||||
|
Arguments:
|
||||||
|
arg:
|
||||||
|
Contract: $.template(CreatedClass2, excludeProperties => [property2])
|
||||||
|
Body:
|
||||||
|
Return: $arg
|
||||||
|
|
||||||
testClassFromIdContract:
|
testClassFromIdContract:
|
||||||
Arguments:
|
Arguments:
|
||||||
arg:
|
arg:
|
||||||
|
@ -42,6 +42,9 @@ Properties:
|
|||||||
classProperty:
|
classProperty:
|
||||||
Contract: $.class(SampleClass1)
|
Contract: $.class(SampleClass1)
|
||||||
|
|
||||||
|
templateProperty:
|
||||||
|
Contract: $.template(SampleClass1, excludeProperties => [stringProperty])
|
||||||
|
|
||||||
defaultProperty:
|
defaultProperty:
|
||||||
Contract: $.int()
|
Contract: $.int()
|
||||||
Default: 999
|
Default: 999
|
||||||
|
@ -126,6 +126,30 @@ class TestContracts(test_case.DslTestCase):
|
|||||||
self.assertIsInstance(result, dsl.MuranoObjectInterface)
|
self.assertIsInstance(result, dsl.MuranoObjectInterface)
|
||||||
self.assertEqual(object_id, result.id)
|
self.assertEqual(object_id, result.id)
|
||||||
|
|
||||||
|
def test_template_contract(self):
|
||||||
|
arg = om.Object('CreatedClass2', property1='qwerty', property2=123)
|
||||||
|
result = self._runner.testTemplateContract(arg)
|
||||||
|
self.assertIsInstance(result, dict)
|
||||||
|
self.assertItemsEqual(['?', 'property1', 'property2'], result.keys())
|
||||||
|
|
||||||
|
def test_template_contract_fail_on_type(self):
|
||||||
|
arg = om.Object('SampleClass2', class2Property='qwerty')
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.ContractViolationException,
|
||||||
|
self._runner.testTemplateContract, arg)
|
||||||
|
|
||||||
|
def test_template_contract_with_property_exclusion(self):
|
||||||
|
arg = om.Object('CreatedClass2', property1='qwerty',
|
||||||
|
property2='INVALID')
|
||||||
|
result = self._runner.testTemplateContractExcludeProperty(arg)
|
||||||
|
self.assertIsInstance(result, dict)
|
||||||
|
self.assertItemsEqual(['?', 'property1'], result.keys())
|
||||||
|
|
||||||
|
def test_template_contract_with_property_exclusion_from_mpl(self):
|
||||||
|
result = self._runner.testTemplateContractExcludePropertyFromMpl()
|
||||||
|
self.assertIsInstance(result, dict)
|
||||||
|
self.assertItemsEqual(['?', 'property1'], result.keys())
|
||||||
|
|
||||||
def test_check_contract(self):
|
def test_check_contract(self):
|
||||||
arg = om.Object('SampleClass2', class2Property='qwerty')
|
arg = om.Object('SampleClass2', class2Property='qwerty')
|
||||||
self.assertIsNone(self._runner.testCheckContract(arg, 100))
|
self.assertIsNone(self._runner.testCheckContract(arg, 100))
|
||||||
|
@ -75,6 +75,15 @@ class TestSchemaGeneration(test_case.DslTestCase):
|
|||||||
'classProperty', ['null', 'muranoObject'])
|
'classProperty', ['null', 'muranoObject'])
|
||||||
self.assertEqual('SampleClass1', schema.get('muranoType'))
|
self.assertEqual('SampleClass1', schema.get('muranoType'))
|
||||||
|
|
||||||
|
def test_template_property(self):
|
||||||
|
schema = self._test_simple_property(
|
||||||
|
'templateProperty', ['null', 'muranoObject'])
|
||||||
|
self.assertEqual('SampleClass1', schema.get('muranoType'))
|
||||||
|
self.assertTrue(schema.get('owned'))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
['stringProperty'],
|
||||||
|
schema.get('excludedProperties'))
|
||||||
|
|
||||||
def test_default_property(self):
|
def test_default_property(self):
|
||||||
schema = self._test_simple_property(
|
schema = self._test_simple_property(
|
||||||
'defaultProperty', ['null', 'integer'])
|
'defaultProperty', ['null', 'integer'])
|
||||||
|
11
releasenotes/notes/template-contract-b71840cbc35eb478.yaml
Normal file
11
releasenotes/notes/template-contract-b71840cbc35eb478.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- New contract function ``template`` was introduced. ``template`` works
|
||||||
|
similar to the ``class`` in regards to the data validation but does not
|
||||||
|
instantiate objects. Instead the data is left in the object model
|
||||||
|
in dictionary format so that it could be instantiated later with the new()
|
||||||
|
function. In addition it allows to exclude specified properties from
|
||||||
|
validation and resulting template so that they could be provided later.
|
||||||
|
Objects that are assigned to the property or argument with ``template``
|
||||||
|
contract will be automatically converted to their object model
|
||||||
|
representation.
|
Loading…
Reference in New Issue
Block a user