shacl2code 1.0.1__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 (135) hide show
  1. {shacl2code-1.0.1 → shacl2code-1.1.0}/PKG-INFO +1 -1
  2. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/python.py +26 -2
  3. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/model.py.j2 +28 -0
  4. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/model.pyi.j2 +54 -2
  5. shacl2code-1.1.0/src/shacl2code/version.py +1 -0
  6. shacl2code-1.1.0/tests/data/reserved-words.ttl +34 -0
  7. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_python.py +160 -1
  8. shacl2code-1.0.1/src/shacl2code/version.py +0 -1
  9. {shacl2code-1.0.1 → shacl2code-1.1.0}/.clang-format +0 -0
  10. {shacl2code-1.0.1 → shacl2code-1.1.0}/.flake8 +0 -0
  11. {shacl2code-1.0.1 → shacl2code-1.1.0}/.github/dependabot.yml +0 -0
  12. {shacl2code-1.0.1 → shacl2code-1.1.0}/.github/workflows/coverage-generate.yaml +0 -0
  13. {shacl2code-1.0.1 → shacl2code-1.1.0}/.github/workflows/coverage-report.yaml +0 -0
  14. {shacl2code-1.0.1 → shacl2code-1.1.0}/.github/workflows/publish.yaml +0 -0
  15. {shacl2code-1.0.1 → shacl2code-1.1.0}/.github/workflows/scorecard.yml +0 -0
  16. {shacl2code-1.0.1 → shacl2code-1.1.0}/.github/workflows/test.yaml +0 -0
  17. {shacl2code-1.0.1 → shacl2code-1.1.0}/.gitignore +0 -0
  18. {shacl2code-1.0.1 → shacl2code-1.1.0}/LICENSE +0 -0
  19. {shacl2code-1.0.1 → shacl2code-1.1.0}/README.md +0 -0
  20. {shacl2code-1.0.1 → shacl2code-1.1.0}/SECURITY.md +0 -0
  21. {shacl2code-1.0.1 → shacl2code-1.1.0}/pyproject.toml +0 -0
  22. {shacl2code-1.0.1 → shacl2code-1.1.0}/scripts/format.sh +0 -0
  23. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/__init__.py +0 -0
  24. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/__main__.py +0 -0
  25. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/context.py +0 -0
  26. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/__init__.py +0 -0
  27. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/common.py +0 -0
  28. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/cpp.py +0 -0
  29. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/golang.py +0 -0
  30. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/jinja.py +0 -0
  31. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/jsonschema.py +0 -0
  32. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/lang.py +0 -0
  33. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/sources/cpp/json.hpp +0 -0
  34. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/Doxyfile.j2 +0 -0
  35. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/Makefile.j2 +0 -0
  36. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/api.hpp.j2 +0 -0
  37. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/datetime.cpp.j2 +0 -0
  38. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/datetime.hpp.j2 +0 -0
  39. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/decode.cpp.j2 +0 -0
  40. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/decode.hpp.j2 +0 -0
  41. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/encode.cpp.j2 +0 -0
  42. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/encode.hpp.j2 +0 -0
  43. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/errorhandler.cpp.j2 +0 -0
  44. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/errorhandler.hpp.j2 +0 -0
  45. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/exceptions.hpp.j2 +0 -0
  46. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/extensible.cpp.j2 +0 -0
  47. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/extensible.hpp.j2 +0 -0
  48. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/header.j2 +0 -0
  49. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/jsonld-header.j2 +0 -0
  50. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/jsonld-source.j2 +0 -0
  51. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/link.cpp.j2 +0 -0
  52. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/link.hpp.j2 +0 -0
  53. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/namedindividual.cpp.j2 +0 -0
  54. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/namedindividual.hpp.j2 +0 -0
  55. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/object.cpp.j2 +0 -0
  56. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/object.hpp.j2 +0 -0
  57. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectpath.cpp.j2 +0 -0
  58. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectpath.hpp.j2 +0 -0
  59. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectset.cpp.j2 +0 -0
  60. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/objectset.hpp.j2 +0 -0
  61. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/pkgconfig.pc.j2 +0 -0
  62. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/prop.hpp.j2 +0 -0
  63. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/property.cpp.j2 +0 -0
  64. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/property.hpp.j2 +0 -0
  65. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/propertyvalue.cpp.j2 +0 -0
  66. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/propertyvalue.hpp.j2 +0 -0
  67. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/ref.hpp.j2 +0 -0
  68. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/refproperty.cpp.j2 +0 -0
  69. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/refproperty.hpp.j2 +0 -0
  70. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/source.j2 +0 -0
  71. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/type.cpp.j2 +0 -0
  72. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/type.hpp.j2 +0 -0
  73. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/util.cpp.j2 +0 -0
  74. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/util.hpp.j2 +0 -0
  75. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/validate.j2 +0 -0
  76. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/cpp/walk.hpp.j2 +0 -0
  77. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/classes.go.j2 +0 -0
  78. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/decode.go.j2 +0 -0
  79. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/encode.go.j2 +0 -0
  80. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/errorhandler.go.j2 +0 -0
  81. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/errors.go.j2 +0 -0
  82. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/extensible.go.j2 +0 -0
  83. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/linkstate.go.j2 +0 -0
  84. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/listproperty.go.j2 +0 -0
  85. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/optional.go.j2 +0 -0
  86. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/path.go.j2 +0 -0
  87. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/property.go.j2 +0 -0
  88. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/ref.go.j2 +0 -0
  89. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/reflistproperty.go.j2 +0 -0
  90. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/refproperty.go.j2 +0 -0
  91. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/shaclobject.go.j2 +0 -0
  92. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/shaclobjectset.go.j2 +0 -0
  93. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/shacltype.go.j2 +0 -0
  94. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/util.go.j2 +0 -0
  95. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/golang/validator.go.j2 +0 -0
  96. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/jsonschema.j2 +0 -0
  97. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/__init__.py.j2 +0 -0
  98. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/__main__.py.j2 +0 -0
  99. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/lang/templates/python/cmd.py.j2 +0 -0
  100. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/main.py +0 -0
  101. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/model.py +0 -0
  102. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/urlcontext.py +0 -0
  103. {shacl2code-1.0.1 → shacl2code-1.1.0}/src/shacl2code/util.py +0 -0
  104. {shacl2code-1.0.1 → shacl2code-1.1.0}/testfixtures/testfixtures/__init__.py +0 -0
  105. {shacl2code-1.0.1 → shacl2code-1.1.0}/testfixtures/testfixtures/httpserver.py +0 -0
  106. {shacl2code-1.0.1 → shacl2code-1.1.0}/testfixtures/testfixtures/jsonvalidation.py +0 -0
  107. {shacl2code-1.0.1 → shacl2code-1.1.0}/testfixtures/testfixtures/timetests.py +0 -0
  108. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/conftest.py +0 -0
  109. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/abort.j2 +0 -0
  110. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/bad-id.j2 +0 -0
  111. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/bad-node-kind.ttl +0 -0
  112. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/bad-pattern-class.ttl +0 -0
  113. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/bad-pattern-integer.ttl +0 -0
  114. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/bad-prop-type.ttl +0 -0
  115. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/bad-reference.jsonld +0 -0
  116. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/context-url.j2 +0 -0
  117. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/context.j2 +0 -0
  118. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/links.json +0 -0
  119. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/missing-range.ttl +0 -0
  120. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/model/test-context.json +0 -0
  121. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/model/test.ttl +0 -0
  122. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/python/bad-object-type-inline.json +0 -0
  123. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/python/bad-object-type-ref-after.json +0 -0
  124. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/python/bad-object-type-ref-before.json +0 -0
  125. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/raw.j2 +0 -0
  126. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/data/roundtrip.json +0 -0
  127. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_cli.py +0 -0
  128. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_common_jinja.py +0 -0
  129. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_common_prefix.py +0 -0
  130. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_context.py +0 -0
  131. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_convert_version_string.py +0 -0
  132. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_cpp.py +0 -0
  133. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_golang.py +0 -0
  134. {shacl2code-1.0.1 → shacl2code-1.1.0}/tests/test_jsonschema.py +0 -0
  135. {shacl2code-1.0.1 → 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.1
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
@@ -36,6 +36,30 @@ DATATYPE_PYTHON_TYPES = {
36
36
  }
37
37
 
38
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
+
39
63
  def varname(*name):
40
64
  """Make a valid Python variable name."""
41
65
  name = "_".join(name)
@@ -45,8 +69,8 @@ def varname(*name):
45
69
  name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
46
70
  # Consolidate runs of "_" to a single one
47
71
  name = re.sub(r"__+", "_", name)
48
- # Add a _ to anything that is a python keyword
49
- 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:
50
74
  name = name + "_"
51
75
  return name
52
76
 
@@ -1008,6 +1008,18 @@ class SHACLObject(metaclass=SHACLObjectMeta):
1008
1008
  for k, v in kwargs.items():
1009
1009
  setattr(self, k, v)
1010
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
+
1011
1023
  def get_type(self) -> str:
1012
1024
  """Return the fully qualified IRI type of this object."""
1013
1025
  return self._TYPE
@@ -1546,6 +1558,22 @@ class SHACLObjectSet(object):
1546
1558
  self.add_index(obj)
1547
1559
  return obj
1548
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
+
1549
1577
  def update(self, *others: Iterable[SHACLObject]) -> None:
1550
1578
  """
1551
1579
  Update object set adding all objects in each other iterable
@@ -233,6 +233,18 @@ class ClassProp:
233
233
  deprecated: bool = ...
234
234
 
235
235
 
236
+ class EncodeState: ...
237
+
238
+
239
+ class DecodeState: ...
240
+
241
+
242
+ class Encoder(ABC): ...
243
+
244
+
245
+ class Decoder(ABC): ...
246
+
247
+
236
248
  T_SHACLObject = TypeVar("T_SHACLObject", bound="SHACLObject")
237
249
 
238
250
 
@@ -249,6 +261,8 @@ class SHACLObject:
249
261
  PROPERTIES: ClassVar[List[ClassProp]]
250
262
 
251
263
  def __init__(self, **kwargs: Any) -> None: ...
264
+ def get_id(self) -> Optional[str]: ...
265
+ def set_id(self, value: Optional[str]) -> None: ...
252
266
  def get_type(self) -> str: ...
253
267
  def get_compact_type(self) -> Optional[str]: ...
254
268
  def __getitem__(self, iri: str) -> Any: ...
@@ -264,6 +278,19 @@ class SHACLObject:
264
278
  def iter_objects(
265
279
  self, *, recursive: bool = False, visited: Optional[Set[SHACLObject]] = None
266
280
  ) -> Iterable[SHACLObject]: ...
281
+ def encode(self, encoder: "Encoder", state: "EncodeState") -> None: ...
282
+ @classmethod
283
+ def _make_object(cls: Type[T_SHACLObject], typ: str) -> SHACLObject: ...
284
+ @classmethod
285
+ def decode(
286
+ cls: Type[T_SHACLObject], decoder: "Decoder", state: "DecodeState"
287
+ ) -> SHACLObject: ...
288
+ def link_helper(
289
+ self,
290
+ objectset: SHACLObjectSet,
291
+ missing: Optional[Set[str]],
292
+ visited: Set[SHACLObject],
293
+ ) -> None: ...
267
294
  def __hash__(self) -> int: ...
268
295
  def __eq__(self, other: Any) -> bool: ...
269
296
  def __lt__(self, other: Any) -> bool: ...
@@ -297,6 +324,7 @@ class SHACLObjectSet:
297
324
  def create_index(self) -> None: ...
298
325
  def add_index(self, obj: SHACLObject) -> None: ...
299
326
  def add(self, obj: T_SHACLObject) -> T_SHACLObject: ...
327
+ def remove(self, obj: SHACLObject) -> None: ...
300
328
  def update(self, *others: Iterable[SHACLObject]) -> None: ...
301
329
  def __contains__(self, item: SHACLObject) -> bool: ...
302
330
  def link(self) -> Set[str]: ...
@@ -316,6 +344,14 @@ class SHACLObjectSet:
316
344
  def inline_blank_nodes(self) -> None: ...
317
345
  def expand_iri(self, iri: str, default: Optional[str] = None) -> Optional[str]: ...
318
346
  def compact_iri(self, iri: str, default: Optional[str] = None) -> Optional[str]: ...
347
+ def encode(
348
+ self, encoder: "Encoder", state: "EncodeState", force_at_graph: bool = False
349
+ ) -> None: ...
350
+ def decode(
351
+ self,
352
+ decoder: "Decoder",
353
+ state: Optional["DecodeState"] = None,
354
+ ) -> None: ...
319
355
 
320
356
 
321
357
  class JSONLDDeserializer:
@@ -403,8 +439,14 @@ class {{ varname(*class.clsname) }}(
403
439
 
404
440
  def __init__(
405
441
  self,
406
- {%- if class.properties %}
442
+ {%- if class.is_extensible %}
443
+ typ: Optional[str] = None,
444
+ {%- endif %}
445
+ {%- if class.id_property or class.properties %}
407
446
  *,
447
+ {%- if class.id_property %}
448
+ {{ class.id_property }}: Optional[str] = None,
449
+ {%- endif %}
408
450
  {%- for prop in class.properties %}
409
451
  {%- set is_list = prop.max_count is none or prop.max_count != 1 %}
410
452
  {%- if prop.enum_values %}
@@ -424,6 +466,9 @@ class {{ varname(*class.clsname) }}(
424
466
  **kwargs: Any
425
467
  ) -> None: ...
426
468
 
469
+ {%- if class.id_property %}
470
+ {{ class.id_property }}: Optional[str]
471
+ {%- endif %}
427
472
  {%- for prop in class.properties %}
428
473
  {%- set is_list = prop.max_count is none or prop.max_count != 1 %}
429
474
  {%- if prop.enum_values %}
@@ -433,7 +478,14 @@ class {{ varname(*class.clsname) }}(
433
478
  {%- else %}
434
479
  {%- set ptype = DATATYPE_PYTHON_TYPES[prop.datatype] %}
435
480
  {%- endif %}
436
- {{ varname(prop.varname) }}: {% if is_list %}ListProxy[{{ ptype }}]{% else %}Optional[{{ ptype }}]{% endif %}
481
+ {%- if is_list %}
482
+ @property
483
+ def {{ varname(prop.varname) }}(self) -> ListProxy[{{ ptype }}]: ...
484
+ @{{ varname(prop.varname) }}.setter
485
+ def {{ varname(prop.varname) }}(self, value: Iterable[{{ ptype }}]) -> None: ...
486
+ {%- else %}
487
+ {{ varname(prop.varname) }}: Optional[{{ ptype }}]
488
+ {%- endif %}
437
489
  {%- endfor %}
438
490
 
439
491
  {% endfor %}
@@ -0,0 +1 @@
1
+ VERSION = "1.1.0"
@@ -0,0 +1,34 @@
1
+ @base <http://example.org/reserved#> .
2
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
3
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
4
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
5
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
6
+
7
+ # A class whose properties collide with SHACLObject keywords.
8
+
9
+ <test-rw-class> a rdfs:Class, sh:NodeShape, owl:Class;
10
+ sh:property [
11
+ sh:datatype xsd:string ;
12
+ sh:name "get_id" ;
13
+ sh:path <prop/get_id> ;
14
+ sh:maxCount 1
15
+ ],
16
+ [
17
+ sh:datatype xsd:string ;
18
+ sh:name "set_id" ;
19
+ sh:path <prop/set_id> ;
20
+ sh:maxCount 1
21
+ ],
22
+ [
23
+ sh:datatype xsd:string ;
24
+ sh:name "encode" ;
25
+ sh:path <prop/encode> ;
26
+ sh:maxCount 1
27
+ ],
28
+ [
29
+ sh:datatype xsd:string ;
30
+ sh:name "class" ;
31
+ sh:path <prop/class> ;
32
+ sh:maxCount 1
33
+ ]
34
+ .
@@ -679,6 +679,11 @@ def test_node_kind_iri(model, test_context_url, cls):
679
679
  ["id_prop_class", "inherited_id_prop_class"],
680
680
  )
681
681
  def test_id_name(model, test_context_url, cls):
682
+ """
683
+ Test that alternate ID property names work correctly,
684
+ including serialization and initialization.
685
+ """
686
+
682
687
  s = model.JSONLDSerializer()
683
688
  c = getattr(model, cls)()
684
689
 
@@ -696,7 +701,7 @@ def test_id_name(model, test_context_url, cls):
696
701
  assert c.testid is None
697
702
  assert c._id is None
698
703
 
699
- # Serialization should the alias name
704
+ # Serialization should use the alias name
700
705
  c._id = TEST_ID
701
706
  result = s.serialize_data(model.SHACLObjectSet([c]))
702
707
  assert result == {
@@ -705,6 +710,69 @@ def test_id_name(model, test_context_url, cls):
705
710
  "testid": TEST_ID,
706
711
  }
707
712
 
713
+ # Test initialization using ID alias
714
+ c2 = getattr(model, cls)(testid=TEST_ID)
715
+ assert c2.get_id() == TEST_ID
716
+ assert c2.testid == TEST_ID
717
+
718
+
719
+ def test_get_id_none(model):
720
+ """get_id returns None when no ID is set."""
721
+ c = model.test_class()
722
+ assert c.get_id() is None
723
+
724
+
725
+ def test_get_id_set(model):
726
+ """get_id returns the assigned IRI."""
727
+ c = model.test_class()
728
+ c._id = "http://example.com/obj"
729
+ assert c.get_id() == "http://example.com/obj"
730
+
731
+
732
+ def test_set_id(model):
733
+ """set_id assigns the IRI."""
734
+ c = model.test_class()
735
+ c.set_id("http://example.com/obj")
736
+ assert c._id == "http://example.com/obj"
737
+ assert c.get_id() == "http://example.com/obj"
738
+
739
+
740
+ def test_set_id_none(model):
741
+ """set_id(None) clears the ID."""
742
+ c = model.test_class()
743
+ c.set_id("http://example.com/obj")
744
+ c.set_id(None)
745
+ assert c.get_id() is None
746
+ assert c._id is None
747
+
748
+
749
+ def test_set_id_roundtrip(model, test_context_url):
750
+ """Object serialized with set_id value encodes correct @id."""
751
+ s = model.JSONLDSerializer()
752
+ c = model.test_class()
753
+ c.set_id("http://example.com/set-id-obj")
754
+ result = s.serialize_data(model.SHACLObjectSet([c]))
755
+ assert result["@id"] == "http://example.com/set-id-obj"
756
+
757
+
758
+ @pytest.mark.parametrize("cls", ["id_prop_class", "inherited_id_prop_class"])
759
+ def test_get_id_with_alias(model, cls):
760
+ """get_id returns ID set via alias property."""
761
+ # id-prop-class is a class with an ID alias "testid"
762
+ c = getattr(model, cls)()
763
+ c.testid = "http://example.com/alias-obj"
764
+ assert c.get_id() == "http://example.com/alias-obj"
765
+
766
+
767
+ @pytest.mark.parametrize("cls", ["id_prop_class", "inherited_id_prop_class"])
768
+ def test_set_id_with_alias(model, cls):
769
+ """set_id makes ID accessible through alias property."""
770
+ # id-prop-class is a class with an ID alias "testid"
771
+ c = getattr(model, cls)()
772
+ c.set_id("http://example.com/alias-obj")
773
+ assert c.testid == "http://example.com/alias-obj"
774
+ assert c.get_id() == "http://example.com/alias-obj"
775
+
708
776
 
709
777
  SAME_AS_VALUE = object()
710
778
 
@@ -1740,6 +1808,44 @@ def test_objset_foreach_type(model, roundtrip):
1740
1808
  assert set(objset.foreach_type(model.abstract_class, match_subclass=True)) == expect
1741
1809
 
1742
1810
 
1811
+ def test_objset_remove(model):
1812
+ """
1813
+ Tests that removing an object from the object set works correctly
1814
+ """
1815
+ obj1 = model.test_class(_id="https://example.org/obj1")
1816
+ obj2 = model.test_class(_id="https://example.org/obj2")
1817
+
1818
+ objset = model.SHACLObjectSet([obj1, obj2])
1819
+ assert obj1 in objset
1820
+ assert obj2 in objset
1821
+
1822
+ objset.remove(obj1)
1823
+
1824
+ assert obj1 not in objset
1825
+ assert obj2 in objset
1826
+
1827
+
1828
+ def test_objset_remove_not_existing(model):
1829
+ """
1830
+ Tests that removing an object that is not in the object set does not
1831
+ raise an error. It should just do nothing silently.
1832
+ """
1833
+ obj = model.test_class(_id="https://example.org/obj")
1834
+ objset = model.SHACLObjectSet()
1835
+
1836
+ objset.remove(obj)
1837
+
1838
+
1839
+ def test_objset_remove_invalid_type(model):
1840
+ """
1841
+ Tests that removing an object that is not a SHACLObject raises a TypeError
1842
+ """
1843
+ objset = model.SHACLObjectSet()
1844
+
1845
+ with pytest.raises(TypeError):
1846
+ objset.remove("not-a-shacl-object")
1847
+
1848
+
1743
1849
  @pytest.mark.parametrize(
1744
1850
  "abstract,concrete",
1745
1851
  [
@@ -1865,6 +1971,56 @@ def test_slots_no(tmp_path):
1865
1971
  assert "_USE_SLOTS = False" in text
1866
1972
 
1867
1973
 
1974
+ RESERVED_WORDS_MODEL = DATA_DIR / "reserved-words.ttl"
1975
+
1976
+
1977
+ def test_varname_reserved_words(tmp_path):
1978
+ """
1979
+ varname() must append '_' to property names that collide with
1980
+ SHACLOBJECT_RESERVED_WORDS or Python keywords so that the generated
1981
+ ClassProp pyname, the __init__ kwargs, and _OBJ_PY_PROPS keys all agree.
1982
+ """
1983
+ output_dir = tmp_path / "temp_rwmodel"
1984
+ shacl2code_generate(["--input", RESERVED_WORDS_MODEL], [], output_dir)
1985
+
1986
+ # Generated source must contain the renamed names, not the originals
1987
+ text = (output_dir / "model.py").read_text()
1988
+ for renamed in ("get_id_", "set_id_", "encode_", "class_"):
1989
+ assert (
1990
+ renamed in text
1991
+ ), f"expected renamed property '{renamed}' in generated code"
1992
+ for original in ('"get_id"', '"set_id"', '"encode"', '"class"'):
1993
+ assert (
1994
+ f"ClassProp({original}," not in text
1995
+ ), f"unrenamed property {original} found as ClassProp pyname"
1996
+
1997
+ # Import the generated module and exercise the renamed properties
1998
+ old_path = sys.path[:]
1999
+ sys.path.append(str(tmp_path))
2000
+ try:
2001
+ import temp_rwmodel as m # noqa: PLC0415
2002
+
2003
+ cls = m.SHACLObject.CLASSES["http://example.org/test-rw-class"]
2004
+
2005
+ # Renamed kwargs must work at construction time
2006
+ obj = cls(get_id_="a", set_id_="b", encode_="c", class_="d")
2007
+ assert obj.get_id_ == "a"
2008
+ assert obj.set_id_ == "b"
2009
+ assert obj.encode_ == "c"
2010
+ assert obj.class_ == "d"
2011
+
2012
+ # SHACLObject.get_id() must still return the object IRI,
2013
+ # not the value of the prop (get_id_)
2014
+ assert obj.get_id() is None
2015
+ obj.set_id("http://example.org/obj")
2016
+ assert obj.get_id() == "http://example.org/obj"
2017
+ finally:
2018
+ sys.path = old_path
2019
+ for mod in list(sys.modules):
2020
+ if mod == "rwmodel" or mod.startswith("rwmodel."):
2021
+ del sys.modules[mod]
2022
+
2023
+
1868
2024
  def test_extensible_properties(model, model_context_url):
1869
2025
 
1870
2026
  class Extension(model.extensible_class):
@@ -2113,6 +2269,9 @@ def test_introspection_extensible(model):
2113
2269
 
2114
2270
 
2115
2271
  def test_version(model):
2272
+ """
2273
+ Tests that the version string is correctly mapped.
2274
+ """
2116
2275
  from shacl2code.util import convert_version_string
2117
2276
 
2118
2277
  assert model.VERSION_STRING == MODEL_VERSION
@@ -1 +0,0 @@
1
- VERSION = "1.0.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes