diff --git a/flavors/template-Exasol-8-python-3.10-cuda-conda/ci.json b/flavors/template-Exasol-8-python-3.10-cuda-conda/ci.json index 47735860..68018ecc 100644 --- a/flavors/template-Exasol-8-python-3.10-cuda-conda/ci.json +++ b/flavors/template-Exasol-8-python-3.10-cuda-conda/ci.json @@ -6,7 +6,7 @@ { "name": "python", "files": [], - "folders": ["python3/all"], + "folders": ["python3/all", "python3/sys_path_check/check_conda"], "goal": "release", "generic_language_tests": [] }, diff --git a/flavors/template-Exasol-8-python-3.12-cuda-conda/ci.json b/flavors/template-Exasol-8-python-3.12-cuda-conda/ci.json index 47735860..68018ecc 100644 --- a/flavors/template-Exasol-8-python-3.12-cuda-conda/ci.json +++ b/flavors/template-Exasol-8-python-3.12-cuda-conda/ci.json @@ -6,7 +6,7 @@ { "name": "python", "files": [], - "folders": ["python3/all"], + "folders": ["python3/all", "python3/sys_path_check/check_conda"], "goal": "release", "generic_language_tests": [] }, diff --git a/flavors/template-Exasol-all-python-3.10-conda/ci.json b/flavors/template-Exasol-all-python-3.10-conda/ci.json index 33a91a99..cca2b0cc 100644 --- a/flavors/template-Exasol-all-python-3.10-conda/ci.json +++ b/flavors/template-Exasol-all-python-3.10-conda/ci.json @@ -6,7 +6,7 @@ { "name": "python", "files": [], - "folders": ["python3/all"], + "folders": ["python3/all", "python3/sys_path_check/check_conda"], "goal": "release", "generic_language_tests": [] }, diff --git a/flavors/template-Exasol-all-python-3.10/ci.json b/flavors/template-Exasol-all-python-3.10/ci.json index 0695644a..120a4c91 100644 --- a/flavors/template-Exasol-all-python-3.10/ci.json +++ b/flavors/template-Exasol-all-python-3.10/ci.json @@ -6,7 +6,7 @@ { "name": "python", "files": [], - "folders": ["python3/all/fast"], + "folders": ["python3/all/fast", "python3/sys_path_check/check_apt"], "goal": "release", "generic_language_tests": [] }, diff --git a/flavors/template-Exasol-all-python-3.12-conda/ci.json b/flavors/template-Exasol-all-python-3.12-conda/ci.json index bc677241..8c6e69e7 100644 --- a/flavors/template-Exasol-all-python-3.12-conda/ci.json +++ b/flavors/template-Exasol-all-python-3.12-conda/ci.json @@ -6,7 +6,7 @@ { "name": "python", "files": [], - "folders": ["python3/all"], + "folders": ["python3/all", "python3/sys_path_check/check_conda"], "goal": "release", "generic_language_tests": [] }, diff --git a/flavors/template-Exasol-all-python-3.12/ci.json b/flavors/template-Exasol-all-python-3.12/ci.json index 41f72a5e..16e04aad 100644 --- a/flavors/template-Exasol-all-python-3.12/ci.json +++ b/flavors/template-Exasol-all-python-3.12/ci.json @@ -6,7 +6,7 @@ { "name": "python", "files": [], - "folders": ["python3/all"], + "folders": ["python3/all", "python3/sys_path_check/check_apt"], "goal": "release", "generic_language_tests": [] }, diff --git a/test_container/tests/test/python3/sys_path_check/check_apt/verify_no_conda_path.py b/test_container/tests/test/python3/sys_path_check/check_apt/verify_no_conda_path.py new file mode 100644 index 00000000..7a7e0b88 --- /dev/null +++ b/test_container/tests/test/python3/sys_path_check/check_apt/verify_no_conda_path.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import json + +from exasol_python_test_framework import udf + + +class AptSysPathCheck(udf.TestCase): + def setUp(self): + self.schema = "SYS_PATH_CHECK_APT" + self.query(f'CREATE SCHEMA {self.schema}', ignore_errors=True) + self.query(f'OPEN SCHEMA {self.schema}', ignore_errors=True) + + def tearDown(self): + self.query(f'DROP SCHEMA {self.schema} CASCADE', ignore_errors=True) + + def test_apt_runtime_does_not_use_conda_paths(self): + """ + For apt-based Python runtimes, sys.executable and sys.path entries + must not point to /opt/conda. + """ + self.query(udf.fixindent(''' + CREATE OR REPLACE python3 SCALAR SCRIPT + check_apt_runtime_no_conda_paths() + RETURNS VARCHAR(10000) AS + + import json + import sys + from pathlib import Path + + def normalize(path_value): + try: + return str(Path(path_value).resolve()) + except Exception: + return str(path_value) + + def run(ctx): + executable = normalize(sys.executable) + normalized_sys_path_entries = [ + normalize(path_entry) for path_entry in sys.path if path_entry + ] + + return json.dumps( + { + "sys_executable": executable, + "sys_path_entries": normalized_sys_path_entries, + } + ) + / + ''')) + + rows = self.query("SELECT check_apt_runtime_no_conda_paths()") + result = json.loads(rows[0][0]) + + executable = result["sys_executable"] + sys_path_entries = result["sys_path_entries"] + + self.assertNotIn( + "/opt/conda", + executable, + f"sys.executable shall not contain /opt/conda; But the value is {executable}", + ) + for path_entry in sys_path_entries: + self.assertNotIn( + "/opt/conda", + path_entry, + f"sys.path entry shall not contain /opt/conda; But the value is {path_entry}", + ) + + +if __name__ == '__main__': + udf.main() diff --git a/test_container/tests/test/python3/sys_path_check/check_conda/verify_conda_path.py b/test_container/tests/test/python3/sys_path_check/check_conda/verify_conda_path.py new file mode 100644 index 00000000..ede2f44e --- /dev/null +++ b/test_container/tests/test/python3/sys_path_check/check_conda/verify_conda_path.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +import json + +from exasol_python_test_framework import udf + + +class CondaSysPathCheck(udf.TestCase): + def setUp(self): + self.schema = "SYS_PATH_CHECK_CONDA" + self.query(f'CREATE SCHEMA {self.schema}', ignore_errors=True) + self.query(f'OPEN SCHEMA {self.schema}', ignore_errors=True) + + def tearDown(self): + self.query(f'DROP SCHEMA {self.schema} CASCADE', ignore_errors=True) + + def test_conda_interpreter_and_sys_path(self): + """ + For conda flavor, Python runtimes shall be: + 1) sys.executable resolves to /opt/conda + 2) first non-empty sys.path entry resolves to /opt/conda + """ + self.query(udf.fixindent(''' + CREATE OR REPLACE python3 SCALAR SCRIPT + check_conda_runtime() + RETURNS VARCHAR(10000) AS + + import json + import sys + from pathlib import Path + + def normalize(path_value): + try: + return str(Path(path_value).resolve()) + except Exception: + return str(path_value) + + def run(ctx): + executable = normalize(sys.executable) + path_entries = [entry for entry in sys.path if entry] + first_path = normalize(path_entries[0]) if path_entries else "" + + return json.dumps( + { + "sys_executable": executable, + "first_sys_path_entry": first_path, + } + ) + / + ''')) + + rows = self.query("SELECT check_conda_runtime()") + result = json.loads(rows[0][0]) + + executable = result["sys_executable"] + first_path = result["first_sys_path_entry"] + + self.assertTrue( + executable.startswith("/opt/conda"), + f"sys.executable shall start with /opt/conda; But the value is {executable}", + ) + self.assertTrue( + first_path.startswith("/opt/conda"), + f"sys.path first entry shall start with /opt/conda; But the value is {first_path}", + ) + + +if __name__ == '__main__': + udf.main()