shacl2code 0.0.12__tar.gz → 0.0.13__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 (62) hide show
  1. {shacl2code-0.0.12 → shacl2code-0.0.13}/.github/workflows/test.yaml +5 -0
  2. {shacl2code-0.0.12 → shacl2code-0.0.13}/PKG-INFO +1 -2
  3. {shacl2code-0.0.12 → shacl2code-0.0.13}/pyproject.toml +0 -1
  4. shacl2code-0.0.13/src/shacl2code/context.py +277 -0
  5. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/common.py +9 -0
  6. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/templates/jsonschema.j2 +5 -5
  7. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/templates/python.j2 +49 -46
  8. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/model.py +1 -1
  9. shacl2code-0.0.13/src/shacl2code/version.py +1 -0
  10. shacl2code-0.0.13/tests/__init__.py +0 -0
  11. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/conftest.py +4 -5
  12. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/context.j2 +2 -2
  13. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/model/test-context.json +61 -8
  14. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/model/test.ttl +0 -2
  15. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/python/roundtrip.json +3 -3
  16. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/jsonschema/test-context.json +17 -17
  17. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/python/test-context.py +52 -49
  18. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/python/test.py +31 -26
  19. shacl2code-0.0.13/tests/http.py +74 -0
  20. shacl2code-0.0.13/tests/test_context.py +136 -0
  21. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/test_jsonschema.py +6 -6
  22. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/test_model_source.py +60 -7
  23. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/test_python.py +14 -5
  24. shacl2code-0.0.12/src/shacl2code/context.py +0 -167
  25. shacl2code-0.0.12/src/shacl2code/version.py +0 -1
  26. shacl2code-0.0.12/tests/test_context.py +0 -144
  27. {shacl2code-0.0.12 → shacl2code-0.0.13}/.flake8 +0 -0
  28. {shacl2code-0.0.12 → shacl2code-0.0.13}/.github/workflows/coverage-generate.yaml +0 -0
  29. {shacl2code-0.0.12 → shacl2code-0.0.13}/.github/workflows/coverage-report.yaml +0 -0
  30. {shacl2code-0.0.12 → shacl2code-0.0.13}/.github/workflows/publish.yaml +0 -0
  31. {shacl2code-0.0.12 → shacl2code-0.0.13}/.gitignore +0 -0
  32. {shacl2code-0.0.12 → shacl2code-0.0.13}/LICENSE +0 -0
  33. {shacl2code-0.0.12 → shacl2code-0.0.13}/README.md +0 -0
  34. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/__init__.py +0 -0
  35. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/__main__.py +0 -0
  36. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/__init__.py +0 -0
  37. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/jinja.py +0 -0
  38. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/jsonschema.py +0 -0
  39. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/lang.py +0 -0
  40. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/lang/python.py +0 -0
  41. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/main.py +0 -0
  42. {shacl2code-0.0.12 → shacl2code-0.0.13}/src/shacl2code/urlcontext.py +0 -0
  43. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/abort.j2 +0 -0
  44. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/bad-id.j2 +0 -0
  45. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/bad-node-kind.ttl +0 -0
  46. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/bad-pattern-class.ttl +0 -0
  47. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/bad-pattern-integer.ttl +0 -0
  48. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/bad-reference.jsonld +0 -0
  49. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/context-url.j2 +0 -0
  50. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/missing-range.ttl +0 -0
  51. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/python/bad-object-type-inline.json +0 -0
  52. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/python/bad-object-type-ref-after.json +0 -0
  53. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/python/bad-object-type-ref-before.json +0 -0
  54. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/python/links.json +0 -0
  55. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/data/raw.j2 +0 -0
  56. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/jsonschema/test.json +0 -0
  57. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/make_expect.py +0 -0
  58. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/raw/test-context.txt +0 -0
  59. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/expect/raw/test.txt +0 -0
  60. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/test_cli.py +0 -0
  61. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/test_common_jinja.py +0 -0
  62. {shacl2code-0.0.12 → shacl2code-0.0.13}/tests/test_common_prefix.py +0 -0
@@ -22,10 +22,15 @@ jobs:
22
22
  uses: actions/setup-python@v4
23
23
  with:
24
24
  python-version: ${{ matrix.python-version }}
25
+ - name: Setup Node
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: 20
25
29
  - name: Install dependencies
26
30
  run: |
27
31
  python -m pip install --upgrade pip
28
32
  pip install build
33
+ npm install -g jsonld-cli
29
34
  - name: Build package
30
35
  run: |
31
36
  python -m build
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: shacl2code
3
- Version: 0.0.12
3
+ Version: 0.0.13
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
@@ -25,7 +25,6 @@ Requires-Dist: flake8>=7.0.0; extra == 'dev'
25
25
  Requires-Dist: jsonschema>=4.21.1; extra == 'dev'
26
26
  Requires-Dist: pyshacl>=0.25.0; extra == 'dev'
27
27
  Requires-Dist: pytest-cov>=4.1; extra == 'dev'
28
- Requires-Dist: pytest-server-fixtures>=1.7; extra == 'dev'
29
28
  Requires-Dist: pytest>=7.4; extra == 'dev'
30
29
  Description-Content-Type: text/markdown
31
30
 
@@ -31,7 +31,6 @@ dev = [
31
31
  "pyshacl >= 0.25.0",
32
32
  "pytest >= 7.4",
33
33
  "pytest-cov >= 4.1",
34
- "pytest-server-fixtures >= 1.7",
35
34
  ]
36
35
 
37
36
  [project.urls]
@@ -0,0 +1,277 @@
1
+ # Copyright (c) 2024 Joshua Watt
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ import re
6
+ from contextlib import contextmanager
7
+
8
+
9
+ def foreach_context(contexts):
10
+ for ctx in contexts:
11
+ for name, value in ctx.items():
12
+ yield name, value
13
+
14
+
15
+ class Context(object):
16
+ def __init__(self, contexts=[]):
17
+ self.contexts = [c for c in contexts if c]
18
+ self.__vocabs = []
19
+ self.__expanded_iris = {}
20
+ self.__expanded_ids = {}
21
+ self.__expanded_vocabs = {}
22
+ self.__compacted_iris = {}
23
+ self.__compacted_ids = {}
24
+ self.__compacted_vocabs = {}
25
+
26
+ @contextmanager
27
+ def vocab_push(self, vocab):
28
+ if not vocab:
29
+ yield self
30
+ return
31
+
32
+ self.__vocabs.append(vocab)
33
+ try:
34
+ yield self
35
+ finally:
36
+ self.__vocabs.pop()
37
+
38
+ def __vocab_key(self):
39
+ if not self.__vocabs:
40
+ return ""
41
+
42
+ return self.__vocabs[-1]
43
+
44
+ def __get_vocab_contexts(self):
45
+ contexts = []
46
+
47
+ for v in self.__vocabs:
48
+ for name, value in foreach_context(self.contexts):
49
+ if (
50
+ isinstance(value, dict)
51
+ and value["@type"] == "@vocab"
52
+ and v == self.expand_iri(value["@id"])
53
+ ):
54
+ if "@context" in value:
55
+ contexts.insert(0, value["@context"])
56
+
57
+ contexts.extend(self.contexts)
58
+
59
+ return contexts
60
+
61
+ def __choose_possible(
62
+ self,
63
+ term,
64
+ default,
65
+ contexts,
66
+ *,
67
+ vocab=False,
68
+ base=False,
69
+ exact=False,
70
+ prefix=False,
71
+ ):
72
+ def remove_prefix(_id, value):
73
+ expanded_id = self.expand_iri(_id)
74
+ expanded_value = self.expand_iri(value)
75
+
76
+ possible = set()
77
+ if expanded_id.startswith(expanded_value):
78
+ tmp_id = _id[len(expanded_value) :]
79
+ possible.add(tmp_id)
80
+ return possible
81
+
82
+ def helper(term):
83
+ possible = set()
84
+ for name, value in foreach_context(contexts):
85
+ if name == "@vocab":
86
+ if vocab:
87
+ possible |= remove_prefix(term, value)
88
+ continue
89
+
90
+ if name == "@base":
91
+ if base:
92
+ possible |= remove_prefix(term, value)
93
+ continue
94
+
95
+ if isinstance(value, dict):
96
+ value = value["@id"]
97
+
98
+ if term == self.expand_iri(value):
99
+ if exact and name not in possible:
100
+ possible.add(name)
101
+ possible |= helper(name)
102
+ continue
103
+
104
+ if not prefix:
105
+ continue
106
+
107
+ if term.startswith(value) and value.endswith("/"):
108
+ tmp_id = name + ":" + term[len(value) :].lstrip("/")
109
+ if tmp_id not in possible:
110
+ possible.add(tmp_id)
111
+ possible |= helper(tmp_id)
112
+ continue
113
+
114
+ if term.startswith(value + ":") and self.expand_iri(value).endswith(
115
+ "/"
116
+ ):
117
+ tmp_id = name + term[len(value) :]
118
+ if tmp_id not in possible:
119
+ possible.add(tmp_id)
120
+ possible |= helper(tmp_id)
121
+ continue
122
+
123
+ return possible
124
+
125
+ possible = helper(term)
126
+
127
+ if not possible:
128
+ return default
129
+
130
+ # To select from the possible identifiers, choose the one that has the
131
+ # least context (fewest ":"), then the shortest, and finally
132
+ # alphabetically
133
+ possible = list(possible)
134
+ possible.sort(key=lambda p: (p.count(":"), len(p), p))
135
+ return possible[0]
136
+
137
+ def compact_iri(self, iri):
138
+ if iri not in self.__compacted_iris:
139
+ self.__compacted_iris[iri] = self.__choose_possible(
140
+ iri,
141
+ iri,
142
+ self.contexts,
143
+ exact=True,
144
+ prefix=True,
145
+ )
146
+
147
+ return self.__compacted_iris[iri]
148
+
149
+ def compact_id(self, _id):
150
+ if ":" not in _id:
151
+ return _id
152
+
153
+ if _id not in self.__compacted_ids:
154
+ self.__compacted_ids[_id] = self.__choose_possible(
155
+ _id,
156
+ _id,
157
+ self.contexts,
158
+ base=True,
159
+ prefix=True,
160
+ )
161
+
162
+ return self.__compacted_ids[_id]
163
+
164
+ def compact_vocab(self, term, vocab=None):
165
+ with self.vocab_push(vocab):
166
+ v = self.__vocab_key()
167
+ if v in self.__compacted_vocabs and term in self.__compacted_vocabs[v]:
168
+ return self.__compacted_vocabs[v][term]
169
+
170
+ compact = self.__choose_possible(
171
+ term,
172
+ None,
173
+ self.__get_vocab_contexts(),
174
+ vocab=True,
175
+ exact=True,
176
+ )
177
+ if compact is not None:
178
+ self.__compacted_vocabs.setdefault(v, {})[term] = self.compact_id(
179
+ compact
180
+ )
181
+ return compact
182
+
183
+ # If unable to compact with a vocabulary, compact as an ID
184
+ return self.compact_id(term)
185
+
186
+ def expand_iri(self, iri):
187
+ if iri not in self.__expanded_iris:
188
+ self.__expanded_iris[iri] = self.__expand(
189
+ iri,
190
+ self.contexts,
191
+ exact=True,
192
+ prefix=True,
193
+ )
194
+
195
+ return self.__expanded_iris[iri]
196
+
197
+ def expand_id(self, _id):
198
+ if _id not in self.__expanded_ids:
199
+ self.__expanded_ids[_id] = self.__expand(
200
+ _id,
201
+ self.contexts,
202
+ base=True,
203
+ prefix=True,
204
+ )
205
+
206
+ return self.__expanded_ids[_id]
207
+
208
+ def expand_vocab(self, term, vocab=None):
209
+ with self.vocab_push(vocab):
210
+ v = self.__vocab_key()
211
+ if v not in self.__expanded_vocabs or term not in self.__expanded_vocabs[v]:
212
+ value = self.__expand(
213
+ term,
214
+ self.__get_vocab_contexts(),
215
+ vocab=True,
216
+ exact=True,
217
+ )
218
+ self.__expanded_vocabs.setdefault(v, {})[term] = self.expand_id(value)
219
+
220
+ return self.__expanded_vocabs[v][term]
221
+
222
+ def __expand(
223
+ self,
224
+ term,
225
+ contexts,
226
+ *,
227
+ base=False,
228
+ exact=False,
229
+ prefix=False,
230
+ vocab=False,
231
+ ):
232
+ def helper(term):
233
+ vocabs = []
234
+ bases = []
235
+ prefixes = []
236
+ exacts = []
237
+ is_short = not re.match(r"[^:]+:", term)
238
+
239
+ for name, value in foreach_context(contexts):
240
+ if name == "@vocab":
241
+ if vocab and is_short:
242
+ vocabs.append(helper(value))
243
+ continue
244
+
245
+ if name == "@base":
246
+ if base and is_short:
247
+ bases.append(value)
248
+ continue
249
+
250
+ if isinstance(value, dict):
251
+ value = value["@id"]
252
+
253
+ if term == name:
254
+ if exact:
255
+ exacts.append(helper(value))
256
+ continue
257
+
258
+ if prefix:
259
+ prefixes.append(name)
260
+
261
+ for e in exacts:
262
+ return e
263
+
264
+ if ":" in term:
265
+ p, suffix = term.split(":", 1)
266
+ for name in prefixes:
267
+ if p == name:
268
+ p = self.expand_iri(p)
269
+ if p.endswith("/"):
270
+ return p + suffix
271
+
272
+ for value in vocabs + bases:
273
+ return value + term
274
+
275
+ return term
276
+
277
+ return helper(term)
@@ -114,6 +114,14 @@ class BasicJinjaRender(object):
114
114
  d.sort()
115
115
  return d
116
116
 
117
+ def get_all_named_individuals(cls):
118
+ ni = set(i._id for i in cls.named_individuals)
119
+
120
+ for d in get_all_derived(cls):
121
+ ni |= set(i._id for i in classes.get(d).named_individuals)
122
+
123
+ return ni
124
+
117
125
  classes = ObjectList(model.classes)
118
126
  concrete_classes = ObjectList(
119
127
  list(c for c in model.classes if not c.is_abstract)
@@ -132,6 +140,7 @@ class BasicJinjaRender(object):
132
140
 
133
141
  env = {
134
142
  "get_all_derived": get_all_derived,
143
+ "get_all_named_individuals": get_all_named_individuals,
135
144
  **self.get_extra_env(),
136
145
  }
137
146
 
@@ -69,7 +69,7 @@
69
69
  {%- endif %}
70
70
  "properties": {
71
71
  "{{ class.id_property or "@id" }}": { "$ref": "#/$defs/{{ class.node_kind.split("#")[-1] }}" },
72
- "{{ context.compact("@type") }}": {
72
+ "{{ context.compact_iri("@type") }}": {
73
73
  {#- Abstract Extensible classes are weird; any type _except_ the specific class type is allowed #}
74
74
  {%- if class.is_abstract and class.is_extensible %}
75
75
  "allOf": [
@@ -98,8 +98,8 @@
98
98
  {%- for d in get_all_derived(class) + [class._id] %}
99
99
  {%- set ns.json_refs = ns.json_refs + ["#/$defs/" + varname(*classes.get(d).clsname)] %}
100
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)] %}
101
+ {%- if context.compact_iri(n._id) != n._id %}
102
+ {%- set ns.named_individuals = ns.named_individuals + [context.compact_iri(n._id)] %}
103
103
  {%- endif %}
104
104
  {%- endfor %}
105
105
  {%- endfor %}
@@ -267,7 +267,7 @@
267
267
  "SHACLClass": {
268
268
  "type": "object",
269
269
  "properties": {
270
- "{{ context.compact("@type") }}": {
270
+ "{{ context.compact_iri("@type") }}": {
271
271
  "oneOf": [
272
272
  { "$ref": "#/$defs/IRI" },
273
273
  {
@@ -280,7 +280,7 @@
280
280
  ]
281
281
  }
282
282
  },
283
- "required": ["{{ context.compact("@type") }}"]
283
+ "required": ["{{ context.compact_iri("@type") }}"]
284
284
  },
285
285
  "AnyClass": {
286
286
  "anyOf": [
@@ -220,13 +220,34 @@ class FloatProp(Property):
220
220
  return decoder.read_float()
221
221
 
222
222
 
223
- class ObjectProp(Property):
223
+ class IRIProp(Property):
224
+ def __init__(self, context=[], *, pattern=None):
225
+ super().__init__(pattern=pattern)
226
+ self.context = context
227
+
228
+ def compact(self, value):
229
+ for iri, compact in self.context:
230
+ if value == iri:
231
+ return compact
232
+ return None
233
+
234
+ def expand(self, value):
235
+ for iri, compact in self.context:
236
+ if value == compact:
237
+ return iri
238
+ return None
239
+
240
+ def iri_values(self):
241
+ return (iri for iri, _ in self.context)
242
+
243
+
244
+ class ObjectProp(IRIProp):
224
245
  """
225
246
  A scalar SHACL object property of a SHACL object
226
247
  """
227
248
 
228
- def __init__(self, cls, required):
229
- super().__init__()
249
+ def __init__(self, cls, required, context=[]):
250
+ super().__init__(context)
230
251
  self.cls = cls
231
252
  self.required = required
232
253
 
@@ -264,8 +285,7 @@ class ObjectProp(Property):
264
285
  raise ValueError("Object cannot be None")
265
286
 
266
287
  if isinstance(value, str):
267
- value = _NI_ENCODE_CONTEXT.get(value, value)
268
- encoder.write_iri(value)
288
+ encoder.write_iri(value, self.compact(value))
269
289
  return
270
290
 
271
291
  return value.encode(encoder, state)
@@ -275,7 +295,7 @@ class ObjectProp(Property):
275
295
  if iri is None:
276
296
  return self.cls.decode(decoder, objectset=objectset)
277
297
 
278
- iri = _NI_DECODE_CONTEXT.get(iri, iri)
298
+ iri = self.expand(iri) or iri
279
299
 
280
300
  if objectset is None:
281
301
  return iri
@@ -445,36 +465,27 @@ class ListProp(Property):
445
465
  return ListProxy(self.prop, data=data)
446
466
 
447
467
 
448
- class EnumProp(Property):
468
+ class EnumProp(IRIProp):
449
469
  VALID_TYPES = str
450
470
 
451
471
  def __init__(self, values, *, pattern=None):
452
- super().__init__(pattern=pattern)
453
- self.values = values
472
+ super().__init__(values, pattern=pattern)
454
473
 
455
474
  def validate(self, value):
456
475
  super().validate(value)
457
476
 
458
- valid_values = (iri for iri, _ in self.values)
477
+ valid_values = self.iri_values()
459
478
  if value not in valid_values:
460
479
  raise ValueError(
461
480
  f"'{value}' is not a valid value. Choose one of {' '.join(valid_values)}"
462
481
  )
463
482
 
464
483
  def encode(self, encoder, value, state):
465
- for iri, compact in self.values:
466
- if iri == value:
467
- encoder.write_enum(value, self, compact)
468
- return
469
-
470
- encoder.write_enum(value, self)
484
+ encoder.write_enum(value, self, self.compact(value))
471
485
 
472
486
  def decode(self, decoder, *, objectset=None):
473
487
  v = decoder.read_enum(self)
474
- for iri, compact in self.values:
475
- if v == compact:
476
- return iri
477
- return v
488
+ return self.expand(v) or v
478
489
 
479
490
 
480
491
  class NodeKind(Enum):
@@ -1518,14 +1529,14 @@ class JSONLDDecoder(Decoder):
1518
1529
 
1519
1530
  def object_keys(self):
1520
1531
  for key in self.data.keys():
1521
- if key in ("@type", "{{ context.compact('@type') }}"):
1532
+ if key in ("@type", "{{ context.compact_iri('@type') }}"):
1522
1533
  continue
1523
1534
  if self.root and key == "@context":
1524
1535
  continue
1525
1536
  yield key
1526
1537
 
1527
1538
  def read_object(self):
1528
- typ = self.__get_value("@type", "{{ context.compact('@type') }}")
1539
+ typ = self.__get_value("@type", "{{ context.compact_iri('@type') }}")
1529
1540
  if typ is not None:
1530
1541
  return typ, self
1531
1542
 
@@ -1717,7 +1728,7 @@ class JSONLDEncoder(Encoder):
1717
1728
  @contextmanager
1718
1729
  def write_object(self, o, _id, needs_id):
1719
1730
  self.data = {
1720
- "{{ context.compact('@type') }}": o.COMPACT_TYPE or o.TYPE,
1731
+ "{{ context.compact_iri('@type') }}": o.COMPACT_TYPE or o.TYPE,
1721
1732
  }
1722
1733
  if needs_id:
1723
1734
  self.data[o.ID_ALIAS or "@id"] = _id
@@ -1841,7 +1852,7 @@ class JSONLDInlineEncoder(Encoder):
1841
1852
  self._write_comma()
1842
1853
 
1843
1854
  self.write("{")
1844
- self.write_string("{{ context.compact('@type') }}")
1855
+ self.write_string("{{ context.compact_iri('@type') }}")
1845
1856
  self.write(":")
1846
1857
  self.write_string(o.COMPACT_TYPE or o.TYPE)
1847
1858
  self.comma = True
@@ -1963,26 +1974,6 @@ CONTEXT_URLS = [
1963
1974
  {%- endfor %}
1964
1975
  ]
1965
1976
 
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
-
1986
1977
 
1987
1978
  # CLASSES
1988
1979
  {%- for class in classes %}
@@ -1991,7 +1982,7 @@ _NI_DECODE_CONTEXT = {
1991
1982
  #{{ (" " + l).rstrip() }}
1992
1983
  {%- endfor %}
1993
1984
  {%- endif %}
1994
- @register("{{ class._id }}"{%- if context.compact(class._id) != class._id %}, compact_type="{{ context.compact(class._id) }}"{%- endif %}, abstract={{ class.is_abstract }})
1985
+ @register("{{ class._id }}"{%- if context.compact_iri(class._id) != class._id %}, compact_type="{{ context.compact_iri(class._id) }}"{%- endif %}, abstract={{ class.is_abstract }})
1995
1986
  class {{ varname(*class.clsname) }}(
1996
1987
  {%- if class.is_extensible -%}
1997
1988
  SHACLExtensibleObject{{", "}}
@@ -2042,7 +2033,19 @@ class {{ varname(*class.clsname) }}(
2042
2033
  {%- endfor %}
2043
2034
  ])
2044
2035
  {%- elif prop.class_id -%}
2045
- ObjectProp({{ varname(*classes.get(prop.class_id).clsname) }}, {% if prop.min_count and not is_list %}True{% else %}False{% endif %})
2036
+ {%- set ctx = [] %}
2037
+ {%- for value in get_all_named_individuals(classes.get(prop.class_id)) %}
2038
+ {%- if context.compact_vocab(value, prop.path) != value %}
2039
+ {{- ctx.append((value, context.compact_vocab(value, prop.path))) or "" }}
2040
+ {%- endif %}
2041
+ {%- endfor -%}
2042
+ ObjectProp({{ varname(*classes.get(prop.class_id).clsname) }}, {% if prop.min_count and not is_list %}True{% else %}False{% endif %}{% if ctx %}, context=[
2043
+ {%- for value, compact in ctx %}
2044
+ ("{{ value }}", "{{ compact }}"),
2045
+ {%- endfor %}
2046
+ ],
2047
+ {%- endif -%}
2048
+ )
2046
2049
  {%- else -%}
2047
2050
  {% if not prop.datatype in DATATYPE_CLASSES -%}
2048
2051
  {{ abort("Unknown data type " + prop.datatype) -}}
@@ -280,7 +280,7 @@ class Model(object):
280
280
  """
281
281
  _id = str(_id)
282
282
  if _id not in self.compact_ids:
283
- self.compact_ids[_id] = self.context.compact(_id)
283
+ self.compact_ids[_id] = self.context.compact_iri(_id)
284
284
 
285
285
  if self.compact_ids[_id] == _id and fallback is not None:
286
286
  return fallback
@@ -0,0 +1 @@
1
+ VERSION = "0.0.13"
File without changes
@@ -9,8 +9,8 @@ import pytest
9
9
  import shutil
10
10
  import subprocess
11
11
  import time
12
+ from .http import HTTPTestServer
12
13
 
13
- from pytest_server_fixtures.http import SimpleHTTPTestServer
14
14
  from pathlib import Path
15
15
 
16
16
  THIS_FILE = Path(__file__)
@@ -21,19 +21,18 @@ MODEL_DIR = THIS_DIR / "data" / "model"
21
21
 
22
22
  @pytest.fixture
23
23
  def http_server():
24
- with SimpleHTTPTestServer() as s:
24
+ with HTTPTestServer() as s:
25
25
  s.start()
26
26
  yield s
27
27
 
28
28
 
29
29
  @pytest.fixture(scope="session")
30
30
  def model_server():
31
- with SimpleHTTPTestServer() as s:
32
- root = Path(s.document_root)
31
+ with HTTPTestServer() as s:
33
32
  for p in MODEL_DIR.iterdir():
34
33
  if not p.is_file():
35
34
  continue
36
- shutil.copyfile(p, root / p.name)
35
+ shutil.copyfile(p, s.document_root / p.name)
37
36
  s.start()
38
37
  yield s.uri
39
38
 
@@ -2,8 +2,8 @@
2
2
  Context: {{ url }}
3
3
  {% endfor -%}
4
4
  {% for enum in enums %}
5
- {{ enum._id }}: {{ context.compact(enum._id) }}
5
+ {{ enum._id }}: {{ context.compact_iri(enum._id) }}
6
6
  {% endfor %}
7
7
  {% for class in classes %}
8
- {{ class._id }}: {{ context.compact(class._id) }}
8
+ {{ class._id }}: {{ context.compact_iri(class._id) }}
9
9
  {% endfor %}