shacl2code 0.0.9__tar.gz → 0.0.11__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 (61) hide show
  1. shacl2code-0.0.11/PKG-INFO +205 -0
  2. shacl2code-0.0.11/README.md +174 -0
  3. {shacl2code-0.0.9 → shacl2code-0.0.11}/pyproject.toml +1 -1
  4. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/common.py +58 -34
  5. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/jsonschema.py +2 -2
  6. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/python.py +2 -1
  7. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/templates/jsonschema.j2 +38 -42
  8. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/templates/python.j2 +515 -200
  9. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/model.py +41 -47
  10. shacl2code-0.0.11/src/shacl2code/version.py +1 -0
  11. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/conftest.py +23 -1
  12. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/model/test-context.json +12 -0
  13. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/model/test.ttl +76 -3
  14. shacl2code-0.0.11/tests/data/python/bad-object-type-inline.json +13 -0
  15. shacl2code-0.0.11/tests/data/python/bad-object-type-ref-after.json +14 -0
  16. shacl2code-0.0.11/tests/data/python/bad-object-type-ref-before.json +14 -0
  17. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/python/roundtrip.json +4 -0
  18. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/expect/jsonschema/test-context.json +392 -60
  19. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/expect/jsonschema/test.json +392 -60
  20. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/expect/python/test-context.py +625 -245
  21. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/expect/python/test.py +622 -236
  22. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/expect/raw/test-context.txt +11 -1
  23. shacl2code-0.0.11/tests/expect/raw/test.txt +24 -0
  24. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_jsonschema.py +32 -0
  25. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_python.py +401 -41
  26. shacl2code-0.0.9/PKG-INFO +0 -96
  27. shacl2code-0.0.9/README.md +0 -66
  28. shacl2code-0.0.9/src/shacl2code/version.py +0 -1
  29. shacl2code-0.0.9/tests/expect/raw/test.txt +0 -19
  30. {shacl2code-0.0.9 → shacl2code-0.0.11}/.flake8 +0 -0
  31. {shacl2code-0.0.9 → shacl2code-0.0.11}/.github/workflows/coverage-generate.yaml +0 -0
  32. {shacl2code-0.0.9 → shacl2code-0.0.11}/.github/workflows/coverage-report.yaml +0 -0
  33. {shacl2code-0.0.9 → shacl2code-0.0.11}/.github/workflows/publish.yaml +0 -0
  34. {shacl2code-0.0.9 → shacl2code-0.0.11}/.github/workflows/test.yaml +0 -0
  35. {shacl2code-0.0.9 → shacl2code-0.0.11}/.gitignore +0 -0
  36. {shacl2code-0.0.9 → shacl2code-0.0.11}/LICENSE +0 -0
  37. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/__init__.py +0 -0
  38. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/__main__.py +0 -0
  39. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/context.py +0 -0
  40. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/__init__.py +0 -0
  41. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/jinja.py +0 -0
  42. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/lang/lang.py +0 -0
  43. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/main.py +0 -0
  44. {shacl2code-0.0.9 → shacl2code-0.0.11}/src/shacl2code/urlcontext.py +0 -0
  45. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/abort.j2 +0 -0
  46. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/bad-id.j2 +0 -0
  47. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/bad-node-kind.ttl +0 -0
  48. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/bad-pattern-class.ttl +0 -0
  49. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/bad-pattern-integer.ttl +0 -0
  50. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/bad-reference.jsonld +0 -0
  51. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/context-url.j2 +0 -0
  52. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/context.j2 +0 -0
  53. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/missing-range.ttl +0 -0
  54. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/python/links.json +0 -0
  55. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/data/raw.j2 +0 -0
  56. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/expect/make_expect.py +0 -0
  57. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_cli.py +0 -0
  58. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_common_jinja.py +0 -0
  59. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_common_prefix.py +0 -0
  60. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_context.py +0 -0
  61. {shacl2code-0.0.9 → shacl2code-0.0.11}/tests/test_model_source.py +0 -0
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.3
2
+ Name: shacl2code
3
+ Version: 0.0.11
4
+ Summary: Convert SHACL model file to code bindings
5
+ Project-URL: Homepage, https://github.com/JPEWdev/shacl2code
6
+ Project-URL: Repository, https://github.com/JPEWdev/shacl2code.git
7
+ Project-URL: Issues, https://github.com/JPEWdev/shacl2code/issues
8
+ Author-email: Joshua Watt <JPEWhacker@gmail.com>
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Build Tools
20
+ Requires-Python: >=3.8
21
+ Requires-Dist: jinja2>=3.1.2
22
+ Requires-Dist: rdflib>=7.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: flake8>=7.0.0; extra == 'dev'
25
+ Requires-Dist: jsonschema>=4.21.1; extra == 'dev'
26
+ Requires-Dist: pyshacl>=0.25.0; extra == 'dev'
27
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
28
+ Requires-Dist: pytest-server-fixtures>=1.7; extra == 'dev'
29
+ Requires-Dist: pytest>=7.4; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # Convert SHACL Model to code bindings
33
+ [![Coverage Report](https://raw.githubusercontent.com/JPEWdev/shacl2code/python-coverage-comment-action-data/badge.svg)](https://htmlpreview.github.io/?https://github.com/JPEWdev/shacl2code/blob/python-coverage-comment-action-data/htmlcov/index.html)
34
+
35
+ This tool can be used to convert a SHACL model into various code bindings
36
+
37
+ ## Installation
38
+
39
+ `shacl2code` can be installed using pip:
40
+
41
+ ```shell
42
+ python3 -m pip install shacl2code
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ `shacl2code` can generate bindings from either a local file:
48
+ ```shell
49
+ shacl2code generate -i model.jsonld python -o out.py
50
+ ```
51
+ Or from a URL:
52
+ ```shell
53
+ shacl2code generate -i https://example.com/rdf/model.jsonld python -o out.py
54
+ ```
55
+ Or from stdin:
56
+ ```shell
57
+ cat model.jsonld | shacl2code generate -i - python -o - > out.py
58
+ ```
59
+
60
+ For more information, run:
61
+ ```shell
62
+ shacl2code --help
63
+ ```
64
+
65
+ The available language bindings can be viewed by running:
66
+ ```shell
67
+ shacl2code list
68
+ ```
69
+
70
+ ## Developing
71
+
72
+ Developing on `shacl2code` is best done using a virtual environment. You can
73
+ configure one and install shacl2code in editable mode with all necessary
74
+ development dependencies by running:
75
+
76
+ ```shell
77
+ python3 -m venv .venv
78
+ . .venv/bin/activate
79
+ pip install -e ".[dev]"
80
+ ```
81
+
82
+ ## Testing
83
+
84
+ `shacl2code` has a test suite written in [pytest][pytest]. To run it, setup a
85
+ virtual environment as shown above, then run:
86
+ ```shell
87
+ pytest
88
+ ```
89
+
90
+ In addition to the test results, a test coverage report will also be generated
91
+ using [pytest-cov][pytest-cov]
92
+
93
+
94
+ ## Custom Annotations
95
+
96
+ `shacl2code` supports a number of custom annotations that can be specified in a
97
+ SHACL model to give hints about the generated code. All of these annotations
98
+ live in the `https://jpewdev.github.io/shacl2code/schema#` namespace, and
99
+ commonly are given the `sh-to-code` prefix to make it easier to reference them.
100
+ For example, in Turtle one would add the prefix mapping:
101
+
102
+ ```ttl
103
+ @prefix sh-to-code: <https://jpewdev.github.io/shacl2code/schema#> .
104
+ ```
105
+
106
+ ### ID Property Name
107
+
108
+ The `idPropertyName` annotation allows a class to specify what the name of the
109
+ "property" that specifies the RDF subject of an object is for serializations
110
+ that support it. For example, in JSON-LD, the `@id` property indicates the
111
+ subject in RDF. If you wanted to alias the `@id` property to another name, the
112
+ `idPropertyName` annotation will let you do this. For example, the following
113
+ turtle will use `MyId` instead of `@id` when writing JSON-LD bindings:
114
+
115
+ ```ttl
116
+ <MyClass> a owl:Class, sh:NodeShape ;
117
+ sh-to-code:idPropertyName "MyId"
118
+ .
119
+ ```
120
+
121
+ When doing this, the class would then look like this in JSON-LD:
122
+ ```json
123
+ {
124
+ "@type": "MyClass",
125
+ "MyId": "http://example.com/id"
126
+ }
127
+ ```
128
+
129
+ The `idProperyName` annotation is inherited by derived classes, so for example
130
+ any class that derived from `MyClass` would also use `MyId` as the subject
131
+ property.
132
+
133
+ **Note:** This only specifies what the name of the field should be in generated
134
+ bindings and has no bearing on how an RDF parser would interpret the property.
135
+ In order to still be parsed by RDF, you would also need context file that maps
136
+ `MyId` to `@id`, for example:
137
+
138
+ ```json
139
+ {
140
+ "@context": {
141
+ "MyId": "@id"
142
+ }
143
+ }
144
+ ```
145
+
146
+ `shacl2code` doesn't do this for you, nor does it validate that you have done
147
+ it.
148
+
149
+ ### Extensible Classes
150
+
151
+ Most bindings generated from `shacl2code` are "closed" in that they do not
152
+ allow extra properties to be added to object outside of what is specified in
153
+ model. This ensures that field name typos and other unintended properties are
154
+ not added to an object. However, in some cases a class may be specifically
155
+ intended to be extended such that arbitrary fields can be added to it, which
156
+ can be done using the `isExtensible` property. This is a boolean property that
157
+ indicates if a class can be extended, and defaults to `false`. For example, the
158
+ following turtle will declare a class as extensible:
159
+
160
+ ```ttl
161
+
162
+ <MyClass> a owl:Class, sh:NodeShape ;
163
+ sh-to-code:isExtensible true
164
+ .
165
+ ```
166
+
167
+ The `isExtensible` property is _not_ inherited by derived classes, meaning it
168
+ is possible to have a class derived from `MyClass` which is itself not
169
+ extensible.
170
+
171
+ The mechanism for dealing with extensible classes will vary between the
172
+ different bindings, but in general it means that they will not be very picky
173
+ about object types and properties in any location where an extensible class is
174
+ allowed.
175
+
176
+ **Note**: You may want to be careful about where and how many extensible
177
+ classes are allowed in your model. If there are too many and they are allowed
178
+ anywhere, it may mean that typos in object types (e.g. `@type` in JSON-LD) are
179
+ not caught by validation as they will have to be assumed to be a derived class
180
+ from an extensible type.
181
+
182
+ ### Abstract Classes
183
+
184
+ By default, classes generated by `shacl2code` are all instantiable (i.e. they
185
+ can be created). In some instances, it may be desirable to declare a class as
186
+ abstract (meaning that it cannot be instantiated, but non-abstract derived
187
+ classes can). This can be done with the boolean `isAbstract` property. For
188
+ example, the following turtle will declare a class as abstract:
189
+
190
+ ```ttl
191
+
192
+ <MyClass> a owl:Class, sh:NodeShape ;
193
+ sh-to-code:isAbstract true
194
+ .
195
+ ```
196
+
197
+ The `isAbstract` property is _not_ inherited by derived classes, so any derived
198
+ classes are automatically concrete unless they indicate otherwise.
199
+
200
+ Note that for compatibility reasons, it is also possible to define a class as
201
+ abstract by declaring it to be of type: `http://spdx.invalid./AbstractClass`,
202
+ but this is not preferred.
203
+
204
+ [pytest]: https://www.pytest.org
205
+ [pytest-cov]: https://pytest-cov.readthedocs.io/en/latest/
@@ -0,0 +1,174 @@
1
+ # Convert SHACL Model to code bindings
2
+ [![Coverage Report](https://raw.githubusercontent.com/JPEWdev/shacl2code/python-coverage-comment-action-data/badge.svg)](https://htmlpreview.github.io/?https://github.com/JPEWdev/shacl2code/blob/python-coverage-comment-action-data/htmlcov/index.html)
3
+
4
+ This tool can be used to convert a SHACL model into various code bindings
5
+
6
+ ## Installation
7
+
8
+ `shacl2code` can be installed using pip:
9
+
10
+ ```shell
11
+ python3 -m pip install shacl2code
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ `shacl2code` can generate bindings from either a local file:
17
+ ```shell
18
+ shacl2code generate -i model.jsonld python -o out.py
19
+ ```
20
+ Or from a URL:
21
+ ```shell
22
+ shacl2code generate -i https://example.com/rdf/model.jsonld python -o out.py
23
+ ```
24
+ Or from stdin:
25
+ ```shell
26
+ cat model.jsonld | shacl2code generate -i - python -o - > out.py
27
+ ```
28
+
29
+ For more information, run:
30
+ ```shell
31
+ shacl2code --help
32
+ ```
33
+
34
+ The available language bindings can be viewed by running:
35
+ ```shell
36
+ shacl2code list
37
+ ```
38
+
39
+ ## Developing
40
+
41
+ Developing on `shacl2code` is best done using a virtual environment. You can
42
+ configure one and install shacl2code in editable mode with all necessary
43
+ development dependencies by running:
44
+
45
+ ```shell
46
+ python3 -m venv .venv
47
+ . .venv/bin/activate
48
+ pip install -e ".[dev]"
49
+ ```
50
+
51
+ ## Testing
52
+
53
+ `shacl2code` has a test suite written in [pytest][pytest]. To run it, setup a
54
+ virtual environment as shown above, then run:
55
+ ```shell
56
+ pytest
57
+ ```
58
+
59
+ In addition to the test results, a test coverage report will also be generated
60
+ using [pytest-cov][pytest-cov]
61
+
62
+
63
+ ## Custom Annotations
64
+
65
+ `shacl2code` supports a number of custom annotations that can be specified in a
66
+ SHACL model to give hints about the generated code. All of these annotations
67
+ live in the `https://jpewdev.github.io/shacl2code/schema#` namespace, and
68
+ commonly are given the `sh-to-code` prefix to make it easier to reference them.
69
+ For example, in Turtle one would add the prefix mapping:
70
+
71
+ ```ttl
72
+ @prefix sh-to-code: <https://jpewdev.github.io/shacl2code/schema#> .
73
+ ```
74
+
75
+ ### ID Property Name
76
+
77
+ The `idPropertyName` annotation allows a class to specify what the name of the
78
+ "property" that specifies the RDF subject of an object is for serializations
79
+ that support it. For example, in JSON-LD, the `@id` property indicates the
80
+ subject in RDF. If you wanted to alias the `@id` property to another name, the
81
+ `idPropertyName` annotation will let you do this. For example, the following
82
+ turtle will use `MyId` instead of `@id` when writing JSON-LD bindings:
83
+
84
+ ```ttl
85
+ <MyClass> a owl:Class, sh:NodeShape ;
86
+ sh-to-code:idPropertyName "MyId"
87
+ .
88
+ ```
89
+
90
+ When doing this, the class would then look like this in JSON-LD:
91
+ ```json
92
+ {
93
+ "@type": "MyClass",
94
+ "MyId": "http://example.com/id"
95
+ }
96
+ ```
97
+
98
+ The `idProperyName` annotation is inherited by derived classes, so for example
99
+ any class that derived from `MyClass` would also use `MyId` as the subject
100
+ property.
101
+
102
+ **Note:** This only specifies what the name of the field should be in generated
103
+ bindings and has no bearing on how an RDF parser would interpret the property.
104
+ In order to still be parsed by RDF, you would also need context file that maps
105
+ `MyId` to `@id`, for example:
106
+
107
+ ```json
108
+ {
109
+ "@context": {
110
+ "MyId": "@id"
111
+ }
112
+ }
113
+ ```
114
+
115
+ `shacl2code` doesn't do this for you, nor does it validate that you have done
116
+ it.
117
+
118
+ ### Extensible Classes
119
+
120
+ Most bindings generated from `shacl2code` are "closed" in that they do not
121
+ allow extra properties to be added to object outside of what is specified in
122
+ model. This ensures that field name typos and other unintended properties are
123
+ not added to an object. However, in some cases a class may be specifically
124
+ intended to be extended such that arbitrary fields can be added to it, which
125
+ can be done using the `isExtensible` property. This is a boolean property that
126
+ indicates if a class can be extended, and defaults to `false`. For example, the
127
+ following turtle will declare a class as extensible:
128
+
129
+ ```ttl
130
+
131
+ <MyClass> a owl:Class, sh:NodeShape ;
132
+ sh-to-code:isExtensible true
133
+ .
134
+ ```
135
+
136
+ The `isExtensible` property is _not_ inherited by derived classes, meaning it
137
+ is possible to have a class derived from `MyClass` which is itself not
138
+ extensible.
139
+
140
+ The mechanism for dealing with extensible classes will vary between the
141
+ different bindings, but in general it means that they will not be very picky
142
+ about object types and properties in any location where an extensible class is
143
+ allowed.
144
+
145
+ **Note**: You may want to be careful about where and how many extensible
146
+ classes are allowed in your model. If there are too many and they are allowed
147
+ anywhere, it may mean that typos in object types (e.g. `@type` in JSON-LD) are
148
+ not caught by validation as they will have to be assumed to be a derived class
149
+ from an extensible type.
150
+
151
+ ### Abstract Classes
152
+
153
+ By default, classes generated by `shacl2code` are all instantiable (i.e. they
154
+ can be created). In some instances, it may be desirable to declare a class as
155
+ abstract (meaning that it cannot be instantiated, but non-abstract derived
156
+ classes can). This can be done with the boolean `isAbstract` property. For
157
+ example, the following turtle will declare a class as abstract:
158
+
159
+ ```ttl
160
+
161
+ <MyClass> a owl:Class, sh:NodeShape ;
162
+ sh-to-code:isAbstract true
163
+ .
164
+ ```
165
+
166
+ The `isAbstract` property is _not_ inherited by derived classes, so any derived
167
+ classes are automatically concrete unless they indicate otherwise.
168
+
169
+ Note that for compatibility reasons, it is also possible to define a class as
170
+ abstract by declaring it to be of type: `http://spdx.invalid./AbstractClass`,
171
+ but this is not preferred.
172
+
173
+ [pytest]: https://www.pytest.org
174
+ [pytest-cov]: https://pytest-cov.readthedocs.io/en/latest/
@@ -6,7 +6,7 @@ dependencies = [
6
6
  "jinja2 >= 3.1.2",
7
7
  "rdflib >= 7.0.0",
8
8
  ]
9
- required-python = ">= 3.8"
9
+ requires-python = ">= 3.8"
10
10
  authors = [
11
11
  {name = "Joshua Watt", email = "JPEWhacker@gmail.com"},
12
12
  ]
@@ -31,15 +31,15 @@ class BasicJinjaRender(object):
31
31
  """
32
32
  Common Jinja Template Renderer
33
33
 
34
- Renderers that only use a single Jinja file can derive from this class. The
35
- class should set the class member variable `TEMPLATE` to indicate which
36
- template file to use. For example:
34
+ Renderers that only use a single Jinja file can derive from this class. For
35
+ example:
37
36
 
38
37
  @language("my-lang")
39
38
  class MyRendered(BasicJinjaRenderer):
40
39
  HELP = "Generates my-lang bindings"
41
- TEMPLATE = "my-lang.j2"
42
40
 
41
+ def __init__(self, args):
42
+ super().__init__(args, PATH / TO / TEMPLATE)
43
43
  """
44
44
 
45
45
  def __init__(self, args, template):
@@ -62,22 +62,31 @@ class BasicJinjaRender(object):
62
62
  def get_extra_env(self):
63
63
  return {}
64
64
 
65
- def output(self, model):
65
+ def render(self, template, output, *, extra_env={}, render_args={}):
66
66
  def abort_helper(msg):
67
67
  raise TemplateRuntimeError(msg)
68
68
 
69
- def get_all_derived(cls):
70
- nonlocal classes
69
+ env = Environment(loader=FileSystemLoader([template.parent, THIS_DIR.parent]))
70
+ for k, v in extra_env.items():
71
+ env.globals[k] = v
72
+ env.globals["abort"] = abort_helper
73
+ env.globals["SHACL2CODE"] = SHACL2CODE
74
+ env.globals["SH"] = SH
75
+ template = env.get_template(template.name)
71
76
 
72
- def _recurse(cls):
73
- result = set(cls.derived_ids)
74
- for r in cls.derived_ids:
75
- result |= _recurse(classes.get(r))
76
- return result
77
+ render = template.render(
78
+ disclaimer=f"This file was automatically generated by {os.path.basename(sys.argv[0])}. DO NOT MANUALLY MODIFY IT",
79
+ **render_args,
80
+ )
77
81
 
78
- d = list(_recurse(cls))
79
- d.sort()
80
- return d
82
+ output.write(render)
83
+ if not render[-1] == "\n":
84
+ output.write("\n")
85
+
86
+ def output(self, model):
87
+ """
88
+ Render the provided model
89
+ """
81
90
 
82
91
  class ObjectList(object):
83
92
  def __init__(self, objs):
@@ -92,29 +101,44 @@ class BasicJinjaRender(object):
92
101
  return o
93
102
  raise KeyError(f"Object with ID {_id} not found")
94
103
 
95
- env = Environment(
96
- loader=FileSystemLoader([self.__template.parent, THIS_DIR.parent])
97
- )
98
- for k, v in self.get_extra_env().items():
99
- env.globals[k] = v
100
- env.globals["abort"] = abort_helper
101
- env.globals["get_all_derived"] = get_all_derived
102
- env.globals["SHACL2CODE"] = SHACL2CODE
103
- env.globals["SH"] = SH
104
- template = env.get_template(self.__template.name)
104
+ def get_all_derived(cls):
105
+ nonlocal classes
106
+
107
+ def _recurse(cls):
108
+ result = set(cls.derived_ids)
109
+ for r in cls.derived_ids:
110
+ result |= _recurse(classes.get(r))
111
+ return result
112
+
113
+ d = list(_recurse(cls))
114
+ d.sort()
115
+ return d
105
116
 
106
117
  classes = ObjectList(model.classes)
118
+ concrete_classes = ObjectList(
119
+ list(c for c in model.classes if not c.is_abstract)
120
+ )
121
+ abstract_classes = ObjectList(list(c for c in model.classes if c.is_abstract))
107
122
  enums = ObjectList(model.enums)
108
123
 
109
- render = template.render(
110
- disclaimer=f"This file was automatically generated by {os.path.basename(sys.argv[0])}. DO NOT MANUALLY MODIFY IT",
111
- enums=enums,
112
- classes=classes,
113
- context=model.context,
124
+ render_args = {
125
+ "classes": classes,
126
+ "concrete_classes": concrete_classes,
127
+ "abstract_classes": abstract_classes,
128
+ "enums": enums,
129
+ "context": model.context,
114
130
  **self.get_additional_render_args(),
115
- )
131
+ }
132
+
133
+ env = {
134
+ "get_all_derived": get_all_derived,
135
+ **self.get_extra_env(),
136
+ }
116
137
 
117
138
  with self.__output.open() as f:
118
- f.write(render)
119
- if not render[-1] == "\n":
120
- f.write("\n")
139
+ self.render(
140
+ self.__template,
141
+ f,
142
+ extra_env=env,
143
+ render_args=render_args,
144
+ )
@@ -10,8 +10,8 @@ import re
10
10
  import keyword
11
11
 
12
12
 
13
- def varname(name):
14
- name = str(name).replace("@", "_")
13
+ def varname(*name):
14
+ name = str("_".join(name)).replace("@", "_")
15
15
  name = re.sub(r"[^a-zA-Z0-9_]", "", name)
16
16
  while keyword.iskeyword(name):
17
17
  name = name + "_"
@@ -10,7 +10,8 @@ import re
10
10
  import keyword
11
11
 
12
12
 
13
- def varname(name):
13
+ def varname(*name):
14
+ name = "_".join(name)
14
15
  # Any invalid characters at the beginning of the name are removed (except
15
16
  # "@")
16
17
  name = re.sub(r"^[^a-zA-Z0-9_@]*", "", name)