Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/shacl2code/lang/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,22 @@
}


# Names a generated property would collide with; varname() renames it instead.
#
# Gap: class names are unguarded. Under a context they compact to short names,
# so a class compacting to "Property", "Ontology", "SHACLObjectSet", etc.
# silently shadows it.
SHACLOBJECT_RESERVED_WORDS = {
"AUTO_NAMED_INDIVIDUALS",
"CLASSES",
"CLOSED",
"COMPACT_TYPE",
"ID_ALIAS",
"IS_ABSTRACT",
"IS_DEPRECATED",
"NAMED_INDIVIDUALS",
"NODE_KIND",
"ONTOLOGY",
"PROPERTIES",
"TYPE",
"decode",
Expand Down
81 changes: 77 additions & 4 deletions src/shacl2code/lang/templates/python/model.py.j2
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from __future__ import annotations

import decimal
import functools
import hashlib
import json
import re
Expand All @@ -33,7 +32,7 @@ from typing import (
List,
Optional,
Set,
TYPE_CHECKING,
TYPE_CHECKING as _TYPE_CHECKING,
Tuple,
Type,
TypeVar,
Expand All @@ -42,6 +41,70 @@ from typing import (
overload,
)

# fmt: off
"""Format Guard{{ '"' }}{{ '"' }}{{ '"' }}
__all__ = [

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this enforced? Like if we forget one, or add an extra does it make an error?

@bact bact Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not by Python or by the generated binding itselft, but by the test at test_model_all_matches_public_symbols (new in this PR).

  • stubtest catch missing stub
  • test_model_all_matches_public_symbols catch missing exports or leaking imports

I hope I getting it right this time. We have fixing this several times. The test should help use catching that.
The idea is import * from of the generated binding should only import things specifically from the binding, and not other things like imports (standard lib or rdflib that the binding happens to import for its internal use) or internal variables (prefixed with _).

"AnyURIProp",
"BooleanProp",
"CONTEXT_URLS",
"ClassProp",
"DataPath",
"DateTimeProp",
"DateTimeStampProp",
"DecodeError",
"DecodeState",
"Decoder",
"EncodeError",
"EncodeState",
"Encoder",
"EnumProp",
"FloatProp",
"IRIProp",
"IntegerProp",
"JSONLDDecoder",
"JSONLDDeserializer",
"JSONLDEncoder",
"JSONLDInlineEncoder",
"JSONLDInlineSerializer",
"JSONLDSerializer",
"ListProp",
"ListProxy",
"NodeKind",
"NonNegativeIntegerProp",
"ObjectProp",
"Ontology",
"PathError",
"PositiveIntegerProp",
"Property",
"SHACLExtensibleObject",
"SHACLObject",
"SHACLObjectMeta",
"SHACLObjectSet",
"StringProp",
"T_PropV",
"T_SHACLObject",
"ValidationError",
"check_type",
"decode_context",
"encode_context",
"is_IRI",
"is_blank_node",
{%- if version_str %}
"VERSION_STRING",
{%- endif %}
{%- if version %}
"VERSION",
{%- endif %}
{%- for o in ontologies %}
"{{ varname(o.name).upper() }}",
{%- endfor %}
{%- for class in classes %}
"{{ varname(*class.clsname) }}",
{%- endfor %}
]
{{ '"' }}{{ '"' }}{{ '"' }}Format Guard"""
# fmt: on

T_PropV = TypeVar("T_PropV")


Expand Down Expand Up @@ -1021,7 +1084,6 @@ _ALL_NAMED_INDIVIDUAL_IDS: Set[str] = set()
T_SHACLObject = TypeVar("T_SHACLObject", bound="SHACLObject")


@functools.total_ordering
class SHACLObject(metaclass=SHACLObjectMeta):
"""Base class for all SHACL-defined objects, providing property access, encoding, and decoding."""

Expand Down Expand Up @@ -1537,6 +1599,15 @@ class SHACLObject(metaclass=SHACLObjectMeta):
def __lt__(self, other: Any) -> bool:
return SHACLObject._sort_key(self) < SHACLObject._sort_key(other)

def __le__(self, other: Any) -> bool:
return SHACLObject._sort_key(self) <= SHACLObject._sort_key(other)

def __gt__(self, other: Any) -> bool:
return SHACLObject._sort_key(self) > SHACLObject._sort_key(other)

def __ge__(self, other: Any) -> bool:
return SHACLObject._sort_key(self) >= SHACLObject._sort_key(other)


class SHACLExtensibleObject(SHACLObject):
"""A SHACLObject that accepts and round-trips arbitrary IRI-keyed extension properties."""
Expand Down Expand Up @@ -2960,7 +3031,7 @@ try:
from rdflib.namespace import RDF
from rdflib.term import IdentifiedNode

if TYPE_CHECKING:
if _TYPE_CHECKING:
from rdflib import Graph # noqa: I300
from rdflib.term import Node

Expand Down Expand Up @@ -3218,6 +3289,8 @@ try:
state = EncodeState(objset)
objset.encode(e, state)

__all__ += ["RDFDecoder", "RDFDeserializer", "RDFEncoder", "RDFSerializer"]

except ImportError:
pass

Expand Down
Loading
Loading