entity_instance: get_info_2 falls back to get_info for unsupported args #4270

get_info_2 advertises the same signature as get_info but raised a bare
AssertionError for anything the C++ fast path does not implement --
including its own default arguments (recursive=False).

Use the fast path when recursive=True, return_type=dict and ignore=()
hold, and delegate to the pure Python get_info otherwise. As noted in
the issue, without recursion there is no meaningful performance gain to
lose by delegating.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Petru Conduraru
2026-07-05 20:40:46 +03:00
committed by Thomas Krijnen
parent fa597536e1
commit 58cfab48e6
2 changed files with 22 additions and 11 deletions
@@ -642,16 +642,16 @@ class entity_instance:
return_type: type[dict] = dict,
ignore: Sequence[str] = (),
) -> dict[str, Any]:
"""More perfomant version of `.get_info()` but with limited arguments values.\n
Method has exactly the same signature as `.get_info()` but it doesn't support getting information non-recursively.
Currently supported arguments values:
* recursive: `True` (will fail with default `False` value from `.get_info()`)
* return_type: `dict`
* ignore: `()` (empty tuple)
"""More perfomant version of `.get_info()`.\n
Method has exactly the same signature as `.get_info()`, but the fast C++
path only implements ``recursive=True``, ``return_type=dict`` and
``ignore=()``. Any other combination falls back to the pure Python
`.get_info()`, where no meaningful performance gain is possible anyway
as the cost is dominated by the recursive traversal.
"""
assert recursive
assert return_type is dict
assert len(ignore) == 0
return ifcopenshell_wrapper.get_info_cpp(self.wrapped_data, include_identifier)
if recursive and return_type is dict and not ignore:
return ifcopenshell_wrapper.get_info_cpp(self.wrapped_data, include_identifier)
return self.get_info(
include_identifier=include_identifier, recursive=recursive, return_type=return_type, ignore=ignore
)
@@ -64,3 +64,14 @@ class TestGetInfo2(test.bootstrap.IFC4):
"Outer": {"CfsFaces": None, "type": "IfcClosedShell"},
"type": "IfcFacetedBrep",
}
def test_unsupported_arguments_fall_back_to_get_info(self):
# Regression test for #4270: get_info_2 raised a bare AssertionError
# when called with its own default arguments (recursive=False) or any
# other combination the C++ fast path does not implement. It must
# delegate to get_info instead of crashing.
brep = self.file.create_entity("IfcFacetedBrep")
shell = self.file.create_entity("IfcClosedShell")
brep.Outer = shell
assert brep.get_info_2() == brep.get_info()
assert brep.get_info_2(recursive=True, ignore=("Outer",)) == brep.get_info(recursive=True, ignore=("Outer",))