Skip to content

Latest commit

 

History

History
60 lines (50 loc) · 4.47 KB

File metadata and controls

60 lines (50 loc) · 4.47 KB

Architecture

ontology-config-generator is a single-module library. The only I/O is reading the extension TTL + starter JSON and writing the output configs; everything else is pure value objects and a generic graph extraction. The heavy dependency (rdflib) is imported lazily inside the two functions that touch a graph, so the module imports — and its non-parsing paths run — on a bare interpreter.

Module map

Module Responsibility
ontology_config_generator/generator.py Everything: ConfigSpec (a spec-table row), generate_configs() (the deterministic compile + provenance + degrade-open), extract_named() (the generic OWL extractor), _deep_merge() (starter/static + extracted merge), the spec-table loader, and the main() CLI. rdflib is imported lazily inside generate_configs/extract_named.
ontology_config_generator/__init__.py Re-exports the public API (ConfigSpec, generate_configs, extract_named, DEFAULT_NAMESPACE) and __version__. No heavy import at module top.
ontology_config_generator/__main__.py python -m ontology_config_generatorgenerator.main.

Data flow

extension.ttl ──► rdflib.Graph (lazy parse; degrade-open on missing/bad file)
                        │
spec_table[i] ──────────┤  for each spec:
  {filename,            │
   extract: {key→cls},  ├─ starter_dir/filename JSON  ─┐
   static}              │                              ├─► _deep_merge(base, static)  = base
                        │  for each (key → class):     │
                        ├─ extract_named(graph, class) ─┘
                        │     • named subClassOf <ns+class>
                        │     • named rdf:type   <ns+class>
                        │     • blank nodes skipped, de-duped
                        │     • {id, label(or id), description(or "")}
                        │                              │
                        └─ _deep_merge(base, extracted) ──► + _generated provenance
                                                            ──► output_dir/filename.json

Two properties make this a compiler rather than a script: the output is a pure function of (graph, spec table, namespace) — deterministic and stamped with provenance — and every failure mode degrades open — a missing/garbage TTL or a per-class extraction error logs a warning and yields empty lists, never an exception that aborts the run.

Seams (injection points)

Seam How to use it
Which configs / class bindings The spec_table — a list of ConfigSpec (or plain dicts). Each row's extract maps an output JSON key to a bound class local-name (or a list of them, concatenated). There is no hard-coded class set.
Namespace generate_configs(..., namespace=...) / extract_named(..., namespace=...). Defaults to the neutral http://example.org/ontology/core/; point it at your core ontology's IRI.
Shape baseline ConfigSpec.static (inline fixed scaffolding) and/or starter_dir= (read-only JSON shape templates loaded per filename). Extracted non-empty lists win over the baseline; an empty extraction preserves it.
Output JSON shape Per-spec: the keys in extract + the static dict define the document. The extractor always emits {id, label, description} rows.
Storage / parsing Reads/writes are plain files. Swap generate_configs for a wrapper that streams to a DB or object store — keep the ConfigSpec contract so callers don't change.
Runtime dependencies None at import. rdflib is required only to parse a graph (lazy import); install via the dev or rdf extra.

Design invariants

  • Deterministic — no LLM, no randomness; identical inputs produce identical configs (byte-for-byte, modulo the _generated.from path).
  • Degrade-open — a missing/unparseable extension or a failed extraction yields starter/static-shaped configs with empty lists, never a raised exception.
  • Lazy heavy importrdflib is imported inside generate_configs/extract_named; import ontology_config_generator works with rdflib absent.
  • Domain-neutral — no class name and no namespace is hard-coded; both are caller-supplied (the namespace has a neutral default).
  • Blank nodes excluded — only IRI-named subjects are emitted, and each subject appears once even if it is both a subclass and an instance.