Skip to content
Merged
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
6 changes: 6 additions & 0 deletions src/shacl2code/lang/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
THIS_DIR = Path(__file__).parent


def prop_is_list(prop):
"""Whether a property's max_count allows more than one value."""
return prop.max_count is None or prop.max_count != 1


class OutputFile(object):
def __init__(self, path):
self.path = path
Expand Down Expand Up @@ -148,6 +153,7 @@ def get_all_named_individuals(cls):
"get_all_derived": get_all_derived,
"get_all_named_individuals": get_all_named_individuals,
"include_file": include_file,
"prop_is_list": prop_is_list,
**self.get_extra_env(),
}

Expand Down
5 changes: 0 additions & 5 deletions src/shacl2code/lang/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ def parent_cpp_classes(cls, classes):
return parents


def prop_is_list(prop):
return prop.max_count is None or prop.max_count != 1


def id_str(s):
return re.sub(r"[^a-zA-Z0-9_]", "_", s)

Expand Down Expand Up @@ -182,7 +178,6 @@ def suffix(s):
def get_extra_env(self):
return {
"varname": varname,
"prop_is_list": prop_is_list,
"parent_cpp_classes": parent_cpp_classes,
"macro_prefix": self.macro_prefix,
"api_def_begin": comment_wrap(textwrap.dedent(f"""\
Expand Down
7 changes: 1 addition & 6 deletions src/shacl2code/lang/golang.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import textwrap
from pathlib import Path

from .common import JinjaTemplateRender
from .common import JinjaTemplateRender, prop_is_list
from .lang import TEMPLATE_DIR, language

GO_KEYWORDS = (
Expand Down Expand Up @@ -75,10 +75,6 @@ def prop_name(prop):
return varname(prop.varname, public=False)


def prop_is_list(prop):
return prop.max_count is None or prop.max_count != 1


def prop_go_type(prop, classes):
if prop.enum_values:
return "string"
Expand Down Expand Up @@ -276,7 +272,6 @@ def get_extra_env(self):
"interface_name": interface_name,
"class_type_var": class_type_var,
"prop_name": prop_name,
"prop_is_list": prop_is_list,
"prop_go_type": prop_go_type,
"prop_ctx_name": prop_ctx_name,
"prop_decode_func": prop_decode_func,
Expand Down
14 changes: 14 additions & 0 deletions src/shacl2code/lang/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ def varname(*name):
return name


def prop_element_pytype(prop, classes):
"""Python type of a single element of prop, ignoring container shape.

Object-reference properties resolve to ``Union[str, 'ClassName']``, since
they may be set from either an id string or the referenced object.
"""
if prop.enum_values:
return "str"
if prop.class_id:
return "Union[str, '" + varname(*classes.get(prop.class_id).clsname) + "']"
return DATATYPE_PYTHON_TYPES[prop.datatype]


@language("python")
class PythonRender(JinjaTemplateRender):
"""Render Python Language Bindings."""
Expand Down Expand Up @@ -144,6 +157,7 @@ def get_file(name):
def get_extra_env(self):
return {
"varname": varname,
"prop_element_pytype": prop_element_pytype,
"DATATYPE_CLASSES": DATATYPE_CLASSES,
"DATATYPE_PYTHON_TYPES": DATATYPE_PYTHON_TYPES,
}
Expand Down
8 changes: 1 addition & 7 deletions src/shacl2code/lang/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import re
from pathlib import Path

from .common import JinjaTemplateRender
from .common import JinjaTemplateRender, prop_is_list
from .lang import TEMPLATE_DIR, language

RUST_KEYWORDS = (
Expand Down Expand Up @@ -108,11 +108,6 @@ def prop_field_name(prop):
return varname(prop.varname)


def prop_is_list(prop):
"""Check if a property is a list."""
return prop.max_count is None or prop.max_count != 1


def prop_rust_type(prop, classes):
"""Get the Rust type for a property's data type."""
if prop.enum_values:
Expand Down Expand Up @@ -268,7 +263,6 @@ def get_extra_env(self):
"type_name": type_name,
"struct_name": struct_name,
"prop_field_name": prop_field_name,
"prop_is_list": prop_is_list,
"prop_rust_type": prop_rust_type,
"prop_full_type": prop_full_type,
"const_name": const_name,
Expand Down
13 changes: 13 additions & 0 deletions src/shacl2code/lang/templates/python/_macros.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileContributor: Arthit Suriyawongkul
# SPDX-FileCopyrightText: 2026 Joshua Watt
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: MIT
#
# Shared Jinja macros for the Python templates.
{% macro class_docstring(comment) %}
{{ '"' }}{{ '"' }}{{ '"' }}
{%- for l in comment.split("\n") %}
{{ l.rstrip() }}
{%- endfor %}
{{ '"' }}{{ '"' }}{{ '"' }}
{%- endmacro %}
11 changes: 3 additions & 8 deletions src/shacl2code/lang/templates/python/model.py.j2
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#! /usr/bin/env python3
#
# {%- import "_macros.j2" as pymacros %}
# {{ disclaimer }}
#
# SPDX-License-Identifier: {{ spdx_license }}
Expand Down Expand Up @@ -3042,12 +3042,7 @@ class {{ varname(*class.clsname) }}(
{%- else -%}
SHACLObject
{%- endif -%}):
{%- if class.comment %}
{{ '"' }}{{ '"' }}{{ '"' }}
{%- for l in class.comment.split("\n") %}
{{ l.rstrip() }}
{%- endfor %}
{{ '"' }}{{ '"' }}{{ '"' }}
{%- if class.comment %}{{ pymacros.class_docstring(class.comment) }}
{% endif %}
TYPE: ClassVar[str] = "{{ class._id }}"
{%- if context.compact_iri(class._id) != class._id %}
Expand Down Expand Up @@ -3080,7 +3075,7 @@ class {{ varname(*class.clsname) }}(
{%- if class.properties %}
PROPERTIES: ClassVar[List[ClassProp]] = [
{%- for prop in class.properties %}
{%- set is_list = prop.max_count is none or prop.max_count != 1 %}
{%- set is_list = prop_is_list(prop) %}
{%- if prop.comment %}
{%- for l in prop.comment.split("\n") %}
#{{ (" " + l).rstrip() }}
Expand Down
29 changes: 6 additions & 23 deletions src/shacl2code/lang/templates/python/model.pyi.j2
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# {{ disclaimer }}
#
# {%- import "_macros.j2" as pymacros %}
# SPDX-License-Identifier: {{ spdx_license }}
"""Generated stub for the generated Python bindings."""

Expand Down Expand Up @@ -412,12 +412,7 @@ class {{ varname(*class.clsname) }}(
{%- else -%}
SHACLObject
{%- endif -%}):
{%- if class.comment %}
{{ '"' }}{{ '"' }}{{ '"' }}
{%- for l in class.comment.split("\n") %}
{{ l.rstrip() }}
{%- endfor %}
{{ '"' }}{{ '"' }}{{ '"' }}
{%- if class.comment %}{{ pymacros.class_docstring(class.comment) }}
{% endif %}
TYPE: ClassVar[str] = "{{ class._id }}"
{%- if context.compact_iri(class._id) != class._id %}
Expand Down Expand Up @@ -458,14 +453,8 @@ class {{ varname(*class.clsname) }}(
{{ class.id_property }}: Optional[str] = None,
{%- endif %}
{%- for prop in class.properties %}
{%- set is_list = prop.max_count is none or prop.max_count != 1 %}
{%- if prop.enum_values %}
{%- set ptype = "str" %}
{%- elif prop.class_id %}
{%- set ptype = "Union[str, '" ~ varname(*classes.get(prop.class_id).clsname) ~ "']" %}
{%- else %}
{%- set ptype = DATATYPE_PYTHON_TYPES[prop.datatype] %}
{%- endif %}
{%- set is_list = prop_is_list(prop) %}
{%- set ptype = prop_element_pytype(prop, classes) %}
{%- if is_list %}
{{ varname(prop.varname) }}: Optional[Iterable[{{ ptype }}]] = None,
{%- else %}
Expand All @@ -480,14 +469,8 @@ class {{ varname(*class.clsname) }}(
{{ class.id_property }}: Optional[str]
{%- endif %}
{%- for prop in class.properties %}
{%- set is_list = prop.max_count is none or prop.max_count != 1 %}
{%- if prop.enum_values %}
{%- set ptype = "str" %}
{%- elif prop.class_id %}
{%- set ptype = "Union[str, '" ~ varname(*classes.get(prop.class_id).clsname) ~ "']" %}
{%- else %}
{%- set ptype = DATATYPE_PYTHON_TYPES[prop.datatype] %}
{%- endif %}
{%- set is_list = prop_is_list(prop) %}
{%- set ptype = prop_element_pytype(prop, classes) %}
{%- if is_list %}
@property
def {{ varname(prop.varname) }}(self) -> ListProxy[{{ ptype }}]: ...
Expand Down
Loading