Skip to content

Commit e447e23

Browse files
Replace deprecated appdirs with platformdirs, fix config path lookup with non conda-based environments (#113)
* Replace `appdirs` with `platformdirs` * Use `user_cache_dir` from `platformdirs` * Set `DEFAULT_CONFIG_PATH` to `user_config_dir` * Move `empack_config.yaml` to inside the project * Revert "Move `empack_config.yaml` to inside the project" This reverts commit 8739917. * Revert "Set `DEFAULT_CONFIG_PATH` to `user_config_dir`" This reverts commit e66b5fa. * Port `_do_i_own` helper method * Check for both conda/virtualenv style venvs * Fix type annotation * Add docstring * Fix linter error * Install appdirs for tests * Fix linter error again
1 parent a6eeb3c commit e447e23

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

.github/workflows/workflow.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ jobs:
4848
run: |
4949
python -m pip install git+https://github.com/emscripten-forge/pyjs-code-runner --no-deps --ignore-installed
5050
51+
# Remove after https://github.com/emscripten-forge/pyjs-code-runner/pull/15 is merged.
52+
- name: Install appdirs
53+
shell: bash -l {0}
54+
run: |
55+
python -m pip install appdirs
56+
5157
- name: Run pytest
5258
shell: bash -l {0}
5359
run: |

empack/pack.py

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from tempfile import TemporaryDirectory
88
from typing import Callable, Optional
99

10-
from appdirs import user_cache_dir
10+
from platformdirs import user_cache_dir
1111

1212
from .filter_env import filter_env, filter_pkg, iterate_env_pkg_meta
1313
from .micromamba_wrapper import create_environment
@@ -16,7 +16,88 @@
1616
EMPACK_CACHE_DIR = Path(user_cache_dir("empack"))
1717
PACKED_PACKAGES_CACHE_DIR = EMPACK_CACHE_DIR / "packed_packages_cache"
1818
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()
20101

21102

22103
def filename_base_from_meta(pkg_meta):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ classifiers = [
2424
"Programming Language :: Python :: 3.11",
2525
]
2626
dependencies = [
27-
"appdirs",
27+
"platformdirs",
2828
"networkx",
2929
"pyyaml",
3030
"requests",

0 commit comments

Comments
 (0)