diff options
| author | robot-piglet <[email protected]> | 2026-03-02 19:21:26 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2026-03-02 21:27:31 +0300 |
| commit | 83fbb037f31247ccbad309651c665682dd7d88e2 (patch) | |
| tree | fb591d12d384303cd4ba91ffde768604ea7d15ca /contrib/python/platformdirs | |
| parent | aaa2572ec1cd37989cd69a4e8286df17394f756a (diff) | |
Intermediate changes
commit_hash:15fe475c75799abfa3a20e1c305672191703d12c
Diffstat (limited to 'contrib/python/platformdirs')
| -rw-r--r-- | contrib/python/platformdirs/.dist-info/METADATA | 61 | ||||
| -rw-r--r-- | contrib/python/platformdirs/README.md | 59 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/__init__.py | 222 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/__main__.py | 6 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/_xdg.py | 19 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/android.py | 32 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/api.py | 90 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/macos.py | 50 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/unix.py | 96 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/version.py | 4 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/windows.py | 88 | ||||
| -rw-r--r-- | contrib/python/platformdirs/ya.make | 2 |
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) |
