onto2py
What it is
A Turtle (.ttl) ontology-to-Python generator that:
- Parses RDF/OWL/SHACL with rdflib
- Extracts classes, inheritance, properties, and some constraints
- Generates Pydantic models that can:
- Serialize to RDF triples (
rdf()) - Load from a SPARQL result stream (
from_iri()via an injected query executor)
- Serialize to RDF triples (
- Optionally writes the generated module next to the input
.ttland creates per-class “action” stubs underontologies/classes/.
Public API
Dataclasses
PropertyInfo- Holds extracted property metadata:
name,property_type("data"/"object")range_classes: Dict[str, Optional[int]](object-property target classes with optional cardinality)datatype(mapped Python type string for data properties)required(from SHACLminCount)description(fromskos:definition)default_value(used for injected metadata fields)
- Holds extracted property metadata:
ClassInfo- Holds extracted class metadata:
name,uri,label,descriptionparent_classesproperties: List[PropertyInfo]property_uris: Dict[str, str](property name → URI)
- Holds extracted class metadata:
Main function
onto2py(ttl_file: str | io.TextIOBase, overwrite: bool = False) -> str- Converts a TTL ontology to generated Python code (string).
- If
ttl_fileis a path:- Writes a sibling
.pyfile - Runs
ruff(if available) - Generates per-class stub files via
create_class_files(...)
- Writes a sibling
- If
ttl_fileis a file-like object:- Only returns the generated code (no filesystem writes).
Name extraction helpers
extract_class_name_from_label(label: str) -> Optional[str]- Converts an
rdfs:labelto a PascalCase Python identifier.
- Converts an
extract_class_name(uri, g: Optional[rdflib.Graph] = None) -> Optional[str]- Prefers
rdfs:label; otherwise derives from URI fragment/path.
- Prefers
extract_property_name_from_label(label: str) -> Optional[str]- Converts an
rdfs:labelto a snake_case Python identifier.
- Converts an
extract_property_name(uri, g: Optional[rdflib.Graph] = None) -> Optional[str]- Prefers
rdfs:label; otherwise derives from URI fragment/path.
- Prefers
OWL restriction / class-expression helpers
extract_classes_from_union(g, union_node, classes) -> List[str]extract_classes_from_intersection(g, intersection_node, classes, OWL) -> List[str]extract_cardinality_from_restriction(g, restriction, OWL) -> Optional[int]extract_classes_with_cardinality_from_intersection(g, intersection_node, classes, OWL) -> Dict[str, Optional[int]]extract_restriction_properties(g, restriction, class_uri, class_info, classes, OWL) -> None- Adds object properties inferred from
rdfs:subClassOfOWLRestrictionnodes using:owl:onProperty+owl:allValuesFrom/owl:someValuesFrom/owl:onClass- Handles
owl:unionOfandowl:intersectionOf - Extracts limited cardinality info from
owl:cardinality/minCardinality/maxCardinality
- Adds object properties inferred from
RDF/SHACL extraction helpers
get_label(g, resource) -> Optional[str](fromrdfs:label)get_description(g, resource) -> Optional[str](fromrdfs:comment, fallbackrdfs:label)get_property_description(g, prop) -> Optional[str](fromskos:definition)get_property_range(g, prop, classes) -> Dict[str, Optional[int]](fromrdfs:range)get_datatype_range(g, prop) -> str- Maps common XSD datatypes to Python type strings (else
"Any").
- Maps common XSD datatypes to Python type strings (else
extract_shacl_constraints(g, classes, properties, SHACL) -> Noneprocess_property_shape(g, prop_shape, class_info, properties, SHACL) -> None- Applies:
sh:minCount→required = Truewhen> 0sh:maxCount→ sets cardinality for allrange_classes(1 vs >1)
- Applies:
Property inheritance / metadata injection
inherit_parent_properties(classes: Dict[str, ClassInfo]) -> None- Copies parent properties into children (preserves
requiredflag as stored).
- Copies parent properties into children (preserves
add_metadata_properties(g, classes: Dict[str, ClassInfo]) -> None- Adds to all classes if missing:
rdfs:label(required,str)dcterms:created(required,datetime.datetime, defaultdatetime.datetime.now())dcterms:creator(required; stored as a"data"property withdefault_value='os.environ.get("USER")')
- Adds to all classes if missing:
Code generation
generate_python_code(classes, properties) -> str- Emits a module containing:
- Imports (conditionally includes
datetime,os,List,Union, etc.) RDFEntitybase model (Pydantic) withfrom_iri()andrdf()- One Pydantic model per ontology class
model_rebuild()calls to resolve forward refs
- Imports (conditionally includes
- Emits a module containing:
topological_sort_classes(classes) -> List[ClassInfo]- Sorts to emit parents before children (best-effort; tolerates cycles).
generate_class_code(class_info, has_any_import=False) -> List[str]generate_property_code(prop, has_any_import=False) -> List[str]
File generation (side effects)
create_class_files(ttl_file_path, classes, py_file, overwrite=False) -> None- Creates per-class stub files under:
<module_base>/ontologies/classes/<uri-path>/ClassName.py - Stub imports the generated class from the main generated module and subclasses it.
- Creates per-class stub files under:
Ruff integration utilities
apply_linting(code: str) -> str(formats via temporary file ifruffavailable)_run_ruff(paths: list[str]) -> None_find_ruff() -> Optional[str]
Configuration/Dependencies
- Runtime Python deps:
rdflibpydantic
- Optional tooling:
ruff(auto-detected asruff,<venv>/ruff, oruvx ruff) used for:ruff check --fix --extend-select Iruff format
- Ontology expectations:
- Uses RDF/OWL terms (
owl:Class,rdfs:Class,owl:ObjectProperty,owl:DatatypeProperty) - Uses SHACL (
sh:NodeShape,sh:targetClass,sh:property,sh:path,sh:minCount,sh:maxCount) - Uses
skos:definitionfor property descriptions
- Uses RDF/OWL terms (
Usage
Generate code as a string (no file writes)
from naas_abi_core.utils.onto2py.onto2py import onto2py
with open("example.ttl", "r") as f:
code = onto2py(f)
print(code[:300])
Generate code and write example.py next to example.ttl
from naas_abi_core.utils.onto2py.onto2py import onto2py
onto2py("example.ttl") # writes example.py, runs ruff if available, creates class stub files
Caveats
onto2py()writes output files only whenttl_fileis a string path; file-like input returns code only.- SHACL constraints applied are limited to:
minCount(setsrequired=True)maxCount(sets a simple cardinality affecting list vs scalar typing)
- OWL restriction parsing focuses on
rdfs:subClassOfowl:RestrictionwithonPropertyand common fillers; it does not implement full OWL semantics. create_class_files()derives package/module import paths from filesystem layout (looks for a folder starting withnaas_abiand special-cases hyphenated path segments).