Example #1
0
    def test_error_paths(self):
        """ Test various loader error situations"""

        fn = env.input_path('loadererror1.yaml')
        with self.assertRaises(ValueError,
                               msg="Unknown slot domain should fail") as e:
            SchemaLoader(fn).resolve()
        self.assertIn('loadererror1.yaml", line 11, col 13', str(e.exception))

        fn = env.input_path('loadererror2.yaml')
        with self.assertRaises(ValueError, msg="No Type URI") as e:
            SchemaLoader(fn).resolve()
        self.assertIn('type "string" does not declare a URI', str(e.exception))

        fn = env.input_path('loadererror2a.yaml')
        with self.assertRaises(ValueError,
                               msg="Optional key slot should fail") as e:
            SchemaLoader(fn).resolve()
        self.assertIn('slot: s1 - key and identifier slots cannot be optional',
                      str(e.exception))

        fn = env.input_path('loadertest1.yaml')
        schema = SchemaLoader(fn).resolve()
        self.assertEqual('string', schema.slots['s1'].range)

        fn = env.input_path('loadererror4.yaml')
        with self.assertRaises(ValueError,
                               msg="Default prefix is not defined") as e:
            SchemaLoader(fn).resolve()
        self.assertIn('loadererror4.yaml", line 6, col 17', str(e.exception))
Example #2
0
 def test_missing_type_uri(self):
     """ A type with neither a typeof or uri is an error """
     fn = env.input_path('loadererror10.yaml')
     with self.assertRaises(ValueError,
                            msg="A non-typeof type has to have a URI") as e:
         _ = SchemaLoader(fn).resolve()
     self.assertIn('loadererror10.yaml", line 12, col 3', str(e.exception))
     fn = env.input_path('loaderpass11.yaml')
     _ = SchemaLoader(fn).resolve()
Example #3
0
 def __init__(self, schema: Union[str, TextIO, SchemaDefinition],
              **kwargs) -> None:
     super().__init__(schema, **kwargs)
     self.graph: Optional[Graph] = None
     self.metamodel = SchemaLoader(LOCAL_METAMODEL_YAML_FILE, importmap=kwargs.get('importmap', None),
                                   mergeimports=self.merge_imports) \
         if os.path.exists(LOCAL_METAMODEL_YAML_FILE) else\
         SchemaLoader(METAMODEL_YAML_URI, base_dir=META_BASE_URI, importmap=kwargs.get('importmap', None),
                      mergeimports=self.merge_imports)
     self.metamodel.resolve()
     self.top_value_uri: Optional[URIRef] = None
 def test_csolink_model(self):
     """ Make sure the csolink model is valid """
     schema = SchemaLoader(CSOLINK_MODEL_YAML)
     errors = []
     try:
         schema.resolve()
     except ValueError as e:
         errors.append(str(e))
     if not errors:
         errors = schema.synopsis.errors()
     self.assertEqual([], errors, "csolink-model.yaml - errors detected")
Example #5
0
def cli(inputs, view: bool):
    """
    Generates SQL VIEW commands from hints embedded in linkml
    """
    for input in inputs:
        with open(input, 'r') as stream:
            schema = load_raw_schema(input)
            print('-- ** REWRITE TABLES AS VIEWS **')
            print(f'-- SCHEMA: {schema.id}')
            loader = SchemaLoader(schema, mergeimports=True)
            loader.resolve()
            generate_views_from_linkml(schema, view)
Example #6
0
 def test_default_range(self):
     """ Validate default slot range settings """
     schema = SchemaLoader(env.input_path('resolver1.yaml')).resolve()
     self.assertEqual({
         's1': 't1',
         's2': 't2'
     }, {slot.name: slot.range
         for slot in schema.slots.values()})
     schema = SchemaLoader(env.input_path('resolver2.yaml')).resolve()
     self.assertEqual({
         's1': 'string',
         's2': 't2'
     }, {slot.name: slot.range
         for slot in schema.slots.values()})
Example #7
0
 def test_representation_errors(self):
     """ Test misformed schema elements """
     fn = env.input_path('typeerror1.yaml')
     with self.assertRaises(ValueError):
         SchemaLoader(fn)
     fn = env.input_path('typeerror2.yaml')
     with self.assertRaises(ValueError):
         SchemaLoader(fn)
     fn = env.input_path('typeerror3.yaml')
     with self.assertRaises(ValueError):
         SchemaLoader(fn)
     fn = env.input_path('typeerror4.yaml')
     with self.assertRaises(ValueError):
         SchemaLoader(fn)
Example #8
0
    def test_multi_key(self):
        """ Multiple keys are not supported """
        fn = env.input_path('loadererror6.yaml')
        with self.assertRaises(
                ValueError, msg='Multiple keys/identifiers not allowed') as e:
            _ = SchemaLoader(fn).resolve()
        self.assertIn('multiple keys/identifiers not allowed',
                      str(e.exception))

        fn = env.input_path('loadererror7.yaml')
        with self.assertRaises(ValueError,
                               msg="Two or more keys are not allowed") as e:
            _ = SchemaLoader(fn).resolve()
        self.assertIn('multiple keys/identifiers not allowed',
                      str(e.exception))
Example #9
0
 def test_empty_range(self):
     """ A type must have either a base or a parent """
     fn = env.input_path('loadererror5.yaml')
     with self.assertRaises(ValueError,
                            msg="Range error should be raised") as e:
         _ = SchemaLoader(fn).resolve()
     self.assertIn('loadererror5.yaml", line 9, col 3', str(e.exception))
Example #10
0
 def eval_loader(self,
                 base_name: str,
                 logger: Optional[logging.Logger] = None,
                 source: Optional[str] = None) -> None:
     loader = SchemaLoader(source
                           or self.env.input_path(base_name + '.yaml'),
                           logger=logger)
     self.env.generate_single_file(base_name + '.json',
                                   lambda: as_json(loader.resolve()),
                                   filtr=json_metadata_filter,
                                   value_is_returned=True)
     self.env.generate_single_file(
         base_name + '.errs',
         lambda: '\n'.join(loader.synopsis.errors()),
         filtr=json_metadata_filter,
         value_is_returned=True)
Example #11
0
 def test_undefined_subset(self):
     """ Throw an error on an undefined subset reference """
     fn = env.input_path('loadererror11.yaml')
     with self.assertRaises(ValueError,
                            msg="Subset references must be valid") as e:
         _ = SchemaLoader(fn).resolve()
     self.assertIn('loadererror11.yaml", line 22, col 16', str(e.exception))
Example #12
0
    def eval_synopsis(self,
                      base_name: str,
                      source: Optional[str] = None) -> None:
        schema = SchemaLoader(source if source else env.input_path(base_name +
                                                                   '.yaml'),
                              importmap=env.import_map)
        schema.resolve()
        self.summary = schema.synopsis.summary()

        self.env.generate_single_file(
            base_name + '.errs',
            lambda: '\n'.join(schema.synopsis.errors()),
            value_is_returned=True)
        self.env.generate_single_file(base_name + '.synopsis',
                                      lambda: self.summary,
                                      value_is_returned=True)
Example #13
0
 def test_multi_usages_3(self):
     """ Illegal alias usage """
     with self.assertRaises(ValueError) as e:
         schema = SchemaLoader(
             env.input_path('multi_usages_3.yaml')).resolve()
     self.assertIn(
         'Class: "child_class1" - alias not permitted in slot_usage slot: foo',
         str(e.exception))
Example #14
0
 def test_mergeerror1(self):
     """ Test conflicting definitions path """
     fn = env.input_path('mergeerror1.yaml')
     with self.assertRaises(ValueError) as ve:
         SchemaLoader(fn)
     self.assertEqual(
         "Conflicting URIs (http://example.org/schema2, http://example.org/schema1) for item: c1",
         str(ve.exception))
Example #15
0
 def test_issue_177_dup(self):
     env.generate_single_file(
         'issue_177_error.yaml',
         lambda: as_yaml(
             SchemaLoader(env.input_path('issue_177_error.yaml')).resolve()
         ),
         value_is_returned=True,
         filtr=yaml_filter)
Example #16
0
 def test_issue_18(self):
     """ Make sure that inverses are automatically generated """
     env.generate_single_file(
         'issue_18.yaml',
         lambda: as_yaml(
             SchemaLoader(env.input_path('issue_18.yaml')).resolve()),
         filtr=yaml_filter,
         value_is_returned=True)
Example #17
0
 def test_missing_inverse(self):
     with self.assertRaises(ValueError) as e:
         env.generate_single_file(
             'issue_18_error2.yaml',
             lambda: as_yaml(
                 SchemaLoader(env.input_path('issue_18_error2.yaml')).
                 resolve()),
             value_is_returned=True)
     self.assertEqual('Slot s1.inverse (s2) is not defined',
                      str(e.exception).strip())
Example #18
0
    def test_key_and_id(self):
        """ A slot cannot be both a key and an identifier """
        fn = env.input_path('loadererror8.yaml')
        with self.assertRaises(
                ValueError,
                msg="A slot cannot be both a key and identifier") as e:
            _ = SchemaLoader(fn).resolve()
        self.assertIn(
            'A slot cannot be both a key and identifier at the same time',
            str(e.exception))

        fn = env.input_path('loadererror9.yaml')
        with self.assertRaises(
                ValueError,
                msg="A slot cannot be both a key and identifier") as e:
            _ = SchemaLoader(fn).resolve()
        self.assertIn(
            'A slot cannot be both a key and identifier at the same time',
            str(e.exception))
Example #19
0
 def test_issue_58(self):
     """ Reject non NSNAME model names"""
     with self.assertRaises(ValueError) as ve:
         env.generate_single_file(
             'issue_58_error1.yaml',
             lambda: as_yaml(
                 SchemaLoader(env.input_path('issue_58_error1.yaml')).
                 resolve()),
             value_is_returned=True)
     self.assertIn('issue 58: Not a valid NCName', str(ve.exception))
Example #20
0
 def test_no_inverse_domain(self):
     with self.assertRaises(ValueError) as e:
         env.generate_single_file(
             'issue_18_error3.yaml',
             lambda: as_yaml(
                 SchemaLoader(env.input_path('issue_18_error3.yaml')).
                 resolve()),
             value_is_returned=True)
     self.assertEqual(
         "Unable to determine the range of slot `s1'. Its inverse (s2) has no declared domain",
         str(e.exception).strip())
Example #21
0
 def test_type_uri(self):
     """ Validate type URI's and the fact that they aren't inherited """
     schema = SchemaLoader(env.input_path('resolver2.yaml')).resolve()
     self.assertEqual(
         {
             'string': 'xsd:string',
             't1': 'xsd:string',
             't2': 'xsd:int',
             't3': 'xsd:string'
         }, {t.name: t.uri
             for t in schema.types.values()})
Example #22
0
def cli(inputs, limit: int):
    """
    Generates SQL VIEW commands from hints embedded in linkml linkml
    """
    for input in inputs:
        with open(input, 'r') as stream:
            schema = load_raw_schema(input)
            print('-- ** REWRITE TABLES AS VIEWS **')
            print(f'-- SCHEMA: {schema.id}')
            loader = SchemaLoader(schema, mergeimports=True)
            loader.resolve()
            for cn, c in schema.classes.items():
                if c.mixin:
                    continue
                if len(c.slots) > 0:
                    if not c.abstract and not c.mixin:
                        sql_table = underscore(cn)
                        print(f'SELECT * FROM {sql_table} LIMIT {limit};')
                else:
                    print(f'-- No slots for {cn}')
Example #23
0
 def test_inverse_mismatch(self):
     """ Test error detection when inverses don't match """
     with self.assertRaises(ValueError) as e:
         env.generate_single_file(
             'issue_18_error1.yaml',
             lambda: as_yaml(
                 SchemaLoader(env.input_path('issue_18_error1.yaml')).
                 resolve()),
             value_is_returned=True)
     self.assertEqual(
         'Slot s1.inverse (s2) does not match slot s2.inverse (s3)',
         str(e.exception).strip())
Example #24
0
 def test_multi_domains(self):
     with self.redirect_logstream() as logger:
         env.generate_single_file(
             'issue_18_warning1.yaml',
             lambda: as_yaml(
                 SchemaLoader(env.input_path('issue_18_warning1.yaml'),
                              logger=logger).resolve()),
             filtr=yaml_filter,
             value_is_returned=True)
     self.assertIn(
         'Slot s2.inverse (s1), has multi domains (c1, c2)  Multi ranges not yet implemented',
         logger.result)
Example #25
0
 def test_importmap(self):
     """ Test the importmap parameter """
     fn = env.input_path('import_test_1.yaml')
     importmap = {
         "http://example.org/import_test_2": "import_test_2",
         "loc/imp3": "import_test_3",
         "base:import_test_4": "http://example.org/import_test_4",
         "http://example.org/import_test_4": "import_test_4",
         "types": "http://w3id.org/linkml/types"
     }
     self.env.generate_single_file(
         'import_test_1.json',
         lambda: as_json(SchemaLoader(fn, importmap=importmap).resolve()),
         filtr=json_metadata_filter)
Example #26
0
 def test_multi_usages_2(self):
     """ Slot usage chain with starting alias """
     schema = SchemaLoader(env.input_path('multi_usages_2.yaml')).resolve()
     self._eval_expected(schema, 's1', 'value', 'root_class', None, None,
                         'string')
     self._eval_expected(schema, 'child_class1_s1', 'value', 'child_class1',
                         's1', 's1', 'boolean')
     self._eval_expected(schema, 'child_class2_s1', 'value', 'child_class2',
                         'child_class1_s1', 's1', 'integer')
     self._eval_expected(schema, 'child_class3_s1', 'value', 'child_class3',
                         'child_class2_s1', 's1', 'integer')
     env.eval_single_file(env.expected_path('multi_usages_2.yaml'),
                          as_yaml(schema),
                          filtr=yaml_filter)
Example #27
0
    def test_element_slots(self):
        """ Test all element slots and their inheritence """
        schema = SchemaLoader(env.input_path('resolver3.yaml')).resolve()
        x = {
            k: v
            for k, v in as_dict(schema.slots['s1']).items()
            if v is not None and v != []
        }
        outfile = env.expected_path('resolver3.json')
        if not os.path.exists(outfile):
            with open(outfile, 'w') as f:
                f.write(as_json(JsonObj(**x)))
        with open(outfile) as f:
            expected = as_dict(load(f))

        self.assertEqual(expected, x)
Example #28
0
 def test_biolink(self):
     """ SchemaLoader should be monotonic - metamodel test """
     biolink_schema = SchemaLoader(LOCAL_METAMODEL_YAML_FILE).resolve()
     biolink_schema_2 = SchemaLoader(biolink_schema).resolve()
     self.assertEqual(biolink_schema, biolink_schema_2)
Example #29
0
class OwlSchemaGenerator(Generator):
    generatorname = os.path.basename(__file__)
    generatorversion = "0.1.1"
    valid_formats = ['owl', 'ttl'] + [
        x.name
        for x in rdflib_plugins(None, rdflib_Parser) if '/' not in str(x.name)
    ]
    visits_are_sorted = True

    def __init__(self, schema: Union[str, TextIO, SchemaDefinition],
                 **kwargs) -> None:
        super().__init__(schema, **kwargs)
        self.graph: Optional[Graph] = None
        self.metamodel = SchemaLoader(LOCAL_METAMODEL_YAML_FILE, importmap=kwargs.get('importmap', None),
                                      mergeimports=self.merge_imports) \
            if os.path.exists(LOCAL_METAMODEL_YAML_FILE) else\
            SchemaLoader(METAMODEL_YAML_URI, base_dir=META_BASE_URI, importmap=kwargs.get('importmap', None),
                         mergeimports=self.merge_imports)
        self.metamodel.resolve()
        self.top_value_uri: Optional[URIRef] = None

    def visit_schema(self, output: Optional[str] = None, **_):
        base = URIRef(self.schema.id)
        self.graph = Graph(identifier=base)
        for prefix in self.metamodel.schema.emit_prefixes:
            self.graph.bind(prefix, self.metamodel.namespaces[prefix])

        self.graph.add((base, RDF.type, OWL.Ontology))
        self._add_element_properties(base, self.schema)

        # add the model types
        for name in [
                'class_definition', 'type_definition', 'slot_definition',
                'subset_definition'
        ]:
            self._add_metamodel_class(name)

        # add value placeholder
        self.top_value_uri = self.metamodel.namespaces[
            METAMODEL_NAMESPACE_NAME]['topValue']
        self.graph.add((self.top_value_uri, RDF.type, OWL.DatatypeProperty))
        self.graph.add((self.top_value_uri, RDFS.label, Literal("value")))

    def end_schema(self, output: Optional[str] = None, **_) -> None:
        data = self.graph.serialize(format='turtle' if self.format in
                                    ['owl', 'ttl'] else self.format).decode()
        if output:
            with open(output, 'w') as outf:
                outf.write(data)
        else:
            print(data)

    def add_metadata(self, e: SchemaDefinition, uri: URIRef) -> None:
        if e.aliases is not None:
            for s in e.aliases:
                self.graph.add((uri, SKOS.altLabel, Literal(s)))
        if e.mappings is not None:
            for m in e.mappings:
                m_uri = self.namespaces.uri_for(m)
                if m_uri is not None:
                    self.graph.add((uri, SKOS.exactMatch, m_uri))
                else:
                    logging.warning(f'No URI for {m}')
        if e.exact_mappings is not None:
            for m in e.exact_mappings:
                m_uri = self.namespaces.uri_for(m)
                if m_uri is not None:
                    self.graph.add((uri, SKOS.exactMatch, m_uri))
                else:
                    logging.warning(f'No URI for {m}')
        if e.close_mappings is not None:
            for m in e.close_mappings:
                m_uri = self.namespaces.uri_for(m)
                if m_uri is not None:
                    self.graph.add((uri, SKOS.closeMatch, m_uri))
                else:
                    logging.warning(f'No URI for {m}')
        if e.narrow_mappings is not None:
            for m in e.narrow_mappings:
                m_uri = self.namespaces.uri_for(m)
                if m_uri is not None:
                    self.graph.add((uri, SKOS.narrowMatch, m_uri))
                else:
                    logging.warning(f'No URI for {m}')
        if e.broad_mappings is not None:
            for m in e.broad_mappings:
                m_uri = self.namespaces.uri_for(m)
                if m_uri is not None:
                    self.graph.add((uri, SKOS.broadMatch, m_uri))
                else:
                    logging.warning(f'No URI for {m}')
        if e.related_mappings is not None:
            for m in e.related_mappings:
                m_uri = self.namespaces.uri_for(m)
                if m_uri is not None:
                    self.graph.add((uri, SKOS.relatedMatch, m_uri))
                else:
                    logging.warning(f'No URI for {m}')

    def visit_class(self, cls: ClassDefinition) -> bool:
        cls_uri = self._class_uri(cls.name)
        self.add_metadata(cls, cls_uri)
        self.graph.add((cls_uri, RDF.type, OWL.Class))
        self.graph.add((cls_uri, RDF.type,
                        self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME][
                            camelcase('class definition')]))
        self._add_element_properties(cls_uri, cls)

        # Parent classes
        # TODO: reintroduce this
        # if not cls.defining_slots:
        if True:
            if cls.is_a:
                self.graph.add(
                    (cls_uri, RDFS.subClassOf, self._class_uri(cls.is_a)))
            if cls.mixin:
                self.graph.add(
                    (cls_uri, RDFS.subClassOf, METAMODEL_NAMESPACE.mixin))
            for mixin in sorted(cls.mixins):
                self.graph.add(
                    (cls_uri, RDFS.subClassOf, self._class_uri(mixin)))
            if cls.name in self.synopsis.applytorefs:
                for appl in sorted(
                        self.synopsis.applytorefs[cls.name].classrefs):
                    self.graph.add(
                        (cls_uri, RDFS.subClassOf, self._class_uri(appl)))
        else:
            raise NotImplementedError("Defining slots need to be implemented")
            # If defining slots, we generate an equivalentClass entry
            # equ_node = BNode()
            # self.graph.add((cls_uri, OWL.equivalentClass, equ_node))
            # self.graph.add((equ_node, RDF.type, OWL.Class))
            #
            # elts = []
            # if cls.is_a:
            #     elts.append(self._class_uri(cls.is_a))
            # if cls.mixin:
            #     self.graph.add((cls_uri, RDFS.subClassOf, META_NS.mixin))
            # for mixin in cls.mixins:
            #     self.graph.add((cls_uri, RDFS.subClassOf, self._class_uri(mixin)))
            # if cls.name in self.synopsis.applytorefs:
            #     for appl in self.synopsis.applytorefs[cls.name].classrefs:
            #         self.graph.add((cls_uri, RDFS.subClassOf, self._class_uri(appl)))
            #
            # for slotname in cls.defining_slots:
            #     restr_node = BNode()
            #     slot = self.schema.slots[slotname]
            #
            #     self.graph.add((restr_node, RDF.type, OWL.Restriction))
            #     self.graph.add((restr_node, OWL.onProperty, self._prop_uri(slotname)))
            #     self._add_cardinality(restr_node, slot)
            #     # TODO: fix this
            #     # self.graph.add((restr_node, OWL.someValuesFrom, self._build_range(slot)))
            #     elts.append(restr_node)
            #
            # coll_bnode = BNode()
            # Collection(self.graph, coll_bnode, elts)
            # self.graph.add((equ_node, OWL.intersectionOf, coll_bnode))

        # TODO: see whether unions belong
        # if cls.union_of:
        #     union_node = BNode()
        #     Collection(self.graph, union_coll, [self.class_uri(union_node) for union_node in cls.union_of])
        #     self.graph.add((union_node, OWL.unionOf, union_coll))
        #     self.graph.add((cls_uri, RDFS.subClassOf, union_node))

        for sn in sorted(self.own_slot_names(cls)):
            # Defining_slots are covered above
            if sn not in cls.defining_slots:
                slot = self.schema.slots[sn]
                # Non-inherited slots are annotation properties
                if self.is_slot_object_property(slot):
                    slot_node = BNode()
                    self.graph.add((cls_uri, RDFS.subClassOf, slot_node))

                    #         required multivalued
                    if slot.required:
                        if slot.multivalued:
                            #    y         y     intersectionOf(restriction(slot only type) restriction(slot some type)
                            restr1 = BNode()
                            self.graph.add((restr1, RDF.type, OWL.Restriction))
                            self.graph.add((restr1, OWL.allValuesFrom,
                                            self._range_uri(slot)))
                            self.graph.add(
                                (restr1, OWL.onProperty,
                                 self._prop_uri(self.aliased_slot_name(slot))))

                            restr2 = BNode()
                            self.graph.add((restr2, RDF.type, OWL.Restriction))
                            self.graph.add((restr2, OWL.someValuesFrom,
                                            self._range_uri(slot)))
                            self.graph.add(
                                (restr2, OWL.onProperty,
                                 self._prop_uri(self.aliased_slot_name(slot))))

                            coll_bnode = BNode()
                            Collection(self.graph, coll_bnode,
                                       [restr1, restr2])
                            self.graph.add(
                                (slot_node, OWL.intersectionOf, coll_bnode))
                            self.graph.add((slot_node, RDF.type, OWL.Class))
                        else:
                            #    y         n      restriction(slot exactly 1 type)
                            self.graph.add(
                                (slot_node, RDF.type, OWL.Restriction))
                            self.graph.add(
                                (slot_node, OWL.qualifiedCardinality,
                                 Literal(1)))
                            self.graph.add(
                                (slot_node, OWL.onProperty,
                                 self._prop_uri(self.aliased_slot_name(slot))))
                            self.graph.add((slot_node, OWL.onClass,
                                            self._range_uri(slot)))
                    else:
                        if slot.multivalued:
                            #    n         y      restriction(slot only type)
                            self.graph.add(
                                (slot_node, RDF.type, OWL.Restriction))
                            self.graph.add((slot_node, OWL.allValuesFrom,
                                            self._range_uri(slot)))
                            self.graph.add(
                                (slot_node, OWL.onProperty,
                                 self._prop_uri(self.aliased_slot_name(slot))))
                        else:
                            #    n         n      intersectionOf(restriction(slot only type) restriction(slot max 1 type))
                            self.graph.add(
                                (slot_node, RDF.type, OWL.Restriction))
                            self.graph.add((slot_node, OWL.onClass,
                                            self._range_uri(slot)))
                            self.graph.add(
                                (slot_node, OWL.maxQualifiedCardinality,
                                 Literal(1)))
                            self.graph.add(
                                (slot_node, OWL.onProperty,
                                 self._prop_uri(self.aliased_slot_name(slot))))

        return True

    def visit_slot(self, slot_name: str, slot: SlotDefinition) -> None:
        """ Add a slot definition per slot

        @param slot_name:
        @param slot:
        @return:
        """
        # Note: We use the raw name in OWL and add a subProperty arc
        slot_uri = self._prop_uri(slot.name)
        self._add_element_properties(slot_uri, slot)
        # Inherited slots are object or data properties
        # All others are annotation properties
        self.graph.add(
            (slot_uri, RDF.type, OWL.ObjectProperty if
             self.is_slot_object_property(slot) else OWL.AnnotationProperty))
        self.graph.add((slot_uri, RDF.type,
                        self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME][
                            camelcase('slot definition')]))
        self.graph.add((slot_uri, RDFS.range, self._range_uri(slot)))
        if slot.domain:
            self.graph.add(
                (slot_uri, RDFS.domain, self._class_uri(slot.domain)))
        if slot.inverse:
            self.graph.add(
                (slot_uri, OWL.inverseOf, self._prop_uri(slot.inverse)))
        if slot.symmetric:
            self.graph.add((slot_uri, RDF.type, OWL.SymmetricProperty))

        # Parent slots
        if slot.is_a:
            self.graph.add(
                (slot_uri, RDFS.subPropertyOf, self._prop_uri(slot.is_a)))
        for mixin in slot.mixins:
            self.graph.add(
                (slot_uri, RDFS.subPropertyOf, self._prop_uri(mixin)))
        if slot.name in self.synopsis.applytorefs:
            for appl in self.synopsis.applytorefs[slot.name].slotrefs:
                self.graph.add(
                    (slot_uri, RDFS.subClassOf, self._prop_uri(appl)))

    def visit_type(self, typ: TypeDefinition) -> None:
        type_uri = self._type_uri(typ.name)
        self.graph.add((type_uri, RDF.type, OWL.Class))
        self.graph.add((type_uri, RDF.type,
                        self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME][
                            camelcase('type definition')]))
        self._add_element_properties(type_uri, typ)
        if typ.typeof:
            self.graph.add(
                (type_uri, RDFS.subClassOf, self._type_uri(typ.typeof)))
        else:
            restr = BNode()
            self.graph.add((restr, RDF.type, OWL.Restriction))
            self.graph.add((restr, OWL.qualifiedCardinality, Literal(1)))
            self.graph.add((restr, OWL.onProperty, self.top_value_uri))
            self.graph.add(
                (restr, OWL.onDataRange, self.namespaces.uri_for(typ.uri)))
            self.graph.add((type_uri, RDFS.subClassOf, restr))

    def _add_element_properties(self, uri: URIRef, el: Element) -> None:
        for k, v in el.__dict__.items():
            if k in self.metamodel.schema.slots:
                defining_slot = self.metamodel.schema.slots[k]
                if v is not None and 'owl' in defining_slot.in_subset:
                    ve = v if isinstance(v, list) else [v]
                    for e in ve:
                        self.graph.add(
                            (uri,
                             URIRef(
                                 self.metamodel.namespaces.uri_for(
                                     defining_slot.slot_uri)), Literal(e)))

    def _add_cardinality(self, subj: Union[BNode, URIRef], slot) -> None:
        """ Add cardinality restrictions to node """
        if slot.required:
            if slot.multivalued:
                self.graph.add((subj, OWL.minCardinality, Literal(1)))
            else:
                self.graph.add((subj, OWL.cardinality, Literal(1)))
        elif not slot.multivalued:
            self.graph.add((subj, OWL.maxCardinality, Literal(1)))

    def _range_uri(self, slot: SlotDefinition) -> URIRef:
        if slot.range in self.schema.types:
            return self._type_uri(TypeDefinitionName(slot.range))
        elif slot.range in self.schema.enums:
            # TODO: enums fill this in
            return self._enum_uri(EnumDefinitionName(slot.range))
        else:
            return self._class_uri(ClassDefinitionName(slot.range))

    def _class_uri(self, cn: ClassDefinitionName) -> URIRef:
        c = self.schema.classes[cn]
        return URIRef(c.definition_uri)

    def _enum_uri(self, en: EnumDefinitionName) -> URIRef:
        # TODO: enums
        e = self.schema.enums[en]
        return URIRef(f"http://UNKNOWN.org/{en}")

    def _prop_uri(self, pn: SlotDefinitionName) -> URIRef:
        p = self.schema.slots.get(pn, None)
        if p is None or p.definition_uri is None:
            return self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME][
                underscore(pn)]
        else:
            return URIRef(p.definition_uri)

    def _type_uri(self, tn: TypeDefinitionName) -> URIRef:
        t = self.schema.types[tn]
        return URIRef(t.definition_uri)

    def _add_metamodel_class(self, cname: str) -> None:
        metac = self.metamodel.schema.classes[cname]
        metac_uri = self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME][
            camelcase(metac.name)]
        self.graph.add((metac_uri, RDF.type, OWL.Class))
        self._add_element_properties(metac_uri, metac)

    def is_slot_object_property(self, slot: SlotDefinition) -> bool:
        return True
 def test_references_typeerror(self):
     """  TypeError: sequence item 0: expected str instance, NoneType found is generated from schemasynopsis """
     SchemaLoader(env.input_path('issue_tccm', 'resourcedescription.yaml'),
                  mergeimports=False).resolve()