platformdirs 4.13.0__tar.gz → 4.13.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {platformdirs-4.13.0 → platformdirs-4.13.2}/PKG-INFO +1 -1
  2. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/_xdg.py +2 -1
  3. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/unix.py +1 -1
  4. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/version.py +2 -2
  5. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/windows.py +5 -3
  6. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_unix.py +36 -0
  7. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_windows.py +23 -0
  8. {platformdirs-4.13.0 → platformdirs-4.13.2}/.gitignore +0 -0
  9. {platformdirs-4.13.0 → platformdirs-4.13.2}/LICENSE +0 -0
  10. {platformdirs-4.13.0 → platformdirs-4.13.2}/README.md +0 -0
  11. {platformdirs-4.13.0 → platformdirs-4.13.2}/pyproject.toml +0 -0
  12. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/__init__.py +0 -0
  13. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/__main__.py +0 -0
  14. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/android.py +0 -0
  15. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/api.py +0 -0
  16. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/ios.py +0 -0
  17. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/macos.py +0 -0
  18. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/py.typed +0 -0
  19. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/pytest_plugin.py +0 -0
  20. {platformdirs-4.13.0 → platformdirs-4.13.2}/src/platformdirs/testing.py +0 -0
  21. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/conftest.py +0 -0
  22. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_android.py +0 -0
  23. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_api.py +0 -0
  24. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_comp_with_appdirs.py +0 -0
  25. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_ios.py +0 -0
  26. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_macos.py +0 -0
  27. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_main.py +0 -0
  28. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_pytest_plugin.py +0 -0
  29. {platformdirs-4.13.0 → platformdirs-4.13.2}/tests/test_testing.py +0 -0
  30. {platformdirs-4.13.0 → platformdirs-4.13.2}/tox.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: platformdirs
3
- Version: 4.13.0
3
+ Version: 4.13.2
4
4
  Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`.
5
5
  Project-URL: Changelog, https://platformdirs.readthedocs.io/en/latest/changelog.html
6
6
  Project-URL: Documentation, https://platformdirs.readthedocs.io
@@ -132,7 +132,8 @@ class XDGMixin(PlatformDirsABC):
132
132
  def user_fonts_dir(self) -> str:
133
133
  """Fonts directory tied to the user, from ``$XDG_DATA_HOME/fonts`` if set, else platform default."""
134
134
  if path := _xdg_dir("XDG_DATA_HOME"):
135
- return f"{os.path.expanduser(path)}/fonts" # ruff:ignore[os-path-expanduser] # API returns str, not Path
135
+ # posixpath keeps the XDG separator even when this mixin runs on a non-POSIX host.
136
+ return posixpath.join(path, "fonts")
136
137
  return super().user_fonts_dir
137
138
 
138
139
  @property
@@ -354,7 +354,7 @@ def _resolve_user_dirs_value(entry: re.Match[str]) -> str | None:
354
354
  return None
355
355
  if entry["quoted"] is not None:
356
356
  # xdg-user-dirs-update backslash-escapes $, `, " and \ inside the quotes.
357
- value = re.sub(r"\\(.)", r"\1", value)
357
+ value = re.sub(r'\\([$`"\\])', r"\1", value)
358
358
  return prefix + value
359
359
 
360
360
 
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '4.13.0'
22
- __version_tuple__ = version_tuple = (4, 13, 0)
21
+ __version__ = version = '4.13.2'
22
+ __version_tuple__ = version_tuple = (4, 13, 2)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -355,9 +355,11 @@ def _build_get_win_folder_via_ctypes() -> Callable[[str], str]:
355
355
  ole32.CLSIDFromString(folder_guid, byref(guid))
356
356
 
357
357
  path_ptr = wintypes.LPWSTR()
358
- shell32.SHGetKnownFolderPath(byref(guid), _KF_FLAG_DONT_VERIFY, None, byref(path_ptr))
359
- result = path_ptr.value
360
- ole32.CoTaskMemFree(path_ptr)
358
+ try:
359
+ shell32.SHGetKnownFolderPath(byref(guid), _KF_FLAG_DONT_VERIFY, None, byref(path_ptr))
360
+ result = path_ptr.value
361
+ finally:
362
+ ole32.CoTaskMemFree(path_ptr)
361
363
 
362
364
  if result is None:
363
365
  msg = f"SHGetKnownFolderPath returned NULL for {csidl_name}"
@@ -701,6 +701,19 @@ def test_without_home_absolute_xdg_dir_applies(
701
701
  assert getattr(dirs_class(appname="app"), prop) == f"/xdg{suffix}"
702
702
 
703
703
 
704
+ @pytest.mark.usefixtures("_clear_xdg_env")
705
+ @pytest.mark.parametrize("dirs_class", _CLASSES)
706
+ @pytest.mark.parametrize(
707
+ ("data_home", "expected"),
708
+ [pytest.param("/", "/fonts", id="root"), pytest.param("/xdg/", "/xdg/fonts", id="trailing_slash")],
709
+ )
710
+ def test_user_fonts_dir_xdg_trailing_separator(
711
+ monkeypatch: pytest.MonkeyPatch, dirs_class: type[Unix | MacOS], data_home: str, expected: str
712
+ ) -> None:
713
+ monkeypatch.setenv("XDG_DATA_HOME", data_home)
714
+ assert dirs_class().user_fonts_dir == expected
715
+
716
+
704
717
  @pytest.mark.usefixtures("_clear_xdg_env", "_unknown_home")
705
718
  def test_without_home_absolute_user_dirs_entry_applies(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
706
719
  (tmp_path / "user-dirs.dirs").write_text('XDG_DOCUMENTS_DIR="/srv/docs"\n', encoding="utf-8")
@@ -1282,6 +1295,29 @@ def test_user_dirs_preserves_percent_signs(folder: str, base: str, tmp_path: Pat
1282
1295
  assert Unix().user_documents_path == Path(tmp_path if base == "$HOME" else base) / folder
1283
1296
 
1284
1297
 
1298
+ @pytest.mark.parametrize(
1299
+ ("folder", "expected"),
1300
+ [
1301
+ pytest.param(r"a\q", r"a\q", id="ordinary-letter"),
1302
+ pytest.param(r"a\ b", r"a\ b", id="space"),
1303
+ pytest.param(r"a\-b", r"a\-b", id="hyphen"),
1304
+ pytest.param(r"a\nb", r"a\nb", id="literal-backslash-n"),
1305
+ pytest.param(r"a\'b", r"a\'b", id="single-quote"),
1306
+ pytest.param(r"a\\b", r"a\b", id="escaped-backslash"),
1307
+ pytest.param(r"a\$b", "a$b", id="escaped-dollar"),
1308
+ pytest.param(r"a\`b", "a`b", id="escaped-backtick"),
1309
+ pytest.param(r"a\"b", 'a"b', id="escaped-double-quote"),
1310
+ pytest.param(r"a\q\\b\$c", r"a\q\b$c", id="mixed-escapes"),
1311
+ ],
1312
+ )
1313
+ @pytest.mark.parametrize("base", ["$HOME", "/absolute"])
1314
+ def test_user_dirs_double_quoted_backslashes(
1315
+ folder: str, expected: str, base: str, tmp_path: Path, user_dirs_file: Path
1316
+ ) -> None:
1317
+ user_dirs_file.write_text(f'XDG_DOCUMENTS_DIR="{base}/{folder}"\n', encoding="utf-8")
1318
+ assert Unix().user_documents_path == Path(tmp_path if base == "$HOME" else base) / expected
1319
+
1320
+
1285
1321
  @pytest.mark.parametrize(
1286
1322
  ("content", "expected"),
1287
1323
  [
@@ -483,6 +483,29 @@ def test_get_win_folder_via_ctypes_null_result(mocker: MockerFixture) -> None:
483
483
  _cleanup_ctypes_mocks()
484
484
 
485
485
 
486
+ @pytest.mark.parametrize(
487
+ "path",
488
+ [pytest.param(None, id="null"), pytest.param(r"C:\Users\Test\AppData\Local", id="populated")],
489
+ )
490
+ def test_get_win_folder_via_ctypes_frees_path_on_error(mocker: MockerFixture, path: str | None) -> None:
491
+ _setup_ctypes_mocks(mocker)
492
+ error = OSError("SHGetKnownFolderPath failed")
493
+ dlls = {"ole32": MagicMock(), "shell32": MagicMock(SHGetKnownFolderPath=MagicMock(side_effect=error))}
494
+ mocker.patch.object(ctypes, "WinDLL", MagicMock(side_effect=dlls.__getitem__))
495
+ mocker.patch("ctypes.byref", side_effect=lambda x: x)
496
+ path_ptr = MagicMock(value=path)
497
+ mocker.patch("ctypes.wintypes.LPWSTR", return_value=path_ptr)
498
+
499
+ try:
500
+ importlib.reload(windows)
501
+ with pytest.raises(OSError, match=r"^SHGetKnownFolderPath failed$"):
502
+ windows.get_win_folder_via_ctypes("CSIDL_LOCAL_APPDATA")
503
+ finally:
504
+ _cleanup_ctypes_mocks()
505
+
506
+ dlls["ole32"].CoTaskMemFree.assert_called_once_with(path_ptr)
507
+
508
+
486
509
  def _short_name(_long: str, buf: ctypes.Array[ctypes.c_wchar], _size: int) -> int:
487
510
  buf.value = r"C:\Users\UKASZ~1\AppData\Local"
488
511
  return len(buf.value)
File without changes
File without changes
File without changes
File without changes