From 9213b31235b583faa7ffaf58058ff1304670b348 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 16 Jul 2026 16:58:55 +0500 Subject: [PATCH] logger: reuse logger_or_root, dedupe optional-logger-arg pattern --- .../ifcopenshell/__init__.py | 27 +++++++---- src/ifcopenshell-python/ifcopenshell/draw.py | 3 +- .../ifcopenshell/geom/main.py | 9 ++-- .../ifcopenshell/ifcopenshell_wrapper.pyi | 48 ++++++++++++++++++- .../test/test_create_shape.py | 1 + 5 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 0a309695df..d0484a6d7e 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -97,7 +97,19 @@ from .file import file as _file from .sql import sqlite, sqlite_entity get_log = ifcopenshell_wrapper.get_log -logger = getattr(ifcopenshell_wrapper, "logger", None) +logger = ifcopenshell_wrapper.logger if hasattr(ifcopenshell_wrapper, "logger") else None +if hasattr(ifcopenshell_wrapper, "logger_or_root"): + logger_or_root = ifcopenshell_wrapper.logger_or_root +else: + + def logger_or_root(_logger: ifcopenshell_wrapper.logger | None) -> None: + return None + + +# TODO: drop this function and all callsites after we migrate to the new build. +def optional_logger_args(logger: ifcopenshell_wrapper.logger | None) -> tuple[logger] | tuple[()]: + return (logger,) if logger is not None else () + # explicitly specify available imported symbols # (it's a requirement for a typed library) @@ -194,10 +206,9 @@ def open( raise FileNotFoundError(f"Path does not exist: '{path}'.") if format is None: format = guess_format(path) - if logger is None and (logger_type := getattr(ifcopenshell_wrapper, "logger", None)): - logger = logger_type.Root() + logger = logger_or_root(logger) if format == ".ifcXML": - f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute()), *((logger,) if logger is not None else ())) + f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute()), *optional_logger_args(logger)) if f: return file(f) raise OSError(f"Failed to parse .ifcXML file from {path}") @@ -214,11 +225,9 @@ def open( if should_stream: return stream(path) if readonly: # Temporary conditional see #7131. Remove once newer builds don't segfault on Linux. - f = ifcopenshell_wrapper.open(str(path.absolute()), readonly, *((logger,) if logger is not None else ())) + f = ifcopenshell_wrapper.open(str(path.absolute()), readonly, *optional_logger_args(logger)) elif bypass_types: - f = ifcopenshell_wrapper.file( - ifcopenshell_wrapper.uninitialized_tag(), *((logger,) if logger is not None else ()) - ) + f = ifcopenshell_wrapper.file(ifcopenshell_wrapper.uninitialized_tag(), *optional_logger_args(logger)) for ty in bypass_types: f.bypass_type(ty) if mmap: @@ -233,7 +242,7 @@ def open( kwargs["logger"] = logger f = ifcopenshell_wrapper.open(str(path.absolute()), **kwargs) else: - f = ifcopenshell_wrapper.open(str(path.absolute()), False, *((logger,) if logger is not None else ())) + f = ifcopenshell_wrapper.open(str(path.absolute()), False, *optional_logger_args(logger)) return file(f) diff --git a/src/ifcopenshell-python/ifcopenshell/draw.py b/src/ifcopenshell-python/ifcopenshell/draw.py index 147a23498d..bbcfa48aea 100644 --- a/src/ifcopenshell-python/ifcopenshell/draw.py +++ b/src/ifcopenshell-python/ifcopenshell/draw.py @@ -107,8 +107,7 @@ def main( progress_function: Callable = DO_NOTHING, logger=None, ): - if logger is None and ifcopenshell.logger is not None: - logger = ifcopenshell.logger.Root() + logger = ifcopenshell.logger_or_root(logger) def by_guid(g): for f in files: diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 984ad9706b..bf5fc00098 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -22,6 +22,8 @@ from __future__ import annotations from collections.abc import Generator, Iterable from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, cast, overload +import ifcopenshell + from .. import ifcopenshell_wrapper, open from ..entity_instance import entity_instance from ..file import file @@ -302,8 +304,7 @@ class iterator(ifcopenshell_wrapper.Iterator): logger=None, ): self.settings = settings - if logger is None and (logger_type := getattr(ifcopenshell_wrapper, "logger", None)): - logger = logger_type.Root() + logger = ifcopenshell.logger_or_root(logger) if isinstance(file_or_filename, file): self.file = file file_or_filename = file_or_filename.wrapped_data @@ -344,10 +345,10 @@ class iterator(ifcopenshell_wrapper.Iterator): include is not None, num_threads, ) - self.this = initializer(*args, *((logger,) if logger is not None else ())) + self.this = initializer(*args, *ifcopenshell.optional_logger_args(logger)) else: args = (geometry_library, self.settings, file_or_filename, num_threads) - self.this = ifcopenshell_wrapper.construct_iterator(*args, *((logger,) if logger is not None else ())) + self.this = ifcopenshell_wrapper.construct_iterator(*args, *ifcopenshell.optional_logger_args(logger)) if has_occ: diff --git a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi index b924e2c03d..d483f23ae7 100644 --- a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi +++ b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi @@ -1058,6 +1058,7 @@ class file: instantiate_typed_instances: bool def key_value_store_iter(self, prefix): ... def key_value_store_query(self, key): ... + def logger(self) -> logger: ... def process_deletion_inverse(self, inst): ... def recalculate_id_counter(self): ... def remove(self, entity: entity_instance) -> None: ... @@ -1201,6 +1202,51 @@ class line_segment: def size(self): ... def swap(self, v): ... +class log_message: + code: Any + instance: Any + message: Any + product: Any + severity: Any + timestamp: Any + + def __init__(self, severity, code_prefix, code_number, timestamp, message, inst=None, current_product=None): ... + @property + def severity_string(self): ... + def to_dict(self): ... + def to_tuple(self): ... + +class logger: + FMT_PLAIN: Literal[0] + FMT_JSON: Literal[1] + FMT_INMEMORY: Literal[2] + + LOG_PERF: Literal[0] + LOG_DEBUG: Literal[1] + LOG_NOTICE: Literal[2] + LOG_WARNING: Literal[3] + LOG_ERROR: Literal[4] + + @staticmethod + def Root() -> logger: ... + def Append(self, logger): ... + def ClearLog(self): ... + def Error(self, *args): ... + def GetLog(self): ... + def MaxSeverity(self): ... + def Message(self, *args): ... + def Notice(self, *args): ... + def OutputFormat(self, *args): ... + def PrintPerformanceStats(self): ... + def PrintPerformanceStatsOnElement(self, *args): ... + def ProgressBar(self, progress): ... + def SetOutput(self, *args): ... + def SetProduct(self, product): ... + def Status(self, message, new_line=True): ... + def Verbosity(self, *args): ... + def Warning(self, *args): ... + def log_messages(self) -> tuple[log_message, ...]: ... + class loft: axis: Any def calc_hash(self): ... @@ -1721,7 +1767,7 @@ def kind_to_string(k): ... def less(arg1, arg2): ... def line_segments_to_polygons(s, eps, segments): ... def map_shape(settings, instance): ... -def logger_or_root(logger): ... +def logger_or_root(logger) -> logger: ... def nary_union(sequence): ... def new_IfcBaseClass(schema_identifier: str, name: str) -> entity_instance: ... def open(fn: str, readonly: bool = False, logger=None) -> file: ... diff --git a/src/ifcopenshell-python/test/test_create_shape.py b/src/ifcopenshell-python/test/test_create_shape.py index bda6767295..e40570d161 100644 --- a/src/ifcopenshell-python/test/test_create_shape.py +++ b/src/ifcopenshell-python/test/test_create_shape.py @@ -217,6 +217,7 @@ def test_iterator(): def test_logging(): + assert ifcopenshell.logger logger = ifcopenshell.logger() logger.OutputFormat(logger.FMT_INMEMORY) settings = ifcopenshell.geom.settings()