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
19 changes: 13 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ dependencies = [
"tomlkit >= 0.11.4",
"inflect >= 6.0.0",
"linkml-runtime >= 1.10.0, < 2",
# NB: the *reverse* Frictionless adapter trans-spec needs the
# is_str / is_bool / is_list type predicates, which land in
# linkml-map 0.5.3. Forward adapter and codes utility work on
# 0.5.2. Bump this and remove the xfail in
# tests/test_adapters/test_frictionless.py once 0.5.3 ships.
"linkml-map >= 0.5.2, < 1",
# Pinned to linkml-map main (see [tool.uv.sources]) — recent work
# adds dot-path populated_from, safe-builtin functions, dictionary_key,
# strict expression mode, and the SchemaReference shape for
# source/target_schema. The EML importer and the next batch of
# adapters need these. This pin also satisfies the is_str /
# is_bool / is_list predicate need that the *reverse* Frictionless
# adapter has — the xfail in tests/test_adapters/test_frictionless.py
# can come off once we're on a real release tag. Restore a version
# constraint and drop the source override here when 0.5.3+ releases.
"linkml-map",
Comment on lines +51 to +60
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct — [tool.uv.sources] is uv-only and the wheel's Requires-Dist is unconstrained. Not solving in this PR: we're deliberately holding this branch (and #212) until linkml-map 0.5.3 releases, at which point the pin reverts to a regular version constraint. Switching to a PEP 508 direct-URL form (linkml-map @ git+...) would propagate to pip but block any release from this state (PyPI rejects direct-URL deps), which is an even worse interim. Leaving open as a tracked concern; we expect this to be naturally resolved when 0.5.3 ships and we drop the pin.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged but by design — this PR is the temporary dev infrastructure noted in the PR body ("Revert the pin (back to a version constraint) when linkml-map 0.5.3+ releases"). The [tool.uv.sources] directive lets uv sync resolve to linkml-map main for local/CI work depending on unreleased features. Once linkml-map 0.5.3 hits PyPI, this becomes a normal >= 0.5.3 constraint in [project.dependencies] that the published wheel will carry. Leaving this thread open as a visible reminder to make that swap before merge.

"click >= 8.1.7, < 9",
"deprecated >= 1.2.15, < 2",
"sqlalchemy >= 2.0.36, < 3",
Expand Down Expand Up @@ -94,6 +98,9 @@ extract-schema = "schema_automator.utils.schema_extractor:cli"
repository = "https://github.com/linkml/schema-automator/"
documentation = "https://linkml.io/schema-automator/"

[tool.uv.sources]
linkml-map = { git = "https://github.com/linkml/linkml-map.git", branch = "main" }

[tool.uv-dynamic-versioning]
vcs = "git"
style = "pep440"
Expand Down
6 changes: 4 additions & 2 deletions schema_automator/adapters/dbgap/dbgap_to_dd.transform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ description: >-
``example_values`` but it would mix "examples" with "exhaustive
observed values" and confuse the slot's purpose.

source_schema: https://w3id.org/linkml/schema-automator/dbgap
target_schema: https://w3id.org/linkml/schema-automator/data-dictionary
source_schema:
name: https://w3id.org/linkml/schema-automator/dbgap
target_schema:
name: https://w3id.org/linkml/schema-automator/data-dictionary

class_derivations:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ description: >-
constraint slot has a known semantic type, so the predicates
double as both sentinel-filter and type-correctness check.

source_schema: https://w3id.org/linkml/schema-automator/data-dictionary
target_schema: https://specs.frictionlessdata.io/table-schema
source_schema:
name: https://w3id.org/linkml/schema-automator/data-dictionary
target_schema:
name: https://specs.frictionlessdata.io/table-schema

class_derivations:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ description: >-
- `unique`, `minLength`, `maxLength` constraints (no DD equivalent).
- Foreign keys, `missingValues`, `primaryKey` (drop; out of DD scope).

source_schema: https://specs.frictionlessdata.io/table-schema
target_schema: https://w3id.org/linkml/schema-automator/data-dictionary
source_schema:
name: https://specs.frictionlessdata.io/table-schema
target_schema:
name: https://w3id.org/linkml/schema-automator/data-dictionary

class_derivations:

Expand Down
6 changes: 5 additions & 1 deletion schema_automator/importers/xsd_import_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,13 @@ def visit_element(self, el: etree._Element) -> SlotDefinition:
name=cls_name,
class_uri=urljoin(self.target_ns, cls_name) if self.target_ns else None
)
self.sb.add_class(cls)
slot.range = cls_name
self.visit_complex_type(child, cls)
# In linkml-runtime >=1.11 add_class() pydantic-validates
# its input, so the registered class is a copy; later
# mutations to the local `cls` don't reach it. Register
# after populating attributes.
self.sb.add_class(cls)
elif child.tag == SIMPLE_TYPE:
# If we find a simple type, the range is a restriction of a primitive type
self.visit_simple_type(child, slot)
Expand Down
11 changes: 7 additions & 4 deletions schema_automator/utils/instance_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ def create_index(self, obj: Union[YAMLRoot, Dict, List] = None,
elif isinstance(obj, YAMLRoot):
cn = type(obj).class_name
for slot in sv.class_induced_slots(cn):
v = getattr(obj, slot.alias, None)
# slot.alias is None unless explicitly set in the schema;
# linkml-runtime <1.11 used to fall back to slot.name on
# access, 1.11 returns None. Mirror the old behavior.
v = getattr(obj, slot.alias or slot.name, None)
if v is not None:
if slot.identifier:
self.references.append((parent, v))
Expand All @@ -69,7 +72,7 @@ def create_index(self, obj: Union[YAMLRoot, Dict, List] = None,
if slot.range in sv.all_classes():
if isinstance(v, str):
self.references.append((parent, v))
self.create_index(v, path=path+[slot.alias], parent=obj)
self.create_index(v, path=path+[slot.alias or slot.name], parent=obj)
logging.debug(f'Created index')

def fetch_object(self, id_ref: ID_REF, default_val = None) -> Optional[YAMLRoot]:
Expand Down Expand Up @@ -133,7 +136,7 @@ def extract(self, seeds: List[ID_REF], preserve_slots: List[SlotDefinitionName]
root_cls = type(self.root)
for slot in self.schemaview.class_induced_slots(root_cls.class_name):
if slot.required:
preserve_slots.append(slot.alias)
preserve_slots.append(slot.alias or slot.name)
for slot in preserve_slots:
xdict[slot] = getattr(self.root, slot)
#print(f'VISITED={visited}')
Expand All @@ -156,7 +159,7 @@ def get_object_references(self, start_obj: Optional[Union[YAMLRoot, Dict, List]]
obj = seeds.pop()
if isinstance(obj, YAMLRoot):
for slot in sv.class_induced_slots(type(obj).class_name):
v = getattr(obj, slot.alias, None)
v = getattr(obj, slot.alias or slot.name, None)
if v is not None:
if slot.identifier:
refs.append(v)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_importers/test_rdfs_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def test_infer_prefix():
"""
rdf = StringIO("""
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix foo: <https://foo.com> .
@prefix foo: <https://foo.com/> .

foo:Class a rdfs:Class ;
rdfs:comment "A class." .
Expand All @@ -73,7 +73,7 @@ def test_infer_prefix():
schema = engine.convert(rdf)
# Although not explicitly provided, the importer should realise that the prefix is "foo"
assert schema.default_prefix == "foo"
assert schema.id == "https://foo.com"
assert schema.id == "https://foo.com/"
assert schema.name == "foo"

def test_from_rdfs():
Expand Down
28 changes: 12 additions & 16 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading