Patch LinkML's ifabsent handling, add regression tests #179

Merged
christian-monch merged 2 commits from add-ifabsent-patch into master 2025-12-15 12:53:47 +00:00
7 changed files with 102 additions and 2 deletions

View file

@ -22,8 +22,14 @@ jobs:
- name: run tests
run: |
hatch run tests:run --ignore=dump_things_service/tests/test_generators.py
hatch run tests:run \
--ignore=dump_things_service/tests/test_generators.py \
--ignore=dump_things_service/tests/test_ifabsent_patch.py
- name: run generator tests
run: |
hatch run tests:run dump_things_service/tests/test_generators.py
- name: run ifabsent-patch tests
run: |
hatch run tests:run dump_things_service/tests/test_ifabsent_patch.py

View file

@ -1,3 +1,10 @@
# 5.3.2 (2025-12-15)
## Bugfixes
- add a patch for faulty `ifabsent`-code generation in LinkML's Pydantic-code generator.
# 5.3.1 (2025-12-11)
## Bugfixes

View file

@ -1 +1 @@
__version__ = '5.3.1'
__version__ = '5.3.2'

View file

@ -1,5 +1,6 @@
import dump_things_service.patches.compile
import dump_things_service.patches.enumerations
import dump_things_service.patches.ifabsent_processing
import dump_things_service.patches.rdflib_loader
import dump_things_service.patches.yamlutils

View file

@ -0,0 +1,22 @@
""" Monkeypatch for linkml.generators.common.ifabsent_processor.IfAbsentProcessor
Patches the ifabsent-processor to not use namespace objects. Those are not
generated in the pydantic code generator.
"""
import logging
from importlib import import_module
logger = logging.getLogger('dump_things_service')
def patched_uri_for(self, s: str) -> str:
uri = str(self.schema_view.namespaces().uri_for(s))
curie = self.schema_view.namespaces().curie_for(uri, True)
return f"'{curie}'" if curie else self._strval(uri)
logger.info('patching linkml.generators.common.ifabsent_processor.IfAbsentProcessor._uri_for')
cls = import_module('linkml.generators.common.ifabsent_processor')
cls.IfAbsentProcessor._uri_for = patched_uri_for

View file

@ -0,0 +1,31 @@
id: https://example.org/ifabsent-error-trigger
name: if-absent-error-trigger
version: UNRELEASED
title: ifabsent error trigger
prefixes:
xsd: http://www.w3.org/2001/XMLSchema#
xyzrse: https://concepts.datalad.org/s/demo-rse-group/unreleased/
types:
uriorcurie:
name: uriorcurie
uri: xsd:anyURI
repr: str
slots:
creator:
slot_uri: xyzrse:creator
range: uriorcurie
classes:
ORCID:
slots:
- creator
slot_usage:
creator:
ifabsent: uriorcurie(xsd:04fa4r544)

View file

@ -0,0 +1,33 @@
from importlib import reload
from pathlib import Path
import linkml
import linkml.generators.common.ifabsent_processor as if_abs_proc
import dump_things_service.patches.ifabsent_processing
# Path to a local simple test schema
schema_dir = Path(__file__).parent / 'assets'
def _original_uri_for(self, s: str) -> str:
uri = str(self.schema_view.namespaces().uri_for(s))
return self.schema_view.namespaces().curie_for(uri, True, True) or self._strval(uri)
def test_ifabsent_patch():
# Patch in the faulty, original code and check for its result
if_abs_proc.IfAbsentProcessor._uri_for = _original_uri_for
gen1 = linkml.generators.PydanticGenerator(str(schema_dir / 'schema-ifabsent-error.yaml'))
x = gen1.serialize()
assert 'default=XSD["04fa4r544"]' in x
# Apply the patch
reload(dump_things_service.patches.ifabsent_processing)
# Check for proper code generation
gen2 = linkml.generators.PydanticGenerator(str(schema_dir / 'schema-ifabsent-error.yaml'))
y = gen2.serialize()
assert 'XSD' not in y