Exemplo n.º 1
0
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
Exemplo n.º 2
0
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)),
        )),
    )
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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"}')
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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))
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
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
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
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
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
 class C(object):
     x = attr.ib(
         metadata=graham.create_metadata(
             field=graham.fields.MixedList(
                 fields=(
                     marshmallow.fields.Nested(graham.schema(A)),
                 ),
                 exclude=(
                     B,
                 ),
             )
         )
     )
Exemplo n.º 19
0
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)
Exemplo n.º 20
0
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
Exemplo n.º 21
0
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
Exemplo n.º 22
0
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
Exemplo n.º 23
0
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
Exemplo n.º 24
0
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
Exemplo n.º 25
0
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
Exemplo n.º 26
0
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
Exemplo n.º 27
0
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,
    )
Exemplo n.º 28
0
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)
Exemplo n.º 29
0
    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
Exemplo n.º 30
0
    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