Skip to content

Commit 0ea247c

Browse files
committed
fix(core): skip non-class attributes when choosing a category class
- a schema title can name an osw.model.entity attribute that is no class - the subclass guard then raised TypeError from issubclass() - the reload refresh raised AttributeError on a value without __module__ - both escaped load_entity; main logs the failed construction instead
1 parent e39a857 commit 0ea247c

2 files changed

Lines changed: 66 additions & 2 deletions

File tree

‎src/osw/core.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,9 @@ def load_entity(
12871287
canonical_cls = getattr(model, cls_name, None)
12881288
if (
12891289
registered_cls is not None
1290-
and canonical_cls is not None
1290+
# the schema title can also name a module
1291+
# attribute that is not a class
1292+
and isinstance(canonical_cls, type)
12911293
and registered_cls is not canonical_cls
12921294
and issubclass(registered_cls, canonical_cls)
12931295
):
@@ -1349,7 +1351,11 @@ def load_entity(
13491351
# class defined there, so a class picked before a later fetch in
13501352
# the loop above may be out of date: take the current object
13511353
for category, chosen_cls in category_to_cls.items():
1352-
if chosen_cls.__module__ == model.__name__:
1354+
# a non-class is left for the construction below to reject
1355+
if (
1356+
isinstance(chosen_cls, type)
1357+
and chosen_cls.__module__ == model.__name__
1358+
):
13531359
category_to_cls[category] = getattr(
13541360
model, chosen_cls.__name__, chosen_cls
13551361
)

‎tests/test_load_entity_registered_class.py‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,61 @@ def fake_fetch_schema(self, fetchSchemaParam=None):
352352
delattr(model, name)
353353
oold_type_registry.pop(cat_a, None)
354354
oold_type_registry.pop(cat_b, None)
355+
356+
357+
def _load_single_category_page(category: str, cls_name: str, suffix: str):
358+
"""Load a page of one category whose schema title is cls_name."""
359+
entity_page = OfflineWtPage(title=f"Item:OSWNonClass{suffix}".ljust(44, "0"))
360+
jsondata = {
361+
"type": [category],
362+
"uuid": "44444444-4444-4444-4444-444444444444",
363+
"name": "x",
364+
"label": [{"text": "x"}],
365+
}
366+
remove_empty(jsondata)
367+
entity_page.set_slot_content("jsondata", jsondata)
368+
return OSW(site=make_offline_wtsite()).load_entity(
369+
OSW.LoadEntityParam(
370+
titles=[entity_page.title],
371+
autofetch_schema=True,
372+
offline_pages={
373+
entity_page.title: entity_page,
374+
category: make_schema_page(category, cls_name),
375+
},
376+
)
377+
)
378+
379+
380+
def test_load_entity_uses_the_registered_class_when_the_name_is_not_a_class():
381+
"""A schema title can collide with a module attribute of osw.model.entity
382+
that is not a class. The subclass guard must not call issubclass() on it,
383+
and the registered class is used as usual."""
384+
category = "Category:OSWNonClassRegistered00000000000000000"
385+
cls_name = "NonClassAttributeA"
386+
registered_cls = make_isolated_cls("NonClassRegistered")
387+
setattr(model, cls_name, "not a class")
388+
oold_type_registry[category] = registered_cls
389+
try:
390+
result = _load_single_category_page(category, cls_name, "A")
391+
assert type(result.entities[0]) is registered_cls
392+
finally:
393+
delattr(model, cls_name)
394+
oold_type_registry.pop(category, None)
395+
396+
397+
def test_load_entity_logs_a_schema_title_that_names_no_class(caplog):
398+
"""With nothing registered, a schema title that names a non-class attribute
399+
of osw.model.entity cannot build an entity. load_entity() logs the failed
400+
construction and returns, as on main, instead of raising."""
401+
category = "Category:OSWNonClassNotRegistered0000000000000000"
402+
cls_name = "NonClassAttributeB"
403+
assert oold_type_registry.get(category) is None
404+
setattr(model, cls_name, "not a class")
405+
try:
406+
_load_single_category_page(category, cls_name, "B")
407+
assert any(
408+
"Error creating entity from page" in record.message
409+
for record in caplog.records
410+
)
411+
finally:
412+
delattr(model, cls_name)

0 commit comments

Comments
 (0)