|
7 | 7 | from tempfile import TemporaryDirectory |
8 | 8 | from typing import Callable, Optional |
9 | 9 |
|
10 | | -from appdirs import user_cache_dir |
| 10 | +from platformdirs import user_cache_dir |
11 | 11 |
|
12 | 12 | from .filter_env import filter_env, filter_pkg, iterate_env_pkg_meta |
13 | 13 | from .micromamba_wrapper import create_environment |
|
16 | 16 | EMPACK_CACHE_DIR = Path(user_cache_dir("empack")) |
17 | 17 | PACKED_PACKAGES_CACHE_DIR = EMPACK_CACHE_DIR / "packed_packages_cache" |
18 | 18 | PACKED_PACKAGES_CACHE_DIR.mkdir(parents=True, exist_ok=True) |
19 | | -DEFAULT_CONFIG_PATH = Path(sys.prefix) / "share" / "empack" / "empack_config.yaml" |
| 19 | + |
| 20 | + |
| 21 | +def _do_i_own(path: str | Path) -> bool: |
| 22 | + """Verify if the current user has write access to the given path. Sourced from |
| 23 | + https://github.com/jupyter/jupyter_core/blob/fa513c1550bbd1ebcc14a4a79eb8c5d95e3e23c9/jupyter_core/paths.py#L75-L99 |
| 24 | + """ |
| 25 | + # Copyright (c) Jupyter Development Team. |
| 26 | + # Distributed under the terms of the Modified BSD License. |
| 27 | + |
| 28 | + # Derived from IPython.utils.path, which is |
| 29 | + # Copyright (c) IPython Development Team. |
| 30 | + # Distributed under the terms of the Modified BSD License. |
| 31 | + |
| 32 | + p = Path(path).resolve() |
| 33 | + |
| 34 | + while not p.exists() and p != p.parent: |
| 35 | + p = p.parent |
| 36 | + |
| 37 | + # simplest check: owner by name |
| 38 | + # not always implemented or available |
| 39 | + try: |
| 40 | + return p.owner() == os.getlogin() |
| 41 | + except Exception: # noqa: S110 |
| 42 | + pass |
| 43 | + |
| 44 | + if hasattr(os, "geteuid"): |
| 45 | + try: |
| 46 | + st = p.stat() |
| 47 | + return st.st_uid == os.geteuid() |
| 48 | + except (NotImplementedError, OSError): |
| 49 | + # geteuid not always implemented |
| 50 | + pass |
| 51 | + |
| 52 | + # no ownership checks worked, check write access |
| 53 | + return os.access(p, os.W_OK) |
| 54 | + |
| 55 | + |
| 56 | +def get_config_path() -> Path: |
| 57 | + """Find the empack configuration file by checking common locations. |
| 58 | +
|
| 59 | + This function checks for the config file in the following order: |
| 60 | + 1. Inside the environment's share directory (conda-style environments) |
| 61 | + 2. Inside a share/ directory next to a virtual environment |
| 62 | +
|
| 63 | + Returns: |
| 64 | + Path: Location of the empack_config.yaml file |
| 65 | + """ |
| 66 | + # Copyright (c) Jupyter Development Team. |
| 67 | + # Distributed under the terms of the Modified BSD License. |
| 68 | + |
| 69 | + # Derived from IPython.utils.path, which is |
| 70 | + # Copyright (c) IPython Development Team. |
| 71 | + # Distributed under the terms of the Modified BSD License. |
| 72 | + |
| 73 | + # 1. Check for config in environment's share directory. This is applicable |
| 74 | + # for conda/mamba/micromamba style environments. |
| 75 | + prefix = None |
| 76 | + if ( |
| 77 | + "CONDA_PREFIX" in os.environ |
| 78 | + and sys.prefix.startswith(os.environ["CONDA_PREFIX"]) |
| 79 | + and os.environ.get("CONDA_DEFAULT_ENV", "base") != "base" |
| 80 | + and _do_i_own(sys.prefix) |
| 81 | + ): |
| 82 | + prefix = Path(os.environ["CONDA_PREFIX"]) |
| 83 | + else: |
| 84 | + prefix = Path(sys.prefix) |
| 85 | + |
| 86 | + config_path = prefix / "share" / "empack" / "empack_config.yaml" |
| 87 | + if config_path.exists(): |
| 88 | + return config_path |
| 89 | + |
| 90 | + # 2. For virtual environments via virtualenv/venv/uv, check if share/ is next |
| 91 | + # to the environment base directory. This is also applicable for the case |
| 92 | + # where there's no virtual environment at all (--user install). |
| 93 | + if sys.prefix != sys.base_prefix and _do_i_own(sys.prefix): |
| 94 | + venv_parent = Path(sys.prefix).parent |
| 95 | + parent_share = venv_parent / "share" / "empack" / "empack_config.yaml" |
| 96 | + if parent_share.exists(): |
| 97 | + return parent_share |
| 98 | + |
| 99 | + |
| 100 | +DEFAULT_CONFIG_PATH = get_config_path() |
20 | 101 |
|
21 | 102 |
|
22 | 103 | def filename_base_from_meta(pkg_meta): |
|
0 commit comments