shacl2code 1.0.0__tar.gz → 1.1.0__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 (137) hide show
  1. {shacl2code-1.0.0 → shacl2code-1.1.0}/PKG-INFO +3 -2
  2. {shacl2code-1.0.0 → shacl2code-1.1.0}/pyproject.toml +18 -1
  3. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/common.py +1 -1
  4. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/python.py +55 -6
  5. shacl2code-1.1.0/src/shacl2code/lang/templates/python/__init__.py.j2 +15 -0
  6. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/model.py.j2 +115 -74
  7. shacl2code-1.1.0/src/shacl2code/lang/templates/python/model.pyi.j2 +493 -0
  8. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/model.py +9 -9
  9. shacl2code-1.1.0/src/shacl2code/util.py +31 -0
  10. shacl2code-1.1.0/src/shacl2code/version.py +1 -0
  11. shacl2code-1.1.0/tests/data/reserved-words.ttl +34 -0
  12. shacl2code-1.1.0/tests/test_convert_version_string.py +33 -0
  13. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_python.py +321 -49
  14. shacl2code-1.0.0/src/shacl2code/lang/templates/python/__init__.py.j2 +0 -8
  15. shacl2code-1.0.0/src/shacl2code/lang/templates/python/stub.pyi.j2 +0 -230
  16. shacl2code-1.0.0/src/shacl2code/version.py +0 -1
  17. {shacl2code-1.0.0 → shacl2code-1.1.0}/.clang-format +0 -0
  18. {shacl2code-1.0.0 → shacl2code-1.1.0}/.flake8 +0 -0
  19. {shacl2code-1.0.0 → shacl2code-1.1.0}/.github/dependabot.yml +0 -0
  20. {shacl2code-1.0.0 → shacl2code-1.1.0}/.github/workflows/coverage-generate.yaml +0 -0
  21. {shacl2code-1.0.0 → shacl2code-1.1.0}/.github/workflows/coverage-report.yaml +0 -0
  22. {shacl2code-1.0.0 → shacl2code-1.1.0}/.github/workflows/publish.yaml +0 -0
  23. {shacl2code-1.0.0 → shacl2code-1.1.0}/.github/workflows/scorecard.yml +0 -0
  24. {shacl2code-1.0.0 → shacl2code-1.1.0}/.github/workflows/test.yaml +0 -0
  25. {shacl2code-1.0.0 → shacl2code-1.1.0}/.gitignore +0 -0
  26. {shacl2code-1.0.0 → shacl2code-1.1.0}/LICENSE +0 -0
  27. {shacl2code-1.0.0 → shacl2code-1.1.0}/README.md +0 -0
  28. {shacl2code-1.0.0 → shacl2code-1.1.0}/SECURITY.md +0 -0
  29. {shacl2code-1.0.0 → shacl2code-1.1.0}/scripts/format.sh +0 -0
  30. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/__init__.py +0 -0
  31. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/__main__.py +0 -0
  32. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/context.py +0 -0
  33. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/__init__.py +0 -0
  34. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/cpp.py +0 -0
  35. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/golang.py +0 -0
  36. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/jinja.py +0 -0
  37. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/jsonschema.py +0 -0
  38. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/lang.py +0 -0
  39. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/sources/cpp/json.hpp +0 -0
  40. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/Doxyfile.j2 +0 -0
  41. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/Makefile.j2 +0 -0
  42. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/api.hpp.j2 +0 -0
  43. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/datetime.cpp.j2 +0 -0
  44. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/datetime.hpp.j2 +0 -0
  45. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/decode.cpp.j2 +0 -0
  46. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/decode.hpp.j2 +0 -0
  47. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/encode.cpp.j2 +0 -0
  48. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/encode.hpp.j2 +0 -0
  49. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/errorhandler.cpp.j2 +0 -0
  50. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/errorhandler.hpp.j2 +0 -0
  51. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/exceptions.hpp.j2 +0 -0
  52. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/extensible.cpp.j2 +0 -0
  53. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/extensible.hpp.j2 +0 -0
  54. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/header.j2 +0 -0
  55. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/jsonld-header.j2 +0 -0
  56. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/jsonld-source.j2 +0 -0
  57. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/link.cpp.j2 +0 -0
  58. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/link.hpp.j2 +0 -0
  59. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/namedindividual.cpp.j2 +0 -0
  60. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/namedindividual.hpp.j2 +0 -0
  61. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/object.cpp.j2 +0 -0
  62. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/object.hpp.j2 +0 -0
  63. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectpath.cpp.j2 +0 -0
  64. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectpath.hpp.j2 +0 -0
  65. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectset.cpp.j2 +0 -0
  66. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectset.hpp.j2 +0 -0
  67. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/pkgconfig.pc.j2 +0 -0
  68. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/prop.hpp.j2 +0 -0
  69. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/property.cpp.j2 +0 -0
  70. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/property.hpp.j2 +0 -0
  71. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/propertyvalue.cpp.j2 +0 -0
  72. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/propertyvalue.hpp.j2 +0 -0
  73. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/ref.hpp.j2 +0 -0
  74. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/refproperty.cpp.j2 +0 -0
  75. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/refproperty.hpp.j2 +0 -0
  76. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/source.j2 +0 -0
  77. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/type.cpp.j2 +0 -0
  78. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/type.hpp.j2 +0 -0
  79. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/util.cpp.j2 +0 -0
  80. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/util.hpp.j2 +0 -0
  81. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/validate.j2 +0 -0
  82. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/walk.hpp.j2 +0 -0
  83. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/classes.go.j2 +0 -0
  84. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/decode.go.j2 +0 -0
  85. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/encode.go.j2 +0 -0
  86. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/errorhandler.go.j2 +0 -0
  87. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/errors.go.j2 +0 -0
  88. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/extensible.go.j2 +0 -0
  89. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/linkstate.go.j2 +0 -0
  90. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/listproperty.go.j2 +0 -0
  91. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/optional.go.j2 +0 -0
  92. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/path.go.j2 +0 -0
  93. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/property.go.j2 +0 -0
  94. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/ref.go.j2 +0 -0
  95. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/reflistproperty.go.j2 +0 -0
  96. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/refproperty.go.j2 +0 -0
  97. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/shaclobject.go.j2 +0 -0
  98. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/shaclobjectset.go.j2 +0 -0
  99. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/shacltype.go.j2 +0 -0
  100. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/util.go.j2 +0 -0
  101. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/validator.go.j2 +0 -0
  102. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/jsonschema.j2 +0 -0
  103. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/__main__.py.j2 +0 -0
  104. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/cmd.py.j2 +0 -0
  105. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/main.py +0 -0
  106. {shacl2code-1.0.0 → shacl2code-1.1.0}/src/shacl2code/urlcontext.py +0 -0
  107. {shacl2code-1.0.0 → shacl2code-1.1.0}/testfixtures/testfixtures/__init__.py +0 -0
  108. {shacl2code-1.0.0 → shacl2code-1.1.0}/testfixtures/testfixtures/httpserver.py +0 -0
  109. {shacl2code-1.0.0 → shacl2code-1.1.0}/testfixtures/testfixtures/jsonvalidation.py +0 -0
  110. {shacl2code-1.0.0 → shacl2code-1.1.0}/testfixtures/testfixtures/timetests.py +0 -0
  111. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/conftest.py +0 -0
  112. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/abort.j2 +0 -0
  113. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/bad-id.j2 +0 -0
  114. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/bad-node-kind.ttl +0 -0
  115. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/bad-pattern-class.ttl +0 -0
  116. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/bad-pattern-integer.ttl +0 -0
  117. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/bad-prop-type.ttl +0 -0
  118. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/bad-reference.jsonld +0 -0
  119. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/context-url.j2 +0 -0
  120. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/context.j2 +0 -0
  121. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/links.json +0 -0
  122. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/missing-range.ttl +0 -0
  123. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/model/test-context.json +0 -0
  124. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/model/test.ttl +0 -0
  125. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/python/bad-object-type-inline.json +0 -0
  126. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/python/bad-object-type-ref-after.json +0 -0
  127. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/python/bad-object-type-ref-before.json +0 -0
  128. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/raw.j2 +0 -0
  129. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/data/roundtrip.json +0 -0
  130. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_cli.py +0 -0
  131. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_common_jinja.py +0 -0
  132. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_common_prefix.py +0 -0
  133. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_context.py +0 -0
  134. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_cpp.py +0 -0
  135. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_golang.py +0 -0
  136. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_jsonschema.py +0 -0
  137. {shacl2code-1.0.0 → shacl2code-1.1.0}/tests/test_model_source.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shacl2code
3
- Version: 1.0.0
3
+ Version: 1.1.0
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,13 +25,14 @@ Requires-Dist: rdflib>=7.0.0
25
25
  Provides-Extra: dev
26
26
  Requires-Dist: flake8-import-order>=0.19.2; extra == 'dev'
27
27
  Requires-Dist: flake8>=7.0.0; extra == 'dev'
28
- Requires-Dist: jsonschema>=4.21.1; extra == 'dev'
28
+ Requires-Dist: jsonschema>=4.23.0; extra == 'dev'
29
29
  Requires-Dist: mypy>=1.14.1; extra == 'dev'
30
30
  Requires-Dist: pyrefly>=0.55.0; extra == 'dev'
31
31
  Requires-Dist: pyright>=1.1.403; extra == 'dev'
32
32
  Requires-Dist: pyshacl>=0.25.0; extra == 'dev'
33
33
  Requires-Dist: pytest-cov>=4.1; extra == 'dev'
34
34
  Requires-Dist: pytest>=7.4; extra == 'dev'
35
+ Requires-Dist: types-jsonschema>=4.23.0; extra == 'dev'
35
36
  Description-Content-Type: text/markdown
36
37
 
37
38
  # Convert SHACL Model to code bindings
@@ -30,13 +30,14 @@ license = "MIT"
30
30
  dev = [
31
31
  "flake8 >= 7.0.0",
32
32
  "flake8-import-order >= 0.19.2",
33
- "jsonschema >= 4.21.1",
33
+ "jsonschema >= 4.23.0", # latest version for Python 3.8 is 4.23.x
34
34
  "mypy >= 1.14.1", # latest version for Python 3.8 is 1.14.x
35
35
  "pyrefly >= 0.55.0",
36
36
  "pyright >= 1.1.403",
37
37
  "pyshacl >= 0.25.0",
38
38
  "pytest >= 7.4",
39
39
  "pytest-cov >= 4.1",
40
+ "types-jsonschema >= 4.23.0",
40
41
  ]
41
42
 
42
43
  [project.urls]
@@ -54,6 +55,22 @@ build-backend = "hatchling.build"
54
55
  [tool.hatch.version]
55
56
  path = "src/shacl2code/version.py"
56
57
 
58
+ [tool.mypy]
59
+ disallow_incomplete_defs = true
60
+ # disallow_untyped_defs = true
61
+ extra_checks = true
62
+ mypy_path = ["testfixtures"]
63
+ pretty = true
64
+ show_column_numbers = true
65
+ # strict = true
66
+ strict_bytes = true
67
+ # strict_equality = true
68
+ # strict_equality_for_none = true
69
+ warn_redundant_casts = true
70
+ # warn_return_any = true
71
+ # warn_unreachable = true
72
+ # warn_unused_ignores = true
73
+
57
74
  [tool.pytest.ini_options]
58
75
  addopts = [
59
76
  "--import-mode=importlib",
@@ -57,7 +57,7 @@ class JinjaTemplateRender(object):
57
57
  if render_args is None:
58
58
  render_args = {} # pragma: no cover
59
59
 
60
- def abort_helper(msg: str):
60
+ def abort_helper(msg: str) -> None:
61
61
  raise TemplateRuntimeError(msg)
62
62
 
63
63
  env = Environment(loader=FileSystemLoader([template.parent, THIS_DIR.parent]))
@@ -9,6 +9,7 @@ from pathlib import Path
9
9
 
10
10
  from .common import JinjaTemplateRender
11
11
  from .lang import TEMPLATE_DIR, language
12
+ from ..util import convert_version_string
12
13
 
13
14
  DATATYPE_CLASSES = {
14
15
  "http://www.w3.org/2001/XMLSchema#string": "StringProp",
@@ -35,6 +36,30 @@ DATATYPE_PYTHON_TYPES = {
35
36
  }
36
37
 
37
38
 
39
+ SHACLOBJECT_RESERVED_WORDS = {
40
+ "AUTO_NAMED_INDIVIDUALS",
41
+ "CLASSES",
42
+ "COMPACT_TYPE",
43
+ "ID_ALIAS",
44
+ "IS_ABSTRACT",
45
+ "IS_DEPRECATED",
46
+ "NAMED_INDIVIDUALS",
47
+ "NODE_KIND",
48
+ "PROPERTIES",
49
+ "TYPE",
50
+ "decode",
51
+ "encode",
52
+ "get_compact_type",
53
+ "get_id",
54
+ "get_type",
55
+ "iter_objects",
56
+ "link_helper",
57
+ "property_keys",
58
+ "set_id",
59
+ "walk",
60
+ }
61
+
62
+
38
63
  def varname(*name):
39
64
  """Make a valid Python variable name."""
40
65
  name = "_".join(name)
@@ -44,8 +69,8 @@ def varname(*name):
44
69
  name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
45
70
  # Consolidate runs of "_" to a single one
46
71
  name = re.sub(r"__+", "_", name)
47
- # Add a _ to anything that is a python keyword
48
- while keyword.iskeyword(name):
72
+ # Append '_' to avoid collisions with Python or SHACLObject keywords
73
+ while keyword.iskeyword(name) or name in SHACLOBJECT_RESERVED_WORDS:
49
74
  name = name + "_"
50
75
  return name
51
76
 
@@ -58,16 +83,20 @@ class PythonRender(JinjaTemplateRender):
58
83
 
59
84
  FILES = (
60
85
  "__init__.py",
61
- "__main__.py",
62
- "cmd.py",
63
86
  "model.py",
64
- "stub.pyi",
87
+ "model.pyi",
65
88
  )
66
89
 
67
90
  def __init__(self, args):
68
91
  super().__init__(args)
69
92
  self.__output = args.output
70
93
  self.__use_slots = args.use_slots
94
+ self.__include_main = args.include_main == "yes"
95
+ self.__version_str = args.version
96
+ if args.version:
97
+ self.__version = repr(convert_version_string(args.version))
98
+ else:
99
+ self.__version = ""
71
100
 
72
101
  @classmethod
73
102
  def get_arguments(cls, parser):
@@ -78,6 +107,12 @@ class PythonRender(JinjaTemplateRender):
78
107
  help="Output directory",
79
108
  required=True,
80
109
  )
110
+ parser.add_argument(
111
+ "--include-main",
112
+ choices=("yes", "no"),
113
+ default="yes",
114
+ help="Generate a main function for the module. Default is '%(default)s'",
115
+ )
81
116
  parser.add_argument(
82
117
  "--use-slots",
83
118
  choices=("auto", "yes", "no"),
@@ -87,13 +122,24 @@ class PythonRender(JinjaTemplateRender):
87
122
  "Slots prevents multiple inheritance. Default is %(default)s"
88
123
  ),
89
124
  )
125
+ parser.add_argument(
126
+ "--version",
127
+ help="Specify model version",
128
+ )
90
129
 
91
130
  def get_outputs(self):
92
131
  t = TEMPLATE_DIR / "python"
93
132
  self.__output.mkdir(parents=True, exist_ok=True)
94
133
 
134
+ def get_file(name):
135
+ return self.__output / name, t / (name + ".j2"), {}
136
+
95
137
  for s in self.FILES:
96
- yield self.__output / s, t / (s + ".j2"), {}
138
+ yield get_file(s)
139
+
140
+ if self.__include_main:
141
+ yield get_file("cmd.py")
142
+ yield get_file("__main__.py")
97
143
 
98
144
  def get_extra_env(self):
99
145
  return {
@@ -111,4 +157,7 @@ class PythonRender(JinjaTemplateRender):
111
157
  use_slots = False
112
158
  return {
113
159
  "use_slots": use_slots,
160
+ "include_main": self.__include_main,
161
+ "version_str": self.__version_str,
162
+ "version": self.__version,
114
163
  }
@@ -0,0 +1,15 @@
1
+ #! /usr/bin/env python3
2
+ #
3
+ # {{ disclaimer }}
4
+ #
5
+ # SPDX-License-Identifier: {{ spdx_license }}
6
+
7
+ from .model import * # noqa: F401, F403
8
+
9
+ # fmt: off
10
+ """Format Guard{{ '"' }}{{ '"' }}{{ '"' }}
11
+ {%- if include_main %}
12
+ from .cmd import main # noqa: F401, I100, I202
13
+ {%- endif %}
14
+ {{ '"' }}{{ '"' }}{{ '"' }}Format Guard"""
15
+ # fmt on
@@ -21,22 +21,26 @@ from contextlib import contextmanager
21
21
  from dataclasses import dataclass
22
22
  from datetime import datetime, timedelta, timezone
23
23
  from enum import Enum
24
- from typing import Generic, TYPE_CHECKING, TypeVar, Union, cast, overload
25
-
26
- if TYPE_CHECKING:
27
- from typing import (
28
- Any,
29
- BinaryIO,
30
- Callable,
31
- Dict,
32
- Iterable,
33
- Iterator,
34
- List,
35
- Optional,
36
- Set,
37
- Tuple,
38
- Type,
39
- )
24
+ from typing import (
25
+ Any,
26
+ BinaryIO,
27
+ Callable,
28
+ ClassVar,
29
+ Dict,
30
+ Generic,
31
+ Iterable,
32
+ Iterator,
33
+ List,
34
+ Optional,
35
+ Set,
36
+ TYPE_CHECKING,
37
+ Tuple,
38
+ Type,
39
+ TypeVar,
40
+ Union,
41
+ cast,
42
+ overload,
43
+ )
40
44
 
41
45
  T_PropV = TypeVar("T_PropV")
42
46
 
@@ -60,7 +64,7 @@ class Property(ABC, Generic[T_PropV]):
60
64
  class
61
65
  """
62
66
 
63
- VALID_TYPES: Union[Type[Any], Tuple[Type[Any], ...]] = ()
67
+ VALID_TYPES: ClassVar[Union[Type[Any], Tuple[Type[Any], ...]]] = ()
64
68
 
65
69
  __slots__ = ("pattern",)
66
70
 
@@ -713,6 +717,11 @@ def is_blank_node(s: Any) -> bool:
713
717
  # fmt: off
714
718
  """Format Guard{{ '"' }}{{ '"' }}{{ '"' }}
715
719
  _USE_SLOTS = {{ use_slots }}
720
+ {% if version_str %}
721
+ VERSION_STRING = "{{ version_str }}"
722
+ {% endif %}{% if version %}
723
+ VERSION = {{ version }}
724
+ {% endif %}
716
725
  {{ '"' }}{{ '"' }}{{ '"' }}Format Guard"""
717
726
  # fmt: on
718
727
 
@@ -740,7 +749,7 @@ class SHACLObjectMeta(type):
740
749
  """Metaclass for SHACLObject that processes PROPERTIES, slots, and named individuals at class creation time."""
741
750
 
742
751
  def __new__(cls, name, bases, attrs):
743
- def check_base_prop(name):
752
+ def check_base_prop(name: str) -> bool:
744
753
  return any(hasattr(b, name) or name in b._EXTRA_SLOTS for b in bases)
745
754
 
746
755
  is_base = name == "SHACLObject"
@@ -755,7 +764,8 @@ class SHACLObjectMeta(type):
755
764
  py_properties = {}
756
765
  iri_properties = {}
757
766
  compact_properties = {}
758
- for p in attrs.get("PROPERTIES", []):
767
+ props: List[ClassProp] = attrs.get("PROPERTIES", [])
768
+ for p in props:
759
769
  if not is_base:
760
770
  for b in bases:
761
771
  if p.pyname in b._OBJ_PY_PROPS or p.pyname in b._EXTRA_SLOTS:
@@ -879,65 +889,65 @@ T_SHACLObject = TypeVar("T_SHACLObject", bound="SHACLObject")
879
889
  class SHACLObject(metaclass=SHACLObjectMeta):
880
890
  """Base class for all SHACL-defined objects, providing property access, encoding, and decoding."""
881
891
 
882
- CLASSES: Dict[str, Type] = {}
892
+ CLASSES: ClassVar[Dict[str, Type[SHACLObject]]] = {}
883
893
 
884
894
  # Class properties that can be set in derived classes:
885
895
 
886
896
  # A dictionary of Named Individuals. The key is the python name of the
887
897
  # individual and the value is the IRI of the individual (not inherited)
888
- NAMED_INDIVIDUALS: Dict[str, str] = {}
898
+ NAMED_INDIVIDUALS: ClassVar[Dict[str, str]] = {}
889
899
 
890
900
  # If True, class member variables will be automatically created for the class
891
901
  # from the NAMED_INDIVIDUALS dictionary
892
902
  # NOTE: This variable is not present in the class
893
- AUTO_NAMED_INDIVIDUALS: bool = True
903
+ AUTO_NAMED_INDIVIDUALS: ClassVar[bool] = True
894
904
 
895
905
  # The type of ID the object is allowed to have (inherited from parent)
896
- NODE_KIND = NodeKind.BlankNodeOrIRI
906
+ NODE_KIND: ClassVar[NodeKind] = NodeKind.BlankNodeOrIRI
897
907
 
898
908
  # An alternate IRI for the ID property, besides "@id" (inherited from
899
909
  # parent)
900
- ID_ALIAS: Optional[str] = None
910
+ ID_ALIAS: ClassVar[Optional[str]] = None
901
911
 
902
912
  # If True, this class is abstract and cannot be implemented (not inherited
903
913
  # from parent)
904
- IS_ABSTRACT: bool = True
914
+ IS_ABSTRACT: ClassVar[bool] = True
905
915
 
906
916
  # If True, this class is deprecated and will raise a warning when created
907
917
  # (not inherited from parent)
908
- IS_DEPRECATED: bool = False
918
+ IS_DEPRECATED: ClassVar[bool] = False
909
919
 
910
920
  # The fully qualified Type IRI for the object. Required
911
921
  # NOTE: This variable is not present in the class. Use get_type() instead
912
- TYPE: str = ""
922
+ TYPE: ClassVar[str] = ""
913
923
 
914
924
  # The compacted Type IRI of the object. Must match how the TYPE property
915
925
  # would be compacted by the context. Not inherited from parent
916
926
  # NOTE: This variable is not present in the class. Use get_compact_type()
917
927
  # instead
918
- COMPACT_TYPE: Optional[str] = None
928
+ COMPACT_TYPE: ClassVar[Optional[str]] = None
919
929
 
920
930
  # A list of properties for this object. Each entry must be a ClassProp
921
931
  # object. Properties are automatically inherited from parent classes and do
922
932
  # not need to be re-listed here.
923
933
  # NOTE: The variable is not present in the class
924
- PROPERTIES = [
934
+ PROPERTIES: ClassVar[List[ClassProp]] = [
925
935
  ClassProp("_id", lambda: StringProp(), "@id"),
926
936
  ]
927
937
 
928
938
  # Internal variables
929
- _EXTRA_SLOTS: Union[Set[str], Tuple[str, ...]] = (
939
+ _EXTRA_SLOTS: ClassVar[Union[Set[str], Tuple[str, ...]]] = (
930
940
  "_extensible",
931
941
  "_metadata",
932
942
  "_birth_index",
933
943
  )
934
- _OBJ_PY_PROPS: Dict[str, ClassProp] = {}
935
- _OBJ_IRI_PROPS: Dict[str, ClassProp] = {}
936
- _OBJ_COMPACT_PROPS: Dict[str, ClassProp] = {}
937
- _NEEDS_REG: bool = True
938
- _next_birth_index: int = 0
939
- _TYPE: str
940
- _COMPACT_TYPE: Optional[str]
944
+ _OBJ_PY_PROPS: ClassVar[Dict[str, ClassProp]] = {}
945
+ _OBJ_IRI_PROPS: ClassVar[Dict[str, ClassProp]] = {}
946
+ _OBJ_COMPACT_PROPS: ClassVar[Dict[str, ClassProp]] = {}
947
+ _NEEDS_REG: ClassVar[bool] = True
948
+ _next_birth_index: ClassVar[int] = 0
949
+ _TYPE: ClassVar[str]
950
+ _COMPACT_TYPE: ClassVar[Optional[str]]
941
951
  _extensible: Dict[str, Any]
942
952
 
943
953
  # Instance variables
@@ -998,6 +1008,18 @@ class SHACLObject(metaclass=SHACLObjectMeta):
998
1008
  for k, v in kwargs.items():
999
1009
  setattr(self, k, v)
1000
1010
 
1011
+ def get_id(self) -> Optional[str]:
1012
+ """Return the IRI or blank node ID of this object, or None if not set."""
1013
+ return self._id
1014
+
1015
+ def set_id(self, value: Optional[str]) -> None:
1016
+ """Set the IRI or blank node ID of this object,
1017
+ or clear it if value is None."""
1018
+ if value is None:
1019
+ del self._id
1020
+ else:
1021
+ self._id = value
1022
+
1001
1023
  def get_type(self) -> str:
1002
1024
  """Return the fully qualified IRI type of this object."""
1003
1025
  return self._TYPE
@@ -1035,7 +1057,7 @@ class SHACLObject(metaclass=SHACLObjectMeta):
1035
1057
  )
1036
1058
  object.__setattr__(self, p.pyname, p.prop.set(value))
1037
1059
 
1038
- def __del(self, p: ClassProp):
1060
+ def __del(self, p: ClassProp) -> None:
1039
1061
  object.__setattr__(self, p.pyname, p.prop.init())
1040
1062
 
1041
1063
  def __get_attr(self, name: str) -> ClassProp:
@@ -1272,7 +1294,7 @@ class SHACLObject(metaclass=SHACLObjectMeta):
1272
1294
  ),
1273
1295
  )
1274
1296
 
1275
- def __str__(self):
1297
+ def __str__(self) -> str:
1276
1298
  parts = [
1277
1299
  f"{self.__class__.__name__}(",
1278
1300
  ]
@@ -1281,10 +1303,10 @@ class SHACLObject(metaclass=SHACLObjectMeta):
1281
1303
  parts.append(")")
1282
1304
  return "".join(parts)
1283
1305
 
1284
- def __hash__(self):
1306
+ def __hash__(self) -> int:
1285
1307
  return super().__hash__()
1286
1308
 
1287
- def __eq__(self, other):
1309
+ def __eq__(self, other: object) -> bool:
1288
1310
  return super().__eq__(other)
1289
1311
 
1290
1312
  @staticmethod
@@ -1347,7 +1369,7 @@ class SHACLExtensibleObject(SHACLObject):
1347
1369
  return obj
1348
1370
 
1349
1371
  def _decode_properties(self, decoder: Decoder, state: DecodeState) -> None:
1350
- def decode_value(d):
1372
+ def decode_value(d: Decoder) -> Any:
1351
1373
  if not d.is_list():
1352
1374
  return d.read_value()
1353
1375
 
@@ -1372,6 +1394,7 @@ class SHACLExtensibleObject(SHACLObject):
1372
1394
  )
1373
1395
 
1374
1396
  with decoder.read_property(key) as prop_d:
1397
+ assert prop_d is not None
1375
1398
  self._ext_data[expanded_key] = decode_value(prop_d)
1376
1399
 
1377
1400
  def _encode_properties(self, encoder: Encoder, state: EncodeState) -> None:
@@ -1519,7 +1542,7 @@ class SHACLObjectSet(object):
1519
1542
 
1520
1543
  self.obj_by_id[obj._id] = obj
1521
1544
 
1522
- def add(self, obj: SHACLObject) -> SHACLObject:
1545
+ def add(self, obj: T_SHACLObject) -> T_SHACLObject:
1523
1546
  """
1524
1547
  Add object to object set
1525
1548
 
@@ -1535,6 +1558,22 @@ class SHACLObjectSet(object):
1535
1558
  self.add_index(obj)
1536
1559
  return obj
1537
1560
 
1561
+ def remove(self, obj: SHACLObject) -> None:
1562
+ """
1563
+ Remove object from object set
1564
+
1565
+ Remove a SHACLObject from the object set and rebuild index.
1566
+
1567
+ NOTE: If the object is still referenced by another object in
1568
+ the object set, it will remain indexed
1569
+ """
1570
+ if not isinstance(obj, SHACLObject):
1571
+ raise TypeError("Object is not of type SHACLObject")
1572
+
1573
+ if obj in self.objects:
1574
+ self.objects.remove(obj)
1575
+ self.create_index()
1576
+
1538
1577
  def update(self, *others: Iterable[SHACLObject]) -> None:
1539
1578
  """
1540
1579
  Update object set adding all objects in each other iterable
@@ -1781,7 +1820,7 @@ class SHACLObjectSet(object):
1781
1820
  elif objects:
1782
1821
  objects[0].encode(encoder, state)
1783
1822
 
1784
- def decode(self, decoder: Decoder, state: DecodeState):
1823
+ def decode(self, decoder: Decoder, state: DecodeState) -> None:
1785
1824
  """Decode objects from the decoder and add them to this set, then link all references."""
1786
1825
  self.create_index()
1787
1826
  for obj_d in decoder.read_list():
@@ -1931,7 +1970,7 @@ class Decoder(ABC):
1931
1970
  raise NotImplementedError("Subclasses must implement read_iri method")
1932
1971
 
1933
1972
  @abstractmethod
1934
- def read_enum(self, e) -> Optional[str]:
1973
+ def read_enum(self, e: EnumProp) -> Optional[str]:
1935
1974
  """
1936
1975
  Consumes the next item as an Enum value string
1937
1976
 
@@ -2087,7 +2126,7 @@ class JSONLDDecoder(Decoder):
2087
2126
  return self.data
2088
2127
  return None
2089
2128
 
2090
- def read_enum(self, e) -> Optional[str]:
2129
+ def read_enum(self, e: EnumProp) -> Optional[str]:
2091
2130
  if isinstance(self.data, str):
2092
2131
  return self.data
2093
2132
  return None
@@ -2442,7 +2481,7 @@ class JSONLDSerializer(object):
2442
2481
  objectset: SHACLObjectSet,
2443
2482
  f: BinaryIO,
2444
2483
  force_at_graph: bool = False,
2445
- **kwargs,
2484
+ **kwargs: Any,
2446
2485
  ) -> str:
2447
2486
  """
2448
2487
  Write a SHACLObjectSet to a JSON LD file
@@ -2582,7 +2621,7 @@ class JSONLDInlineSerializer(object):
2582
2621
  objectset: SHACLObjectSet,
2583
2622
  f: BinaryIO,
2584
2623
  force_at_graph: bool = False,
2585
- ):
2624
+ ) -> str:
2586
2625
  """
2587
2626
  Write a SHACLObjectSet to a JSON LD file
2588
2627
 
@@ -2607,7 +2646,7 @@ def encode_context(encoder: Encoder, objectset: SHACLObjectSet) -> None:
2607
2646
  if not context:
2608
2647
  return
2609
2648
 
2610
- def write_context(e, ctx):
2649
+ def write_context(e: Encoder, ctx: Union[str, Dict[str, str]]) -> None:
2611
2650
  if isinstance(ctx, str):
2612
2651
  e.write_string(ctx)
2613
2652
  else:
@@ -2627,7 +2666,7 @@ def encode_context(encoder: Encoder, objectset: SHACLObjectSet) -> None:
2627
2666
 
2628
2667
 
2629
2668
  def decode_context(decoder: Decoder, objectset: SHACLObjectSet) -> None:
2630
- def _decode_ctx(d: Decoder):
2669
+ def _decode_ctx(d: Decoder) -> None:
2631
2670
  if not d.is_object():
2632
2671
  return
2633
2672
 
@@ -2650,7 +2689,7 @@ try:
2650
2689
  from rdflib.term import IdentifiedNode
2651
2690
 
2652
2691
  if TYPE_CHECKING:
2653
- from rdflib import Graph
2692
+ from rdflib import Graph # noqa: I300
2654
2693
  from rdflib.term import Node
2655
2694
 
2656
2695
  class RDFDecoder(Decoder):
@@ -2718,7 +2757,7 @@ try:
2718
2757
  return v.n3()
2719
2758
  return None
2720
2759
 
2721
- def read_enum(self, e) -> Optional[str]:
2760
+ def read_enum(self, e: EnumProp) -> Optional[str]:
2722
2761
  v = self.__read_node()
2723
2762
  if isinstance(v, URIRef):
2724
2763
  return v.toPython()
@@ -2792,7 +2831,7 @@ try:
2792
2831
 
2793
2832
  return typ.toPython(), self.__class__(self.graph, s)
2794
2833
 
2795
- def read_object_id(self, alias=None) -> Optional[Any]:
2834
+ def read_object_id(self, alias: Optional[str] = None) -> Optional[Any]:
2796
2835
  if isinstance(self.subject, BNode):
2797
2836
  return self.subject.n3()
2798
2837
  if not isinstance(self.subject, IdentifiedNode):
@@ -2830,29 +2869,31 @@ try:
2830
2869
  raise TypeError("Subject and predicate of URIRef value cannot be None")
2831
2870
  self.graph.add((self.subject, self.predicate, URIRef(v)))
2832
2871
 
2833
- def write_string(self, v):
2872
+ def write_string(self, v: str) -> None:
2834
2873
  self.__add_literal(v)
2835
2874
 
2836
- def write_datetime(self, v):
2875
+ def write_datetime(self, v: str) -> None:
2837
2876
  self.__add_literal(v)
2838
2877
 
2839
- def write_integer(self, v):
2878
+ def write_integer(self, v: int) -> None:
2840
2879
  self.__add_literal(v)
2841
2880
 
2842
- def write_iri(self, v, compact=None):
2881
+ def write_iri(self, v: str, compact: Optional[str] = None) -> None:
2843
2882
  self.__add_uriref(v)
2844
2883
 
2845
- def write_enum(self, v, e, compact=None):
2884
+ def write_enum(self, v: str, e: Any, compact: Optional[str] = None) -> None:
2846
2885
  self.__add_uriref(v)
2847
2886
 
2848
- def write_bool(self, v):
2887
+ def write_bool(self, v: bool) -> None:
2849
2888
  self.__add_literal(v)
2850
2889
 
2851
- def write_float(self, v):
2890
+ def write_float(self, v: float) -> None:
2852
2891
  self.__add_literal(v)
2853
2892
 
2854
2893
  @contextmanager
2855
- def write_property(self, iri: str, compact: Optional[str] = None):
2894
+ def write_property(
2895
+ self, iri: str, compact: Optional[str] = None
2896
+ ) -> Iterator[Encoder]:
2856
2897
  yield self.__class__(self.graph, self.subject, URIRef(iri))
2857
2898
 
2858
2899
  @contextmanager
@@ -2864,7 +2905,7 @@ try:
2864
2905
  _id: str,
2865
2906
  compact_id: Optional[str],
2866
2907
  needs_id: bool,
2867
- ):
2908
+ ) -> Iterator[Encoder]:
2868
2909
  obj: Node
2869
2910
  if _id.startswith("_:"):
2870
2911
  obj = BNode(_id[2:])
@@ -2879,19 +2920,19 @@ try:
2879
2920
  yield self.__class__(self.graph, obj)
2880
2921
 
2881
2922
  @contextmanager
2882
- def write_list(self):
2923
+ def write_list(self) -> Iterator["RDFEncoder"]:
2883
2924
  yield self
2884
2925
 
2885
2926
  @contextmanager
2886
- def write_list_item(self):
2927
+ def write_list_item(self) -> Iterator["RDFEncoder"]:
2887
2928
  yield self
2888
2929
 
2889
2930
  @contextmanager
2890
- def write_object_list(self):
2931
+ def write_object_list(self) -> Iterator["RDFEncoder"]:
2891
2932
  yield self
2892
2933
 
2893
2934
  @contextmanager
2894
- def write_dict(self):
2935
+ def write_dict(self) -> Iterator["RDFEncoder"]:
2895
2936
  yield self
2896
2937
 
2897
2938
  class RDFSerializer(object):
@@ -2940,22 +2981,22 @@ class {{ varname(*class.clsname) }}(
2940
2981
  {%- endfor %}
2941
2982
  {{ '"' }}{{ '"' }}{{ '"' }}
2942
2983
  {% endif %}
2943
- TYPE = "{{ class._id }}"
2984
+ TYPE: ClassVar[str] = "{{ class._id }}"
2944
2985
  {%- if context.compact_iri(class._id) != class._id %}
2945
- COMPACT_TYPE = "{{ context.compact_iri(class._id) }}"
2986
+ COMPACT_TYPE: ClassVar[Optional[str]] = "{{ context.compact_iri(class._id) }}"
2946
2987
  {%- endif %}
2947
- NODE_KIND: NodeKind = NodeKind.{{ class.node_kind.split("#")[-1] }}
2988
+ NODE_KIND: ClassVar[NodeKind] = NodeKind.{{ class.node_kind.split("#")[-1] }}
2948
2989
  {%- if class.id_property %}
2949
- ID_ALIAS: Optional[str] = "{{ class.id_property }}"
2990
+ ID_ALIAS: ClassVar[Optional[str]] = "{{ class.id_property }}"
2950
2991
  {%- endif %}
2951
2992
  {%- if class.is_abstract %}
2952
- IS_ABSTRACT: bool = True
2993
+ IS_ABSTRACT: ClassVar[bool] = True
2953
2994
  {%- endif %}
2954
2995
  {%- if class.deprecated %}
2955
- IS_DEPRECATED: bool = True
2996
+ IS_DEPRECATED: ClassVar[bool] = True
2956
2997
  {%- endif %}
2957
2998
  {%- if class.named_individuals %}
2958
- NAMED_INDIVIDUALS: Dict[str, str] = {
2999
+ NAMED_INDIVIDUALS: ClassVar[Dict[str, str]] = {
2959
3000
  {%- for member in class.named_individuals %}
2960
3001
  {%- if member.comment %}
2961
3002
  {%- for l in member.comment.split("\n") %}
@@ -2968,7 +3009,7 @@ class {{ varname(*class.clsname) }}(
2968
3009
  {%- endif %}
2969
3010
 
2970
3011
  {%- if class.properties %}
2971
- PROPERTIES = [
3012
+ PROPERTIES: ClassVar[List[ClassProp]] = [
2972
3013
  {%- for prop in class.properties %}
2973
3014
  {%- set is_list = prop.max_count is none or prop.max_count != 1 %}
2974
3015
  {%- if prop.comment %}