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
2 changes: 1 addition & 1 deletion src/linkml_map/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ def compile(
tr = ObjectTransformer()
tr.source_schemaview = sv
tr.load_transformer_specification(transformer_specification)
result = compiler.compile(tr.specification)
result = compiler.compile(tr.derived_specification)
# dump as-is, no encoding
dump_output(result.serialization, None, output)

Expand Down
8 changes: 5 additions & 3 deletions src/linkml_map/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ class Compiler(ABC):

def compile(self, specification: TransformationSpecification) -> CompiledSpecification:
"""
Transform source object into an instance of the target class.
Compile a resolved transformation specification into an alternative representation.

:param specification:
:return:
:param specification: A fully resolved specification (e.g. from
``Transformer.derived_specification``). Must not be ``None``.
:return: The compiled specification.
"""
assert specification is not None, "compile() requires a resolved specification"
s = self._compile_header(specification)
for chunk in self._compile_iterator(specification):
s += chunk
Expand Down
4 changes: 0 additions & 4 deletions src/linkml_map/compiler/python_compiler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections.abc import Iterator
from copy import deepcopy
from dataclasses import dataclass

from jinja2 import Template
Expand All @@ -9,7 +8,6 @@
ClassDerivation,
TransformationSpecification,
)
from linkml_map.inference.inference import induce_missing_values

CD_TEMPLATE = """
{% macro gen_slot_derivation_value(sd, var) -%}
Expand Down Expand Up @@ -88,8 +86,6 @@ def _compile_header(self, specification: TransformationSpecification) -> str:
return s

def _compile_iterator(self, specification: TransformationSpecification) -> Iterator[str]:
specification = deepcopy(specification)
induce_missing_values(specification, self.source_schemaview)
for cd in specification.class_derivations:
yield from self._compiled_class_derivations_iter(cd)

Expand Down
26 changes: 25 additions & 1 deletion src/linkml_map/transformer/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,21 @@ class Transformer(ABC):
an instance of a source class, making use of a specification.

This is an abstract class. Different implementations will
subclass this
subclass this.

Specification normalization has two phases:

1. **Load-time normalization** (``_normalize_spec_dict``): Structural fixes
applied to a raw dict before Pydantic instantiation — YAML quirk handling,
``$ref`` expansion, dict-to-list conversion. Does not require a source schema.
All entry points (``load_transformer_specification``,
``create_transformer_specification``, ``Session``, ``loaders``) go through
this single method.

2. **Schema-bind-time induction** (``derived_specification``): Semantic defaults
inferred from the source schema — ``populated_from``, ``range``, foreign-key
resolution. Runs lazily on first access to ``derived_specification`` and
requires ``source_schemaview`` to be set.
"""

specification: TransformationSpecification = None
Expand Down Expand Up @@ -218,6 +232,16 @@ def _apply_source_schema_patches(self) -> None:

@property
def derived_specification(self) -> TransformationSpecification | None:
"""Return the specification with schema-inferred defaults filled in.

Creates a deep copy of ``self.specification``, applies any source schema
patches, then calls ``induce_missing_values`` to fill in ``populated_from``,
``range``, and other fields that require knowledge of the source schema.
The result is cached for subsequent access.

This is the second phase of normalization — see the class docstring for
the full two-phase pipeline.
"""
if self._derived_specification is None:
if self.specification is None:
return None
Expand Down
32 changes: 16 additions & 16 deletions tests/input/examples/personinfo_basic/output/personinfo_compiled.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
| agents | | None | persons | . | . |
| agents | | Agent | persons | . | . |

### Entity `<-` None
### Entity `<-` Entity

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
Expand All @@ -18,41 +18,41 @@

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
| id | | None | None | . | . |
| id | | None | id | . | . |
| label | | None | name | . | . |
| age | | None | [expression] | . | . |
| primary_email | | None | None | . | . |
| primary_email | | None | primary_email | . | . |
| secondary_email | | None | [expression] | . | . |
| gender | | None | [expression] | . | . |
| driving_since | | None | [expression] | . | . |
| first_known_event | | None | [expression] | . | . |
| death_date | | None | [expression] | . | . |
| current_address | | None | None | . | . |
| has_familial_relationships | | None | has_familial_relationships | . | . |
| current_address | | Address | current_address | . | . |
| has_familial_relationships | | FamilialRelationship | has_familial_relationships | . | . |

### Job `<-` None
### Job `<-` Job

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
| type | | None | None | . | . |
| type | | None | type | . | . |
| current | | None | [expression] | . | . |

### Address `<-` Address

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
| address_of | | None | [expression] | . | . |
| street | | None | None | . | . |
| city | | None | None | . | . |
| street | | None | street | . | . |
| city | | None | city | . | . |

### FamilialRelationship `<-` FamilialRelationship

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
| type | | None | None | . | . |
| related_to | | None | None | . | . |
| type | | None | type | . | . |
| related_to | | Agent | related_to | . | . |

### SequenceFeature `<-` None
### SequenceFeature `<-` SequenceFeature

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
Expand All @@ -62,9 +62,9 @@

| Target | Target Range | Source | Source Range | Info |
| ------ | ------ | ---- | ---- | ---- |
| id | | None | None | . | . |
| creator | | None | None | . | . |
| license | | None | None | . | . |
| id | | None | id | . | . |
| creator | | None | creator | . | . |
| license | | None | license | . | . |
| subject_id | | None | [expression] | . | . |
| subject_name | | None | [expression] | . | . |
| object_id | | None | [expression] | . | . |
Expand Down
7 changes: 4 additions & 3 deletions tests/test_compiler/test_python_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import tests.input.examples.personinfo_basic.model.personinfo_model as src
from linkml_map.compiler.python_compiler import PythonCompiler
from linkml_map.utils.loaders import load_specification
from linkml_map.transformer.object_transformer import ObjectTransformer
from tests import SCHEMA1, SPECIFICATION


Expand All @@ -24,8 +24,9 @@ def compiler() -> PythonCompiler:

def test_compile(compiler: PythonCompiler) -> None:
"""Basic test of Python Compiler functionality."""
spec = load_specification(SPECIFICATION)
pycode = compiler.compile(spec)
tr = ObjectTransformer(source_schemaview=SchemaView(SCHEMA1))
tr.load_transformer_specification(SPECIFICATION)
pycode = compiler.compile(tr.derived_specification)
# TODO: include imports so that code compiles
print(pycode.serialization)
mod = compile_python(pycode.serialization)
Expand Down
12 changes: 3 additions & 9 deletions tests/test_compliance/test_compliance_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,22 +154,16 @@ def map_object(
:param raises_error: if not None, the expected error to be raised during transformation
:return: state object including transformed object plus intermediate objects
"""
pc = PythonCompiler(source_schemaview=source_sv)
python_code = pc.compile(spec)
logger.debug(f"Python Code: {python_code}\n\n")
# TODO: enable this
# print("Python Code (Generated)\n\n")
# print("```python")
# print(python_code.serialization)
# print("```\n")
# mod = python_code.module
schema_mapper = SchemaMapper(source_schemaview=source_sv)
target_schema = schema_mapper.derive_schema(spec)
target_sv = SchemaView(yaml_dumper.dumps(target_schema))
if supply_source_schema:
mapper = ObjectTransformer(source_schemaview=source_sv, specification=spec)
else:
mapper = ObjectTransformer(specification=spec)
pc = PythonCompiler(source_schemaview=source_sv)
python_code = pc.compile(mapper.derived_specification or spec)
logger.debug(f"Python Code: {python_code}\n\n")
if index:
mapper.index(source_object, target=source_root)
if raises_error:
Expand Down
Loading