Skip to content

Commit 7be59c5

Browse files
committed
Merge branch 'stage' into fix/overwrite-policy-followup
2 parents 1eddbe8 + f524925 commit 7be59c5

2 files changed

Lines changed: 540 additions & 16 deletions

File tree

‎src/osw/core.py‎

Lines changed: 99 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
set_resolver,
3333
)
3434
from oold.generator import Generator
35+
from oold.model.v1 import _types as oold_type_registry
3536
from oold.utils.codegen import OOLDJsonSchemaParser
3637
from opensemantic.v1 import OswBaseModel
3738
from pydantic import PydanticDeprecatedSince20
@@ -1430,6 +1431,10 @@ def load_entity(
14301431
entity = None
14311432
schemas = []
14321433
schemas_fetched = True
1434+
# maps a category (page title, e.g. "Category:OSW...") to the
1435+
# class to use for it, either an already registered class (e.g. a
1436+
# packaged model class) or a freshly compiled one
1437+
category_to_cls: Dict[str, Type[model.Entity]] = {}
14331438
jsondata = page.get_slot_content("jsondata")
14341439
if param.remove_empty:
14351440
remove_empty(jsondata)
@@ -1451,23 +1456,99 @@ def load_entity(
14511456
# If a schema_to_use is provided, we do not need to check if
14521457
# the model exists
14531458
if not param.model_to_use:
1454-
if not hasattr(model, cls_name):
1455-
if param.autofetch_schema:
1456-
self.fetch_schema(
1457-
OSW.FetchSchemaParam(
1458-
schema_title=category,
1459-
mode="append",
1460-
offline_pages=param.offline_pages,
1459+
# Prefer a class already registered for this category
1460+
# IRI (e.g. a packaged model class) over compiling a
1461+
# new one. Compiling one anyway would take over the
1462+
# oold type registry entry for this category and hide
1463+
# the packaged class' typed fields/helpers.
1464+
registered_cls = oold_type_registry.get(category)
1465+
# Controllers and result wrappers (e.g.
1466+
# WikiFileController, UploadFileResult) inherit
1467+
# their category IRI from the model class they
1468+
# extend, and the registry keeps whichever class
1469+
# was defined last. Such a specialization asks for
1470+
# fields a plain page's jsondata does not carry, so
1471+
# prefer the canonical class from osw.model.entity
1472+
# over a subclass of it.
1473+
canonical_cls = getattr(model, cls_name, None)
1474+
if (
1475+
registered_cls is not None
1476+
# the schema title can also name a module
1477+
# attribute that is not a class
1478+
and isinstance(canonical_cls, type)
1479+
and registered_cls is not canonical_cls
1480+
and issubclass(registered_cls, canonical_cls)
1481+
):
1482+
_logger.debug(
1483+
f"Ignoring '{registered_cls}' registered for "
1484+
f"category '{category}': it specializes "
1485+
f"'{canonical_cls}', which is used instead."
1486+
)
1487+
registered_cls = None
1488+
if registered_cls is not None:
1489+
category_to_cls[category] = registered_cls
1490+
else:
1491+
if not hasattr(model, cls_name):
1492+
if param.autofetch_schema:
1493+
self.fetch_schema(
1494+
OSW.FetchSchemaParam(
1495+
schema_title=category,
1496+
mode="append",
1497+
offline_pages=param.offline_pages,
1498+
)
14611499
)
1500+
if not hasattr(model, cls_name):
1501+
schemas_fetched = False
1502+
_logger.error(
1503+
f"Model {cls_name} not found. Schema "
1504+
f"{category} needs to be fetched first."
14621505
)
1463-
if not hasattr(model, cls_name):
1464-
schemas_fetched = False
1465-
_logger.error(
1466-
f"Model {cls_name} not found. Schema {category} "
1467-
f"needs to be fetched first."
1468-
)
1506+
else:
1507+
generated_cls = getattr(model, cls_name)
1508+
# The class we are about to use may have just
1509+
# claimed (or may already hold) the registry
1510+
# slot for this category. If a different class
1511+
# is registered for it, someone's registration
1512+
# was silently overwritten - do not raise, but
1513+
# make sure this does not pass silently.
1514+
conflicting_cls = oold_type_registry.get(category)
1515+
if (
1516+
conflicting_cls is not None
1517+
and conflicting_cls is not generated_cls
1518+
# the slot holder specializes
1519+
# generated_cls, e.g. a controller
1520+
# that inherits the category IRI:
1521+
# the case the guard above expects,
1522+
# not a conflict. A fetch can leave
1523+
# a non-class under the schema title.
1524+
and not (
1525+
isinstance(generated_cls, type)
1526+
and issubclass(
1527+
conflicting_cls, generated_cls
1528+
)
1529+
)
1530+
):
1531+
_logger.warning(
1532+
f"Class '{generated_cls}' generated for "
1533+
f"category '{category}' claims the oold "
1534+
f"type registry slot already held by a "
1535+
f"different class '{conflicting_cls}'."
1536+
)
1537+
category_to_cls[category] = generated_cls
14691538
if not schemas_fetched:
14701539
continue
1540+
# fetch_schema() reloads osw.model.entity, which replaces every
1541+
# class defined there, so a class picked before a later fetch in
1542+
# the loop above may be out of date: take the current object
1543+
for category, chosen_cls in category_to_cls.items():
1544+
# a non-class is left for the construction below to reject
1545+
if (
1546+
isinstance(chosen_cls, type)
1547+
and chosen_cls.__module__ == model.__name__
1548+
):
1549+
category_to_cls[category] = getattr(
1550+
model, chosen_cls.__name__, chosen_cls
1551+
)
14711552

14721553
try:
14731554
if param.model_to_use:
@@ -1477,13 +1558,15 @@ def load_entity(
14771558
_logger.error("no schema defined")
14781559

14791560
elif len(schemas) == 1:
1480-
cls: Type[model.Entity] = getattr(model, schemas[0]["title"])
1561+
# category_to_cls is fully populated for every category
1562+
# that reached this point (see the loop above)
1563+
cls: Type[model.Entity] = category_to_cls[jsondata["type"][0]]
14811564
entity: model.Entity = cls(**jsondata)
14821565

14831566
else:
14841567
bases = []
1485-
for schema in schemas:
1486-
bases.append(getattr(model, schema["title"]))
1568+
for category in jsondata["type"]:
1569+
bases.append(category_to_cls[category])
14871570
cls = create_model("Test", __base__=tuple(bases))
14881571
entity: model.Entity = cls(**jsondata)
14891572
except Exception as e:

0 commit comments

Comments
 (0)