shacl2code 1.2.0__tar.gz → 1.3.2__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 (146) hide show
  1. shacl2code-1.3.2/.github/dependabot.yml +14 -0
  2. shacl2code-1.3.2/.github/workflows/bandit.yaml +30 -0
  3. {shacl2code-1.2.0 → shacl2code-1.3.2}/.github/workflows/coverage-generate.yaml +13 -7
  4. {shacl2code-1.2.0 → shacl2code-1.3.2}/.github/workflows/coverage-report.yaml +1 -1
  5. shacl2code-1.3.2/.github/workflows/publish.yaml +71 -0
  6. {shacl2code-1.2.0 → shacl2code-1.3.2}/.github/workflows/scorecard.yml +2 -2
  7. shacl2code-1.3.2/.github/workflows/test-sbom.yaml +73 -0
  8. {shacl2code-1.2.0 → shacl2code-1.3.2}/.github/workflows/test.yaml +22 -7
  9. {shacl2code-1.2.0 → shacl2code-1.3.2}/PKG-INFO +3 -2
  10. {shacl2code-1.2.0 → shacl2code-1.3.2}/pyproject.toml +6 -0
  11. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/common.py +22 -6
  12. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/cpp.py +0 -5
  13. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/golang.py +1 -6
  14. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/python.py +14 -0
  15. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/rust.py +1 -7
  16. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/Makefile.j2 +5 -1
  17. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/jsonld-source.j2 +15 -0
  18. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/shaclobjectset.go.j2 +12 -0
  19. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/jsonschema.j2 +73 -2
  20. shacl2code-1.3.2/src/shacl2code/lang/templates/python/_macros.j2 +13 -0
  21. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/python/model.py.j2 +49 -26
  22. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/python/model.pyi.j2 +6 -23
  23. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/rust/lib.rs.j2 +37 -1
  24. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/main.py +11 -1
  25. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/model.py +2 -1
  26. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/version.py +1 -1
  27. {shacl2code-1.2.0 → shacl2code-1.3.2}/testfixtures/testfixtures/jsonvalidation.py +61 -0
  28. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/conftest.py +2 -0
  29. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_cpp.py +182 -161
  30. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_golang.py +3 -0
  31. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_jsonschema.py +8 -4
  32. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_python.py +110 -26
  33. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_rust.py +61 -59
  34. shacl2code-1.2.0/.github/dependabot.yml +0 -6
  35. shacl2code-1.2.0/.github/workflows/publish.yaml +0 -37
  36. {shacl2code-1.2.0 → shacl2code-1.3.2}/.clang-format +0 -0
  37. {shacl2code-1.2.0 → shacl2code-1.3.2}/.flake8 +0 -0
  38. {shacl2code-1.2.0 → shacl2code-1.3.2}/.gitignore +0 -0
  39. {shacl2code-1.2.0 → shacl2code-1.3.2}/LICENSE +0 -0
  40. {shacl2code-1.2.0 → shacl2code-1.3.2}/README.md +0 -0
  41. {shacl2code-1.2.0 → shacl2code-1.3.2}/SECURITY.md +0 -0
  42. {shacl2code-1.2.0 → shacl2code-1.3.2}/scripts/ajv-compile.js +0 -0
  43. {shacl2code-1.2.0 → shacl2code-1.3.2}/scripts/format.sh +0 -0
  44. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/__init__.py +0 -0
  45. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/__main__.py +0 -0
  46. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/context.py +0 -0
  47. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/__init__.py +0 -0
  48. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/jinja.py +0 -0
  49. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/jsonschema.py +0 -0
  50. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/lang.py +0 -0
  51. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/sources/cpp/json.hpp +0 -0
  52. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/Doxyfile.j2 +0 -0
  53. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/api.hpp.j2 +0 -0
  54. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/datetime.cpp.j2 +0 -0
  55. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/datetime.hpp.j2 +0 -0
  56. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/decode.cpp.j2 +0 -0
  57. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/decode.hpp.j2 +0 -0
  58. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/encode.cpp.j2 +0 -0
  59. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/encode.hpp.j2 +0 -0
  60. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/errorhandler.cpp.j2 +0 -0
  61. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/errorhandler.hpp.j2 +0 -0
  62. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/exceptions.hpp.j2 +0 -0
  63. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/extensible.cpp.j2 +0 -0
  64. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/extensible.hpp.j2 +0 -0
  65. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/header.j2 +0 -0
  66. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/jsonld-header.j2 +0 -0
  67. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/link.cpp.j2 +0 -0
  68. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/link.hpp.j2 +0 -0
  69. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/namedindividual.cpp.j2 +0 -0
  70. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/namedindividual.hpp.j2 +0 -0
  71. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/object.cpp.j2 +0 -0
  72. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/object.hpp.j2 +0 -0
  73. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/objectpath.cpp.j2 +0 -0
  74. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/objectpath.hpp.j2 +0 -0
  75. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/objectset.cpp.j2 +0 -0
  76. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/objectset.hpp.j2 +0 -0
  77. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/pkgconfig.pc.j2 +0 -0
  78. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/prop.hpp.j2 +0 -0
  79. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/property.cpp.j2 +0 -0
  80. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/property.hpp.j2 +0 -0
  81. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/propertyvalue.cpp.j2 +0 -0
  82. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/propertyvalue.hpp.j2 +0 -0
  83. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/ref.hpp.j2 +0 -0
  84. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/refproperty.cpp.j2 +0 -0
  85. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/refproperty.hpp.j2 +0 -0
  86. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/source.j2 +0 -0
  87. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/type.cpp.j2 +0 -0
  88. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/type.hpp.j2 +0 -0
  89. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/util.cpp.j2 +0 -0
  90. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/util.hpp.j2 +0 -0
  91. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/validate.j2 +0 -0
  92. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/cpp/walk.hpp.j2 +0 -0
  93. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/classes.go.j2 +0 -0
  94. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/decode.go.j2 +0 -0
  95. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/encode.go.j2 +0 -0
  96. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/errorhandler.go.j2 +0 -0
  97. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/errors.go.j2 +0 -0
  98. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/extensible.go.j2 +0 -0
  99. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/linkstate.go.j2 +0 -0
  100. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/listproperty.go.j2 +0 -0
  101. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/optional.go.j2 +0 -0
  102. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/path.go.j2 +0 -0
  103. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/property.go.j2 +0 -0
  104. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/ref.go.j2 +0 -0
  105. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/reflistproperty.go.j2 +0 -0
  106. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/refproperty.go.j2 +0 -0
  107. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/shaclobject.go.j2 +0 -0
  108. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/shacltype.go.j2 +0 -0
  109. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/util.go.j2 +0 -0
  110. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/golang/validator.go.j2 +0 -0
  111. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/python/__init__.py.j2 +0 -0
  112. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/python/__main__.py.j2 +0 -0
  113. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/python/cmd.py.j2 +0 -0
  114. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/lang/templates/rust/Cargo.toml.j2 +0 -0
  115. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/urlcontext.py +0 -0
  116. {shacl2code-1.2.0 → shacl2code-1.3.2}/src/shacl2code/util.py +0 -0
  117. {shacl2code-1.2.0 → shacl2code-1.3.2}/testfixtures/testfixtures/__init__.py +0 -0
  118. {shacl2code-1.2.0 → shacl2code-1.3.2}/testfixtures/testfixtures/httpserver.py +0 -0
  119. {shacl2code-1.2.0 → shacl2code-1.3.2}/testfixtures/testfixtures/timetests.py +0 -0
  120. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/abort.j2 +0 -0
  121. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/bad-id.j2 +0 -0
  122. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/bad-node-kind.ttl +0 -0
  123. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/bad-pattern-class.ttl +0 -0
  124. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/bad-pattern-integer.ttl +0 -0
  125. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/bad-prop-type.ttl +0 -0
  126. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/bad-reference.jsonld +0 -0
  127. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/context-url.j2 +0 -0
  128. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/context.j2 +0 -0
  129. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/links.json +0 -0
  130. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/missing-range.ttl +0 -0
  131. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/model/test-context.json +0 -0
  132. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/model/test.ttl +0 -0
  133. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/python/bad-object-type-inline.json +0 -0
  134. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/python/bad-object-type-ref-after.json +0 -0
  135. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/python/bad-object-type-ref-before.json +0 -0
  136. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/raw.j2 +0 -0
  137. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/reserved-words.ttl +0 -0
  138. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/data/roundtrip.json +0 -0
  139. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_cli.py +0 -0
  140. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_common_jinja.py +0 -0
  141. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_common_prefix.py +0 -0
  142. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_context.py +0 -0
  143. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_convert_version_string.py +0 -0
  144. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_lint.py +0 -0
  145. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_model.py +0 -0
  146. {shacl2code-1.2.0 → shacl2code-1.3.2}/tests/test_model_source.py +0 -0
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ cooldown:
8
+ default-days: 7
9
+ - package-ecosystem: "github-actions"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+ cooldown:
14
+ default-days: 7
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Bandit
3
+
4
+ on:
5
+ push:
6
+ # Only run when shacl2code's own Python source could be affected
7
+ paths: &trigger-paths
8
+ - "src/shacl2code/**"
9
+ - "pyproject.toml"
10
+ - ".github/workflows/bandit.yaml"
11
+ pull_request:
12
+ paths: *trigger-paths
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ bandit:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ # Needed to upload results to the code scanning dashboard
22
+ security-events: write
23
+ actions: read
24
+ contents: read
25
+ steps:
26
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
27
+ - name: Run Bandit
28
+ uses: PyCQA/bandit-action@67a458d90fa11fb1463e91e7f4c8f068b5863c7f # v1.0.1
29
+ with:
30
+ targets: src/shacl2code
@@ -7,6 +7,10 @@ on:
7
7
  branches:
8
8
  - "main"
9
9
 
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
12
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
13
+
10
14
  permissions:
11
15
  contents: read
12
16
 
@@ -23,13 +27,15 @@ jobs:
23
27
  # comments (to avoid publishing multiple comments in the same PR)
24
28
  contents: write
25
29
  steps:
26
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
27
- - name: Setup Python ${{ matrix.python-version }}
28
- uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
30
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
+ - name: Setup Python
32
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
29
33
  with:
30
34
  python-version: "3.11"
35
+ cache: "pip"
36
+ cache-dependency-path: "pyproject.toml"
31
37
  - name: Setup Node
32
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
38
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
33
39
  with:
34
40
  node-version: 24
35
41
  - name: Install dependencies
@@ -46,21 +52,21 @@ jobs:
46
52
  rustup component add clippy
47
53
  - name: Install package
48
54
  run: |
55
+ pip install --upgrade pip
49
56
  pip install -e .[dev]
50
57
  - name: Generate coverage report
51
58
  run: |
52
59
  pytest -v
53
60
  - name: Coverage comment
54
61
  id: coverage_comment
55
- uses: py-cov-action/python-coverage-comment-action@2b84cfb222691291ba358a586c13606f0a9c172c # v3
62
+ uses: py-cov-action/python-coverage-comment-action@a05be3d2e8a6272d3ef5fb2840ab20368bb2eb71 # v4.3
56
63
  with:
57
64
  GITHUB_TOKEN: ${{ github.token }}
58
65
  - name: Store Pull Request comment to be posted
59
- uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
66
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
60
67
  if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true'
61
68
  with:
62
69
  # If you use a different name, update COMMENT_ARTIFACT_NAME accordingly
63
70
  name: python-coverage-comment-action
64
71
  # If you use a different name, update COMMENT_FILENAME accordingly
65
72
  path: python-coverage-comment-action.txt
66
-
@@ -30,7 +30,7 @@ jobs:
30
30
  # DO NOT run actions/checkout here, for security reasons
31
31
  # For details, refer to https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
32
32
  - name: Post comment
33
- uses: py-cov-action/python-coverage-comment-action@2b84cfb222691291ba358a586c13606f0a9c172c # v3
33
+ uses: py-cov-action/python-coverage-comment-action@a05be3d2e8a6272d3ef5fb2840ab20368bb2eb71 # v4.3
34
34
  with:
35
35
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36
36
  GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }}
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: Upload Python Package
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+
8
+ jobs:
9
+ test:
10
+ uses: ./.github/workflows/test.yaml
11
+
12
+ deploy:
13
+ needs:
14
+ - test
15
+
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ id-token: write
19
+
20
+ steps:
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
25
+ with:
26
+ python-version: '3.x'
27
+
28
+ - name: Install dependencies
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ pip install build spdx3-validate
32
+
33
+ - name: Build package
34
+ run: python -m build
35
+
36
+ - name: Embed SBOM into the wheel
37
+ id: pitloom
38
+ uses: bact/pitloom@68b02e2052902012716c6b888addbaf95d5e1bf5 # v0.17.0
39
+ with:
40
+ embed-wheel: "dist/*.whl"
41
+ extras: "content-type"
42
+ content-type: "true"
43
+ artifact-name: "sbom"
44
+
45
+ - name: Extract and validate embedded SBOM
46
+ env:
47
+ SBOM_PATH: ${{ steps.pitloom.outputs.sbom-path }}
48
+ run: |
49
+ set -euo pipefail
50
+ whl=$(ls dist/*.whl)
51
+ sbom_basename=$(basename "${SBOM_PATH}")
52
+
53
+ entry=""
54
+ while IFS= read -r line; do
55
+ case "${line}" in
56
+ *".dist-info/sboms/${sbom_basename}") entry="${line}"; break ;;
57
+ esac
58
+ done < <(unzip -Z1 "${whl}")
59
+
60
+ if [ -z "${entry}" ]; then
61
+ echo "::error::${sbom_basename} not found under */.dist-info/sboms/ in ${whl}"
62
+ exit 1
63
+ fi
64
+ echo "Found: ${entry}"
65
+
66
+ mkdir -p extracted-sbom
67
+ unzip -p "${whl}" "${entry}" > "extracted-sbom/${sbom_basename}"
68
+ spdx3-validate --json "extracted-sbom/${sbom_basename}"
69
+
70
+ - name: Publish distribution to PyPI
71
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -32,12 +32,12 @@ jobs:
32
32
 
33
33
  steps:
34
34
  - name: "Checkout code"
35
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
35
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
36
36
  with:
37
37
  persist-credentials: false
38
38
 
39
39
  - name: "Run analysis"
40
- uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
40
+ uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
41
41
  with:
42
42
  results_file: results.sarif
43
43
  results_format: sarif
@@ -0,0 +1,73 @@
1
+ ---
2
+ # Tests that the SBOM generation and embedding step works correctly on every PR.
3
+ name: Test SBOM generation
4
+
5
+ on:
6
+ push:
7
+ paths-ignore: &ignore-paths
8
+ - "docs/**"
9
+ - "**/*.md"
10
+ - "LICENSE*"
11
+ pull_request:
12
+ paths-ignore: *ignore-paths
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ concurrency:
18
+ group: ${{ github.workflow }}-${{ github.ref }}
19
+ cancel-in-progress: true
20
+
21
+ jobs:
22
+ test-sbom:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+
27
+ - name: Setup Python
28
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
29
+ with:
30
+ python-version: "3.x"
31
+ cache: pip
32
+
33
+ - name: Install build tooling
34
+ run: |
35
+ python -m pip install --upgrade pip
36
+ pip install build spdx3-validate
37
+
38
+ - name: Build wheel
39
+ run: python -m build --wheel
40
+
41
+ - name: Embed SBOM into the wheel
42
+ id: pitloom
43
+ uses: bact/pitloom@68b02e2052902012716c6b888addbaf95d5e1bf5 # v0.17.0
44
+ with:
45
+ embed-wheel: "dist/*.whl"
46
+ extras: "content-type"
47
+ content-type: "true"
48
+ artifact-name: "sbom"
49
+
50
+ - name: Extract and validate embedded SBOM
51
+ env:
52
+ SBOM_PATH: ${{ steps.pitloom.outputs.sbom-path }}
53
+ run: |
54
+ set -euo pipefail
55
+ whl=$(ls dist/*.whl)
56
+ sbom_basename=$(basename "${SBOM_PATH}")
57
+
58
+ entry=""
59
+ while IFS= read -r line; do
60
+ case "${line}" in
61
+ *".dist-info/sboms/${sbom_basename}") entry="${line}"; break ;;
62
+ esac
63
+ done < <(unzip -Z1 "${whl}")
64
+
65
+ if [ -z "${entry}" ]; then
66
+ echo "::error::${sbom_basename} not found under */.dist-info/sboms/ in ${whl}"
67
+ exit 1
68
+ fi
69
+ echo "Found: ${entry}"
70
+
71
+ mkdir -p extracted-sbom
72
+ unzip -p "${whl}" "${entry}" > "extracted-sbom/${sbom_basename}"
73
+ spdx3-validate --json "extracted-sbom/${sbom_basename}"
@@ -14,6 +14,13 @@ on:
14
14
  paths: *trigger-paths
15
15
  workflow_call:
16
16
 
17
+ concurrency:
18
+ group: ${{ github.workflow }}-${{ github.ref }}
19
+ cancel-in-progress: true
20
+
21
+ permissions:
22
+ contents: read
23
+
17
24
  jobs:
18
25
  build:
19
26
  runs-on: ${{ matrix.os }}
@@ -25,15 +32,18 @@ jobs:
25
32
  - os: macos-latest
26
33
  python-version: "3.10"
27
34
  steps:
28
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
35
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
29
36
  - name: Setup Python ${{ matrix.python-version }}
30
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
37
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
31
38
  with:
32
39
  python-version: ${{ matrix.python-version }}
40
+ cache: pip
33
41
  - name: Setup Node
34
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
42
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
35
43
  with:
36
44
  node-version: 24
45
+ cache: npm
46
+ cache-dependency-path: scripts/package-lock.json
37
47
  - name: Install dependencies
38
48
  run: |
39
49
  python -m pip install --upgrade pip
@@ -44,12 +54,12 @@ jobs:
44
54
  if: runner.os == 'Linux'
45
55
  run: |
46
56
  sudo apt update
47
- sudo apt install -y build-essential cppcheck doxygen graphviz
57
+ sudo apt install -y build-essential cppcheck doxygen graphviz ccache
48
58
  - name: Install macOS packages
49
59
  if: runner.os == 'macOS'
50
60
  run: |
51
61
  brew update
52
- brew install cppcheck doxygen gcc go graphviz
62
+ brew install cppcheck doxygen gcc go graphviz ccache
53
63
  - name: Install Go static analysis tools
54
64
  run: |
55
65
  echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
@@ -57,6 +67,10 @@ jobs:
57
67
  - name: Install Rust static analysis tools
58
68
  run: |
59
69
  rustup component add clippy
70
+ - name: Configure ccache
71
+ uses: hendrikmuhs/ccache-action@v1.2
72
+ with:
73
+ key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.python-version }}
60
74
  - name: Build package
61
75
  run: |
62
76
  python -m build
@@ -69,12 +83,13 @@ jobs:
69
83
  lint:
70
84
  runs-on: ubuntu-latest
71
85
  steps:
72
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
86
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
73
87
  - name: Setup Python
74
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
88
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
75
89
  with:
76
90
  # Black in this Python version must support all target-versions listed in pyproject.toml
77
91
  python-version: "3.14"
92
+ cache: pip
78
93
  - uses: psf/black@87928e6d6761a4a6d22250e1fee5601b3998086e # 26.5.1
79
94
  - uses: py-actions/flake8@84ec6726560b6d5bd68f2a5bed83d62b52bb50ba # v2.3.0
80
95
  - name: Install dependencies
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: shacl2code
3
- Version: 1.2.0
3
+ Version: 1.3.2
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
@@ -27,6 +27,7 @@ Requires-Python: >=3.9
27
27
  Requires-Dist: jinja2>=3.1.2
28
28
  Requires-Dist: rdflib>=7.0.0
29
29
  Provides-Extra: dev
30
+ Requires-Dist: bandit<1.10.0,>=1.8.6; extra == 'dev'
30
31
  Requires-Dist: black>=25.11.0; extra == 'dev'
31
32
  Requires-Dist: flake8-import-order>=0.19.2; extra == 'dev'
32
33
  Requires-Dist: flake8>=7.0.0; extra == 'dev'
@@ -32,6 +32,7 @@ license = "MIT"
32
32
 
33
33
  [project.optional-dependencies]
34
34
  dev = [
35
+ "bandit >= 1.8.6, < 1.10.0", # latest version for Python 3.9 is 1.8.x
35
36
  "black >= 25.11.0",
36
37
  "flake8 >= 7.0.0",
37
38
  "flake8-import-order >= 0.19.2",
@@ -77,6 +78,11 @@ warn_redundant_casts = true
77
78
  # warn_unreachable = true
78
79
  # warn_unused_ignores = true
79
80
 
81
+ [[tool.pitloom.creator]]
82
+ name = "Joshua Watt"
83
+ email = "JPEWhacker@gmail.com"
84
+ type = "person"
85
+
80
86
  [tool.pytest.ini_options]
81
87
  addopts = [
82
88
  "--import-mode=importlib",
@@ -22,6 +22,11 @@ from ..version import VERSION
22
22
  THIS_DIR = Path(__file__).parent
23
23
 
24
24
 
25
+ def prop_is_list(prop):
26
+ """Whether a property's max_count allows more than one value."""
27
+ return prop.max_count is None or prop.max_count != 1
28
+
29
+
25
30
  class OutputFile(object):
26
31
  def __init__(self, path):
27
32
  self.path = path
@@ -38,7 +43,9 @@ class OutputFile(object):
38
43
  @jinja2.pass_context
39
44
  def include_file(ctx, name):
40
45
  env = ctx.environment
41
- return Markup(env.loader.get_source(env, name)[0])
46
+ # Reads this repo's own template source (not user-controlled input) to
47
+ # embed verbatim into generated source code, never rendered as HTML.
48
+ return Markup(env.loader.get_source(env, name)[0]) # nosec B704
42
49
 
43
50
 
44
51
  class JinjaTemplateRender(object):
@@ -60,7 +67,11 @@ class JinjaTemplateRender(object):
60
67
  def abort_helper(msg: str) -> None:
61
68
  raise TemplateRuntimeError(msg)
62
69
 
63
- env = Environment(loader=FileSystemLoader([template.parent, THIS_DIR.parent]))
70
+ # Templates render source code (C++/Go/Python/Rust), not HTML;
71
+ # autoescape would corrupt generated code by escaping <, >, & etc.
72
+ env = Environment(
73
+ loader=FileSystemLoader([template.parent, THIS_DIR.parent])
74
+ ) # nosec B701
64
75
  for k, v in extra_env.items():
65
76
  env.globals[k] = v
66
77
  env.globals["abort"] = abort_helper
@@ -87,15 +98,18 @@ class JinjaTemplateRender(object):
87
98
  class ObjectList(object):
88
99
  def __init__(self, objs):
89
100
  self.__objs = objs
101
+ self.__objs_by_id = {}
102
+ for o in objs:
103
+ self.__objs_by_id.setdefault(o._id, o)
90
104
 
91
105
  def __iter__(self):
92
106
  return iter(self.__objs)
93
107
 
94
108
  def get(self, _id):
95
- for o in self.__objs:
96
- if o._id == _id:
97
- return o
98
- raise KeyError(f"Object with ID {_id} not found")
109
+ try:
110
+ return self.__objs_by_id[_id]
111
+ except KeyError:
112
+ raise KeyError(f"Object with ID {_id} not found") from None
99
113
 
100
114
  def get_all_derived(cls):
101
115
  def _recurse(cls):
@@ -131,6 +145,7 @@ class JinjaTemplateRender(object):
131
145
  "concrete_classes": concrete_classes,
132
146
  "abstract_classes": abstract_classes,
133
147
  "context": model.context,
148
+ "jss_signature": model.jss_signature,
134
149
  **self.get_additional_render_args(model),
135
150
  }
136
151
 
@@ -138,6 +153,7 @@ class JinjaTemplateRender(object):
138
153
  "get_all_derived": get_all_derived,
139
154
  "get_all_named_individuals": get_all_named_individuals,
140
155
  "include_file": include_file,
156
+ "prop_is_list": prop_is_list,
141
157
  **self.get_extra_env(),
142
158
  }
143
159
 
@@ -37,10 +37,6 @@ def parent_cpp_classes(cls, classes):
37
37
  return parents
38
38
 
39
39
 
40
- def prop_is_list(prop):
41
- return prop.max_count is None or prop.max_count != 1
42
-
43
-
44
40
  def id_str(s):
45
41
  return re.sub(r"[^a-zA-Z0-9_]", "_", s)
46
42
 
@@ -182,7 +178,6 @@ class CppRender(JinjaTemplateRender):
182
178
  def get_extra_env(self):
183
179
  return {
184
180
  "varname": varname,
185
- "prop_is_list": prop_is_list,
186
181
  "parent_cpp_classes": parent_cpp_classes,
187
182
  "macro_prefix": self.macro_prefix,
188
183
  "api_def_begin": comment_wrap(textwrap.dedent(f"""\
@@ -8,7 +8,7 @@ import re
8
8
  import textwrap
9
9
  from pathlib import Path
10
10
 
11
- from .common import JinjaTemplateRender
11
+ from .common import JinjaTemplateRender, prop_is_list
12
12
  from .lang import TEMPLATE_DIR, language
13
13
 
14
14
  GO_KEYWORDS = (
@@ -75,10 +75,6 @@ def prop_name(prop):
75
75
  return varname(prop.varname, public=False)
76
76
 
77
77
 
78
- def prop_is_list(prop):
79
- return prop.max_count is None or prop.max_count != 1
80
-
81
-
82
78
  def prop_go_type(prop, classes):
83
79
  if prop.enum_values:
84
80
  return "string"
@@ -276,7 +272,6 @@ class GoLangRender(JinjaTemplateRender):
276
272
  "interface_name": interface_name,
277
273
  "class_type_var": class_type_var,
278
274
  "prop_name": prop_name,
279
- "prop_is_list": prop_is_list,
280
275
  "prop_go_type": prop_go_type,
281
276
  "prop_ctx_name": prop_ctx_name,
282
277
  "prop_decode_func": prop_decode_func,
@@ -75,6 +75,19 @@ def varname(*name):
75
75
  return name
76
76
 
77
77
 
78
+ def prop_element_pytype(prop, classes):
79
+ """Python type of a single element of prop, ignoring container shape.
80
+
81
+ Object-reference properties resolve to ``Union[str, 'ClassName']``, since
82
+ they may be set from either an id string or the referenced object.
83
+ """
84
+ if prop.enum_values:
85
+ return "str"
86
+ if prop.class_id:
87
+ return "Union[str, '" + varname(*classes.get(prop.class_id).clsname) + "']"
88
+ return DATATYPE_PYTHON_TYPES[prop.datatype]
89
+
90
+
78
91
  @language("python")
79
92
  class PythonRender(JinjaTemplateRender):
80
93
  """Render Python Language Bindings."""
@@ -144,6 +157,7 @@ class PythonRender(JinjaTemplateRender):
144
157
  def get_extra_env(self):
145
158
  return {
146
159
  "varname": varname,
160
+ "prop_element_pytype": prop_element_pytype,
147
161
  "DATATYPE_CLASSES": DATATYPE_CLASSES,
148
162
  "DATATYPE_PYTHON_TYPES": DATATYPE_PYTHON_TYPES,
149
163
  }
@@ -7,7 +7,7 @@
7
7
  import re
8
8
  from pathlib import Path
9
9
 
10
- from .common import JinjaTemplateRender
10
+ from .common import JinjaTemplateRender, prop_is_list
11
11
  from .lang import TEMPLATE_DIR, language
12
12
 
13
13
  RUST_KEYWORDS = (
@@ -108,11 +108,6 @@ def prop_field_name(prop):
108
108
  return varname(prop.varname)
109
109
 
110
110
 
111
- def prop_is_list(prop):
112
- """Check if a property is a list."""
113
- return prop.max_count is None or prop.max_count != 1
114
-
115
-
116
111
  def prop_rust_type(prop, classes):
117
112
  """Get the Rust type for a property's data type."""
118
113
  if prop.enum_values:
@@ -268,7 +263,6 @@ class RustRender(JinjaTemplateRender):
268
263
  "type_name": type_name,
269
264
  "struct_name": struct_name,
270
265
  "prop_field_name": prop_field_name,
271
- "prop_is_list": prop_is_list,
272
266
  "prop_rust_type": prop_rust_type,
273
267
  "prop_full_type": prop_full_type,
274
268
  "const_name": const_name,
@@ -30,11 +30,12 @@ LIB_OBJS = {{ lib_obj_files }}
30
30
 
31
31
  VALIDATE_OBJS = {{ basename }}-validate.o
32
32
  ALL_OBJS = $(LIB_OBJS) $(VALIDATE_OBJS)
33
+ ALL_DEPS = $(ALL_OBJS:.o=.d)
33
34
 
34
35
  all: lib{{ basename }}.so lib{{ basename }}.a {{ basename }}-validate docs
35
36
 
36
37
  clean:
37
- rm -rf *.o *.so *.a {{ basename }}-validate doc/
38
+ rm -rf *.d *.o *.so *.a {{ basename }}-validate doc/
38
39
 
39
40
  install: lib{{ basename }}.so lib{{ basename }}.a docs
40
41
  # Library
@@ -72,6 +73,7 @@ install: lib{{ basename }}.so lib{{ basename }}.a docs
72
73
  @echo " [CC] $<"
73
74
  $(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS) \
74
75
  -fPIC \
76
+ -MMD -MP \
75
77
  -fvisibility=hidden \
76
78
  -fvisibility-inlines-hidden \
77
79
  -D{{ macro_prefix }}_DLL=1 \
@@ -109,6 +111,8 @@ $(V).SILENT:
109
111
 
110
112
  .PHONY: clean all docs
111
113
 
114
+ -include $(ALL_DEPS)
115
+
112
116
  {#
113
117
  vim: ft=make
114
118
  #}
@@ -485,6 +485,21 @@ void JSONLDDeserializer::read(std::istream& input, SHACLObjectSet& objectSet,
485
485
  errorHandler.handleDeserializeError("Invalid context URLs", path);
486
486
  return;
487
487
  }
488
+ {%- if jss_signature %}
489
+ {
490
+ const auto jss_signature = "{{ jss_signature }}";
491
+
492
+ if (d.contains(jss_signature)) {
493
+ path.pushMember(jss_signature, [&] {
494
+ if (!d[jss_signature].is_array()) {
495
+ errorHandler.handleDeserializeError("Signature {{ jss_signature }} must be an array", path);
496
+ return;
497
+ }
498
+ });
499
+ d.erase(jss_signature);
500
+ }
501
+ }
502
+ {%- endif %}
488
503
 
489
504
  if (!d.contains("@graph")) {
490
505
  JSONData data;
@@ -53,6 +53,8 @@ var CONTEXT_URLS = []string{
53
53
  {{ '/' }}{{ '*' }}*/
54
54
  }
55
55
 
56
+ var JSS_SIGNATURE = "{{ jss_signature or '' }}"
57
+
56
58
  func makeshaclObjectList(lst []SHACLObject) shaclObjectList {
57
59
  idx := make(map[SHACLObject]int)
58
60
  for i, o := range lst {
@@ -187,6 +189,16 @@ func (self *SHACLObjectSetObject) Decode(decoder *json.Decoder) error {
187
189
  return DecodeSHACLObject[SHACLObject](data, path, context, nil, state)
188
190
  }
189
191
 
192
+ if JSS_SIGNATURE != "" {
193
+ if signature, found := data[JSS_SIGNATURE]; found {
194
+ _, ok := signature.([]interface{})
195
+ if !ok {
196
+ return &DecodeError{path, "Bad type for signature '" + JSS_SIGNATURE + "'"}
197
+ }
198
+ }
199
+ delete(data, JSS_SIGNATURE)
200
+ }
201
+
190
202
  _, has_graph := data["@graph"]
191
203
  if has_graph {
192
204
  for k, v := range data {