From aa967b0157b52a5ffc39ed8b40cf0b3b5b4df884 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 25 Jun 2026 12:25:00 +1000 Subject: [PATCH 1/3] #3461 Try to import scripts differently to allow for different scripts with the same name. --- src/psyclone/generator.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index f0005709a2..f5c4886a02 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -47,13 +47,14 @@ ''' import argparse +import importlib +import logging import os +import pathlib +import shutil import sys import traceback -import importlib -import shutil from typing import Callable, Iterable, List, Optional, Tuple, Union -import logging from fparser.api import get_reader from fparser.two import Fortran2003 @@ -149,12 +150,11 @@ def load_script( raise GenerationError( f"generator: expected the script file '{filename}' to have " f"the '.py' extension") - # prepend file path - if none, the empty string equates to the current - # working directory - to the system path to guarantee we find the user - # provided module instead of a similarly named module that might - # already exist elsewhere in the system path - sys.path.insert(0, filepath) - recipe_module = importlib.import_module(module_name) + + script_path = pathlib.Path(script_name) + spec = importlib.util.spec_from_file_location(module_name, script_path) + recipe_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(recipe_module) if hasattr(recipe_module, "FILES_TO_SKIP"): files_to_skip = recipe_module.FILES_TO_SKIP From 22cbd78d7d48117fdd540f7458ac4c836e53134a Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 25 Jun 2026 13:04:49 +1000 Subject: [PATCH 2/3] #3461 Fixed failing test that relied on setting sys.path. --- src/psyclone/tests/generator_test.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/psyclone/tests/generator_test.py b/src/psyclone/tests/generator_test.py index 4e993b4620..fd06c3b2b5 100644 --- a/src/psyclone/tests/generator_test.py +++ b/src/psyclone/tests/generator_test.py @@ -540,11 +540,12 @@ def test_script_null_trans(script_factory): '\n'.join(str(psy2).split('\n')[1:]) -def test_script_null_trans_relative(script_factory): +def test_script_null_trans_relative(script_factory, monkeypatch, tmpdir): '''Checks that generator.py works correctly when the trans() function in a valid script file does no transformations (it simply passes input to output). In this case the valid script file contains no - path and must therefore be found via the PYTHOPATH path list. + path, but is invoked from the script's directory, as a relative + path. ''' alg1, psy1 = generate(os.path.join(BASE_PATH, "lfric", @@ -552,14 +553,14 @@ def test_script_null_trans_relative(script_factory): api="lfric") empty_script = script_factory("def trans(psyir):\n pass") basename = os.path.basename(empty_script) - path = os.path.dirname(empty_script) - # Set the script directory in the PYTHONPATH - os.sys.path.append(path) + + # Change into the script's directory so it's found as a relative import. + monkeypatch.chdir(tmpdir) + alg2, psy2 = generate(os.path.join(BASE_PATH, "lfric", "1_single_invoke.f90"), api="lfric", script_name=basename) - # Remove the path from PYTHONPATH - os.sys.path.pop() + # we need to remove the first line before comparing output as # this line is an instance specific header assert '\n'.join(str(alg1).split('\n')[1:]) == \ From ab86dd3c628561f56ed4a4b64da2497b43fc7c1b Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 25 Jun 2026 15:42:54 +1000 Subject: [PATCH 3/3] #3461 Add script dir back to sys.path to support psyclone scripts importing helper scripts, but clean up sys.pathin the end. --- src/psyclone/generator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index f5c4886a02..f08b541520 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -151,6 +151,7 @@ def load_script( f"generator: expected the script file '{filename}' to have " f"the '.py' extension") + sys.path.insert(0, filepath) script_path = pathlib.Path(script_name) spec = importlib.util.spec_from_file_location(module_name, script_path) recipe_module = importlib.util.module_from_spec(spec) @@ -287,6 +288,7 @@ def generate(filename: str, # Apply provided recipe to PSyIR recipe, _, _ = load_script(script_name) recipe(psy.container.root) + del sys.path[0] alg_gen = None elif api in GOCEAN_API_NAMES or (api in LFRIC_API_NAMES and LFRIC_TESTING): @@ -340,6 +342,7 @@ def generate(filename: str, is_optional=True) if recipe: recipe(psyir) + del sys.path[0] # For each kernel called from the algorithm layer kernels = {} @@ -428,6 +431,7 @@ def generate(filename: str, # Call the optimisation script for psy-layer optimisations recipe, _, _ = load_script(script_name) recipe(psy.container.root) + del sys.path[0] # TODO issue #1618 remove Alg class and tests from PSyclone if api in LFRIC_API_NAMES and not LFRIC_TESTING: @@ -979,3 +983,5 @@ def code_transformation_mode(input_file, recipe_file, output_file, else: print(f"File '{input_file}' skipped because it is listed in " "FILES_TO_SKIP.", file=sys.stdout) + + del sys.path[0]