From e590d7285b209dcf19d7a1674cd54b0c795c6019 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 10:56:23 +0000 Subject: [PATCH 1/5] Initial plan From 08012f74082e9bb556cd0c65786838db92171e1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:00:08 +0000 Subject: [PATCH 2/5] Handle None from inspect.getmodule() in get_scope with fallback and clear ValueError Co-authored-by: samwaseda <37879103+samwaseda@users.noreply.github.com> --- flowrep/_version.py | 34 +++++++++++++++++++ flowrep/models/parsers/object_scope.py | 14 +++++++- .../unit/models/parsers/test_object_scope.py | 31 +++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 flowrep/_version.py diff --git a/flowrep/_version.py b/flowrep/_version.py new file mode 100644 index 00000000..0527cf06 --- /dev/null +++ b/flowrep/_version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '0.1.dev3+ge590d7285.d20260318' +__version_tuple__ = version_tuple = (0, 1, 'dev3', 'ge590d7285.d20260318') + +__commit_id__ = commit_id = None diff --git a/flowrep/models/parsers/object_scope.py b/flowrep/models/parsers/object_scope.py index 3923cad7..ea28cb90 100644 --- a/flowrep/models/parsers/object_scope.py +++ b/flowrep/models/parsers/object_scope.py @@ -3,6 +3,7 @@ import ast import builtins import inspect +import sys from collections.abc import Callable from typing import Any @@ -45,7 +46,18 @@ def fork(self) -> ScopeProxy: def get_scope(func: Callable[..., Any] | type[Any]) -> ScopeProxy: - return ScopeProxy(inspect.getmodule(func).__dict__ | vars(builtins)) + module = inspect.getmodule(func) + if module is None: + module_name = getattr(func, "__module__", None) + if module_name is not None: + module = sys.modules.get(module_name) + if module is None: + raise ValueError( + f"Cannot determine the module for {func!r}. " + "inspect.getmodule() returned None and no resolvable __module__ " + "attribute was found." + ) + return ScopeProxy(module.__dict__ | vars(builtins)) def resolve_attribute_to_object(attribute: str, scope: ScopeProxy | object) -> object: diff --git a/tests/unit/models/parsers/test_object_scope.py b/tests/unit/models/parsers/test_object_scope.py index 7fc2bcb0..1b4e6d25 100644 --- a/tests/unit/models/parsers/test_object_scope.py +++ b/tests/unit/models/parsers/test_object_scope.py @@ -1,4 +1,6 @@ import ast +import sys +import types import unittest from flowrep.models.parsers import object_scope @@ -42,6 +44,35 @@ def test_includes_builtins(self): self.assertIs(scope.int, int) self.assertIs(scope.ValueError, ValueError) + def test_none_module_fallback_via_dunder_module(self): + """When inspect.getmodule returns None but __module__ is set, fall back.""" + mod = types.ModuleType("_test_dynamic_mod") + mod.__dict__["sentinel"] = object() + sys.modules["_test_dynamic_mod"] = mod + try: + func = types.FunctionType( + (lambda: None).__code__, + {}, + "_test_func", + ) + # Manually set __module__ but keep the function out of a real module + # so inspect.getmodule() returns None. + func.__module__ = "_test_dynamic_mod" + scope = object_scope.get_scope(func) + self.assertIs(scope.sentinel, mod.__dict__["sentinel"]) + finally: + del sys.modules["_test_dynamic_mod"] + + def test_no_resolvable_module_raises_value_error(self): + """When neither inspect.getmodule nor __module__ resolves, raise ValueError.""" + func = types.FunctionType( + (lambda: None).__code__, + {}, + "_orphan_func", + ) + func.__module__ = None # type: ignore[assignment] + with self.assertRaises(ValueError): + object_scope.get_scope(func) class TestResolveSymbolToObject(unittest.TestCase): def test_simple_name(self): From 539fe52c43dd2767bfc109c005594805aa865ffa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:32:22 +0000 Subject: [PATCH 3/5] Remove accidentally committed _version.py build artifact and add to .gitignore Co-authored-by: samwaseda <37879103+samwaseda@users.noreply.github.com> --- .gitignore | 1 + flowrep/_version.py | 34 ---------------------------------- 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 flowrep/_version.py diff --git a/.gitignore b/.gitignore index b4fb6bde..928e0f38 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ apidoc/ .ipynb_checkpoints/ test_times.dat core.* +_version.py diff --git a/flowrep/_version.py b/flowrep/_version.py deleted file mode 100644 index 0527cf06..00000000 --- a/flowrep/_version.py +++ /dev/null @@ -1,34 +0,0 @@ -# file generated by setuptools-scm -# don't change, don't track in version control - -__all__ = [ - "__version__", - "__version_tuple__", - "version", - "version_tuple", - "__commit_id__", - "commit_id", -] - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Tuple - from typing import Union - - VERSION_TUPLE = Tuple[Union[int, str], ...] - COMMIT_ID = Union[str, None] -else: - VERSION_TUPLE = object - COMMIT_ID = object - -version: str -__version__: str -__version_tuple__: VERSION_TUPLE -version_tuple: VERSION_TUPLE -commit_id: COMMIT_ID -__commit_id__: COMMIT_ID - -__version__ = version = '0.1.dev3+ge590d7285.d20260318' -__version_tuple__ = version_tuple = (0, 1, 'dev3', 'ge590d7285.d20260318') - -__commit_id__ = commit_id = None From 1f8b6e447bf195af50de1b669ad7e6279c6d0d3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:34:59 +0000 Subject: [PATCH 4/5] Apply black formatting to changed files Co-authored-by: samwaseda <37879103+samwaseda@users.noreply.github.com> --- tests/unit/models/parsers/test_object_scope.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/models/parsers/test_object_scope.py b/tests/unit/models/parsers/test_object_scope.py index 1b4e6d25..a10c0e70 100644 --- a/tests/unit/models/parsers/test_object_scope.py +++ b/tests/unit/models/parsers/test_object_scope.py @@ -74,6 +74,7 @@ def test_no_resolvable_module_raises_value_error(self): with self.assertRaises(ValueError): object_scope.get_scope(func) + class TestResolveSymbolToObject(unittest.TestCase): def test_simple_name(self): scope = object_scope.ScopeProxy({"add": add}) From 1f660170d931efb291624296b4abbde20e880250 Mon Sep 17 00:00:00 2001 From: Sam Dareska <37879103+samwaseda@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:36:59 +0100 Subject: [PATCH 5/5] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 928e0f38..b4fb6bde 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,3 @@ apidoc/ .ipynb_checkpoints/ test_times.dat core.* -_version.py