From 52156170132c23ef09c40608c4100af8ee264605 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 6 Jul 2023 09:44:32 +0200 Subject: [PATCH 1/5] pass certain env vars during elevation --- menuinst/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/menuinst/utils.py b/menuinst/utils.py index b39cea50..15152912 100644 --- a/menuinst/utils.py +++ b/menuinst/utils.py @@ -341,11 +341,17 @@ def wrapper_elevate( ) else: import_func = f"from {func.__module__} import {func.__name__};" + env_vars = ";".join([ + f"os.environ.setdefault('{k}', '{v}')" + for (k, v) in os.environ.items() + if k.startswith(("CONDA_", "CONSTRUCTOR_", "MENUINST_")) + ]) cmd = [ *python_executable(), "-c", f"import os;" f"os.environ.setdefault('_MENUINST_RECURSING', '1');" + f"{env_vars};" f"{import_func}" f"{func.__name__}(" f"*{args!r}," @@ -376,7 +382,7 @@ def wrapper_elevate( return wrapper_elevate -def _test_elevation(base_prefix: Optional[os.PathLike] = None, _mode: str = "user"): +def _test_elevation(base_prefix: Optional[os.PathLike] = None, _mode: _UserOrSystem = "user"): if os.name == "nt": if base_prefix: output = os.path.join(base_prefix, "_test_output.txt") From 742f043061bb90875a28a86900e8cb9d65d43599 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 6 Jul 2023 09:48:17 +0200 Subject: [PATCH 2/5] amend news --- news/138-elevation | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/138-elevation b/news/138-elevation index 65fb712d..f2af1ac0 100644 --- a/news/138-elevation +++ b/news/138-elevation @@ -1,6 +1,6 @@ ### Enhancements -* Enable automatic elevation on Unix platforms too, and add tests. (#137 via #138) +* Enable automatic elevation on Unix platforms too, and add tests. (#137 via #138, #139) ### Bug fixes From f5271272a975945899e457ddf4a28d65acf7dad1 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 6 Jul 2023 09:50:31 +0200 Subject: [PATCH 3/5] pre-commit --- menuinst/utils.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/menuinst/utils.py b/menuinst/utils.py index 15152912..bb83665a 100644 --- a/menuinst/utils.py +++ b/menuinst/utils.py @@ -341,11 +341,13 @@ def wrapper_elevate( ) else: import_func = f"from {func.__module__} import {func.__name__};" - env_vars = ";".join([ - f"os.environ.setdefault('{k}', '{v}')" - for (k, v) in os.environ.items() - if k.startswith(("CONDA_", "CONSTRUCTOR_", "MENUINST_")) - ]) + env_vars = ";".join( + [ + f"os.environ.setdefault('{k}', '{v}')" + for (k, v) in os.environ.items() + if k.startswith(("CONDA_", "CONSTRUCTOR_", "MENUINST_")) + ] + ) cmd = [ *python_executable(), "-c", From 74186b1ff399b4d00d4d13607d37c5a20f2afd38 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 07:51:15 +0000 Subject: [PATCH 4/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- menuinst/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/menuinst/__init__.py b/menuinst/__init__.py index 14b93a8e..039986c4 100644 --- a/menuinst/__init__.py +++ b/menuinst/__init__.py @@ -4,7 +4,8 @@ import json import os import sys -from logging import getLogger as _getLogger, basicConfig as _basicConfig +from logging import basicConfig as _basicConfig +from logging import getLogger as _getLogger from os import PathLike try: From 0836dd831025c8ee58e3bcd429ebfc4678831bcc Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 6 Jul 2023 09:58:26 +0200 Subject: [PATCH 5/5] add test --- menuinst/utils.py | 26 +++++++++++++++++--------- tests/test_elevation.py | 17 ++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/menuinst/utils.py b/menuinst/utils.py index bb83665a..e1260c83 100644 --- a/menuinst/utils.py +++ b/menuinst/utils.py @@ -98,9 +98,9 @@ def slugify(text: str): Remove characters that aren't alphanumerics, or hyphens. Convert to lowercase. Also strip leading and trailing whitespace. """ - text = normalize('NFKD', text).encode('ascii', 'ignore').decode('ascii') - text = re.sub(r'[^\w\s-]', '', text).strip().lower() - return re.sub(r'[_\-\s]+', '-', text) + text = normalize("NFKD", text).encode("ascii", "ignore").decode("ascii") + text = re.sub(r"[^\w\s-]", "", text).strip().lower() + return re.sub(r"[_\-\s]+", "-", text) def indent_xml_tree(elem, level=0): @@ -247,11 +247,11 @@ def deep_update(mapping: Mapping, *updating_mappings: Iterable[Mapping]) -> Mapp def user_is_admin() -> bool: - if os.name == 'nt': + if os.name == "nt": from .platforms.win_utils.win_elevate import isUserAdmin return bool(isUserAdmin()) - elif os.name == 'posix': + elif os.name == "posix": # Check for root on Linux, macOS and other posix systems return os.getuid() == 0 else: @@ -262,11 +262,11 @@ def run_as_admin(argv: Sequence[str]) -> int: """ Rerun this command in a new process with admin permissions. """ - if os.name == 'nt': + if os.name == "nt": from .platforms.win_utils.win_elevate import runAsAdmin return runAsAdmin(argv) - elif os.name == 'posix': + elif os.name == "posix": return subprocess.call(["sudo", *argv]) else: raise RuntimeError(f"Unsupported operating system: {os.name}") @@ -279,7 +279,7 @@ def python_executable(base_prefix: Optional[os.PathLike] = None) -> Sequence[str # in these cases, we prefer using the base env python to # avoid a 2nd decompression + hacky console, so we try that # first; otherwise, we use 'conda-standalone.exe python' - if getattr(sys, 'frozen', False): + if getattr(sys, "frozen", False): if os.name == "nt": base_prefix_python = base_prefix / "python.exe" else: @@ -393,7 +393,15 @@ def _test_elevation(base_prefix: Optional[os.PathLike] = None, _mode: _UserOrSys out = open(output, "a") else: out = sys.stdout - print("user_is_admin():", user_is_admin(), "_mode:", _mode, file=out) + print( + "user_is_admin():", + user_is_admin(), + "env_var:", + os.environ.get("MENUINST_TEST", "N/A"), + "_mode:", + _mode, + file=out, + ) if os.name == "nt": out.close() diff --git a/tests/test_elevation.py b/tests/test_elevation.py index 8c70a1b1..f0e087f6 100644 --- a/tests/test_elevation.py +++ b/tests/test_elevation.py @@ -4,6 +4,7 @@ def test_elevation(tmp_path, capfd): + os.environ["MENUINST_TEST"] = "TEST" if os.name == "nt": on_ci = os.environ.get("CI") is_admin = user_is_admin() @@ -14,25 +15,27 @@ def test_elevation(tmp_path, capfd): _test_elevation(str(tmp_path)) output = (tmp_path / "_test_output.txt").read_text().strip() if on_ci: - assert output.endswith("_mode: user") + assert output.endswith("env_var: TEST _mode: user") else: - assert output.endswith("user_is_admin(): False _mode: user") + assert output.endswith("user_is_admin(): False env_var: TEST _mode: user") elevate_as_needed(_test_elevation)(base_prefix=str(tmp_path)) output = (tmp_path / "_test_output.txt").read_text().strip() if on_ci: - assert output.endswith("_mode: system") + assert output.endswith("env_var: TEST _mode: system") else: - assert output.endswith("user_is_admin(): True _mode: system") + assert output.endswith("user_is_admin(): True env_var: TEST _mode: system") else: assert not user_is_admin() # We need to start as a non-root user _test_elevation(str(tmp_path)) - assert capfd.readouterr().out.strip() == "user_is_admin(): False _mode: user" + assert capfd.readouterr().out.strip() == "user_is_admin(): False env_var: TEST _mode: user" elevate_as_needed(_test_elevation)(base_prefix=str(tmp_path)) - assert capfd.readouterr().out.strip() == "user_is_admin(): True _mode: system" + assert ( + capfd.readouterr().out.strip() == "user_is_admin(): True env_var: TEST _mode: system" + ) (tmp_path / ".nonadmin").touch() elevate_as_needed(_test_elevation)(base_prefix=str(tmp_path)) - assert capfd.readouterr().out.strip() == "user_is_admin(): False _mode: user" + assert capfd.readouterr().out.strip() == "user_is_admin(): False env_var: TEST _mode: user"