From 161a9278e19dbcd6366aa0498207dd4e980e8226 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 23 Apr 2026 14:11:52 +1000 Subject: [PATCH 1/6] #557 Do not add contained subroutines/functions to the exported symbols. --- source/fab/parse/fortran.py | 12 ++++++++---- .../parse/fortran/test_contained_subroutine.py | 11 ++++++----- .../parse/fortran/test_contained_subroutine/main.f90 | 6 +++++- .../parse/fortran/test_fortran_analyser.py | 3 --- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/source/fab/parse/fortran.py b/source/fab/parse/fortran.py index c5de83f95..433c516bd 100644 --- a/source/fab/parse/fortran.py +++ b/source/fab/parse/fortran.py @@ -16,7 +16,7 @@ Function_Stmt, Language_Binding_Spec, Char_Literal_Constant, Interface_Block, Name, Comment, Module, Call_Stmt, Derived_Type_Def, Derived_Type_Stmt, Type_Attr_Spec_List, Type_Attr_Spec, Type_Name, - Subroutine_Subprogram, Function_Subprogram) + Subroutine_Subprogram, Function_Subprogram, Internal_Subprogram_Part) from fparser.two.utils import walk # type: ignore # todo: what else should we be importing from 2008 instead of 2003? This seems fragile. @@ -406,13 +406,17 @@ def _process_subroutine_or_function(self, analysed_file, fpath, obj): else: analysed_file.add_symbol_def(bind_name) - # not bound, just record the presence of the fortran symbol - # we don't need to record stuff in modules (we think!) + # Not bound, just record the presence of the Fortran symbol. + # We don't need to record stuff in modules. Do not record + # any functions/subroutine that are part of a module, contained, + # or an interface block (since these symbols will not be external + # visible, and might otherwise trigger duplicated symbols in Fab) elif (not self._find_ancestor(obj, Module) and + not self._find_ancestor(obj, Internal_Subprogram_Part) and not self._find_ancestor(obj, Interface_Block)): if isinstance(obj, Subroutine_Stmt): analysed_file.add_symbol_def(str(obj.get_name())) - if isinstance(obj, Function_Stmt): + elif isinstance(obj, Function_Stmt): _, name, _, _ = obj.items analysed_file.add_symbol_def(name.string) diff --git a/tests/unit_tests/parse/fortran/test_contained_subroutine.py b/tests/unit_tests/parse/fortran/test_contained_subroutine.py index 1fe89d25f..6d18023e1 100644 --- a/tests/unit_tests/parse/fortran/test_contained_subroutine.py +++ b/tests/unit_tests/parse/fortran/test_contained_subroutine.py @@ -52,16 +52,17 @@ def test_contained_subroutine(tmp_path): analyse(config, root_symbol='main') build_tree = config.artefact_store[ArtefactSet.BUILD_TREES]["main"] - af_mod_with_contain = None - for file_name in build_tree: - if "mod_with_contain" in str(file_name): - af_mod_with_contain = build_tree[file_name] - break + source_path = tmp_path / "contained_subroutine" / "source" + af_mod_with_contain = build_tree[source_path / "mod_with_contain.f90"] # The module should not contain any dependencies, the dependency to # `contained` is resolved from the subroutine it contains. assert af_mod_with_contain.symbol_deps == set() + # The contained subroutine in main should not be exported + af_main = build_tree[source_path / "main.f90"] + assert af_main.symbol_defs == set(["main"]) + # Just in case, also compile and link compile_fortran(config) link_exe(config) diff --git a/tests/unit_tests/parse/fortran/test_contained_subroutine/main.f90 b/tests/unit_tests/parse/fortran/test_contained_subroutine/main.f90 index eccaab8d0..efb53b15b 100644 --- a/tests/unit_tests/parse/fortran/test_contained_subroutine/main.f90 +++ b/tests/unit_tests/parse/fortran/test_contained_subroutine/main.f90 @@ -1,3 +1,7 @@ program main use mod_with_contain, only: my_sub -end program main \ No newline at end of file + +contains + subroutine should_not_be_exported + end subroutine should_not_be_exported +end program main diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.py b/tests/unit_tests/parse/fortran/test_fortran_analyser.py index d92fa381d..af4260bd8 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.py +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.py @@ -146,9 +146,6 @@ def test_program_file(self, module_expected._file_hash = 325155675 module_expected.program_defs = {'foo_mod'} module_expected.module_defs = set() - module_expected.symbol_defs.update({'internal_func', - 'internal_sub', - 'openmp_sentinel'}) assert analysis == module_expected assert isinstance(analysis, AnalysedFortran) From 13d943193b509688358ab2782d62ee9a4a822a8c Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 23 Apr 2026 14:22:03 +1000 Subject: [PATCH 2/6] 557 Addressed several pylint issues. --- .../parse/fortran/test_fortran_analyser.py | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.py b/tests/unit_tests/parse/fortran/test_fortran_analyser.py index af4260bd8..49f2d6d1e 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.py +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.py @@ -11,7 +11,7 @@ from pathlib import Path from unittest import mock -from fparser.common.readfortran import FortranFileReader # type: ignore +from fparser.common.readfortran import FortranStringReader # type: ignore from fparser.two.Fortran2008 import Type_Declaration_Stmt # type: ignore from fparser.two.parser import ParserFactory # type: ignore from fparser.two.utils import walk # type: ignore @@ -21,19 +21,18 @@ from fab.parse import EmptySourceFile from fab.parse.fortran import FortranAnalyser, AnalysedFortran from fab.tools.tool_box import ToolBox -from fab.tools.tool_repository import ToolRepository # todo: test function binding -@pytest.fixture -def module_fpath() -> Path: +@pytest.fixture(name="module_fpath") +def module_fpath_fixture() -> Path: '''Simple fixture that sets the name of the module test file.''' return Path(__file__).parent / "test_fortran_analyser.f90" -@pytest.fixture -def module_expected(module_fpath: Path) -> AnalysedFortran: +@pytest.fixture(name="module_expected") +def module_expected_fixture(module_fpath: Path) -> AnalysedFortran: '''Returns the expected AnalysedFortran instance for the Fortran test module.''' return AnalysedFortran( @@ -49,12 +48,17 @@ def module_expected(module_fpath: Path) -> AnalysedFortran: class TestAnalyser: + """ + Tests the Fortran analyser in various combinations. + """ @pytest.fixture def fortran_analyser( self, - tmp_path: Path, - stub_tool_repository: ToolRepository) -> FortranAnalyser: + tmp_path: Path) -> FortranAnalyser: + """ + A simple fixture that enables OpenMP and runs the analyser + """ # Enable openmp, so fparser will handle the lines with omp sentinels config = BuildConfig('proj', ToolBox(), fab_workspace=tmp_path, openmp=True) @@ -62,7 +66,9 @@ def fortran_analyser( return fortran_analyser def test_empty_file(self, fortran_analyser: FortranAnalyser) -> None: - # make sure we get back an EmptySourceFile + """ + Make sure we get back an EmptySourceFile if an empty file is given. + """ with mock.patch('fab.parse.AnalysedFile.save'): analysis, artefact = fortran_analyser.run( fpath=Path(Path(__file__).parent / "empty.f90")) @@ -71,6 +77,10 @@ def test_empty_file(self, fortran_analyser: FortranAnalyser) -> None: def test_module_file(self, fortran_analyser, module_fpath, module_expected): + """ + Tests handling of module statement, including making sure that + subroutines in a module are not exported as external symbols. + """ with mock.patch('fab.parse.AnalysedFile.save'): analysis, artefact = fortran_analyser.run(fpath=module_fpath) assert analysis == module_expected @@ -132,7 +142,10 @@ def test_program_file(self, fortran_analyser: FortranAnalyser, module_fpath: Path, module_expected: AnalysedFortran) -> None: - # same as test_module_file() but replacing MODULE with PROGRAM + """ + Test the handling of a Program. This test replaces 'MODULE' + in the standard test here with 'PROGRAM'. + """ prog_path = tmp_path / "prog.f90" with prog_path.open('w') as tmp_file: tmp_file.write(module_fpath.open().read().replace("MODULE", @@ -161,12 +174,11 @@ class TestProcessVariableBinding: # todo: define and depend, with and without bind name - def test_define_without_bind_name(self, tmp_path: Path, + def test_define_without_bind_name(self, stub_configuration: BuildConfig) -> None: '''Test usage of bind''' - fpath = tmp_path / 'temp.f90' - open(fpath, 'wt').write(""" + code = """ MODULE f_var USE, INTRINSIC :: ISO_C_BINDING @@ -179,10 +191,10 @@ def test_define_without_bind_name(self, tmp_path: Path, helloworld=['H','e','L','l','O',' ','w','O','r','L','d','?'] END MODULE f_var - """) + """ # parse - reader = FortranFileReader(str(fpath), ignore_comments=False) + reader = FortranStringReader(code, ignore_comments=False) f2008_parser = ParserFactory().create(std="f2008") tree = f2008_parser(reader) From fff96338014d39cce7bbf71e0a389bf435cc3b4c Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Fri, 24 Apr 2026 17:47:29 +1000 Subject: [PATCH 3/6] #559 Add support for external declarations. --- source/fab/parse/fortran.py | 7 +++++-- tests/unit_tests/parse/fortran/test_fortran_analyser.f90 | 1 + tests/unit_tests/parse/fortran/test_fortran_analyser.py | 7 ++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/source/fab/parse/fortran.py b/source/fab/parse/fortran.py index 433c516bd..e3df5584f 100644 --- a/source/fab/parse/fortran.py +++ b/source/fab/parse/fortran.py @@ -16,7 +16,8 @@ Function_Stmt, Language_Binding_Spec, Char_Literal_Constant, Interface_Block, Name, Comment, Module, Call_Stmt, Derived_Type_Def, Derived_Type_Stmt, Type_Attr_Spec_List, Type_Attr_Spec, Type_Name, - Subroutine_Subprogram, Function_Subprogram, Internal_Subprogram_Part) + Subroutine_Subprogram, Function_Subprogram, Internal_Subprogram_Part, + External_Stmt) from fparser.two.utils import walk # type: ignore # todo: what else should we be importing from 2008 instead of 2003? This seems fragile. @@ -234,7 +235,9 @@ def walk_nodes(self, fpath, file_hash, node_tree) -> AnalysedFortran: # Or the new match statement, Python 3.10 if obj_type == Use_Stmt: self._process_use_statement(analysed_fortran, obj) # raises - + elif obj_type == External_Stmt: + for external in obj.items[1].items: + analysed_fortran.add_symbol_dep(external.string) elif obj_type == Call_Stmt: called_name = _typed_child(obj, Name) # called_name will be None for calls like thing%method(), diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 b/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 index 2c5302699..bdd220c95 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 @@ -14,6 +14,7 @@ MODULE foo_mod SUBROUTINE internal_sub ! DEPENDS ON: monty_func + external some_external_symbol RETURN END SUBROUTINE internal_sub diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.py b/tests/unit_tests/parse/fortran/test_fortran_analyser.py index 49f2d6d1e..1c9dab149 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.py +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.py @@ -37,11 +37,12 @@ def module_expected_fixture(module_fpath: Path) -> AnalysedFortran: test module.''' return AnalysedFortran( fpath=module_fpath, - file_hash=3737289404, + file_hash=1015362680, module_defs={'foo_mod'}, symbol_defs={'external_sub', 'external_func', 'foo_mod'}, module_deps={'bar_mod', 'compute_chunk_size_mod'}, - symbol_deps={'monty_func', 'bar_mod', 'compute_chunk_size_mod'}, + symbol_deps={'monty_func', 'bar_mod', 'compute_chunk_size_mod', + 'some_external_symbol'}, file_deps=set(), mo_commented_file_deps={'some_file.o'}, ) @@ -156,7 +157,7 @@ def test_program_file(self, fpath=Path(tmp_file.name)) module_expected.fpath = Path(tmp_file.name) - module_expected._file_hash = 325155675 + module_expected._file_hash = 3277624132 module_expected.program_defs = {'foo_mod'} module_expected.module_defs = set() From 63355ae4a755e518b592d789ae1d4e553a4f00b9 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Fri, 24 Apr 2026 23:01:24 +1000 Subject: [PATCH 4/6] #559 Replace f2008 import with f2003 to avoid circular import in some cases (e.g. when just running this one test). --- tests/unit_tests/parse/fortran/test_fortran_analyser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.py b/tests/unit_tests/parse/fortran/test_fortran_analyser.py index 1c9dab149..78aa6cb2b 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.py +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.py @@ -12,7 +12,7 @@ from unittest import mock from fparser.common.readfortran import FortranStringReader # type: ignore -from fparser.two.Fortran2008 import Type_Declaration_Stmt # type: ignore +from fparser.two.Fortran2003 import Type_Declaration_Stmt # type: ignore from fparser.two.parser import ParserFactory # type: ignore from fparser.two.utils import walk # type: ignore import pytest From dcdcae4eb51bd9023fbf1a67cf5d28d17c49c233 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Tue, 28 Apr 2026 13:08:06 +1000 Subject: [PATCH 5/6] #559 Fixed handling of interfaces. --- source/fab/parse/fortran.py | 45 +++++++++++++------ .../parse/fortran/test_fortran_analyser.f90 | 4 ++ .../parse/fortran/test_fortran_analyser.py | 6 +-- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/source/fab/parse/fortran.py b/source/fab/parse/fortran.py index e3df5584f..074df755f 100644 --- a/source/fab/parse/fortran.py +++ b/source/fab/parse/fortran.py @@ -277,10 +277,10 @@ def walk_nodes(self, fpath, file_hash, node_tree) -> AnalysedFortran: analysed_fortran.add_symbol_dep(called_name.string) elif obj_type == Program_Stmt: - analysed_fortran.add_program_def(str(obj.get_name())) + analysed_fortran.add_program_def(obj.get_name().string) elif obj_type == Module_Stmt: - analysed_fortran.add_module_def(str(obj.get_name())) + analysed_fortran.add_module_def(obj.get_name().string) elif obj_type in (Subroutine_Stmt, Function_Stmt): self._process_subroutine_or_function(analysed_fortran, @@ -386,7 +386,23 @@ def _process_comment(self, analysed_file, obj): # Without .o means a Fortran symbol analysed_file.add_symbol_dep(dep) - def _process_subroutine_or_function(self, analysed_file, fpath, obj): + def _process_subroutine_or_function( + self, + analysed_file: AnalysedFortran, + fpath: Path, + obj: Union[Function_Stmt, Subroutine_Stmt]): + """ + Processes a subroutine statement. It handles: + - a potential 'bind' attribute (which can change the external symbol + used), + - declaration of subroutines/functions in modules (which will not be + visible as external symbols, any dependencies will be covered by + the module symbol) + - declarations contained in other subroutines (which will not + at all be visible) + - declarations in an interface block (which declare an external + dependency) + """ # binding? bind = _typed_child(obj, Language_Binding_Spec) if bind: @@ -410,18 +426,19 @@ def _process_subroutine_or_function(self, analysed_file, fpath, obj): analysed_file.add_symbol_def(bind_name) # Not bound, just record the presence of the Fortran symbol. - # We don't need to record stuff in modules. Do not record - # any functions/subroutine that are part of a module, contained, - # or an interface block (since these symbols will not be external - # visible, and might otherwise trigger duplicated symbols in Fab) + + elif self._find_ancestor(obj, Interface_Block): + # If the subroutine/function declaration is inside an interface + # block, we have an external dependency: + analysed_file.add_symbol_dep(str(obj.get_name())) + elif (not self._find_ancestor(obj, Module) and - not self._find_ancestor(obj, Internal_Subprogram_Part) and - not self._find_ancestor(obj, Interface_Block)): - if isinstance(obj, Subroutine_Stmt): - analysed_file.add_symbol_def(str(obj.get_name())) - elif isinstance(obj, Function_Stmt): - _, name, _, _ = obj.items - analysed_file.add_symbol_def(name.string) + not self._find_ancestor(obj, Internal_Subprogram_Part)): + # We don't need to record stuff in modules, and any functions / + # subroutines that contained in a subroutine either (since these + # will not be externally visible). But otherwise record the + # declaration of the subroutine/function. + analysed_file.add_symbol_def(str(obj.get_name())) class FortranParserWorkaround(): diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 b/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 index bdd220c95..11041e8ef 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 @@ -15,6 +15,10 @@ MODULE foo_mod SUBROUTINE internal_sub ! DEPENDS ON: monty_func external some_external_symbol + INTERFACE + SUBROUTINE sub_in_interface + END SUBROUTINE sub_in_interface + END INTERFACE RETURN END SUBROUTINE internal_sub diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.py b/tests/unit_tests/parse/fortran/test_fortran_analyser.py index 78aa6cb2b..fda791856 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.py +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.py @@ -37,12 +37,12 @@ def module_expected_fixture(module_fpath: Path) -> AnalysedFortran: test module.''' return AnalysedFortran( fpath=module_fpath, - file_hash=1015362680, + file_hash=2985613950, module_defs={'foo_mod'}, symbol_defs={'external_sub', 'external_func', 'foo_mod'}, module_deps={'bar_mod', 'compute_chunk_size_mod'}, symbol_deps={'monty_func', 'bar_mod', 'compute_chunk_size_mod', - 'some_external_symbol'}, + 'some_external_symbol', 'sub_in_interface'}, file_deps=set(), mo_commented_file_deps={'some_file.o'}, ) @@ -157,7 +157,7 @@ def test_program_file(self, fpath=Path(tmp_file.name)) module_expected.fpath = Path(tmp_file.name) - module_expected._file_hash = 3277624132 + module_expected._file_hash = 3599767944 module_expected.program_defs = {'foo_mod'} module_expected.module_defs = set() From b5401f9f47d6fbafe2e79c0b0fc994b1799589af Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Tue, 28 Apr 2026 13:28:58 +1000 Subject: [PATCH 6/6] #559 Support external attribute. --- source/fab/parse/fortran.py | 36 +++++++++++++------ .../parse/fortran/test_fortran_analyser.f90 | 1 + .../parse/fortran/test_fortran_analyser.py | 7 ++-- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/source/fab/parse/fortran.py b/source/fab/parse/fortran.py index 074df755f..04b2bdb31 100644 --- a/source/fab/parse/fortran.py +++ b/source/fab/parse/fortran.py @@ -17,13 +17,9 @@ Interface_Block, Name, Comment, Module, Call_Stmt, Derived_Type_Def, Derived_Type_Stmt, Type_Attr_Spec_List, Type_Attr_Spec, Type_Name, Subroutine_Subprogram, Function_Subprogram, Internal_Subprogram_Part, - External_Stmt) + External_Stmt, Type_Declaration_Stmt) from fparser.two.utils import walk # type: ignore -# todo: what else should we be importing from 2008 instead of 2003? This seems fragile. -from fparser.two.Fortran2008 import ( # type: ignore - Type_Declaration_Stmt, Attr_Spec_List) - from fab.build_config import BuildConfig from fab.dep_tree import AnalysedDependent from fab.parse.fortran_common import _typed_child, FortranAnalyserBase @@ -292,12 +288,8 @@ def walk_nodes(self, fpath, file_hash, node_tree) -> AnalysedFortran: # use in C. Variable bindings are bidirectional - does # this work the other way round, too? # Make sure we have a test for it. - elif obj_type == Type_Declaration_Stmt: - # bound? - specs = _typed_child(obj, Attr_Spec_List) - if specs and _typed_child(specs, Language_Binding_Spec): - self._process_variable_binding(analysed_fortran, obj) - + elif isinstance(obj, Type_Declaration_Stmt): + self._process_type_declaration(analysed_fortran, obj) elif obj_type == Comment: self._process_comment(analysed_fortran, obj) @@ -344,6 +336,28 @@ def _process_use_statement(self, analysed_file, obj): # found a dependency on fortran analysed_file.add_module_dep(use_name) + def _process_type_declaration(self, analysed_fortran, obj): + """ + Handles a type declaration statement. A type declaration symbol + implies an outside dependency if: + 1. there is a bind attribute (dependency to other language) + 2. external attribute. + + Syntax rule: + Type_Declaration_Stmt( ! obj + Intrinsic_Type_Spec ! obj.items[0] + Attr_Spec_List ! obj.items[1] + Entity_Decl_List ! obj.items[2] + """ + attr_spec_list = obj.items[1].items if obj.items[1] else [] + for attr in attr_spec_list: + if attr.string == "EXTERNAL": + for symbol in obj.items[2].items: + analysed_fortran.add_symbol_dep(symbol.string) + elif isinstance(attr, Language_Binding_Spec): + # Bind attribute + self._process_variable_binding(analysed_fortran, obj) + def _process_variable_binding(self, analysed_file, obj: Type_Declaration_Stmt): # The name keyword on the bind statement is optional. diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 b/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 index 11041e8ef..f8c6e55f5 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.f90 @@ -15,6 +15,7 @@ MODULE foo_mod SUBROUTINE internal_sub ! DEPENDS ON: monty_func external some_external_symbol + integer, external :: some_external_as_attribute INTERFACE SUBROUTINE sub_in_interface END SUBROUTINE sub_in_interface diff --git a/tests/unit_tests/parse/fortran/test_fortran_analyser.py b/tests/unit_tests/parse/fortran/test_fortran_analyser.py index fda791856..49f8c5020 100644 --- a/tests/unit_tests/parse/fortran/test_fortran_analyser.py +++ b/tests/unit_tests/parse/fortran/test_fortran_analyser.py @@ -37,12 +37,13 @@ def module_expected_fixture(module_fpath: Path) -> AnalysedFortran: test module.''' return AnalysedFortran( fpath=module_fpath, - file_hash=2985613950, + file_hash=3447500859, module_defs={'foo_mod'}, symbol_defs={'external_sub', 'external_func', 'foo_mod'}, module_deps={'bar_mod', 'compute_chunk_size_mod'}, symbol_deps={'monty_func', 'bar_mod', 'compute_chunk_size_mod', - 'some_external_symbol', 'sub_in_interface'}, + 'some_external_symbol', 'some_external_as_attribute', + 'sub_in_interface'}, file_deps=set(), mo_commented_file_deps={'some_file.o'}, ) @@ -157,7 +158,7 @@ def test_program_file(self, fpath=Path(tmp_file.name)) module_expected.fpath = Path(tmp_file.name) - module_expected._file_hash = 3599767944 + module_expected._file_hash = 975186955 module_expected.program_defs = {'foo_mod'} module_expected.module_defs = set()