From 99263e0bc1a9958e77bc9832c3c36320b82f2047 Mon Sep 17 00:00:00 2001 From: "Isorae (Osas) Osarenren" <49724411+osasisorae@users.noreply.github.com> Date: Tue, 30 Dec 2025 07:59:54 +0100 Subject: [PATCH] fix: avoid recursion in lazy imports --- src/hume/__init__.py | 2 ++ src/hume/core/__init__.py | 2 ++ src/hume/empathic_voice/__init__.py | 2 ++ src/hume/empathic_voice/chat/__init__.py | 2 ++ .../empathic_voice/chat/types/__init__.py | 2 ++ src/hume/empathic_voice/errors/__init__.py | 2 ++ src/hume/empathic_voice/types/__init__.py | 2 ++ src/hume/expression_measurement/__init__.py | 2 ++ .../expression_measurement/batch/__init__.py | 2 ++ .../batch/types/__init__.py | 2 ++ .../expression_measurement/stream/__init__.py | 2 ++ .../stream/stream/__init__.py | 2 ++ .../stream/stream/types/__init__.py | 2 ++ .../stream/types/__init__.py | 2 ++ src/hume/tts/__init__.py | 2 ++ src/hume/tts/errors/__init__.py | 2 ++ src/hume/tts/types/__init__.py | 2 ++ tests/custom/test_lazy_imports.py | 28 +++++++++++++++++++ 18 files changed, 62 insertions(+) create mode 100644 tests/custom/test_lazy_imports.py diff --git a/src/hume/__init__.py b/src/hume/__init__.py index 85884f21..4fb68097 100644 --- a/src/hume/__init__.py +++ b/src/hume/__init__.py @@ -30,6 +30,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/core/__init__.py b/src/hume/core/__init__.py index aac268b3..af93e815 100644 --- a/src/hume/core/__init__.py +++ b/src/hume/core/__init__.py @@ -66,6 +66,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/empathic_voice/__init__.py b/src/hume/empathic_voice/__init__.py index 40a93622..df31a214 100644 --- a/src/hume/empathic_voice/__init__.py +++ b/src/hume/empathic_voice/__init__.py @@ -262,6 +262,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/empathic_voice/chat/__init__.py b/src/hume/empathic_voice/chat/__init__.py index bfc25bab..17991097 100644 --- a/src/hume/empathic_voice/chat/__init__.py +++ b/src/hume/empathic_voice/chat/__init__.py @@ -15,6 +15,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/empathic_voice/chat/types/__init__.py b/src/hume/empathic_voice/chat/types/__init__.py index 30f84fab..5ac4cb9e 100644 --- a/src/hume/empathic_voice/chat/types/__init__.py +++ b/src/hume/empathic_voice/chat/types/__init__.py @@ -15,6 +15,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/empathic_voice/errors/__init__.py b/src/hume/empathic_voice/errors/__init__.py index a0d9436d..0fc6f08d 100644 --- a/src/hume/empathic_voice/errors/__init__.py +++ b/src/hume/empathic_voice/errors/__init__.py @@ -19,6 +19,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/empathic_voice/types/__init__.py b/src/hume/empathic_voice/types/__init__.py index ebbcc4cd..3654b695 100644 --- a/src/hume/empathic_voice/types/__init__.py +++ b/src/hume/empathic_voice/types/__init__.py @@ -249,6 +249,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/__init__.py b/src/hume/expression_measurement/__init__.py index c617c81d..0b2ec22b 100644 --- a/src/hume/expression_measurement/__init__.py +++ b/src/hume/expression_measurement/__init__.py @@ -15,6 +15,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/batch/__init__.py b/src/hume/expression_measurement/batch/__init__.py index 8c3a1f21..3e656cd8 100644 --- a/src/hume/expression_measurement/batch/__init__.py +++ b/src/hume/expression_measurement/batch/__init__.py @@ -259,6 +259,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/batch/types/__init__.py b/src/hume/expression_measurement/batch/types/__init__.py index 6d385ed1..866933ec 100644 --- a/src/hume/expression_measurement/batch/types/__init__.py +++ b/src/hume/expression_measurement/batch/types/__init__.py @@ -265,6 +265,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/stream/__init__.py b/src/hume/expression_measurement/stream/__init__.py index 8b466473..9ae41db7 100644 --- a/src/hume/expression_measurement/stream/__init__.py +++ b/src/hume/expression_measurement/stream/__init__.py @@ -81,6 +81,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/stream/stream/__init__.py b/src/hume/expression_measurement/stream/stream/__init__.py index 3e56da3b..a0e75d6e 100644 --- a/src/hume/expression_measurement/stream/stream/__init__.py +++ b/src/hume/expression_measurement/stream/stream/__init__.py @@ -59,6 +59,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/stream/stream/types/__init__.py b/src/hume/expression_measurement/stream/stream/types/__init__.py index b96b1151..8750515a 100644 --- a/src/hume/expression_measurement/stream/stream/types/__init__.py +++ b/src/hume/expression_measurement/stream/stream/types/__init__.py @@ -57,6 +57,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/expression_measurement/stream/types/__init__.py b/src/hume/expression_measurement/stream/types/__init__.py index a6de05e6..4ebdf636 100644 --- a/src/hume/expression_measurement/stream/types/__init__.py +++ b/src/hume/expression_measurement/stream/types/__init__.py @@ -33,6 +33,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/tts/__init__.py b/src/hume/tts/__init__.py index 1144cd51..9d518238 100644 --- a/src/hume/tts/__init__.py +++ b/src/hume/tts/__init__.py @@ -87,6 +87,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/tts/errors/__init__.py b/src/hume/tts/errors/__init__.py index a0d9436d..0fc6f08d 100644 --- a/src/hume/tts/errors/__init__.py +++ b/src/hume/tts/errors/__init__.py @@ -19,6 +19,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/src/hume/tts/types/__init__.py b/src/hume/tts/types/__init__.py index 91c0a84f..c9c1f283 100644 --- a/src/hume/tts/types/__init__.py +++ b/src/hume/tts/types/__init__.py @@ -79,6 +79,8 @@ def __getattr__(attr_name: str) -> typing.Any: if module_name is None: raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") try: + if module_name == ".": + return import_module(f".{attr_name}", __package__) module = import_module(module_name, __package__) result = getattr(module, attr_name) return result diff --git a/tests/custom/test_lazy_imports.py b/tests/custom/test_lazy_imports.py new file mode 100644 index 00000000..c19db1ad --- /dev/null +++ b/tests/custom/test_lazy_imports.py @@ -0,0 +1,28 @@ +import importlib +import types + +import pytest + + +MODULES_TO_CHECK = [ + "hume", + "hume.tts", + "hume.empathic_voice", + "hume.expression_measurement", + "hume.expression_measurement.stream", +] + + +@pytest.mark.parametrize("module_name", MODULES_TO_CHECK) +def test_lazy_submodule_resolution_does_not_recurse(module_name: str) -> None: + """Modules that lazily expose submodules via _dynamic_imports should not recurse.""" + module = importlib.import_module(module_name) + dynamic_imports = getattr(module, "_dynamic_imports", {}) + + for attr, destination in dynamic_imports.items(): + if destination != ".": + continue + + submodule = getattr(module, attr) + assert isinstance(submodule, types.ModuleType) + assert submodule.__name__.endswith(f"{attr}")