Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/changelog/+relative-isolation.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pin a relative ``isolated_dirs`` root on context entry so changing the working directory keeps directory lookups and
creation under the original isolation root. The context now yields an absolute path.
8 changes: 5 additions & 3 deletions docs/howto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,11 @@ isolates under ``tmp_path`` and yields the root:
assert my_app_init(PlatformDirs("MyApp")) == {"theme": "dark"}

Each kind lands in ``<root>/<kind>``, for example ``<root>/user_config/MyApp``, and the module-level functions follow
the same layout. On Unix, isolation ignores ``use_site_for_root`` so user and site kinds stay separate even for root.
The redirection covers :data:`~platformdirs.PlatformDirs` in the current process only: a subprocess, or another platform
class such as :class:`~platformdirs.unix.Unix` on macOS, still resolves the real directories.
the same layout. A relative root is made absolute when entering the context and the yielded root is absolute, so
changing the working directory inside the context keeps all paths under the original root. On Unix, isolation ignores
``use_site_for_root`` so user and site kinds stay separate even for root. The redirection covers
:data:`~platformdirs.PlatformDirs` in the current process only: a subprocess, or another platform class such as
:class:`~platformdirs.unix.Unix` on macOS, still resolves the real directories.

Overriding directories with environment variables
=================================================
Expand Down
7 changes: 4 additions & 3 deletions src/platformdirs/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ def isolated_dirs(root: str | os.PathLike[str]) -> Generator[Path, None, None]:
``PlatformDirs("app", version="1.0").user_config_path`` becomes ``<root>/user_config/app/1.0``. Every thread of the
current process sees the change, and subprocesses do not.

On Unix, ``use_site_for_root`` is ignored inside the context so user and site directories keep separate kinds.
On Unix, ``use_site_for_root`` is ignored inside the context so user and site directories keep separate kinds. A
relative ``root`` is made absolute on entry, so changing the working directory does not move the isolation root.

:param root: directory that holds the redirected directories

:returns: ``root`` as a :class:`~pathlib.Path`
:returns: the absolute isolation root as a :class:`~pathlib.Path`

"""
base = Path(root)
base = Path(root).absolute()
patches = {
name: _redirect(
base / (kind := name.removesuffix("_dir")), app=kind in _APP_KINDS, private=kind.startswith("user_")
Expand Down
18 changes: 18 additions & 0 deletions tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ def test_isolated_dirs_yields_root_as_path(tmp_path: Path) -> None:
assert root == tmp_path


@pytest.mark.parametrize("root_name", [".", "isolated"])
def test_isolated_dirs_relative_root_survives_chdir(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, root_name: str
) -> None:
monkeypatch.chdir(tmp_path)
expected = tmp_path / root_name
(other := tmp_path / "other").mkdir()
with isolated_dirs(root_name) as root:
monkeypatch.chdir(other)
assert _outside(expected) == {}
assert root == expected
dirs = platformdirs.PlatformDirs("app", ensure_exists=True)
assert dirs.user_config_path == expected / "user_config" / "app"
assert dirs.user_config_path.is_dir()
assert not (other / "user_config").exists()
assert not (other / "isolated").exists()


def test_isolated_dirs_redirects_every_accessor(tmp_path: Path) -> None:
with isolated_dirs(tmp_path):
assert _outside(tmp_path) == {}
Expand Down