summaryrefslogtreecommitdiffstats
path: root/contrib/python/platformdirs
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-03-02 19:21:26 +0300
committerrobot-piglet <[email protected]>2026-03-02 21:27:31 +0300
commit83fbb037f31247ccbad309651c665682dd7d88e2 (patch)
treefb591d12d384303cd4ba91ffde768604ea7d15ca /contrib/python/platformdirs
parentaaa2572ec1cd37989cd69a4e8286df17394f756a (diff)
Intermediate changes
commit_hash:15fe475c75799abfa3a20e1c305672191703d12c
Diffstat (limited to 'contrib/python/platformdirs')
-rw-r--r--contrib/python/platformdirs/.dist-info/METADATA61
-rw-r--r--contrib/python/platformdirs/README.md59
-rw-r--r--contrib/python/platformdirs/platformdirs/__init__.py222
-rw-r--r--contrib/python/platformdirs/platformdirs/__main__.py6
-rw-r--r--contrib/python/platformdirs/platformdirs/_xdg.py19
-rw-r--r--contrib/python/platformdirs/platformdirs/android.py32
-rw-r--r--contrib/python/platformdirs/platformdirs/api.py90
-rw-r--r--contrib/python/platformdirs/platformdirs/macos.py50
-rw-r--r--contrib/python/platformdirs/platformdirs/unix.py96
-rw-r--r--contrib/python/platformdirs/platformdirs/version.py4
-rw-r--r--contrib/python/platformdirs/platformdirs/windows.py88
-rw-r--r--contrib/python/platformdirs/ya.make2
12 files changed, 658 insertions, 71 deletions
diff --git a/contrib/python/platformdirs/.dist-info/METADATA b/contrib/python/platformdirs/.dist-info/METADATA
index ee9eb1f16ee..f3e2dcea219 100644
--- a/contrib/python/platformdirs/.dist-info/METADATA
+++ b/contrib/python/platformdirs/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: platformdirs
-Version: 4.7.0
+Version: 4.9.1
Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`.
Project-URL: Changelog, https://platformdirs.readthedocs.io/en/latest/changelog.html
Project-URL: Documentation, https://platformdirs.readthedocs.io
@@ -44,13 +44,27 @@ differences between macOS, Windows, Linux/Unix, and Android so you don't have to
from platformdirs import PlatformDirs
dirs = PlatformDirs("MyApp", "MyCompany")
-dirs.user_data_dir # e.g. ~/.local/share/MyApp on Linux
-dirs.user_config_dir # e.g. ~/.config/MyApp on Linux
-dirs.user_cache_dir # e.g. ~/.cache/MyApp on Linux
-dirs.user_log_dir # e.g. ~/.local/state/MyApp/log on Linux
+dirs.user_data_dir # ~/.local/share/MyApp (Linux)
+dirs.user_config_dir # ~/.config/MyApp (Linux)
+dirs.user_cache_dir # ~/.cache/MyApp (Linux)
+dirs.user_state_dir # ~/.local/state/MyApp (Linux)
+dirs.user_log_dir # ~/.local/state/MyApp/log (Linux)
+dirs.user_documents_dir # ~/Documents
+dirs.user_downloads_dir # ~/Downloads
+dirs.user_runtime_dir # /run/user/<uid>/MyApp (Linux)
```
-Convenience functions are also available:
+For Path objects instead of strings:
+
+```python
+from platformdirs import PlatformDirs
+
+dirs = PlatformDirs("MyApp", "MyCompany")
+dirs.user_data_path # pathlib.Path('~/.local/share/MyApp')
+dirs.user_config_path # pathlib.Path('~/.config/MyApp')
+```
+
+Convenience functions for quick access:
```python
from platformdirs import user_data_dir, user_config_path
@@ -59,25 +73,28 @@ user_data_dir("MyApp", "MyCompany") # returns str
user_config_path("MyApp", "MyCompany") # returns pathlib.Path
```
-## Documentation
+## Directory types
-Full documentation is available at [platformdirs.readthedocs.io](https://platformdirs.readthedocs.io), including:
+- **Data**: Persistent application data (`user_data_dir`, `site_data_dir`)
+- **Config**: Configuration files and settings (`user_config_dir`, `site_config_dir`)
+- **Cache**: Cached data that can be regenerated (`user_cache_dir`, `site_cache_dir`)
+- **State**: Non-essential runtime state like window positions (`user_state_dir`, `site_state_dir`)
+- **Logs**: Log files (`user_log_dir`, `site_log_dir`)
+- **Runtime**: Runtime files like sockets and PIDs (`user_runtime_dir`, `site_runtime_dir`)
-- [Usage guide](https://platformdirs.readthedocs.io/en/latest/usage.html) -- parameters, examples, and patterns
-- [API reference](https://platformdirs.readthedocs.io/en/latest/api.html) -- all functions and classes
-- [Platform details](https://platformdirs.readthedocs.io/en/latest/platforms.html) -- per-platform paths and behavior
+Each type has both `user_*` (per-user, writable) and `site_*` (system-wide, read-only for users) variants.
-## Why this fork?
-
-This repository is a friendly fork of the wonderful work started by
-[ActiveState](https://github.com/ActiveState/appdirs) who created `appdirs`, this package's ancestor.
+## Documentation
-Maintaining an open source project is no easy task, particularly from within an organization, and the Python community
-is indebted to `appdirs` (and to Trent Mick and Jeff Rouse in particular) for creating an incredibly useful simple
-module, as evidenced by the wide number of users it has attracted over the years.
+Full documentation is available at [platformdirs.readthedocs.io](https://platformdirs.readthedocs.io):
-Nonetheless, given the number of long-standing open issues and pull requests, and no clear path towards
-[ensuring that maintenance of the package would continue or grow](https://github.com/ActiveState/appdirs/issues/79),
-this fork was created.
+- **[Getting started tutorial](https://platformdirs.readthedocs.io/en/latest/usage.html)** -- learn core concepts
+ through real-world examples
+- **[How-to guides](https://platformdirs.readthedocs.io/en/latest/howto.html)** -- recipes for common tasks and
+ platform-specific tips
+- **[API reference](https://platformdirs.readthedocs.io/en/latest/api.html)** -- complete list of functions and classes
+- **[Platform details](https://platformdirs.readthedocs.io/en/latest/platforms.html)** -- default paths for each
+ operating system
-Contributions are most welcome.
+Contributions are welcome! See [CONTRIBUTING.md](https://github.com/tox-dev/platformdirs/blob/main/CONTRIBUTING.md) for
+details.
diff --git a/contrib/python/platformdirs/README.md b/contrib/python/platformdirs/README.md
index 04779d681ff..4642eac68f7 100644
--- a/contrib/python/platformdirs/README.md
+++ b/contrib/python/platformdirs/README.md
@@ -14,13 +14,27 @@ differences between macOS, Windows, Linux/Unix, and Android so you don't have to
from platformdirs import PlatformDirs
dirs = PlatformDirs("MyApp", "MyCompany")
-dirs.user_data_dir # e.g. ~/.local/share/MyApp on Linux
-dirs.user_config_dir # e.g. ~/.config/MyApp on Linux
-dirs.user_cache_dir # e.g. ~/.cache/MyApp on Linux
-dirs.user_log_dir # e.g. ~/.local/state/MyApp/log on Linux
+dirs.user_data_dir # ~/.local/share/MyApp (Linux)
+dirs.user_config_dir # ~/.config/MyApp (Linux)
+dirs.user_cache_dir # ~/.cache/MyApp (Linux)
+dirs.user_state_dir # ~/.local/state/MyApp (Linux)
+dirs.user_log_dir # ~/.local/state/MyApp/log (Linux)
+dirs.user_documents_dir # ~/Documents
+dirs.user_downloads_dir # ~/Downloads
+dirs.user_runtime_dir # /run/user/<uid>/MyApp (Linux)
```
-Convenience functions are also available:
+For Path objects instead of strings:
+
+```python
+from platformdirs import PlatformDirs
+
+dirs = PlatformDirs("MyApp", "MyCompany")
+dirs.user_data_path # pathlib.Path('~/.local/share/MyApp')
+dirs.user_config_path # pathlib.Path('~/.config/MyApp')
+```
+
+Convenience functions for quick access:
```python
from platformdirs import user_data_dir, user_config_path
@@ -29,25 +43,28 @@ user_data_dir("MyApp", "MyCompany") # returns str
user_config_path("MyApp", "MyCompany") # returns pathlib.Path
```
-## Documentation
+## Directory types
-Full documentation is available at [platformdirs.readthedocs.io](https://platformdirs.readthedocs.io), including:
+- **Data**: Persistent application data (`user_data_dir`, `site_data_dir`)
+- **Config**: Configuration files and settings (`user_config_dir`, `site_config_dir`)
+- **Cache**: Cached data that can be regenerated (`user_cache_dir`, `site_cache_dir`)
+- **State**: Non-essential runtime state like window positions (`user_state_dir`, `site_state_dir`)
+- **Logs**: Log files (`user_log_dir`, `site_log_dir`)
+- **Runtime**: Runtime files like sockets and PIDs (`user_runtime_dir`, `site_runtime_dir`)
-- [Usage guide](https://platformdirs.readthedocs.io/en/latest/usage.html) -- parameters, examples, and patterns
-- [API reference](https://platformdirs.readthedocs.io/en/latest/api.html) -- all functions and classes
-- [Platform details](https://platformdirs.readthedocs.io/en/latest/platforms.html) -- per-platform paths and behavior
+Each type has both `user_*` (per-user, writable) and `site_*` (system-wide, read-only for users) variants.
-## Why this fork?
-
-This repository is a friendly fork of the wonderful work started by
-[ActiveState](https://github.com/ActiveState/appdirs) who created `appdirs`, this package's ancestor.
+## Documentation
-Maintaining an open source project is no easy task, particularly from within an organization, and the Python community
-is indebted to `appdirs` (and to Trent Mick and Jeff Rouse in particular) for creating an incredibly useful simple
-module, as evidenced by the wide number of users it has attracted over the years.
+Full documentation is available at [platformdirs.readthedocs.io](https://platformdirs.readthedocs.io):
-Nonetheless, given the number of long-standing open issues and pull requests, and no clear path towards
-[ensuring that maintenance of the package would continue or grow](https://github.com/ActiveState/appdirs/issues/79),
-this fork was created.
+- **[Getting started tutorial](https://platformdirs.readthedocs.io/en/latest/usage.html)** -- learn core concepts
+ through real-world examples
+- **[How-to guides](https://platformdirs.readthedocs.io/en/latest/howto.html)** -- recipes for common tasks and
+ platform-specific tips
+- **[API reference](https://platformdirs.readthedocs.io/en/latest/api.html)** -- complete list of functions and classes
+- **[Platform details](https://platformdirs.readthedocs.io/en/latest/platforms.html)** -- default paths for each
+ operating system
-Contributions are most welcome.
+Contributions are welcome! See [CONTRIBUTING.md](https://github.com/tox-dev/platformdirs/blob/main/CONTRIBUTING.md) for
+details.
diff --git a/contrib/python/platformdirs/platformdirs/__init__.py b/contrib/python/platformdirs/platformdirs/__init__.py
index 7896bf67b04..fb530c92998 100644
--- a/contrib/python/platformdirs/platformdirs/__init__.py
+++ b/contrib/python/platformdirs/platformdirs/__init__.py
@@ -53,12 +53,13 @@ else:
AppDirs = PlatformDirs #: Backwards compatibility with appdirs
-def user_data_dir(
+def user_data_dir( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
roaming: bool = False, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> str:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -66,6 +67,7 @@ def user_data_dir(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: data directory tied to the user
"""
return PlatformDirs(
@@ -74,6 +76,7 @@ def user_data_dir(
version=version,
roaming=roaming,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_data_dir
@@ -101,12 +104,13 @@ def site_data_dir(
).site_data_dir
-def user_config_dir(
+def user_config_dir( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
roaming: bool = False, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> str:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -114,6 +118,7 @@ def user_config_dir(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: config directory tied to the user
"""
return PlatformDirs(
@@ -122,6 +127,7 @@ def user_config_dir(
version=version,
roaming=roaming,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_config_dir
@@ -149,12 +155,13 @@ def site_config_dir(
).site_config_dir
-def user_cache_dir(
+def user_cache_dir( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> str:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -162,6 +169,7 @@ def user_cache_dir(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: cache directory tied to the user
"""
return PlatformDirs(
@@ -170,6 +178,7 @@ def user_cache_dir(
version=version,
opinion=opinion,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_cache_dir
@@ -197,12 +206,13 @@ def site_cache_dir(
).site_cache_dir
-def user_state_dir(
+def user_state_dir( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
roaming: bool = False, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> str:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -210,6 +220,7 @@ def user_state_dir(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: state directory tied to the user
"""
return PlatformDirs(
@@ -218,15 +229,38 @@ def user_state_dir(
version=version,
roaming=roaming,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_state_dir
-def user_log_dir(
+def site_state_dir(
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :returns: state directory shared by users
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ ensure_exists=ensure_exists,
+ ).site_state_dir
+
+
+def user_log_dir( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> str:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -234,6 +268,7 @@ def user_log_dir(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: log directory tied to the user
"""
return PlatformDirs(
@@ -242,9 +277,34 @@ def user_log_dir(
version=version,
opinion=opinion,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_log_dir
+def site_log_dir(
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ opinion: bool = True, # noqa: FBT001, FBT002
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :returns: log directory shared by users
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ opinion=opinion,
+ ensure_exists=ensure_exists,
+ ).site_log_dir
+
+
def user_documents_dir() -> str:
""":returns: documents directory tied to the user"""
return PlatformDirs().user_documents_dir
@@ -275,12 +335,43 @@ def user_desktop_dir() -> str:
return PlatformDirs().user_desktop_dir
-def user_runtime_dir(
+def user_bin_dir() -> str:
+ """:returns: bin directory tied to the user"""
+ return PlatformDirs().user_bin_dir
+
+
+def site_bin_dir() -> str:
+ """:returns: bin directory shared by users"""
+ return PlatformDirs().site_bin_dir
+
+
+def user_applications_dir() -> str:
+ """:returns: applications directory tied to the user"""
+ return PlatformDirs().user_applications_dir
+
+
+def site_applications_dir(
+ multipath: bool = False, # noqa: FBT001, FBT002
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
+) -> str:
+ """
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :returns: applications directory shared by users
+ """
+ return PlatformDirs(
+ multipath=multipath,
+ ensure_exists=ensure_exists,
+ ).site_applications_dir
+
+
+def user_runtime_dir( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> str:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -288,6 +379,7 @@ def user_runtime_dir(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: runtime directory tied to the user
"""
return PlatformDirs(
@@ -296,6 +388,7 @@ def user_runtime_dir(
version=version,
opinion=opinion,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_runtime_dir
@@ -323,12 +416,13 @@ def site_runtime_dir(
).site_runtime_dir
-def user_data_path(
+def user_data_path( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
roaming: bool = False, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> Path:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -336,6 +430,7 @@ def user_data_path(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: data path tied to the user
"""
return PlatformDirs(
@@ -344,6 +439,7 @@ def user_data_path(
version=version,
roaming=roaming,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_data_path
@@ -371,12 +467,13 @@ def site_data_path(
).site_data_path
-def user_config_path(
+def user_config_path( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
roaming: bool = False, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> Path:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -384,6 +481,7 @@ def user_config_path(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: config path tied to the user
"""
return PlatformDirs(
@@ -392,6 +490,7 @@ def user_config_path(
version=version,
roaming=roaming,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_config_path
@@ -443,12 +542,13 @@ def site_cache_path(
).site_cache_path
-def user_cache_path(
+def user_cache_path( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> Path:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -456,6 +556,7 @@ def user_cache_path(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: cache path tied to the user
"""
return PlatformDirs(
@@ -464,15 +565,17 @@ def user_cache_path(
version=version,
opinion=opinion,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_cache_path
-def user_state_path(
+def user_state_path( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
roaming: bool = False, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> Path:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -480,6 +583,7 @@ def user_state_path(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: state path tied to the user
"""
return PlatformDirs(
@@ -488,15 +592,38 @@ def user_state_path(
version=version,
roaming=roaming,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_state_path
-def user_log_path(
+def site_state_path(
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :returns: state path shared by users
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ ensure_exists=ensure_exists,
+ ).site_state_path
+
+
+def user_log_path( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> Path:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -504,6 +631,7 @@ def user_log_path(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: log path tied to the user
"""
return PlatformDirs(
@@ -512,9 +640,34 @@ def user_log_path(
version=version,
opinion=opinion,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_log_path
+def site_log_path(
+ appname: str | None = None,
+ appauthor: str | Literal[False] | None = None,
+ version: str | None = None,
+ opinion: bool = True, # noqa: FBT001, FBT002
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :returns: log path shared by users
+ """
+ return PlatformDirs(
+ appname=appname,
+ appauthor=appauthor,
+ version=version,
+ opinion=opinion,
+ ensure_exists=ensure_exists,
+ ).site_log_path
+
+
def user_documents_path() -> Path:
""":returns: documents path tied to the user"""
return PlatformDirs().user_documents_path
@@ -545,12 +698,43 @@ def user_desktop_path() -> Path:
return PlatformDirs().user_desktop_path
-def user_runtime_path(
+def user_bin_path() -> Path:
+ """:returns: bin path tied to the user"""
+ return PlatformDirs().user_bin_path
+
+
+def site_bin_path() -> Path:
+ """:returns: bin path shared by users"""
+ return PlatformDirs().site_bin_path
+
+
+def user_applications_path() -> Path:
+ """:returns: applications path tied to the user"""
+ return PlatformDirs().user_applications_path
+
+
+def site_applications_path(
+ multipath: bool = False, # noqa: FBT001, FBT002
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
+) -> Path:
+ """
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :returns: applications path shared by users
+ """
+ return PlatformDirs(
+ multipath=multipath,
+ ensure_exists=ensure_exists,
+ ).site_applications_path
+
+
+def user_runtime_path( # noqa: PLR0913, PLR0917
appname: str | None = None,
appauthor: str | Literal[False] | None = None,
version: str | None = None,
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> Path:
"""
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
@@ -558,6 +742,7 @@ def user_runtime_path(
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
:returns: runtime path tied to the user
"""
return PlatformDirs(
@@ -566,6 +751,7 @@ def user_runtime_path(
version=version,
opinion=opinion,
ensure_exists=ensure_exists,
+ use_site_for_root=use_site_for_root,
).user_runtime_path
@@ -599,14 +785,26 @@ __all__ = [
"PlatformDirsABC",
"__version__",
"__version_info__",
+ "site_applications_dir",
+ "site_applications_path",
+ "site_bin_dir",
+ "site_bin_path",
"site_cache_dir",
"site_cache_path",
"site_config_dir",
"site_config_path",
"site_data_dir",
"site_data_path",
+ "site_log_dir",
+ "site_log_path",
"site_runtime_dir",
"site_runtime_path",
+ "site_state_dir",
+ "site_state_path",
+ "user_applications_dir",
+ "user_applications_path",
+ "user_bin_dir",
+ "user_bin_path",
"user_cache_dir",
"user_cache_path",
"user_config_dir",
diff --git a/contrib/python/platformdirs/platformdirs/__main__.py b/contrib/python/platformdirs/platformdirs/__main__.py
index 922c521358e..2490ffbbe1b 100644
--- a/contrib/python/platformdirs/platformdirs/__main__.py
+++ b/contrib/python/platformdirs/platformdirs/__main__.py
@@ -15,10 +15,16 @@ PROPS = (
"user_pictures_dir",
"user_videos_dir",
"user_music_dir",
+ "user_bin_dir",
+ "site_bin_dir",
+ "user_applications_dir",
"user_runtime_dir",
"site_data_dir",
"site_config_dir",
"site_cache_dir",
+ "site_state_dir",
+ "site_log_dir",
+ "site_applications_dir",
"site_runtime_dir",
)
diff --git a/contrib/python/platformdirs/platformdirs/_xdg.py b/contrib/python/platformdirs/platformdirs/_xdg.py
index 59765ebdf2c..4060e89f434 100644
--- a/contrib/python/platformdirs/platformdirs/_xdg.py
+++ b/contrib/python/platformdirs/platformdirs/_xdg.py
@@ -118,6 +118,25 @@ class XDGMixin(PlatformDirsABC):
return os.path.expanduser(path) # noqa: PTH111
return super().user_desktop_dir
+ @property
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
+ if path := os.environ.get("XDG_DATA_HOME", "").strip():
+ return os.path.join(os.path.expanduser(path), "applications") # noqa: PTH111, PTH118
+ return super().user_applications_dir
+
+ @property
+ def _site_applications_dirs(self) -> list[str]:
+ if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip():
+ return [os.path.join(p, "applications") for p in xdg_dirs.split(os.pathsep) if p.strip()] # noqa: PTH118
+ return super()._site_applications_dirs # type: ignore[misc]
+
+ @property
+ def site_applications_dir(self) -> str:
+ """:return: applications directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default"""
+ dirs = self._site_applications_dirs
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
__all__ = [
"XDGMixin",
diff --git a/contrib/python/platformdirs/platformdirs/android.py b/contrib/python/platformdirs/platformdirs/android.py
index dec1e5eb044..708355bf1a8 100644
--- a/contrib/python/platformdirs/platformdirs/android.py
+++ b/contrib/python/platformdirs/platformdirs/android.py
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, cast
from .api import PlatformDirsABC
-class Android(PlatformDirsABC):
+class Android(PlatformDirsABC): # noqa: PLR0904
"""
Platform directories for Android.
@@ -63,6 +63,11 @@ class Android(PlatformDirsABC):
return self.user_data_dir
@property
+ def site_state_dir(self) -> str:
+ """:return: state directory shared by users, same as `user_state_dir`"""
+ return self.user_state_dir
+
+ @property
def user_log_dir(self) -> str:
"""
:return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it,
@@ -74,6 +79,11 @@ class Android(PlatformDirsABC):
return path
@property
+ def site_log_dir(self) -> str:
+ """:return: log directory shared by users, same as `user_log_dir`"""
+ return self.user_log_dir
+
+ @property
def user_documents_dir(self) -> str:
""":return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``"""
return _android_documents_folder()
@@ -104,6 +114,26 @@ class Android(PlatformDirsABC):
return "/storage/emulated/0/Desktop"
@property
+ def user_bin_dir(self) -> str:
+ """:return: bin directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/files/bin``"""
+ return os.path.join(cast("str", _android_folder()), "files", "bin") # noqa: PTH118
+
+ @property
+ def site_bin_dir(self) -> str:
+ """:return: bin directory shared by users, same as `user_bin_dir`"""
+ return self.user_bin_dir
+
+ @property
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user, same as `user_data_dir`"""
+ return self.user_data_dir
+
+ @property
+ def site_applications_dir(self) -> str:
+ """:return: applications directory shared by users, same as `user_applications_dir`"""
+ return self.user_applications_dir
+
+ @property
def user_runtime_dir(self) -> str:
"""
:return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it,
diff --git a/contrib/python/platformdirs/platformdirs/api.py b/contrib/python/platformdirs/platformdirs/api.py
index 1e038d8b5d2..46afe0a82f8 100644
--- a/contrib/python/platformdirs/platformdirs/api.py
+++ b/contrib/python/platformdirs/platformdirs/api.py
@@ -32,6 +32,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
multipath: bool = False, # noqa: FBT001, FBT002
opinion: bool = True, # noqa: FBT001, FBT002
ensure_exists: bool = False, # noqa: FBT001, FBT002
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
) -> None:
"""
Create a new platform directory.
@@ -43,6 +44,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
:param multipath: See `multipath`.
:param opinion: See `opinion`.
:param ensure_exists: See `ensure_exists`.
+ :param use_site_for_root: See `use_site_for_root`.
"""
self.appname = appname #: The name of the application.
@@ -93,6 +95,14 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
By default, no directories are created.
"""
+ self.use_site_for_root = use_site_for_root
+ """
+ Whether to redirect ``user_*_dir`` calls to their ``site_*_dir`` equivalents when running as root (uid 0).
+
+ Only has an effect on Unix. Disabled by default for backwards compatibility. When enabled, XDG user environment
+ variables (e.g. ``XDG_DATA_HOME``) are bypassed for the redirected directories.
+
+ """
def _append_app_name_and_version(self, *base: str) -> str:
params = list(base[1:])
@@ -151,11 +161,21 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
@property
@abstractmethod
+ def site_state_dir(self) -> str:
+ """:return: state directory shared by users"""
+
+ @property
+ @abstractmethod
def user_log_dir(self) -> str:
""":return: log directory tied to the user"""
@property
@abstractmethod
+ def site_log_dir(self) -> str:
+ """:return: log directory shared by users"""
+
+ @property
+ @abstractmethod
def user_documents_dir(self) -> str:
""":return: documents directory tied to the user"""
@@ -186,6 +206,26 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
@property
@abstractmethod
+ def user_bin_dir(self) -> str:
+ """:return: bin directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def site_bin_dir(self) -> str:
+ """:return: bin directory shared by users"""
+
+ @property
+ @abstractmethod
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def site_applications_dir(self) -> str:
+ """:return: applications directory shared by users"""
+
+ @property
+ @abstractmethod
def user_runtime_dir(self) -> str:
""":return: runtime directory tied to the user"""
@@ -230,11 +270,21 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
return Path(self.user_state_dir)
@property
+ def site_state_path(self) -> Path:
+ """:return: state path shared by users"""
+ return Path(self.site_state_dir)
+
+ @property
def user_log_path(self) -> Path:
""":return: log path tied to the user"""
return Path(self.user_log_dir)
@property
+ def site_log_path(self) -> Path:
+ """:return: log path shared by users"""
+ return Path(self.site_log_dir)
+
+ @property
def user_documents_path(self) -> Path:
""":return: documents path tied to the user"""
return Path(self.user_documents_dir)
@@ -265,6 +315,26 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
return Path(self.user_desktop_dir)
@property
+ def user_bin_path(self) -> Path:
+ """:return: bin path tied to the user"""
+ return Path(self.user_bin_dir)
+
+ @property
+ def site_bin_path(self) -> Path:
+ """:return: bin path shared by users"""
+ return Path(self.site_bin_dir)
+
+ @property
+ def user_applications_path(self) -> Path:
+ """:return: applications path tied to the user"""
+ return Path(self.user_applications_dir)
+
+ @property
+ def site_applications_path(self) -> Path:
+ """:return: applications path shared by users"""
+ return Path(self.site_applications_dir)
+
+ @property
def user_runtime_path(self) -> Path:
""":return: runtime path tied to the user"""
return Path(self.user_runtime_dir)
@@ -289,6 +359,16 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
yield self.user_cache_dir
yield self.site_cache_dir
+ def iter_state_dirs(self) -> Iterator[str]:
+ """:yield: all user and site state directories."""
+ yield self.user_state_dir
+ yield self.site_state_dir
+
+ def iter_log_dirs(self) -> Iterator[str]:
+ """:yield: all user and site log directories."""
+ yield self.user_log_dir
+ yield self.site_log_dir
+
def iter_runtime_dirs(self) -> Iterator[str]:
""":yield: all user and site runtime directories."""
yield self.user_runtime_dir
@@ -309,6 +389,16 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
for path in self.iter_cache_dirs():
yield Path(path)
+ def iter_state_paths(self) -> Iterator[Path]:
+ """:yield: all user and site state paths."""
+ for path in self.iter_state_dirs():
+ yield Path(path)
+
+ def iter_log_paths(self) -> Iterator[Path]:
+ """:yield: all user and site log paths."""
+ for path in self.iter_log_dirs():
+ yield Path(path)
+
def iter_runtime_paths(self) -> Iterator[Path]:
""":yield: all user and site runtime paths."""
for path in self.iter_runtime_dirs():
diff --git a/contrib/python/platformdirs/platformdirs/macos.py b/contrib/python/platformdirs/platformdirs/macos.py
index 80a6c7348f0..3343a0bfffc 100644
--- a/contrib/python/platformdirs/platformdirs/macos.py
+++ b/contrib/python/platformdirs/platformdirs/macos.py
@@ -6,6 +6,9 @@ import os.path
import sys
from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from collections.abc import Iterator
+
from ._xdg import XDGMixin
from .api import PlatformDirsABC
@@ -13,7 +16,7 @@ if TYPE_CHECKING:
from pathlib import Path
-class _MacOSDefaults(PlatformDirsABC):
+class _MacOSDefaults(PlatformDirsABC): # noqa: PLR0904
"""
Default platform directories for macOS without XDG environment variable overrides.
@@ -83,11 +86,21 @@ class _MacOSDefaults(PlatformDirsABC):
return self.user_data_dir
@property
+ def site_state_dir(self) -> str:
+ """:return: state directory shared by users, same as `site_data_dir`"""
+ return self.site_data_dir
+
+ @property
def user_log_dir(self) -> str:
""":return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) # noqa: PTH111
@property
+ def site_log_dir(self) -> str:
+ """:return: log directory shared by users, e.g. ``/Library/Logs/$appname/$version``"""
+ return self._append_app_name_and_version("/Library/Logs")
+
+ @property
def user_documents_dir(self) -> str:
""":return: documents directory tied to the user, e.g. ``~/Documents``"""
return os.path.expanduser("~/Documents") # noqa: PTH111
@@ -118,6 +131,31 @@ class _MacOSDefaults(PlatformDirsABC):
return os.path.expanduser("~/Desktop") # noqa: PTH111
@property
+ def user_bin_dir(self) -> str:
+ """:return: bin directory tied to the user, e.g. ``~/.local/bin``"""
+ return os.path.expanduser("~/.local/bin") # noqa: PTH111
+
+ @property
+ def site_bin_dir(self) -> str:
+ """:return: bin directory shared by users, e.g. ``/usr/local/bin``"""
+ return "/usr/local/bin"
+
+ @property
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user, e.g. ``~/Applications``"""
+ return os.path.expanduser("~/Applications") # noqa: PTH111
+
+ @property
+ def _site_applications_dirs(self) -> list[str]:
+ return ["/Applications"]
+
+ @property
+ def site_applications_dir(self) -> str:
+ """:return: applications directory shared by users, e.g. ``/Applications``"""
+ dirs = self._site_applications_dirs
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+ @property
def user_runtime_dir(self) -> str:
""":return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) # noqa: PTH111
@@ -127,6 +165,16 @@ class _MacOSDefaults(PlatformDirsABC):
""":return: runtime directory shared by users, same as `user_runtime_dir`"""
return self.user_runtime_dir
+ def iter_config_dirs(self) -> Iterator[str]:
+ """:yield: all user and site configuration directories."""
+ yield self.user_config_dir
+ yield from self._site_config_dirs
+
+ def iter_data_dirs(self) -> Iterator[str]:
+ """:yield: all user and site data directories."""
+ yield self.user_data_dir
+ yield from self._site_data_dirs
+
class MacOS(XDGMixin, _MacOSDefaults):
"""
diff --git a/contrib/python/platformdirs/platformdirs/unix.py b/contrib/python/platformdirs/platformdirs/unix.py
index d0cb6719d9e..e388fcbd2d7 100644
--- a/contrib/python/platformdirs/platformdirs/unix.py
+++ b/contrib/python/platformdirs/platformdirs/unix.py
@@ -5,6 +5,7 @@ from __future__ import annotations
import os
import sys
from configparser import ConfigParser
+from functools import cached_property
from pathlib import Path
from tempfile import gettempdir
from typing import TYPE_CHECKING, NoReturn
@@ -25,13 +26,17 @@ else:
from os import getuid
-class _UnixDefaults(PlatformDirsABC):
+class _UnixDefaults(PlatformDirsABC): # noqa: PLR0904
"""
Default directories for Unix/Linux without XDG environment variable overrides.
The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`.
"""
+ @cached_property
+ def _use_site(self) -> bool:
+ return self.use_site_for_root and getuid() == 0
+
@property
def user_data_dir(self) -> str:
"""
@@ -78,6 +83,11 @@ class _UnixDefaults(PlatformDirsABC):
return self._append_app_name_and_version(os.path.expanduser("~/.local/state")) # noqa: PTH111
@property
+ def site_state_dir(self) -> str:
+ """:return: state directory shared by users, e.g. ``/var/lib/$appname/$version``"""
+ return self._append_app_name_and_version("/var/lib")
+
+ @property
def user_log_dir(self) -> str:
""":return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it"""
path = self.user_state_dir
@@ -87,6 +97,15 @@ class _UnixDefaults(PlatformDirsABC):
return path
@property
+ def site_log_dir(self) -> str:
+ """
+ :return: log directory shared by users, e.g. ``/var/log/$appname/$version``
+
+ Unlike `user_log_dir`, ``opinion`` has no effect since ``/var/log`` is inherently a log directory.
+ """
+ return self._append_app_name_and_version("/var/log")
+
+ @property
def user_documents_dir(self) -> str:
""":return: documents directory tied to the user, e.g. ``~/Documents``"""
return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents")
@@ -117,15 +136,42 @@ class _UnixDefaults(PlatformDirsABC):
return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop")
@property
+ def user_bin_dir(self) -> str:
+ """:return: bin directory tied to the user, e.g. ``~/.local/bin``"""
+ return os.path.expanduser("~/.local/bin") # noqa: PTH111
+
+ @property
+ def site_bin_dir(self) -> str:
+ """:return: bin directory shared by users, e.g. ``/usr/local/bin``"""
+ return "/usr/local/bin"
+
+ @property
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user, e.g. ``~/.local/share/applications``"""
+ return os.path.join(os.path.expanduser("~/.local/share"), "applications") # noqa: PTH111, PTH118
+
+ @property
+ def _site_applications_dirs(self) -> list[str]:
+ return [os.path.join(p, "applications") for p in ["/usr/local/share", "/usr/share"]] # noqa: PTH118
+
+ @property
+ def site_applications_dir(self) -> str:
+ """:return: applications directory shared by users, e.g. ``/usr/share/applications``"""
+ dirs = self._site_applications_dirs
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+ @property
def user_runtime_dir(self) -> str:
"""
:return: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``.
- If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/var/run/user/$(id -u)`` on
- FreeBSD/OpenBSD/NetBSD, ``/run/user/$(id -u)`` otherwise). If the default is not writable,
- falls back to a temporary directory.
+ If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/tmp/run/user/$(id -u)`` on
+ OpenBSD, ``/var/run/user/$(id -u)`` on FreeBSD/NetBSD, ``/run/user/$(id -u)`` otherwise).
+ If the default is not writable, falls back to a temporary directory.
"""
- if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
+ if sys.platform.startswith("openbsd"):
+ path = f"/tmp/run/user/{getuid()}" # noqa: S108
+ elif sys.platform.startswith(("freebsd", "netbsd")):
path = f"/var/run/user/{getuid()}"
else:
path = f"/run/user/{getuid()}"
@@ -190,6 +236,46 @@ class Unix(XDGMixin, _UnixDefaults):
<platformdirs.api.PlatformDirsABC.ensure_exists>`.
"""
+ @property
+ def user_data_dir(self) -> str:
+ """:return: data directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_data_dir if self._use_site else super().user_data_dir
+
+ @property
+ def user_config_dir(self) -> str:
+ """:return: config directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_config_dir if self._use_site else super().user_config_dir
+
+ @property
+ def user_cache_dir(self) -> str:
+ """:return: cache directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_cache_dir if self._use_site else super().user_cache_dir
+
+ @property
+ def user_state_dir(self) -> str:
+ """:return: state directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_state_dir if self._use_site else super().user_state_dir
+
+ @property
+ def user_log_dir(self) -> str:
+ """:return: log directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_log_dir if self._use_site else super().user_log_dir
+
+ @property
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_applications_dir if self._use_site else super().user_applications_dir
+
+ @property
+ def user_runtime_dir(self) -> str:
+ """:return: runtime directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_runtime_dir if self._use_site else super().user_runtime_dir
+
+ @property
+ def user_bin_dir(self) -> str:
+ """:return: bin directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+ return self.site_bin_dir if self._use_site else super().user_bin_dir
+
def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
if media_dir := _get_user_dirs_folder(env_var):
diff --git a/contrib/python/platformdirs/platformdirs/version.py b/contrib/python/platformdirs/platformdirs/version.py
index f00ce0cd04a..1ad854cdb25 100644
--- a/contrib/python/platformdirs/platformdirs/version.py
+++ b/contrib/python/platformdirs/platformdirs/version.py
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
commit_id: COMMIT_ID
__commit_id__: COMMIT_ID
-__version__ = version = '4.7.0'
-__version_tuple__ = version_tuple = (4, 7, 0)
+__version__ = version = '4.9.1'
+__version_tuple__ = version_tuple = (4, 9, 1)
__commit_id__ = commit_id = None
diff --git a/contrib/python/platformdirs/platformdirs/windows.py b/contrib/python/platformdirs/platformdirs/windows.py
index 838e07fad60..84afaa75565 100644
--- a/contrib/python/platformdirs/platformdirs/windows.py
+++ b/contrib/python/platformdirs/platformdirs/windows.py
@@ -4,16 +4,18 @@ from __future__ import annotations
import os
import sys
-from functools import lru_cache
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Final
from .api import PlatformDirsABC
if TYPE_CHECKING:
from collections.abc import Callable
+# Not exposed by CPython; defined in the Windows SDK (shlobj_core.h)
+_KF_FLAG_DONT_VERIFY: Final[int] = 0x00004000
-class Windows(PlatformDirsABC):
+
+class Windows(PlatformDirsABC): # noqa: PLR0904
"""
`MSDN on where to store app data files <https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid>`_.
@@ -87,6 +89,11 @@ class Windows(PlatformDirsABC):
return self.user_data_dir
@property
+ def site_state_dir(self) -> str:
+ """:return: state directory shared by users, same as `site_data_dir`"""
+ return self.site_data_dir
+
+ @property
def user_log_dir(self) -> str:
""":return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it"""
path = self.user_data_dir
@@ -96,6 +103,15 @@ class Windows(PlatformDirsABC):
return path
@property
+ def site_log_dir(self) -> str:
+ """:return: log directory shared by users, same as `site_data_dir` if not opinionated else ``Logs`` in it"""
+ path = self.site_data_dir
+ if self.opinion:
+ path = os.path.join(path, "Logs") # noqa: PTH118
+ self._optionally_create_directory(path)
+ return path
+
+ @property
def user_documents_dir(self) -> str:
""":return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``"""
return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
@@ -126,6 +142,29 @@ class Windows(PlatformDirsABC):
return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY"))
@property
+ def user_bin_dir(self) -> str:
+ """:return: bin directory tied to the user, e.g. ``%LOCALAPPDATA%\\Programs``"""
+ return os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Programs")) # noqa: PTH118
+
+ @property
+ def site_bin_dir(self) -> str:
+ """:return: bin directory shared by users, e.g. ``C:\\ProgramData\\bin``"""
+ return os.path.normpath(os.path.join(get_win_folder("CSIDL_COMMON_APPDATA"), "bin")) # noqa: PTH118
+
+ @property
+ def user_applications_dir(self) -> str:
+ """:return: applications directory tied to the user, e.g. ``Start Menu\\Programs``"""
+ return os.path.normpath(get_win_folder("CSIDL_PROGRAMS"))
+
+ @property
+ def site_applications_dir(self) -> str:
+ """
+ :return: applications directory shared by users, e.g. \
+ ``C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs``
+ """
+ return os.path.normpath(get_win_folder("CSIDL_COMMON_PROGRAMS"))
+
+ @property
def user_runtime_dir(self) -> str:
"""
:return: runtime directory tied to the user, e.g.
@@ -161,7 +200,7 @@ def get_win_folder_from_env_vars(csidl_name: str) -> str:
return result
-def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None:
+def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None: # noqa: PLR0911
"""Get a folder for a CSIDL name that does not exist as an environment variable."""
if csidl_name == "CSIDL_PERSONAL":
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") # noqa: PTH118
@@ -177,6 +216,24 @@ def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None:
if csidl_name == "CSIDL_MYMUSIC":
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music") # noqa: PTH118
+
+ if csidl_name == "CSIDL_PROGRAMS":
+ return os.path.join( # noqa: PTH118
+ os.path.normpath(os.environ["APPDATA"]),
+ "Microsoft",
+ "Windows",
+ "Start Menu",
+ "Programs",
+ )
+
+ if csidl_name == "CSIDL_COMMON_PROGRAMS":
+ return os.path.join( # noqa: PTH118
+ os.path.normpath(os.environ.get("PROGRAMDATA", os.environ.get("ALLUSERSPROFILE", "C:\\ProgramData"))),
+ "Microsoft",
+ "Windows",
+ "Start Menu",
+ "Programs",
+ )
return None
@@ -190,6 +247,7 @@ def get_win_folder_from_registry(csidl_name: str) -> str:
"""
machine_names = {
"CSIDL_COMMON_APPDATA",
+ "CSIDL_COMMON_PROGRAMS",
}
shell_folder_name = {
"CSIDL_APPDATA": "AppData",
@@ -200,6 +258,8 @@ def get_win_folder_from_registry(csidl_name: str) -> str:
"CSIDL_MYPICTURES": "My Pictures",
"CSIDL_MYVIDEO": "My Video",
"CSIDL_MYMUSIC": "My Music",
+ "CSIDL_PROGRAMS": "Programs",
+ "CSIDL_COMMON_PROGRAMS": "Common Programs",
}.get(csidl_name)
if shell_folder_name is None:
msg = f"Unknown CSIDL name: {csidl_name}"
@@ -226,6 +286,8 @@ _KNOWN_FOLDER_GUIDS: dict[str, str] = {
"CSIDL_MYMUSIC": "{4BD8D571-6D19-48D3-BE97-422220080E43}",
"CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
"CSIDL_DESKTOPDIRECTORY": "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}",
+ "CSIDL_PROGRAMS": "{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}",
+ "CSIDL_COMMON_PROGRAMS": "{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}",
}
@@ -271,7 +333,7 @@ def get_win_folder_via_ctypes(csidl_name: str) -> str:
ole32.CLSIDFromString(folder_guid, byref(guid))
path_ptr = wintypes.LPWSTR()
- shell32.SHGetKnownFolderPath(byref(guid), 0, None, byref(path_ptr))
+ shell32.SHGetKnownFolderPath(byref(guid), _KF_FLAG_DONT_VERIFY, None, byref(path_ptr))
result = path_ptr.value
ole32.CoTaskMemFree(path_ptr)
@@ -303,7 +365,21 @@ def _pick_get_win_folder() -> Callable[[str], str]:
return get_win_folder_from_registry
-get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder())
+_resolve_win_folder = _pick_get_win_folder()
+
+
+def get_win_folder(csidl_name: str) -> str:
+ """
+ Get a Windows folder path, checking for ``WIN_PD_OVERRIDE_*`` environment variable overrides first.
+
+ For example, ``CSIDL_LOCAL_APPDATA`` can be overridden by setting ``WIN_PD_OVERRIDE_LOCAL_APPDATA``.
+
+ """
+ env_var = f"WIN_PD_OVERRIDE_{csidl_name.removeprefix('CSIDL_')}"
+ if override := os.environ.get(env_var, "").strip():
+ return override
+ return _resolve_win_folder(csidl_name)
+
__all__ = [
"Windows",
diff --git a/contrib/python/platformdirs/ya.make b/contrib/python/platformdirs/ya.make
index e763ebaa093..67c5b0e022a 100644
--- a/contrib/python/platformdirs/ya.make
+++ b/contrib/python/platformdirs/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(4.7.0)
+VERSION(4.9.1)
LICENSE(MIT)