shacl2code 0.0.11__tar.gz → 0.0.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {shacl2code-0.0.11 → shacl2code-0.0.12}/PKG-INFO +1 -1
  2. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/templates/jsonschema.j2 +18 -2
  3. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/templates/python.j2 +47 -2
  4. shacl2code-0.0.12/src/shacl2code/version.py +1 -0
  5. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/model/test-context.json +5 -0
  6. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/model/test.ttl +25 -0
  7. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/python/roundtrip.json +12 -0
  8. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/jsonschema/test-context.json +93 -0
  9. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/jsonschema/test.json +88 -0
  10. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/python/test-context.py +71 -2
  11. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/python/test.py +62 -2
  12. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/raw/test-context.txt +4 -0
  13. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/raw/test.txt +3 -1
  14. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_jsonschema.py +36 -0
  15. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_python.py +46 -0
  16. shacl2code-0.0.11/src/shacl2code/version.py +0 -1
  17. {shacl2code-0.0.11 → shacl2code-0.0.12}/.flake8 +0 -0
  18. {shacl2code-0.0.11 → shacl2code-0.0.12}/.github/workflows/coverage-generate.yaml +0 -0
  19. {shacl2code-0.0.11 → shacl2code-0.0.12}/.github/workflows/coverage-report.yaml +0 -0
  20. {shacl2code-0.0.11 → shacl2code-0.0.12}/.github/workflows/publish.yaml +0 -0
  21. {shacl2code-0.0.11 → shacl2code-0.0.12}/.github/workflows/test.yaml +0 -0
  22. {shacl2code-0.0.11 → shacl2code-0.0.12}/.gitignore +0 -0
  23. {shacl2code-0.0.11 → shacl2code-0.0.12}/LICENSE +0 -0
  24. {shacl2code-0.0.11 → shacl2code-0.0.12}/README.md +0 -0
  25. {shacl2code-0.0.11 → shacl2code-0.0.12}/pyproject.toml +0 -0
  26. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/__init__.py +0 -0
  27. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/__main__.py +0 -0
  28. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/context.py +0 -0
  29. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/__init__.py +0 -0
  30. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/common.py +0 -0
  31. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/jinja.py +0 -0
  32. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/jsonschema.py +0 -0
  33. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/lang.py +0 -0
  34. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/lang/python.py +0 -0
  35. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/main.py +0 -0
  36. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/model.py +0 -0
  37. {shacl2code-0.0.11 → shacl2code-0.0.12}/src/shacl2code/urlcontext.py +0 -0
  38. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/conftest.py +0 -0
  39. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/abort.j2 +0 -0
  40. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/bad-id.j2 +0 -0
  41. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/bad-node-kind.ttl +0 -0
  42. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/bad-pattern-class.ttl +0 -0
  43. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/bad-pattern-integer.ttl +0 -0
  44. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/bad-reference.jsonld +0 -0
  45. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/context-url.j2 +0 -0
  46. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/context.j2 +0 -0
  47. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/missing-range.ttl +0 -0
  48. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/python/bad-object-type-inline.json +0 -0
  49. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/python/bad-object-type-ref-after.json +0 -0
  50. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/python/bad-object-type-ref-before.json +0 -0
  51. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/python/links.json +0 -0
  52. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/data/raw.j2 +0 -0
  53. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/expect/make_expect.py +0 -0
  54. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_cli.py +0 -0
  55. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_common_jinja.py +0 -0
  56. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_common_prefix.py +0 -0
  57. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_context.py +0 -0
  58. {shacl2code-0.0.11 → shacl2code-0.0.12}/tests/test_model_source.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: shacl2code
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: Convert SHACL model file to code bindings
5
5
  Project-URL: Homepage, https://github.com/JPEWdev/shacl2code
6
6
  Project-URL: Repository, https://github.com/JPEWdev/shacl2code.git
@@ -59,7 +59,7 @@
59
59
  {#- Classes are divided into 2 parts. The properties are separated into a separate object #}
60
60
  {#- so that a object can references the properties of its parent without needing the const #}
61
61
  {#- @type tag #}
62
- {%- if not class.is_abstract %}
62
+ {%- if not class.is_abstract or class.is_extensible %}
63
63
  "{{ varname(*class.clsname) }}": {
64
64
  "allOf": [
65
65
  {
@@ -70,12 +70,20 @@
70
70
  "properties": {
71
71
  "{{ class.id_property or "@id" }}": { "$ref": "#/$defs/{{ class.node_kind.split("#")[-1] }}" },
72
72
  "{{ context.compact("@type") }}": {
73
+ {#- Abstract Extensible classes are weird; any type _except_ the specific class type is allowed #}
74
+ {%- if class.is_abstract and class.is_extensible %}
75
+ "allOf": [
76
+ { "$ref": "#/$defs/IRI" },
77
+ { "not": { "const": "{{ context.compact_vocab(class._id) }}" } }
78
+ ]
79
+ {%- else %}
73
80
  "oneOf": [
74
81
  {%- if class.is_extensible %}
75
82
  { "$ref": "#/$defs/IRI" },
76
83
  {%- endif %}
77
84
  { "const": "{{ context.compact_vocab(class._id) }}" }
78
85
  ]
86
+ {%- endif %}
79
87
  }
80
88
  }{%- if class.node_kind == SH.IRI %},
81
89
  "required": ["{{ class.id_property or "@id" }}"]
@@ -86,9 +94,14 @@
86
94
  },
87
95
  {%- endif %}
88
96
  "{{ varname(*class.clsname) }}_derived": {
89
- {%- set ns = namespace(external_ref=False, local_ref=False, any_ref=False, json_refs=[]) %}
97
+ {%- set ns = namespace(json_refs=[], named_individuals=[]) %}
90
98
  {%- for d in get_all_derived(class) + [class._id] %}
91
99
  {%- set ns.json_refs = ns.json_refs + ["#/$defs/" + varname(*classes.get(d).clsname)] %}
100
+ {%- for n in classes.get(d).named_individuals %}
101
+ {%- if context.compact(n._id) != n._id %}
102
+ {%- set ns.named_individuals = ns.named_individuals + [context.compact(n._id)] %}
103
+ {%- endif %}
104
+ {%- endfor %}
92
105
  {%- endfor %}
93
106
  "anyOf": [
94
107
  {%- if ns.json_refs %}
@@ -104,6 +117,9 @@
104
117
  ]
105
118
  },
106
119
  {%- endif %}
120
+ {%- for n in ns.named_individuals %}
121
+ { "const": "{{ n }}" },
122
+ {%- endfor %}
107
123
  { "$ref": "#/$defs/BlankNodeOrIRI" }
108
124
  ]
109
125
  },
@@ -264,6 +264,7 @@ class ObjectProp(Property):
264
264
  raise ValueError("Object cannot be None")
265
265
 
266
266
  if isinstance(value, str):
267
+ value = _NI_ENCODE_CONTEXT.get(value, value)
267
268
  encoder.write_iri(value)
268
269
  return
269
270
 
@@ -274,6 +275,8 @@ class ObjectProp(Property):
274
275
  if iri is None:
275
276
  return self.cls.decode(decoder, objectset=objectset)
276
277
 
278
+ iri = _NI_DECODE_CONTEXT.get(iri, iri)
279
+
277
280
  if objectset is None:
278
281
  return iri
279
282
 
@@ -506,6 +509,8 @@ def register(type_iri, *, compact_type=None, abstract=False):
506
509
  SHACLObject.CLASSES[key] = c
507
510
 
508
511
  def decorator(c):
512
+ global NAMED_INDIVIDUALS
513
+
509
514
  assert issubclass(
510
515
  c, SHACLObject
511
516
  ), f"{c.__name__} is not derived from SHACLObject"
@@ -518,6 +523,8 @@ def register(type_iri, *, compact_type=None, abstract=False):
518
523
  if compact_type:
519
524
  add_class(compact_type, c)
520
525
 
526
+ NAMED_INDIVIDUALS |= set(c.NAMED_INDIVIDUALS.values())
527
+
521
528
  # Registration is deferred until the first instance of class is created
522
529
  # so that it has access to any other defined class
523
530
  c._NEEDS_REG = True
@@ -527,6 +534,7 @@ def register(type_iri, *, compact_type=None, abstract=False):
527
534
 
528
535
 
529
536
  register_lock = threading.Lock()
537
+ NAMED_INDIVIDUALS = set()
530
538
 
531
539
 
532
540
  @functools.total_ordering
@@ -537,7 +545,7 @@ class SHACLObject(object):
537
545
  IS_ABSTRACT = True
538
546
 
539
547
  def __init__(self, **kwargs):
540
- if self.__class__.IS_ABSTRACT:
548
+ if self._is_abstract():
541
549
  raise NotImplementedError(
542
550
  f"{self.__class__.__name__} is abstract and cannot be implemented"
543
551
  )
@@ -559,6 +567,9 @@ class SHACLObject(object):
559
567
  for k, v in kwargs.items():
560
568
  setattr(self, k, v)
561
569
 
570
+ def _is_abstract(self):
571
+ return self.__class__.IS_ABSTRACT
572
+
562
573
  @classmethod
563
574
  def _register_props(cls):
564
575
  cls._add_property("_id", StringProp(), iri="@id")
@@ -875,11 +886,20 @@ class SHACLExtensibleObject(object):
875
886
  CLOSED = False
876
887
 
877
888
  def __init__(self, typ=None, **kwargs):
878
- super().__init__(**kwargs)
879
889
  if typ:
880
890
  self.__dict__["_obj_TYPE"] = (typ, None)
881
891
  else:
882
892
  self.__dict__["_obj_TYPE"] = (self._OBJ_TYPE, self._OBJ_COMPACT_TYPE)
893
+ super().__init__(**kwargs)
894
+
895
+ def _is_abstract(self):
896
+ # Unknown classes are assumed to not be abstract so that they can be
897
+ # deserialized
898
+ typ = self.__dict__["_obj_TYPE"][0]
899
+ if typ in self.__class__.CLASSES:
900
+ return self.__class__.CLASSES[typ].IS_ABSTRACT
901
+
902
+ return False
883
903
 
884
904
  @classmethod
885
905
  def _make_object(cls, typ):
@@ -1079,6 +1099,8 @@ class SHACLObjectSet(object):
1079
1099
  return self._link()
1080
1100
 
1081
1101
  def _link(self):
1102
+ global NAMED_INDIVIDUALS
1103
+
1082
1104
  self.missing_ids = set()
1083
1105
  visited = set()
1084
1106
 
@@ -1101,6 +1123,9 @@ class SHACLObjectSet(object):
1101
1123
  obj_by_id[_id] = obj
1102
1124
  self.obj_by_id = obj_by_id
1103
1125
 
1126
+ # Named individuals aren't considered missing
1127
+ self.missing_ids -= NAMED_INDIVIDUALS
1128
+
1104
1129
  return self.missing_ids
1105
1130
 
1106
1131
  def find_by_id(self, _id, default=None):
@@ -1938,6 +1963,26 @@ CONTEXT_URLS = [
1938
1963
  {%- endfor %}
1939
1964
  ]
1940
1965
 
1966
+ _NI_ENCODE_CONTEXT = {
1967
+ {%- for class in classes %}
1968
+ {%- for member in class.named_individuals %}
1969
+ {%- if context.compact(member._id) != member._id %}
1970
+ "{{ member._id }}": "{{ context.compact(member._id) }}",
1971
+ {%- endif %}
1972
+ {%- endfor %}
1973
+ {%- endfor %}
1974
+ }
1975
+
1976
+ _NI_DECODE_CONTEXT = {
1977
+ {%- for class in classes %}
1978
+ {%- for member in class.named_individuals %}
1979
+ {%- if context.compact(member._id) != member._id %}
1980
+ "{{ context.compact(member._id) }}": "{{ member._id }}",
1981
+ {%- endif %}
1982
+ {%- endfor %}
1983
+ {%- endfor %}
1984
+ }
1985
+
1941
1986
 
1942
1987
  # CLASSES
1943
1988
  {%- for class in classes %}
@@ -0,0 +1 @@
1
+ VERSION = "0.0.12"
@@ -87,6 +87,7 @@
87
87
  "@id": "test:test-class/enum-prop-no-class",
88
88
  "@type": "@id"
89
89
  },
90
+ "test-class/named": "test:test-class/named",
90
91
  "test-class/regex": {
91
92
  "@id": "test:test-class/regex",
92
93
  "@type": "xsd:string"
@@ -130,6 +131,10 @@
130
131
  "extensible-class/required": {
131
132
  "@id": "test:extensible-class/required",
132
133
  "@type": "xsd:string"
134
+ },
135
+ "uses-extensible-abstract-class/prop": {
136
+ "@id": "test:uses-extensible-abstract-class/prop",
137
+ "@type": "@id"
133
138
  }
134
139
  }
135
140
  }
@@ -166,6 +166,10 @@
166
166
  ]
167
167
  .
168
168
 
169
+ <test-class/named> a owl:NamedIndividual, <test-class> ;
170
+ rdfs:label "A named individual of the test class"
171
+ .
172
+
169
173
  <test-class-required> a sh:NodeShape, owl:Class ;
170
174
  rdfs:subClassOf <test-class> ;
171
175
  sh:property [
@@ -510,3 +514,24 @@
510
514
  rdfs:comment "A required abstract class property" ;
511
515
  rdfs:range <abstract-class>
512
516
  .
517
+
518
+ <extensible-abstract-class> a rdf:Class, sh:NodeShape, owl:Class ;
519
+ sh-to-code:isExtensible true ;
520
+ sh-to-code:isAbstract true ;
521
+ rdfs:comment "An extensible abstract class"
522
+ .
523
+
524
+ <uses-extensible-abstract-class> a rdf:Class, sh:NodeShape, owl:Class ;
525
+ rdfs:comment "A class that uses an abstract extensible class" ;
526
+ sh:property [
527
+ sh:class <extensible-abstract-class> ;
528
+ sh:path <uses-extensible-abstract-class/prop> ;
529
+ sh:minCount 1 ;
530
+ sh:maxCount 1
531
+ ]
532
+ .
533
+
534
+ <uses-extensible-abstract-class/prop> a rdf:Property ;
535
+ rdfs:comment "A property that references and abstract extensible class" ;
536
+ rdfs:range <extensible-abstract-class>
537
+ .
@@ -76,10 +76,22 @@
76
76
  "@id": "http://serialize.example.com/test-derived",
77
77
  "@type": "test-derived-class"
78
78
  },
79
+ {
80
+ "@type": "test-class",
81
+ "@id": "http://serialize.example.com/test-named-individual-reference",
82
+ "test-class/class-prop": "test-class/named"
83
+ },
79
84
  {
80
85
  "@type": "test-class",
81
86
  "@id": "http://serialize.example.com/test-special-chars",
82
87
  "test-class/string-scalar-prop": "special chars \"\n\r:{}[]"
88
+ },
89
+ {
90
+ "@type": "uses-extensible-abstract-class",
91
+ "@id": "http://serialize.example.com/test-uses-extensible-abstract",
92
+ "uses-extensible-abstract-class/prop": {
93
+ "@type": " http://serialize.example.com/custom-extensible"
94
+ }
83
95
  }
84
96
  ]
85
97
  }
@@ -178,6 +178,9 @@
178
178
  { "$ref": "#/$defs/enumType" }
179
179
  ]
180
180
  },
181
+ { "const": "enumType/foo" },
182
+ { "const": "enumType/bar" },
183
+ { "const": "enumType/nolabel" },
181
184
  { "$ref": "#/$defs/BlankNodeOrIRI" }
182
185
  ]
183
186
  },
@@ -191,6 +194,45 @@
191
194
  }
192
195
  ]
193
196
  },
197
+ "extensibleabstractclass": {
198
+ "allOf": [
199
+ {
200
+ "type": "object",
201
+ "unevaluatedProperties": true,
202
+ "properties": {
203
+ "@id": { "$ref": "#/$defs/BlankNodeOrIRI" },
204
+ "@type": {
205
+ "allOf": [
206
+ { "$ref": "#/$defs/IRI" },
207
+ { "not": { "const": "extensible-abstract-class" } }
208
+ ]
209
+ }
210
+ }
211
+ },
212
+ { "$ref": "#/$defs/extensibleabstractclass_props" }
213
+ ]
214
+ },
215
+ "extensibleabstractclass_derived": {
216
+ "anyOf": [
217
+ {
218
+ "type": "object",
219
+ "anyOf": [
220
+ { "$ref": "#/$defs/extensibleabstractclass" }
221
+ ]
222
+ },
223
+ { "$ref": "#/$defs/BlankNodeOrIRI" }
224
+ ]
225
+ },
226
+ "extensibleabstractclass_props": {
227
+ "allOf": [
228
+ { "$ref": "#/$defs/SHACLClass" },
229
+ {
230
+ "type": "object",
231
+ "properties": {
232
+ }
233
+ }
234
+ ]
235
+ },
194
236
  "idpropclass": {
195
237
  "allOf": [
196
238
  {
@@ -564,6 +606,7 @@
564
606
  { "$ref": "#/$defs/parentclass" }
565
607
  ]
566
608
  },
609
+ { "const": "test-class/named" },
567
610
  { "$ref": "#/$defs/BlankNodeOrIRI" }
568
611
  ]
569
612
  },
@@ -689,6 +732,7 @@
689
732
  { "$ref": "#/$defs/testclass" }
690
733
  ]
691
734
  },
735
+ { "const": "test-class/named" },
692
736
  { "$ref": "#/$defs/BlankNodeOrIRI" }
693
737
  ]
694
738
  },
@@ -1065,6 +1109,53 @@
1065
1109
  "prop_testderivedclass_testderivedclassstringprop": {
1066
1110
  "type": "string"
1067
1111
  },
1112
+ "usesextensibleabstractclass": {
1113
+ "allOf": [
1114
+ {
1115
+ "type": "object",
1116
+ "properties": {
1117
+ "@id": { "$ref": "#/$defs/BlankNodeOrIRI" },
1118
+ "@type": {
1119
+ "oneOf": [
1120
+ { "const": "uses-extensible-abstract-class" }
1121
+ ]
1122
+ }
1123
+ }
1124
+ },
1125
+ { "$ref": "#/$defs/usesextensibleabstractclass_props" }
1126
+ ]
1127
+ },
1128
+ "usesextensibleabstractclass_derived": {
1129
+ "anyOf": [
1130
+ {
1131
+ "type": "object",
1132
+ "unevaluatedProperties": false,
1133
+ "anyOf": [
1134
+ { "$ref": "#/$defs/usesextensibleabstractclass" }
1135
+ ]
1136
+ },
1137
+ { "$ref": "#/$defs/BlankNodeOrIRI" }
1138
+ ]
1139
+ },
1140
+ "usesextensibleabstractclass_props": {
1141
+ "allOf": [
1142
+ { "$ref": "#/$defs/SHACLClass" },
1143
+ {
1144
+ "type": "object",
1145
+ "properties": {
1146
+ "uses-extensible-abstract-class/prop": {
1147
+ "$ref": "#/$defs/prop_usesextensibleabstractclass_usesextensibleabstractclassprop"
1148
+ }
1149
+ },
1150
+ "required": [
1151
+ "uses-extensible-abstract-class/prop"
1152
+ ]
1153
+ }
1154
+ ]
1155
+ },
1156
+ "prop_usesextensibleabstractclass_usesextensibleabstractclassprop": {
1157
+ "$ref": "#/$defs/extensibleabstractclass_derived"
1158
+ },
1068
1159
  "aaaderivedclass": {
1069
1160
  "allOf": [
1070
1161
  {
@@ -1238,6 +1329,7 @@
1238
1329
  "test-class",
1239
1330
  "test-class-required",
1240
1331
  "test-derived-class",
1332
+ "uses-extensible-abstract-class",
1241
1333
  "aaa-derived-class",
1242
1334
  "derived-node-kind-iri",
1243
1335
  "extensible-class"
@@ -1267,6 +1359,7 @@
1267
1359
  { "$ref": "#/$defs/testclass" },
1268
1360
  { "$ref": "#/$defs/testclassrequired" },
1269
1361
  { "$ref": "#/$defs/testderivedclass" },
1362
+ { "$ref": "#/$defs/usesextensibleabstractclass" },
1270
1363
  { "$ref": "#/$defs/aaaderivedclass" },
1271
1364
  { "$ref": "#/$defs/derivednodekindiri" },
1272
1365
  { "$ref": "#/$defs/extensibleclass" }
@@ -187,6 +187,45 @@
187
187
  }
188
188
  ]
189
189
  },
190
+ "http_exampleorgextensibleabstractclass": {
191
+ "allOf": [
192
+ {
193
+ "type": "object",
194
+ "unevaluatedProperties": true,
195
+ "properties": {
196
+ "@id": { "$ref": "#/$defs/BlankNodeOrIRI" },
197
+ "@type": {
198
+ "allOf": [
199
+ { "$ref": "#/$defs/IRI" },
200
+ { "not": { "const": "http://example.org/extensible-abstract-class" } }
201
+ ]
202
+ }
203
+ }
204
+ },
205
+ { "$ref": "#/$defs/http_exampleorgextensibleabstractclass_props" }
206
+ ]
207
+ },
208
+ "http_exampleorgextensibleabstractclass_derived": {
209
+ "anyOf": [
210
+ {
211
+ "type": "object",
212
+ "anyOf": [
213
+ { "$ref": "#/$defs/http_exampleorgextensibleabstractclass" }
214
+ ]
215
+ },
216
+ { "$ref": "#/$defs/BlankNodeOrIRI" }
217
+ ]
218
+ },
219
+ "http_exampleorgextensibleabstractclass_props": {
220
+ "allOf": [
221
+ { "$ref": "#/$defs/SHACLClass" },
222
+ {
223
+ "type": "object",
224
+ "properties": {
225
+ }
226
+ }
227
+ ]
228
+ },
190
229
  "http_exampleorgidpropclass": {
191
230
  "allOf": [
192
231
  {
@@ -1061,6 +1100,53 @@
1061
1100
  "prop_http_exampleorgtestderivedclass_stringprop": {
1062
1101
  "type": "string"
1063
1102
  },
1103
+ "http_exampleorgusesextensibleabstractclass": {
1104
+ "allOf": [
1105
+ {
1106
+ "type": "object",
1107
+ "properties": {
1108
+ "@id": { "$ref": "#/$defs/BlankNodeOrIRI" },
1109
+ "@type": {
1110
+ "oneOf": [
1111
+ { "const": "http://example.org/uses-extensible-abstract-class" }
1112
+ ]
1113
+ }
1114
+ }
1115
+ },
1116
+ { "$ref": "#/$defs/http_exampleorgusesextensibleabstractclass_props" }
1117
+ ]
1118
+ },
1119
+ "http_exampleorgusesextensibleabstractclass_derived": {
1120
+ "anyOf": [
1121
+ {
1122
+ "type": "object",
1123
+ "unevaluatedProperties": false,
1124
+ "anyOf": [
1125
+ { "$ref": "#/$defs/http_exampleorgusesextensibleabstractclass" }
1126
+ ]
1127
+ },
1128
+ { "$ref": "#/$defs/BlankNodeOrIRI" }
1129
+ ]
1130
+ },
1131
+ "http_exampleorgusesextensibleabstractclass_props": {
1132
+ "allOf": [
1133
+ { "$ref": "#/$defs/SHACLClass" },
1134
+ {
1135
+ "type": "object",
1136
+ "properties": {
1137
+ "http://example.org/uses-extensible-abstract-class/prop": {
1138
+ "$ref": "#/$defs/prop_http_exampleorgusesextensibleabstractclass_prop"
1139
+ }
1140
+ },
1141
+ "required": [
1142
+ "http://example.org/uses-extensible-abstract-class/prop"
1143
+ ]
1144
+ }
1145
+ ]
1146
+ },
1147
+ "prop_http_exampleorgusesextensibleabstractclass_prop": {
1148
+ "$ref": "#/$defs/http_exampleorgextensibleabstractclass_derived"
1149
+ },
1064
1150
  "http_exampleorgaaaderivedclass": {
1065
1151
  "allOf": [
1066
1152
  {
@@ -1234,6 +1320,7 @@
1234
1320
  "http://example.org/test-class",
1235
1321
  "http://example.org/test-class-required",
1236
1322
  "http://example.org/test-derived-class",
1323
+ "http://example.org/uses-extensible-abstract-class",
1237
1324
  "http://example.org/aaa-derived-class",
1238
1325
  "http://example.org/derived-node-kind-iri",
1239
1326
  "http://example.org/extensible-class"
@@ -1263,6 +1350,7 @@
1263
1350
  { "$ref": "#/$defs/http_exampleorgtestclass" },
1264
1351
  { "$ref": "#/$defs/http_exampleorgtestclassrequired" },
1265
1352
  { "$ref": "#/$defs/http_exampleorgtestderivedclass" },
1353
+ { "$ref": "#/$defs/http_exampleorgusesextensibleabstractclass" },
1266
1354
  { "$ref": "#/$defs/http_exampleorgaaaderivedclass" },
1267
1355
  { "$ref": "#/$defs/http_exampleorgderivednodekindiri" },
1268
1356
  { "$ref": "#/$defs/http_exampleorgextensibleclass" }
@@ -264,6 +264,7 @@ class ObjectProp(Property):
264
264
  raise ValueError("Object cannot be None")
265
265
 
266
266
  if isinstance(value, str):
267
+ value = _NI_ENCODE_CONTEXT.get(value, value)
267
268
  encoder.write_iri(value)
268
269
  return
269
270
 
@@ -274,6 +275,8 @@ class ObjectProp(Property):
274
275
  if iri is None:
275
276
  return self.cls.decode(decoder, objectset=objectset)
276
277
 
278
+ iri = _NI_DECODE_CONTEXT.get(iri, iri)
279
+
277
280
  if objectset is None:
278
281
  return iri
279
282
 
@@ -506,6 +509,8 @@ def register(type_iri, *, compact_type=None, abstract=False):
506
509
  SHACLObject.CLASSES[key] = c
507
510
 
508
511
  def decorator(c):
512
+ global NAMED_INDIVIDUALS
513
+
509
514
  assert issubclass(
510
515
  c, SHACLObject
511
516
  ), f"{c.__name__} is not derived from SHACLObject"
@@ -518,6 +523,8 @@ def register(type_iri, *, compact_type=None, abstract=False):
518
523
  if compact_type:
519
524
  add_class(compact_type, c)
520
525
 
526
+ NAMED_INDIVIDUALS |= set(c.NAMED_INDIVIDUALS.values())
527
+
521
528
  # Registration is deferred until the first instance of class is created
522
529
  # so that it has access to any other defined class
523
530
  c._NEEDS_REG = True
@@ -527,6 +534,7 @@ def register(type_iri, *, compact_type=None, abstract=False):
527
534
 
528
535
 
529
536
  register_lock = threading.Lock()
537
+ NAMED_INDIVIDUALS = set()
530
538
 
531
539
 
532
540
  @functools.total_ordering
@@ -537,7 +545,7 @@ class SHACLObject(object):
537
545
  IS_ABSTRACT = True
538
546
 
539
547
  def __init__(self, **kwargs):
540
- if self.__class__.IS_ABSTRACT:
548
+ if self._is_abstract():
541
549
  raise NotImplementedError(
542
550
  f"{self.__class__.__name__} is abstract and cannot be implemented"
543
551
  )
@@ -559,6 +567,9 @@ class SHACLObject(object):
559
567
  for k, v in kwargs.items():
560
568
  setattr(self, k, v)
561
569
 
570
+ def _is_abstract(self):
571
+ return self.__class__.IS_ABSTRACT
572
+
562
573
  @classmethod
563
574
  def _register_props(cls):
564
575
  cls._add_property("_id", StringProp(), iri="@id")
@@ -875,11 +886,20 @@ class SHACLExtensibleObject(object):
875
886
  CLOSED = False
876
887
 
877
888
  def __init__(self, typ=None, **kwargs):
878
- super().__init__(**kwargs)
879
889
  if typ:
880
890
  self.__dict__["_obj_TYPE"] = (typ, None)
881
891
  else:
882
892
  self.__dict__["_obj_TYPE"] = (self._OBJ_TYPE, self._OBJ_COMPACT_TYPE)
893
+ super().__init__(**kwargs)
894
+
895
+ def _is_abstract(self):
896
+ # Unknown classes are assumed to not be abstract so that they can be
897
+ # deserialized
898
+ typ = self.__dict__["_obj_TYPE"][0]
899
+ if typ in self.__class__.CLASSES:
900
+ return self.__class__.CLASSES[typ].IS_ABSTRACT
901
+
902
+ return False
883
903
 
884
904
  @classmethod
885
905
  def _make_object(cls, typ):
@@ -1079,6 +1099,8 @@ class SHACLObjectSet(object):
1079
1099
  return self._link()
1080
1100
 
1081
1101
  def _link(self):
1102
+ global NAMED_INDIVIDUALS
1103
+
1082
1104
  self.missing_ids = set()
1083
1105
  visited = set()
1084
1106
 
@@ -1101,6 +1123,9 @@ class SHACLObjectSet(object):
1101
1123
  obj_by_id[_id] = obj
1102
1124
  self.obj_by_id = obj_by_id
1103
1125
 
1126
+ # Named individuals aren't considered missing
1127
+ self.missing_ids -= NAMED_INDIVIDUALS
1128
+
1104
1129
  return self.missing_ids
1105
1130
 
1106
1131
  def find_by_id(self, _id, default=None):
@@ -1924,6 +1949,20 @@ CONTEXT_URLS = [
1924
1949
  "https://spdx.github.io/spdx-3-model/context.json",
1925
1950
  ]
1926
1951
 
1952
+ _NI_ENCODE_CONTEXT = {
1953
+ "http://example.org/enumType/foo": "enumType/foo",
1954
+ "http://example.org/enumType/bar": "enumType/bar",
1955
+ "http://example.org/enumType/nolabel": "enumType/nolabel",
1956
+ "http://example.org/test-class/named": "test-class/named",
1957
+ }
1958
+
1959
+ _NI_DECODE_CONTEXT = {
1960
+ "enumType/foo": "http://example.org/enumType/foo",
1961
+ "enumType/bar": "http://example.org/enumType/bar",
1962
+ "enumType/nolabel": "http://example.org/enumType/nolabel",
1963
+ "test-class/named": "http://example.org/test-class/named",
1964
+ }
1965
+
1927
1966
 
1928
1967
  # CLASSES
1929
1968
  # An Abstract class
@@ -1975,6 +2014,14 @@ class enumType(SHACLObject):
1975
2014
  nolabel = "http://example.org/enumType/nolabel"
1976
2015
 
1977
2016
 
2017
+ # An extensible abstract class
2018
+ @register("http://example.org/extensible-abstract-class", compact_type="extensible-abstract-class", abstract=True)
2019
+ class extensible_abstract_class(SHACLExtensibleObject, SHACLObject):
2020
+ NODE_KIND = NodeKind.BlankNodeOrIRI
2021
+ NAMED_INDIVIDUALS = {
2022
+ }
2023
+
2024
+
1978
2025
  # A class with an ID alias
1979
2026
  @register("http://example.org/id-prop-class", compact_type="id-prop-class", abstract=False)
1980
2027
  class id_prop_class(SHACLObject):
@@ -2114,7 +2161,9 @@ class test_another_class(SHACLObject):
2114
2161
  class test_class(parent_class):
2115
2162
  NODE_KIND = NodeKind.BlankNodeOrIRI
2116
2163
  NAMED_INDIVIDUALS = {
2164
+ "named": "http://example.org/test-class/named",
2117
2165
  }
2166
+ named = "http://example.org/test-class/named"
2118
2167
 
2119
2168
  @classmethod
2120
2169
  def _register_props(cls):
@@ -2365,6 +2414,26 @@ class test_derived_class(test_class):
2365
2414
  )
2366
2415
 
2367
2416
 
2417
+ # A class that uses an abstract extensible class
2418
+ @register("http://example.org/uses-extensible-abstract-class", compact_type="uses-extensible-abstract-class", abstract=False)
2419
+ class uses_extensible_abstract_class(SHACLObject):
2420
+ NODE_KIND = NodeKind.BlankNodeOrIRI
2421
+ NAMED_INDIVIDUALS = {
2422
+ }
2423
+
2424
+ @classmethod
2425
+ def _register_props(cls):
2426
+ super()._register_props()
2427
+ # A property that references and abstract extensible class
2428
+ cls._add_property(
2429
+ "uses_extensible_abstract_class_prop",
2430
+ ObjectProp(extensible_abstract_class, True),
2431
+ iri="http://example.org/uses-extensible-abstract-class/prop",
2432
+ min_count=1,
2433
+ compact="uses-extensible-abstract-class/prop",
2434
+ )
2435
+
2436
+
2368
2437
  # Derived class that sorts before the parent to test ordering
2369
2438
  @register("http://example.org/aaa-derived-class", compact_type="aaa-derived-class", abstract=False)
2370
2439
  class aaa_derived_class(parent_class):
@@ -264,6 +264,7 @@ class ObjectProp(Property):
264
264
  raise ValueError("Object cannot be None")
265
265
 
266
266
  if isinstance(value, str):
267
+ value = _NI_ENCODE_CONTEXT.get(value, value)
267
268
  encoder.write_iri(value)
268
269
  return
269
270
 
@@ -274,6 +275,8 @@ class ObjectProp(Property):
274
275
  if iri is None:
275
276
  return self.cls.decode(decoder, objectset=objectset)
276
277
 
278
+ iri = _NI_DECODE_CONTEXT.get(iri, iri)
279
+
277
280
  if objectset is None:
278
281
  return iri
279
282
 
@@ -506,6 +509,8 @@ def register(type_iri, *, compact_type=None, abstract=False):
506
509
  SHACLObject.CLASSES[key] = c
507
510
 
508
511
  def decorator(c):
512
+ global NAMED_INDIVIDUALS
513
+
509
514
  assert issubclass(
510
515
  c, SHACLObject
511
516
  ), f"{c.__name__} is not derived from SHACLObject"
@@ -518,6 +523,8 @@ def register(type_iri, *, compact_type=None, abstract=False):
518
523
  if compact_type:
519
524
  add_class(compact_type, c)
520
525
 
526
+ NAMED_INDIVIDUALS |= set(c.NAMED_INDIVIDUALS.values())
527
+
521
528
  # Registration is deferred until the first instance of class is created
522
529
  # so that it has access to any other defined class
523
530
  c._NEEDS_REG = True
@@ -527,6 +534,7 @@ def register(type_iri, *, compact_type=None, abstract=False):
527
534
 
528
535
 
529
536
  register_lock = threading.Lock()
537
+ NAMED_INDIVIDUALS = set()
530
538
 
531
539
 
532
540
  @functools.total_ordering
@@ -537,7 +545,7 @@ class SHACLObject(object):
537
545
  IS_ABSTRACT = True
538
546
 
539
547
  def __init__(self, **kwargs):
540
- if self.__class__.IS_ABSTRACT:
548
+ if self._is_abstract():
541
549
  raise NotImplementedError(
542
550
  f"{self.__class__.__name__} is abstract and cannot be implemented"
543
551
  )
@@ -559,6 +567,9 @@ class SHACLObject(object):
559
567
  for k, v in kwargs.items():
560
568
  setattr(self, k, v)
561
569
 
570
+ def _is_abstract(self):
571
+ return self.__class__.IS_ABSTRACT
572
+
562
573
  @classmethod
563
574
  def _register_props(cls):
564
575
  cls._add_property("_id", StringProp(), iri="@id")
@@ -875,11 +886,20 @@ class SHACLExtensibleObject(object):
875
886
  CLOSED = False
876
887
 
877
888
  def __init__(self, typ=None, **kwargs):
878
- super().__init__(**kwargs)
879
889
  if typ:
880
890
  self.__dict__["_obj_TYPE"] = (typ, None)
881
891
  else:
882
892
  self.__dict__["_obj_TYPE"] = (self._OBJ_TYPE, self._OBJ_COMPACT_TYPE)
893
+ super().__init__(**kwargs)
894
+
895
+ def _is_abstract(self):
896
+ # Unknown classes are assumed to not be abstract so that they can be
897
+ # deserialized
898
+ typ = self.__dict__["_obj_TYPE"][0]
899
+ if typ in self.__class__.CLASSES:
900
+ return self.__class__.CLASSES[typ].IS_ABSTRACT
901
+
902
+ return False
883
903
 
884
904
  @classmethod
885
905
  def _make_object(cls, typ):
@@ -1079,6 +1099,8 @@ class SHACLObjectSet(object):
1079
1099
  return self._link()
1080
1100
 
1081
1101
  def _link(self):
1102
+ global NAMED_INDIVIDUALS
1103
+
1082
1104
  self.missing_ids = set()
1083
1105
  visited = set()
1084
1106
 
@@ -1101,6 +1123,9 @@ class SHACLObjectSet(object):
1101
1123
  obj_by_id[_id] = obj
1102
1124
  self.obj_by_id = obj_by_id
1103
1125
 
1126
+ # Named individuals aren't considered missing
1127
+ self.missing_ids -= NAMED_INDIVIDUALS
1128
+
1104
1129
  return self.missing_ids
1105
1130
 
1106
1131
  def find_by_id(self, _id, default=None):
@@ -1923,6 +1948,12 @@ def print_tree(objects, all_fields=False):
1923
1948
  CONTEXT_URLS = [
1924
1949
  ]
1925
1950
 
1951
+ _NI_ENCODE_CONTEXT = {
1952
+ }
1953
+
1954
+ _NI_DECODE_CONTEXT = {
1955
+ }
1956
+
1926
1957
 
1927
1958
  # CLASSES
1928
1959
  # An Abstract class
@@ -1974,6 +2005,14 @@ class http_example_org_enumType(SHACLObject):
1974
2005
  nolabel = "http://example.org/enumType/nolabel"
1975
2006
 
1976
2007
 
2008
+ # An extensible abstract class
2009
+ @register("http://example.org/extensible-abstract-class", abstract=True)
2010
+ class http_example_org_extensible_abstract_class(SHACLExtensibleObject, SHACLObject):
2011
+ NODE_KIND = NodeKind.BlankNodeOrIRI
2012
+ NAMED_INDIVIDUALS = {
2013
+ }
2014
+
2015
+
1977
2016
  # A class with an ID alias
1978
2017
  @register("http://example.org/id-prop-class", abstract=False)
1979
2018
  class http_example_org_id_prop_class(SHACLObject):
@@ -2108,7 +2147,9 @@ class http_example_org_test_another_class(SHACLObject):
2108
2147
  class http_example_org_test_class(http_example_org_parent_class):
2109
2148
  NODE_KIND = NodeKind.BlankNodeOrIRI
2110
2149
  NAMED_INDIVIDUALS = {
2150
+ "named": "http://example.org/test-class/named",
2111
2151
  }
2152
+ named = "http://example.org/test-class/named"
2112
2153
 
2113
2154
  @classmethod
2114
2155
  def _register_props(cls):
@@ -2330,6 +2371,25 @@ class http_example_org_test_derived_class(http_example_org_test_class):
2330
2371
  )
2331
2372
 
2332
2373
 
2374
+ # A class that uses an abstract extensible class
2375
+ @register("http://example.org/uses-extensible-abstract-class", abstract=False)
2376
+ class http_example_org_uses_extensible_abstract_class(SHACLObject):
2377
+ NODE_KIND = NodeKind.BlankNodeOrIRI
2378
+ NAMED_INDIVIDUALS = {
2379
+ }
2380
+
2381
+ @classmethod
2382
+ def _register_props(cls):
2383
+ super()._register_props()
2384
+ # A property that references and abstract extensible class
2385
+ cls._add_property(
2386
+ "prop",
2387
+ ObjectProp(http_example_org_extensible_abstract_class, True),
2388
+ iri="http://example.org/uses-extensible-abstract-class/prop",
2389
+ min_count=1,
2390
+ )
2391
+
2392
+
2333
2393
  # Derived class that sorts before the parent to test ordering
2334
2394
  @register("http://example.org/aaa-derived-class", abstract=False)
2335
2395
  class http_example_org_aaa_derived_class(http_example_org_parent_class):
@@ -11,6 +11,8 @@ http://example.org/concrete-spdx-class: concrete-spdx-class
11
11
 
12
12
  http://example.org/enumType: enumType
13
13
 
14
+ http://example.org/extensible-abstract-class: extensible-abstract-class
15
+
14
16
  http://example.org/id-prop-class: id-prop-class
15
17
 
16
18
  http://example.org/inherited-id-prop-class: inherited-id-prop-class
@@ -39,6 +41,8 @@ http://example.org/test-class-required: test-class-required
39
41
 
40
42
  http://example.org/test-derived-class: test-derived-class
41
43
 
44
+ http://example.org/uses-extensible-abstract-class: uses-extensible-abstract-class
45
+
42
46
  http://example.org/aaa-derived-class: aaa-derived-class
43
47
 
44
48
  http://example.org/derived-node-kind-iri: derived-node-kind-iri
@@ -5,6 +5,7 @@ Class(_id='http://example.org/abstract-spdx-class', clsname=['http', '//example.
5
5
  Class(_id='http://example.org/concrete-class', clsname=['http', '//example.org/concrete-class'], parent_ids=['http://example.org/abstract-class'], derived_ids=[], properties=[], comment='A concrete class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
6
6
  Class(_id='http://example.org/concrete-spdx-class', clsname=['http', '//example.org/concrete-spdx-class'], parent_ids=['http://example.org/abstract-spdx-class'], derived_ids=[], properties=[], comment='A concrete class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
7
7
  Class(_id='http://example.org/enumType', clsname=['http', '//example.org/enumType'], parent_ids=[], derived_ids=[], properties=[], comment='An enumerated type', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[Individual(_id='http://example.org/enumType/foo', varname='foo', comment='The foo value of enumType'), Individual(_id='http://example.org/enumType/bar', varname='bar', comment='The bar value of enumType'), Individual(_id='http://example.org/enumType/nolabel', varname='nolabel', comment='This value has no label')])
8
+ Class(_id='http://example.org/extensible-abstract-class', clsname=['http', '//example.org/extensible-abstract-class'], parent_ids=[], derived_ids=[], properties=[], comment='An extensible abstract class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=True, is_abstract=True, named_individuals=[])
8
9
  Class(_id='http://example.org/id-prop-class', clsname=['http', '//example.org/id-prop-class'], parent_ids=[], derived_ids=['http://example.org/inherited-id-prop-class'], properties=[], comment='A class with an ID alias', id_property='testid', node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
9
10
  Class(_id='http://example.org/inherited-id-prop-class', clsname=['http', '//example.org/inherited-id-prop-class'], parent_ids=['http://example.org/id-prop-class'], derived_ids=[], properties=[], comment='A class that inherits its idPropertyName from the parent', id_property='testid', node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
10
11
  Class(_id='http://example.org/link-class', clsname=['http', '//example.org/link-class'], parent_ids=[], derived_ids=['http://example.org/extensible-class', 'http://example.org/link-derived-class', 'http://example.org/node-kind-blank', 'http://example.org/node-kind-iri', 'http://example.org/node-kind-iri-or-blank'], properties=[Property(path='http://example.org/link-class-extensible', varname='-extensible', comment='A link to an extensible-class', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/extensible-class', datatype='', pattern=''), Property(path='http://example.org/link-class-link-list-prop', varname='-link-list-prop', comment='A link-class list property', max_count=None, min_count=None, enum_values=None, class_id='http://example.org/link-class', datatype='', pattern=''), Property(path='http://example.org/link-class-link-prop', varname='-link-prop', comment='A link-class property', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/link-class', datatype='', pattern=''), Property(path='http://example.org/link-class-link-prop-no-class', varname='-link-prop-no-class', comment='A link-class property with no sh:class', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/link-class', datatype='', pattern='')], comment='A class to test links', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
@@ -16,9 +17,10 @@ Class(_id='http://example.org/non-shape-class', clsname=['http', '//example.org/
16
17
  Class(_id='http://example.org/parent-class', clsname=['http', '//example.org/parent-class'], parent_ids=[], derived_ids=['http://example.org/aaa-derived-class', 'http://example.org/test-class'], properties=[], comment='The parent class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
17
18
  Class(_id='http://example.org/required-abstract', clsname=['http', '//example.org/required-abstract'], parent_ids=[], derived_ids=[], properties=[Property(path='http://example.org/required-abstract/abstract-class-prop', varname='abstract-class-prop', comment='A required abstract class property', max_count=1, min_count=1, enum_values=None, class_id='http://example.org/abstract-class', datatype='', pattern='')], comment='A class with a mandatory abstract class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
18
19
  Class(_id='http://example.org/test-another-class', clsname=['http', '//example.org/test-another-class'], parent_ids=[], derived_ids=[], properties=[], comment='Another class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
19
- Class(_id='http://example.org/test-class', clsname=['http', '//example.org/test-class'], parent_ids=['http://example.org/parent-class'], derived_ids=['http://example.org/test-class-required', 'http://example.org/test-derived-class'], properties=[Property(path='http://example.org/encode', varname='encode', comment='A property that conflicts with an existing SHACLObject property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/import', varname='import', comment='A property that is a keyword', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/anyuri-prop', varname='anyuri-prop', comment='a URI', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#anyURI', pattern=''), Property(path='http://example.org/test-class/boolean-prop', varname='boolean-prop', comment='a boolean property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#boolean', pattern=''), Property(path='http://example.org/test-class/class-list-prop', varname='class-list-prop', comment='A test-class list property', max_count=None, min_count=None, enum_values=None, class_id='http://example.org/test-class', datatype='', pattern=''), Property(path='http://example.org/test-class/class-prop', varname='class-prop', comment='A test-class property', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/test-class', datatype='', pattern=''), Property(path='http://example.org/test-class/class-prop-no-class', varname='class-prop-no-class', comment='A test-class property with no sh:class', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/test-class', datatype='', pattern=''), Property(path='http://example.org/test-class/datetime-list-prop', varname='datetime-list-prop', comment='A datetime list property', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTime', pattern=''), Property(path='http://example.org/test-class/datetime-scalar-prop', varname='datetime-scalar-prop', comment='A scalar datetime property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTime', pattern=''), Property(path='http://example.org/test-class/datetimestamp-scalar-prop', varname='datetimestamp-scalar-prop', comment='A scalar dateTimeStamp property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTimeStamp', pattern=''), Property(path='http://example.org/test-class/enum-list-prop', varname='enum-list-prop', comment='A enum list property', max_count=None, min_count=None, enum_values=[rdflib.term.URIRef('http://example.org/enumType/bar'), rdflib.term.URIRef('http://example.org/enumType/foo'), rdflib.term.URIRef('http://example.org/enumType/nolabel'), rdflib.term.URIRef('http://example.org/enumType/non-named-individual')], class_id='http://example.org/enumType', datatype='', pattern=''), Property(path='http://example.org/test-class/enum-prop', varname='enum-prop', comment='A enum property', max_count=1, min_count=None, enum_values=[rdflib.term.URIRef('http://example.org/enumType/bar'), rdflib.term.URIRef('http://example.org/enumType/foo'), rdflib.term.URIRef('http://example.org/enumType/nolabel'), rdflib.term.URIRef('http://example.org/enumType/non-named-individual')], class_id='http://example.org/enumType', datatype='', pattern=''), Property(path='http://example.org/test-class/enum-prop-no-class', varname='enum-prop-no-class', comment='A enum property with no sh:class', max_count=1, min_count=None, enum_values=[rdflib.term.URIRef('http://example.org/enumType/bar'), rdflib.term.URIRef('http://example.org/enumType/foo'), rdflib.term.URIRef('http://example.org/enumType/nolabel'), rdflib.term.URIRef('http://example.org/enumType/non-named-individual')], class_id='http://example.org/enumType', datatype='', pattern=''), Property(path='http://example.org/test-class/float-prop', varname='float-prop', comment='a float property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#decimal', pattern=''), Property(path='http://example.org/test-class/integer-prop', varname='integer-prop', comment='a non-negative integer', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#integer', pattern=''), Property(path='http://example.org/test-class/named-property', varname=rdflib.term.Literal('named_property'), comment='A named property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/non-shape', varname='non-shape', comment='A class with no shape', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/non-shape-class', datatype='', pattern=''), Property(path='http://example.org/test-class/nonnegative-integer-prop', varname='nonnegative-integer-prop', comment='a non-negative integer', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#nonNegativeInteger', pattern=''), Property(path='http://example.org/test-class/positive-integer-prop', varname='positive-integer-prop', comment='A positive integer', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#positiveInteger', pattern=''), Property(path='http://example.org/test-class/regex', varname='regex', comment='A regex validated string', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='^foo\\d'), Property(path='http://example.org/test-class/regex-datetime', varname='regex-datetime', comment='A regex dateTime', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTime', pattern='^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\+01:00$'), Property(path='http://example.org/test-class/regex-datetimestamp', varname='regex-datetimestamp', comment='A regex dateTimeStamp', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTimeStamp', pattern='^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\dZ$'), Property(path='http://example.org/test-class/regex-list', varname='regex-list', comment='A regex validated string list', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='^foo\\d'), Property(path='http://example.org/test-class/string-list-no-datatype', varname='string-list-no-datatype', comment='A string list property with no sh:datatype', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/string-list-prop', varname='string-list-prop', comment='A string list property', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/string-scalar-prop', varname='string-scalar-prop', comment='A scalar string propery', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='')], comment='The test class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
20
+ Class(_id='http://example.org/test-class', clsname=['http', '//example.org/test-class'], parent_ids=['http://example.org/parent-class'], derived_ids=['http://example.org/test-class-required', 'http://example.org/test-derived-class'], properties=[Property(path='http://example.org/encode', varname='encode', comment='A property that conflicts with an existing SHACLObject property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/import', varname='import', comment='A property that is a keyword', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/anyuri-prop', varname='anyuri-prop', comment='a URI', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#anyURI', pattern=''), Property(path='http://example.org/test-class/boolean-prop', varname='boolean-prop', comment='a boolean property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#boolean', pattern=''), Property(path='http://example.org/test-class/class-list-prop', varname='class-list-prop', comment='A test-class list property', max_count=None, min_count=None, enum_values=None, class_id='http://example.org/test-class', datatype='', pattern=''), Property(path='http://example.org/test-class/class-prop', varname='class-prop', comment='A test-class property', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/test-class', datatype='', pattern=''), Property(path='http://example.org/test-class/class-prop-no-class', varname='class-prop-no-class', comment='A test-class property with no sh:class', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/test-class', datatype='', pattern=''), Property(path='http://example.org/test-class/datetime-list-prop', varname='datetime-list-prop', comment='A datetime list property', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTime', pattern=''), Property(path='http://example.org/test-class/datetime-scalar-prop', varname='datetime-scalar-prop', comment='A scalar datetime property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTime', pattern=''), Property(path='http://example.org/test-class/datetimestamp-scalar-prop', varname='datetimestamp-scalar-prop', comment='A scalar dateTimeStamp property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTimeStamp', pattern=''), Property(path='http://example.org/test-class/enum-list-prop', varname='enum-list-prop', comment='A enum list property', max_count=None, min_count=None, enum_values=[rdflib.term.URIRef('http://example.org/enumType/bar'), rdflib.term.URIRef('http://example.org/enumType/foo'), rdflib.term.URIRef('http://example.org/enumType/nolabel'), rdflib.term.URIRef('http://example.org/enumType/non-named-individual')], class_id='http://example.org/enumType', datatype='', pattern=''), Property(path='http://example.org/test-class/enum-prop', varname='enum-prop', comment='A enum property', max_count=1, min_count=None, enum_values=[rdflib.term.URIRef('http://example.org/enumType/bar'), rdflib.term.URIRef('http://example.org/enumType/foo'), rdflib.term.URIRef('http://example.org/enumType/nolabel'), rdflib.term.URIRef('http://example.org/enumType/non-named-individual')], class_id='http://example.org/enumType', datatype='', pattern=''), Property(path='http://example.org/test-class/enum-prop-no-class', varname='enum-prop-no-class', comment='A enum property with no sh:class', max_count=1, min_count=None, enum_values=[rdflib.term.URIRef('http://example.org/enumType/bar'), rdflib.term.URIRef('http://example.org/enumType/foo'), rdflib.term.URIRef('http://example.org/enumType/nolabel'), rdflib.term.URIRef('http://example.org/enumType/non-named-individual')], class_id='http://example.org/enumType', datatype='', pattern=''), Property(path='http://example.org/test-class/float-prop', varname='float-prop', comment='a float property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#decimal', pattern=''), Property(path='http://example.org/test-class/integer-prop', varname='integer-prop', comment='a non-negative integer', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#integer', pattern=''), Property(path='http://example.org/test-class/named-property', varname=rdflib.term.Literal('named_property'), comment='A named property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/non-shape', varname='non-shape', comment='A class with no shape', max_count=1, min_count=None, enum_values=None, class_id='http://example.org/non-shape-class', datatype='', pattern=''), Property(path='http://example.org/test-class/nonnegative-integer-prop', varname='nonnegative-integer-prop', comment='a non-negative integer', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#nonNegativeInteger', pattern=''), Property(path='http://example.org/test-class/positive-integer-prop', varname='positive-integer-prop', comment='A positive integer', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#positiveInteger', pattern=''), Property(path='http://example.org/test-class/regex', varname='regex', comment='A regex validated string', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='^foo\\d'), Property(path='http://example.org/test-class/regex-datetime', varname='regex-datetime', comment='A regex dateTime', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTime', pattern='^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\+01:00$'), Property(path='http://example.org/test-class/regex-datetimestamp', varname='regex-datetimestamp', comment='A regex dateTimeStamp', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#dateTimeStamp', pattern='^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\dZ$'), Property(path='http://example.org/test-class/regex-list', varname='regex-list', comment='A regex validated string list', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='^foo\\d'), Property(path='http://example.org/test-class/string-list-no-datatype', varname='string-list-no-datatype', comment='A string list property with no sh:datatype', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/string-list-prop', varname='string-list-prop', comment='A string list property', max_count=None, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/string-scalar-prop', varname='string-scalar-prop', comment='A scalar string propery', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='')], comment='The test class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[Individual(_id='http://example.org/test-class/named', varname='named', comment='')])
20
21
  Class(_id='http://example.org/test-class-required', clsname=['http', '//example.org/test-class-required'], parent_ids=['http://example.org/test-class'], derived_ids=[], properties=[Property(path='http://example.org/test-class/required-string-list-prop', varname='required-string-list-prop', comment='A required string list property', max_count=2, min_count=1, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/test-class/required-string-scalar-prop', varname='required-string-scalar-prop', comment='A required scalar string property', max_count=1, min_count=1, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='')], comment='', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
21
22
  Class(_id='http://example.org/test-derived-class', clsname=['http', '//example.org/test-derived-class'], parent_ids=['http://example.org/test-class'], derived_ids=[], properties=[Property(path='http://example.org/test-derived-class/string-prop', varname='string-prop', comment='A string property in a derived class', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='')], comment='A class derived from test-class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
23
+ Class(_id='http://example.org/uses-extensible-abstract-class', clsname=['http', '//example.org/uses-extensible-abstract-class'], parent_ids=[], derived_ids=[], properties=[Property(path='http://example.org/uses-extensible-abstract-class/prop', varname='prop', comment='A property that references and abstract extensible class', max_count=1, min_count=1, enum_values=None, class_id='http://example.org/extensible-abstract-class', datatype='', pattern='')], comment='A class that uses an abstract extensible class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
22
24
  Class(_id='http://example.org/aaa-derived-class', clsname=['http', '//example.org/aaa-derived-class'], parent_ids=['http://example.org/parent-class'], derived_ids=[], properties=[], comment='Derived class that sorts before the parent to test ordering', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=False, is_abstract=False, named_individuals=[])
23
25
  Class(_id='http://example.org/derived-node-kind-iri', clsname=['http', '//example.org/derived-node-kind-iri'], parent_ids=['http://example.org/node-kind-iri'], derived_ids=[], properties=[], comment='A class that derives its nodeKind from parent', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#IRI'), is_extensible=False, is_abstract=False, named_individuals=[])
24
26
  Class(_id='http://example.org/extensible-class', clsname=['http', '//example.org/extensible-class'], parent_ids=['http://example.org/link-class'], derived_ids=[], properties=[Property(path='http://example.org/extensible-class/property', varname='property', comment='An extensible property', max_count=1, min_count=None, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern=''), Property(path='http://example.org/extensible-class/required', varname='required', comment='A required extensible property', max_count=1, min_count=1, enum_values=None, class_id='', datatype='http://www.w3.org/2001/XMLSchema#string', pattern='')], comment='An extensible class', id_property=None, node_kind=rdflib.term.URIRef('http://www.w3.org/ns/shacl#BlankNodeOrIRI'), is_extensible=True, is_abstract=False, named_individuals=[])
@@ -701,6 +701,38 @@ def node_kind_tests(name, blank, iri):
701
701
  "@type": "concrete-spdx-class",
702
702
  },
703
703
  ),
704
+ # An extensible abstract class cannot be instantiated
705
+ (
706
+ False,
707
+ {
708
+ "@context": CONTEXT,
709
+ "@type": "extensible-abstract-class",
710
+ },
711
+ ),
712
+ # Any can type can be used where a extensible abstract class is
713
+ # references, except... (SEE NEXT)
714
+ (
715
+ True,
716
+ {
717
+ "@context": CONTEXT,
718
+ "@type": "uses-extensible-abstract-class",
719
+ "uses-extensible-abstract-class/prop": {
720
+ "@type": "http://example.com/extended",
721
+ },
722
+ },
723
+ ),
724
+ # ... the exact type of the extensible abstract class is specifically
725
+ # not allowed
726
+ (
727
+ False,
728
+ {
729
+ "@context": CONTEXT,
730
+ "@type": "uses-extensible-abstract-class",
731
+ "uses-extensible-abstract-class/prop": {
732
+ "@type": "extensible-abstract-class",
733
+ },
734
+ },
735
+ ),
704
736
  # Base object for type tests
705
737
  (True, BASE_OBJ),
706
738
  ],
@@ -815,10 +847,14 @@ def type_tests(name, *, good=[], bad=[], typ=[]):
815
847
  {"@type": "test-derived-class"},
816
848
  "_:blanknode",
817
849
  "http://serialize.example.org/test",
850
+ # Named individual
851
+ "test-class/named",
818
852
  ],
819
853
  bad=[
820
854
  {"@type": "test-another-class"},
821
855
  {"@type": "parent-class"},
856
+ "not/an/iri",
857
+ "not-an-iri",
822
858
  ],
823
859
  typ=[object, str],
824
860
  ),
@@ -754,6 +754,7 @@ def type_tests(name, *typ):
754
754
  ),
755
755
  ("test_class_class_prop", lambda model: model.test_another_class(), TypeError),
756
756
  ("test_class_class_prop", lambda model: model.parent_class(), TypeError),
757
+ ("test_class_class_prop", lambda model: model.test_class.named, SAME_AS_VALUE),
757
758
  ("test_class_class_prop", "_:blanknode", "_:blanknode"),
758
759
  (
759
760
  "test_class_class_prop",
@@ -1619,6 +1620,11 @@ def test_objset_foreach_type(model, roundtrip):
1619
1620
 
1620
1621
  expect.add(objset.find_by_id("http://serialize.example.com/test"))
1621
1622
  expect.add(objset.find_by_id("http://serialize.example.com/nested-parent"))
1623
+ expect.add(
1624
+ objset.find_by_id(
1625
+ "http://serialize.example.com/test-named-individual-reference"
1626
+ )
1627
+ )
1622
1628
  expect.add(
1623
1629
  objset.find_by_id(
1624
1630
  "http://serialize.example.com/nested-parent"
@@ -1739,3 +1745,43 @@ def test_required_abstract_class_property(model, tmp_path):
1739
1745
  # Deleting an abstract class property should return it to None
1740
1746
  del c.required_abstract_abstract_class_prop
1741
1747
  assert c.required_abstract_abstract_class_prop is None
1748
+
1749
+
1750
+ def test_extensible_abstract_class(model):
1751
+ @model.register("http://example.org/custom-extension-class")
1752
+ class Extension(model.extensible_abstract_class):
1753
+ pass
1754
+
1755
+ # Test that an extensible abstract class cannot be created
1756
+ with pytest.raises(NotImplementedError):
1757
+ model.extensible_abstract_class()
1758
+
1759
+ # Test that a class derived from an abstract extensible class can be
1760
+ # created
1761
+ Extension()
1762
+
1763
+
1764
+ def test_named_individual(model, roundtrip):
1765
+ objset = model.SHACLObjectSet()
1766
+ with roundtrip.open("r") as f:
1767
+ d = model.JSONLDDeserializer()
1768
+ d.read(f, objset)
1769
+
1770
+ c = objset.find_by_id(
1771
+ "http://serialize.example.com/test-named-individual-reference"
1772
+ )
1773
+ assert c is not None
1774
+ assert c.test_class_class_prop == model.test_class.named
1775
+
1776
+ assert model.test_class.named not in objset.missing_ids
1777
+
1778
+
1779
+ def test_missing_ids(model, roundtrip):
1780
+ objset = model.SHACLObjectSet()
1781
+ with roundtrip.open("r") as f:
1782
+ d = model.JSONLDDeserializer()
1783
+ d.read(f, objset)
1784
+
1785
+ assert objset.missing_ids == {
1786
+ "http://serialize.example.com/non-shape",
1787
+ }
@@ -1 +0,0 @@
1
- VERSION = "0.0.11"
File without changes
File without changes
File without changes
File without changes
File without changes