From c48b3982623927f95a05c4698b3ff836e5f35a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sat, 21 Feb 2026 15:57:21 +0100 Subject: [PATCH 1/5] Do not insert local paths before standard library paths Inserting local paths like `salt/utils` before standard library makes many modules in utils conflict with standard library. This is especially relevant for salt-ssh. For example salt.utils.configparser - it tries to import configparser from stdlib, but due to salt/utils path being prepended to sys.path, it imports itself: [ERROR ] Failed to import fileserver gitfs, this is due most likely to a syntax error: Traceback (most recent call last): File "/var/tmp/.root_dd8a91_salt/pyall/salt/loader/lazy.py", line 902, in _load_module self.run(spec.loader.exec_module, mod) ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/tmp/.root_dd8a91_salt/pyall/salt/loader/lazy.py", line 1365, in run return self._last_context.run(self._run_as, _func_or_method, *args, **kwargs) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/tmp/.root_dd8a91_salt/pyall/salt/loader/lazy.py", line 1380, in _run_as ret = _func_or_method(*args, **kwargs) File "", line 1023, in exec_module File "", line 488, in _call_with_frames_removed File "/var/tmp/.root_dd8a91_salt/pyall/salt/fileserver/gitfs.py", line 52, in import salt.utils.gitfs File "/var/tmp/.root_dd8a91_salt/pyall/salt/utils/gitfs.py", line 31, in import salt.utils.configparser File "/var/tmp/.root_dd8a91_salt/pyall/salt/utils/configparser.py", line 7, in from configparser import * # pylint: disable=no-name-in-module,wildcard-import,unused-wildcard-import ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/tmp/.root_dd8a91_salt/running_data/var/cache/salt/minion/extmods/utils/configparser.py", line 12, in class GitConfigParser(RawConfigParser): ^^^^^^^^^^^^^^^ NameError: name 'RawConfigParser' is not defined On the other hand, places that try to use such duplicated modules from utils, import them via full name (as seen above: `import salt.utils.configparser`). Change the insert_system_path() function to not insert paths before standard library. Fixes #68755 --- changelog/68755.fixed.md | 1 + salt/config/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/68755.fixed.md diff --git a/changelog/68755.fixed.md b/changelog/68755.fixed.md new file mode 100644 index 000000000000..be6a5677bc61 --- /dev/null +++ b/changelog/68755.fixed.md @@ -0,0 +1 @@ +Fix salt.utils conflicting with standard library when using salt-ssh diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 27cf2ba78076..51a8785f47f0 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -2295,7 +2295,7 @@ def insert_system_path(opts, paths): path_options = {"path": path, "root_dir": opts["root_dir"]} prepend_root_dir(path_options, path_options) if os.path.isdir(path_options["path"]) and path_options["path"] not in sys.path: - sys.path.insert(0, path_options["path"]) + sys.path.append(path_options["path"]) def minion_config( From c92447e3f210f5efad039cf475aa27e1125b7aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 27 Feb 2026 22:40:46 +0100 Subject: [PATCH 2/5] WIP Revert "Do not insert local paths before standard library paths" Temporarily for checkig if test works. This reverts commit b13c2033c475889d28b70ddedb4c1d61a1e5ceab. --- changelog/68755.fixed.md | 1 - salt/config/__init__.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 changelog/68755.fixed.md diff --git a/changelog/68755.fixed.md b/changelog/68755.fixed.md deleted file mode 100644 index be6a5677bc61..000000000000 --- a/changelog/68755.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix salt.utils conflicting with standard library when using salt-ssh diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 51a8785f47f0..27cf2ba78076 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -2295,7 +2295,7 @@ def insert_system_path(opts, paths): path_options = {"path": path, "root_dir": opts["root_dir"]} prepend_root_dir(path_options, path_options) if os.path.isdir(path_options["path"]) and path_options["path"] not in sys.path: - sys.path.append(path_options["path"]) + sys.path.insert(0, path_options["path"]) def minion_config( From addda7949763d9bf0aef791ce321804ae7aa9cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 27 Feb 2026 23:26:05 +0100 Subject: [PATCH 3/5] Add test for pkg.installed state Check if it works correctly, especially if it can be imported. --- tests/pytests/integration/ssh/state/test_state.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/pytests/integration/ssh/state/test_state.py b/tests/pytests/integration/ssh/state/test_state.py index f9fd126bb67c..5fbbc15426e0 100644 --- a/tests/pytests/integration/ssh/state/test_state.py +++ b/tests/pytests/integration/ssh/state/test_state.py @@ -113,3 +113,10 @@ def test_state_test(salt_ssh_cli, state_tree): ret.data["test_|-Ok with def_|-Ok with def_|-succeed_with_changes"]["result"] is None ) + + +def test_state_pkg(salt_ssh_cli): + ret = salt_ssh_cli.run("state.single", "pkg.installed", "coreutils", "test=True") + assert ret.returncode == 0 + assert ret.data + assert ret.data["pkg_|-coreutils_|-coreutils_|-installed"]["result"] is not False From 3acb7847d1068592e4a07b447ad4c27e664b22d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 2 Mar 2026 04:05:05 +0100 Subject: [PATCH 4/5] Avoid sys.path modification as a side effect of LazyLoader LazyLoader._load_module() appends a dir of imported module (fpath_dirname) to sys.path and then undoes that change by sys.path.remove(). But if that directory was in sys.path already, the remove call will remove an earlier item than the appended one - effectively changing the items order in sys.path. Fix this by avoiding adding and removing fpath_dirname if it was in sys.path already. Ironically, this side effect sometimes (depending on module discovery order) cancels out with bug #68755 (the wrongly removed path is the one that was wrongly inserted in the first place). While contributed to it being unnoticed for quite some time, and made writting a test of the fix harder. --- salt/loader/lazy.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/salt/loader/lazy.py b/salt/loader/lazy.py index 1a89c796fce8..9127430ebffe 100644 --- a/salt/loader/lazy.py +++ b/salt/loader/lazy.py @@ -778,9 +778,12 @@ def _load_module(self, name): self.loaded_files.add(name) fpath_dirname = os.path.dirname(fpath) + fpath_appended = False try: self.__populate_sys_path() - sys.path.append(fpath_dirname) + if fpath_dirname not in sys.path: + sys.path.append(fpath_dirname) + fpath_appended = True if suffix == ".pyx": mod = pyximport.load_module(name, fpath, tempfile.gettempdir()) elif suffix == ".o": @@ -917,7 +920,8 @@ def _load_module(self, name): self.missing_modules[name] = error return False finally: - sys.path.remove(fpath_dirname) + if fpath_appended: + sys.path.remove(fpath_dirname) self.__clean_sys_path() loader_context = salt.loader.context.LoaderContext() From 782b07c28d50b07ba31b41bc63a782569af733e1 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Sun, 7 Jun 2026 16:43:52 -0700 Subject: [PATCH 5/5] Restore changelog/68755.fixed.md (reverted in c92447e along with the abandoned config-branch commit) --- changelog/68755.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/68755.fixed.md diff --git a/changelog/68755.fixed.md b/changelog/68755.fixed.md new file mode 100644 index 000000000000..b6f106c582c0 --- /dev/null +++ b/changelog/68755.fixed.md @@ -0,0 +1 @@ +Don't insert local paths before standard library paths in LazyLoader, preventing sys.path reordering when loader modules are already importable.