def test_done(): tag = 'test' expected = 42 result = [] @graham.schemify(tag=tag, done='done') @attr.s class Test(object): a = attr.ib() graham.attrib(attribute=a, field=marshmallow.fields.Integer()) def done(self): result.append(self.a) serialized = '{{"{tag_name}": "{tag}", "a": {expected}}}' serialized = serialized.format( tag_name=type_name, tag=tag, expected=expected, ) graham.schema(Test).loads(serialized) result, = result assert result == expected
class Group(object): name = attr.ib(default='<unnamed group>', ) graham.attrib( attribute=name, field=marshmallow.fields.String(), ) groups = attr.ib(default=attr.Factory(list), ) graham.attrib( attribute=groups, field=marshmallow.fields.Nested('self', many=True), ) leaves = attr.ib(default=attr.Factory(list), ) graham.attrib( attribute=leaves, field=marshmallow.fields.List( marshmallow.fields.Nested(graham.schema(Leaf))), ) mixed_list = attr.ib(default=attr.Factory(list), ) graham.attrib( attribute=mixed_list, field=graham.fields.MixedList(fields=( marshmallow.fields.Nested('Group'), marshmallow.fields.Nested(graham.schema(Leaf)), )), )
class FixedBlock(epyqlib.treenode.TreeNode): name = attr.ib( default="Fixed Block", metadata=graham.create_metadata(field=marshmallow.fields.String(), ), ) offset = attr.ib( default=2, converter=int, ) children = attr.ib( factory=list, metadata=graham.create_metadata(field=graham.fields.MixedList(fields=( marshmallow.fields.Nested(graham.schema(DataPoint)), marshmallow.fields.Nested(graham.schema(DataPointBitfield)), )), ), ) uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() self.pyqt_signals.child_added_complete.connect(self.update) self.pyqt_signals.child_removed_complete.connect(self.update) def can_drop_on(self, node): return isinstance( node, ( epyqlib.pm.parametermodel.Parameter, DataPoint, DataPointBitfield, ), ) def can_delete(self, node=None): if node is None: return self.tree_parent.can_delete(node=self) return isinstance(node, (DataPoint, DataPointBitfield)) def child_from(self, node): if isinstance(node, epyqlib.pm.parametermodel.Parameter): return DataPoint(parameter_uuid=node.uuid) return node def update(self): block_offset = 0 for pt in self.children: pt.block_offset = block_offset block_offset = block_offset + pt.size check_offsets_and_length = check_block_offsets_and_length remove_old_on_drop = epyqlib.attrsmodel.default_remove_old_on_drop internal_move = epyqlib.attrsmodel.default_internal_move check = epyqlib.attrsmodel.check_just_children
def test_strict(): @graham.schemify(tag='test', strict=True) @attr.s class Test(object): email = attr.ib() graham.attrib( attribute=email, field=marshmallow.fields.Email(), ) with pytest.raises(marshmallow.exceptions.ValidationError): graham.schema(Test).loads('{"email": "invalid"}')
def test_load_missing_tag(): tag = 'test' @graham.schemify(tag=tag) @attr.s class Test(object): pass serialized = '{}' with pytest.raises( marshmallow.exceptions.ValidationError, match=repr(type_name), ): graham.schema(Test).loads(serialized).data
def test_overall(): # Attempt to recreate the List vs. many=True issues # https://repl.it/KFXo/3 subgroup = Group(name='subgroup') subgroup.leaves.append(Leaf(name='subgroup leaf')) group = Group() group.groups.append(subgroup) group.leaves.append(Leaf()) group.mixed_list.append(Leaf(name='mixed list leaf')) group.mixed_list.append(Group(name='mixed list group')) def json_dumps(instance): return json.dumps(attr.asdict(instance), indent=4) print(' - - - - - - before') print(json_dumps(group)) marshalled = graham.dumps(group) print() print(' - - - - - - serialized type', type(marshalled.data)) unmarshalled = graham.schema(Group).loads(marshalled.data) print() print(' - - - - - - after') print(json_dumps(unmarshalled.data)) print() print(' - - - - - - Original is equal to serialized/deserialized?') print(unmarshalled.data == group) assert unmarshalled.data == group
def get_sunspec_models(path): root_schema = graham.schema(epcpm.sunspecmodel.Root) raw = path.read_bytes() root = root_schema.loads(raw).data return tuple(child.id for child in root.children if isinstance(child, epcpm.sunspecmodel.Model))
class OverlayConfiguration: output_path = create_path_attribute() recipes = attr.ib(metadata=graham.create_metadata( field=marshmallow.fields.List( marshmallow.fields.Nested(graham.schema(OverlayRecipe))), ), ) reference_path = attr.ib( default=None, converter=epyqlib.attrsmodel.to_pathlib_or_none, ) path = attr.ib(default=None) @classmethod def load(cls, path): schema = graham.schema(cls) configuration_text = path.read_text(encoding="utf-8") configuration = schema.loads(configuration_text).data configuration.path = path if configuration.reference_path is None: configuration.reference_path = path.parent return configuration def recipe_output_path(self, recipe): return self.reference_path / self.output_path / recipe.output_path def recipe_base_pmvs_path(self, recipe): return self.reference_path / recipe.base_pmvs_path def recipe_overlay_pmvs_paths(self, recipe): return [ self.reference_path / path for path in recipe.overlay_pmvs_paths ] def raw(self, echo=lambda *args, **kwargs: None): input_modification_times = [] output_modification_times = [] input_modification_times.append(self.path.stat().st_mtime) for recipe in self.recipes: output_path = self.recipe_output_path(recipe=recipe) echo(f"Checking: {os.fspath(output_path)}") try: stat = output_path.stat() except FileNotFoundError: output_modification_time = -math.inf else: output_modification_time = stat.st_mtime output_modification_times.append(output_modification_time) overlay_pmvs_paths = self.recipe_overlay_pmvs_paths(recipe=recipe) for overlay_pmvs_path in overlay_pmvs_paths: input_modification_times.append( overlay_pmvs_path.stat().st_mtime, ) return min(output_modification_times) < max(input_modification_times)
class Enumeration(epyqlib.treenode.TreeNode): name = attr.ib( default='New Enumeration', metadata=graham.create_metadata( field=marshmallow.fields.String(), ), ) children = attr.ib( default=attr.Factory(list), repr=False, metadata=graham.create_metadata( field=graham.fields.MixedList(fields=( marshmallow.fields.Nested(graham.schema(Enumerator)), marshmallow.fields.Nested(graham.schema(SunSpecEnumerator)), )), ), ) # children = attr.ib( # default=attr.Factory(list), # metadata=graham.create_metadata( # field=marshmallow.fields.List( # marshmallow.fields.Nested(graham.schema(Enumerator)), # ), # ), # ) uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() def items(self): for child in self.children: yield (child.name, child.value) def values(self): for child in self.children: yield child.value def can_drop_on(self, node): return isinstance(node, Enumerator) def can_delete(self, node=None): if node is None: return self.tree_parent.can_delete(node=self) return True
class DataPointBitfield(epyqlib.treenode.TreeNode): parameter_uuid = create_parameter_uuid_attribute() children = attr.ib( factory=list, metadata=graham.create_metadata( field=graham.fields.MixedList(fields=(marshmallow.fields.Nested( graham.schema(DataPointBitfieldMember)), )), ), ) # TODO: though this only really makes sense as one of the bitfield types type_uuid = epyqlib.attrsmodel.attr_uuid( default=None, allow_none=True, human_name="Type", data_display=epyqlib.attrsmodel.name_from_uuid, list_selection_root="sunspec types", ) block_offset = attr.ib( default=0, converter=int, metadata=graham.create_metadata(field=marshmallow.fields.Integer(), ), ) size = create_size_attribute() uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() def can_drop_on(self, node): return isinstance( node, ( DataPointBitfieldMember, epyqlib.pm.parametermodel.Parameter, ), ) def can_delete(self, node=None): if node is None: return self.tree_parent.can_delete(node=self) return True def child_from(self, node): if isinstance(node, epyqlib.pm.parametermodel.Parameter): self.parameter_uuid = node.uuid return None return node remove_old_on_drop = epyqlib.attrsmodel.default_remove_old_on_drop internal_move = epyqlib.attrsmodel.default_internal_move check = epyqlib.attrsmodel.check_just_children
class TableRepeatingBlockReference(epyqlib.treenode.TreeNode): name = attr.ib( default="Table Repeating Block Reference", metadata=graham.create_metadata( field=marshmallow.fields.String(), ), ) # offset = attr.ib( # default=2, # converter=int, # ) children = attr.ib( factory=list, metadata=graham.create_metadata( field=graham.fields.MixedList( fields=( marshmallow.fields.Nested( graham.schema( TableRepeatingBlockReferenceFunctionDataReference, ) ), ) ), ), ) original = epyqlib.attrsmodel.create_reference_attribute() uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() @classmethod def all_addable_types(cls): return epyqlib.attrsmodel.create_addable_types(()) @staticmethod def addable_types(): return {} def can_drop_on(self, node): return False def can_delete(self, node=None): if node is None: return self.tree_parent.can_delete(node=self) return False # def check_offsets_and_length(self): # return self.original.check_block_offsets_and_length() remove_old_on_drop = epyqlib.attrsmodel.default_remove_old_on_drop child_from = epyqlib.attrsmodel.default_child_from internal_move = epyqlib.attrsmodel.default_internal_move check = epyqlib.attrsmodel.check_just_children
def test_load_wrong_tag(): tag = 'test' @graham.schemify(tag=tag) @attr.s class Test(object): pass serialized = '{{"{tag_name}": "{tag}"}}' serialized = serialized.format( tag_name=type_name, tag=tag + '-', ) with pytest.raises( marshmallow.exceptions.ValidationError, match=repr(type_name), ): graham.schema(Test).loads(serialized).data
def loads(s, project_path=None, post_load=True): project = graham.schema(Project).loads(s).data if project_path is not None: project.filename = pathlib.Path(project_path).absolute() if post_load: _post_load(project) return project
def loads(s, path=None): root = graham.schema(Root).loads(s).data value_set = ValueSet(path=path) if path is not None: value_set.path = pathlib.Path(path).absolute() _post_load(value_set, root=root) return value_set
def load(cls, path): schema = graham.schema(cls) configuration_text = path.read_text(encoding="utf-8") configuration = schema.loads(configuration_text).data configuration.path = path if configuration.reference_path is None: configuration.reference_path = path.parent return configuration
def test_load_missing_version(): tag = 'test' version = 42 @graham.schemify(tag=tag, version=version) @attr.s class Test(object): pass serialized = '{{"{tag_name}": "{tag}"}}' serialized = serialized.format( tag_name=type_name, tag=tag, ) with pytest.raises( marshmallow.exceptions.ValidationError, match=repr(version_name), ): graham.schema(Test).loads(serialized).data
def addable_types(cls): if cls.addable_types_cache is None: field = graham.schema(cls).fields.get(attribute_name) if field is None: return {} resolved_types = tuple( types.resolve(type_=t.nested, default=cls) for t in field.instances ) cls.addable_types_cache = create_addable_types(resolved_types) return cls.addable_types_cache
class C(object): x = attr.ib( metadata=graham.create_metadata( field=graham.fields.MixedList( fields=( marshmallow.fields.Nested(graham.schema(A)), ), exclude=( B, ), ) ) )
class AccessLevels(epyqlib.treenode.TreeNode): name = attr.ib( default='New Access Levels', metadata=graham.create_metadata( field=marshmallow.fields.String(), ), ) children = attr.ib( default=attr.Factory(list), repr=False, metadata=graham.create_metadata( field=graham.fields.MixedList(fields=( marshmallow.fields.Nested(graham.schema(AccessLevel)), )), ), ) uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() def items(self): for child in self.children: yield (child.name, child.value) def values(self): for child in self.children: yield child.value def can_drop_on(self, node): return False def can_delete(self, node=None): if node is None: return self.tree_parent.can_delete(node=self) return True def by_name(self, name): level, = ( level for level in self.children if level.name.casefold() == name.casefold() ) return level def default(self): return min(self.children, key=lambda x: x.value)
class TableGroupElement(epyqlib.treenode.TreeNode): name = attr.ib( default=None, convert=epyqlib.attrsmodel.to_str_or_none, metadata=graham.create_metadata( field=marshmallow.fields.String(allow_none=True) ), ) path = attr.ib( factory=tuple, ) epyqlib.attrsmodel.attrib( attribute=path, no_column=True, ) graham.attrib( attribute=path, field=graham.fields.Tuple(marshmallow.fields.UUID()), ) children = attr.ib( default=attr.Factory(list), cmp=False, repr=False, metadata=graham.create_metadata( field=graham.fields.MixedList(fields=( marshmallow.fields.Nested('TableGroupElement'), marshmallow.fields.Nested(graham.schema(TableArrayElement)), )), ), ) uuid = epyqlib.attrsmodel.attr_uuid() original = attr.ib(default=None) epyqlib.attrsmodel.attrib( attribute=original, no_column=True, ) def __attrs_post_init__(self): super().__init__() def can_drop_on(self, node): return False def can_delete(self, node=None): return False
def test_load_from_dump_to(): @graham.schemify(tag='test') @attr.s class Test(object): test = attr.ib() graham.attrib(attribute=test, field=marshmallow.fields.String( load_from='test_load_dump', dump_to='test_load_dump', )) test = Test(test='test string') serialized = '{{"{}": "test", "test_load_dump": "test string"}}'.format( type_name, ) assert graham.dumps(test).data == serialized assert graham.schema(Test).loads(serialized).data == test
def test_nonserialized(): @graham.schemify(tag='test') @attr.s class Test(object): test = attr.ib() graham.attrib( attribute=test, field=marshmallow.fields.String(), ) nope = attr.ib(default=None) test = Test(test='test') serialized = '{{"{}": "test", "test": "test"}}'.format(type_name) assert graham.dumps(test).data == serialized assert graham.schema(Test).loads(serialized).data == test
def test_table_update_same_uuid(qapp): root = ( graham.schema(epyqlib.pm.parametermodel.Root) .loads( serialized_sample, ) .data ) model = epyqlib.attrsmodel.Model( root=root, columns=epyqlib.pm.parametermodel.columns, ) root.model = model (table_a,) = root.nodes_by_attribute( attribute_value="Table A", attribute_name="name", ) # TODO: CAMPid 9784566547216435136479765479163496731 def collect(node): def collect(node, payload): payload[node.uuid] = node.name results = {} node.traverse( call_this=collect, internal_nodes=True, payload=results, ) return results table_a.update() original = collect(table_a) table_a.update() after_update = collect(table_a) assert after_update == original
def test_hex_field(): @graham.schemify(tag='test') @attr.s(hash=False) class Test: field = attr.ib(metadata=graham.create_metadata( field=epcpm.canmodel.HexadecimalIntegerField(), ), ) none = attr.ib( default=None, metadata=graham.create_metadata( field=epcpm.canmodel.HexadecimalIntegerField( allow_none=True, ), ), ) t = Test(field=0x123) serialized = '{"_type": "test", "field": "0x123", "none": null}' assert graham.dumps(t).data == serialized assert graham.schema(Test).loads(serialized).data == t
class FixedBlock(epyqlib.treenode.TreeNode): name = attr.ib( default='Fixed Block', metadata=graham.create_metadata(field=marshmallow.fields.String(), ), ) offset = attr.ib( default=2, converter=int, ) children = attr.ib( factory=list, metadata=graham.create_metadata(field=graham.fields.MixedList( fields=(marshmallow.fields.Nested(graham.schema(DataPoint)), )), ), ) uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() def can_drop_on(self, node): return isinstance( node, ( epyqlib.pm.parametermodel.Parameter, DataPoint, ), ) def can_delete(self, node=None): return False def child_from(self, node): if isinstance(node, epyqlib.pm.parametermodel.Parameter): return DataPoint(parameter_uuid=node.uuid) return node check_offsets_and_length = check_block_offsets_and_length remove_old_on_drop = epyqlib.attrsmodel.default_remove_old_on_drop internal_move = epyqlib.attrsmodel.default_internal_move check = epyqlib.attrsmodel.check_just_children
class Group(epyqlib.treenode.TreeNode): name = attr.ib( default='New Group', metadata=graham.create_metadata( field=marshmallow.fields.String(), ), ) type_name = attr.ib( default=None, converter=epyqlib.attrsmodel.to_str_or_none, metadata=graham.create_metadata( field=marshmallow.fields.String(allow_none=True), ), ) children = attr.ib( default=attr.Factory(list), cmp=False, repr=False, metadata=graham.create_metadata( field=graham.fields.MixedList(fields=( # TODO: would be nice to self reference without a name marshmallow.fields.Nested('Group'), marshmallow.fields.Nested('Array'), marshmallow.fields.Nested('Table'), marshmallow.fields.Nested(graham.schema(Parameter)), )), ), ) uuid = epyqlib.attrsmodel.attr_uuid() def __attrs_post_init__(self): super().__init__() def can_drop_on(self, node): return isinstance(node, tuple(self.addable_types().values())) def can_delete(self, node=None): if node is None: return self.tree_parent.can_delete(node=self) return True
def load_model(project, path, root_type, columns, drop_sources=()): resolved_path = path if project.filename is not None: resolved_path = project.filename.parents[0] / resolved_path with open(resolved_path) as f: raw = f.read() root_schema = graham.schema(root_type) root = root_schema.loads(raw).data def collect(node, payload): payload[node.uuid] = node uuid_to_node = {} root.traverse(call_this=collect, payload=uuid_to_node, internal_nodes=True) def update(node, payload): for field in attr.fields(type(node)): if ( field.metadata.get(graham.core.metadata_key) is None or not isinstance( field.metadata.get(graham.core.metadata_key).field, epyqlib.attrsmodel.Reference, ) ): continue value = getattr(node, field.name) original = payload.get(value) if original is not None: setattr(node, field.name, original) root.traverse(call_this=update, payload=uuid_to_node, internal_nodes=True) return epyqlib.attrsmodel.Model( root=root, columns=columns, drop_sources=drop_sources, )
def cli(parameters, declaration): root_schema = graham.schema(epyqlib.pm.parametermodel.Root) root = root_schema.loads(parameters.read()).data model = epyqlib.attrsmodel.Model( root=root, columns=epyqlib.pm.parametermodel.columns, ) builder = epcpm.parameterstoc.builders.wrap(model.root) generator = pycparser.c_generator.CGenerator() if declaration: items = builder.definition() else: items = builder.instantiation() ast = pycparser.c_ast.FileAST(items) s = generator.visit(ast) click.echo(s, nl=False)
class Root(epyqlib.treenode.TreeNode): name = attr.ib( default=default_name, ) graham.attrib( attribute=name, field=marshmallow.fields.String(), ) children = attr.ib( default=attr.Factory(list), ) graham.attrib( attribute=children, field=graham.fields.MixedList(fields=( marshmallow.fields.Nested(graham.schema(type_)) for type_ in valid_types # marshmallow.fields.Nested('Group'), # marshmallow.fields.Nested(graham.schema(Leaf)), )), ) model = attr.ib(default=None) uuid = attr_uuid() def __attrs_post_init__(self): super().__init__() def can_drop_on(self, node): return isinstance(node, tuple(self.addable_types().values())) @staticmethod def can_delete(node=None): if node is None: return False return True
class Root(epyqlib.treenode.TreeNode): name = attr.ib( default=default_name, ) graham.attrib( attribute=name, field=marshmallow.fields.String(), ) children = attr.ib( default=attr.Factory(list), ) graham.attrib( attribute=children, field=graham.fields.MixedList( fields=( marshmallow.fields.Nested(graham.schema(type_)) for type_ in valid_types # marshmallow.fields.Nested('Group'), # marshmallow.fields.Nested(graham.schema(Leaf)), ) ), ) model = attr.ib(default=None) uuid = attr_uuid() def __attrs_post_init__(self): super().__init__() def can_drop_on(self, node): return isinstance(node, tuple(self.addable_types().values())) remove_old_on_drop = default_remove_old_on_drop child_from = default_child_from internal_move = default_internal_move def check_and_append(self, models, parent=None): # TODO: ugh, circular dependencies import epyqlib.checkresultmodel if parent is None: parent = epyqlib.checkresultmodel.Root() for child in self.children: result = child.check(models=models) if result is not None: parent.append_child(result) return parent def check(self, models): return self.check_and_append(models=models) @staticmethod def can_delete(node=None): if node is None: return False return True