Skip to content

load_entity regenerates a class for a category that a packaged class already registers, and overwrites it in the type lookup #138

Description

@simontaurus

Version

osw 1.1.2, oold 0.16.5, opensemantic.base 0.42.8.post1000002004006.

Summary

When load_entity autofetches a schema, it compiles a new class into
osw.model.entity even if a packaged class is already registered for
that category IRI. The generated class then takes over the entry in
oold's type lookup, so every later resolution by IRI returns the
generated class instead of the packaged one.

Measured

registry size: 153
key: Category:OSW51ad0d17...  ->  osw.model.entity.Database

packaged  (opensemantic.base.v1._model.Database)  cls_iri: ['Category:OSW51ad0d17...']
generated (osw.model.entity.Database)             cls_iri: ['Category:OSW51ad0d17...']

Both classes claim the same category IRI, and the generated one holds
the slot.

Why it matters

  1. Packaged behaviour is lost. The packaged classes carry typed
    fields and helpers (unit enums, to_base(), and so on) that the
    generated ones do not necessarily reproduce. Code that resolves a
    reference gets the generated class and silently loses them.

  2. References are dropped on assignment. A field declared as the
    packaged class receives an instance of the generated class, so
    isinstance fails, and pydantic re-constructs the value from
    dict(value) rather than accepting it. For oold range fields the
    value in __dict__ is None (the IRI lives in __iris__), so the
    reference is lost with no error. Details and a reproduction in
    Reference is lost when a resolved node is re-validated into a different class for the same category OO-LD/oold-python#106.

    Concretely: a Tool resolves its storage location, but the database
    server behind it comes back None, and the caller silently falls back
    to a local file
    (_init_archive_database silently falls back to a local SQLite file OpenSemanticWorld-Packages/opensemantic.base-python#9).

Suggested fix

Before fetching and compiling a schema, look the category IRI up in
oold's type registry and reuse the registered class if there is one.
Roughly:

cls = lookup_registered_type(category_iri)   # oold _types
if cls is None:
    cls = fetch_and_compile(category_iri)    # current path

Two details worth deciding explicitly:

  • Do not silently overwrite. If a class is generated for an IRI that
    is already registered, that is a conflict. Either keep the first
    registration, or log it loudly. Silent last-one-wins is what makes
    this hard to spot.
  • autofetch_schema=False is not a workaround. Passing it for the
    top-level load does not help, because the resolver used for nested
    references calls load_entity with default parameters.

Workaround

Pass model_to_use explicitly for every load, including anything reached
by following a reference, or re-load second-level entities directly by
IRI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions