diff --git a/CHANGES.md b/CHANGES.md index cdc413c64..625213baa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,15 @@ ones in. --> -------------------------------------------------------------------------------- +## 2.2.0 (Upcoming) + +### Breaking Changes + +[#2736](https://github.com/metomi/rose/pull/2736) - +Rose now ignores `PYTHONPATH` to make it more robust to task environments +which set this value. If you want to add to the Rose environment itself, +e.g. to write a rose-ana test, use `ROSE_PYTHONPATH`. + ## 2.1.0 (Released 2023-07-21) ### Fixes diff --git a/metomi/rose/rose.py b/metomi/rose/rose.py index be4606cff..9b0f06532 100644 --- a/metomi/rose/rose.py +++ b/metomi/rose/rose.py @@ -14,11 +14,42 @@ # # You should have received a copy of the GNU General Public License # along with Rose. If not, see . + +import os +import sys + + +def pythonpath_manip(): + """Stop PYTHONPATH contaminating the Rose Environment + + * Remove PYTHONPATH items from sys.path to prevent PYTHONPATH + contaminating the Rose Environment. + * Add items from ROSE_PYTHONPATH to sys.path. + + See Also: + https://github.com/cylc/cylc-flow/issues/5124 + """ + if 'ROSE_PYTHONPATH' in os.environ: + paths = [ + os.path.abspath(item) + for item in os.environ['ROSE_PYTHONPATH'].split(os.pathsep) + ] + paths.extend(sys.path) + sys.path = paths + + if 'PYTHONPATH' in os.environ: + for item in os.environ['PYTHONPATH'].split(os.pathsep): + abspath = os.path.abspath(item) + if abspath in sys.path: + sys.path.remove(abspath) + + +pythonpath_manip() + + import argparse from inspect import signature -import os from pathlib import Path -import sys from pkg_resources import ( DistributionNotFound, diff --git a/metomi/rose/tests/test_rose.py b/metomi/rose/tests/test_rose.py new file mode 100644 index 000000000..7f7e95346 --- /dev/null +++ b/metomi/rose/tests/test_rose.py @@ -0,0 +1,44 @@ +# Copyright (C) British Crown (Met Office) & Contributors. +# +# This file is part of Rose, a framework for meteorological suites. +# +# Rose is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Rose is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Rose. If not, see . +"""Test metomi/rose/rose.py +""" + +import os +import sys + +from metomi.rose.rose import pythonpath_manip + + +def test_pythonpath_manip(monkeypatch): + """pythonpath_manip removes items in PYTHONPATH from sys.path + and adds items from ROSE_PYTHONPATH + """ + # If PYTHONPATH is set... + monkeypatch.setenv('PYTHONPATH', '/remove1:/remove2') + monkeypatch.setattr('sys.path', ['/leave-alone', '/remove1', '/remove2']) + pythonpath_manip() + # ... we don't change PYTHONPATH + assert os.environ['PYTHONPATH'] == '/remove1:/remove2' + # ... but we do remove PYTHONPATH items from sys.path, and don't remove + # items there not in PYTHONPATH + assert sys.path == ['/leave-alone'] + + # If ROSE_PYTHONPATH is set we retrieve its contents and + # add them to the sys.path: + monkeypatch.setenv('ROSE_PYTHONPATH', '/add1:/add2') + pythonpath_manip() + assert sys.path == ['/add1', '/add2', '/leave-alone'] diff --git a/tox.ini b/tox.ini index 8dad1c445..83a93c182 100644 --- a/tox.ini +++ b/tox.ini @@ -13,3 +13,5 @@ ignore= E731, # no longer best practice: W503 + ; module level import not at top of file + E402,