diff options
| author | robot-piglet <[email protected]> | 2026-02-28 22:22:15 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2026-02-28 22:47:08 +0300 |
| commit | 4327fd33b91e71935a178c932880f9a575603a33 (patch) | |
| tree | ebed7608d3a05218dcf972c376e1574226e67a3a /contrib/python/platformdirs | |
| parent | 09594345cca8209c70479f9a34e7880f09d323fc (diff) | |
Intermediate changes
commit_hash:a01a1ccee8f63c56eaa87c462b96e7ee22f7afae
Diffstat (limited to 'contrib/python/platformdirs')
| -rw-r--r-- | contrib/python/platformdirs/.dist-info/METADATA | 343 | ||||
| -rw-r--r-- | contrib/python/platformdirs/README.md | 53 | ||||
| -rw-r--r-- | contrib/python/platformdirs/README.rst | 307 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/__init__.py | 27 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/_xdg.py | 124 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/android.py | 22 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/api.py | 30 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/macos.py | 46 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/unix.py | 133 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/version.py | 4 | ||||
| -rw-r--r-- | contrib/python/platformdirs/platformdirs/windows.py | 96 | ||||
| -rw-r--r-- | contrib/python/platformdirs/ya.make | 3 |
12 files changed, 401 insertions, 787 deletions
diff --git a/contrib/python/platformdirs/.dist-info/METADATA b/contrib/python/platformdirs/.dist-info/METADATA index e7a4602a05f..ee9eb1f16ee 100644 --- a/contrib/python/platformdirs/.dist-info/METADATA +++ b/contrib/python/platformdirs/.dist-info/METADATA @@ -1,8 +1,8 @@ Metadata-Version: 2.4 Name: platformdirs -Version: 4.5.1 +Version: 4.7.0 Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`. -Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases +Project-URL: Changelog, https://platformdirs.readthedocs.io/en/latest/changelog.html Project-URL: Documentation, https://platformdirs.readthedocs.io Project-URL: Homepage, https://github.com/tox-dev/platformdirs Project-URL: Source, https://github.com/tox-dev/platformdirs @@ -26,325 +26,58 @@ Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.10 -Provides-Extra: docs -Requires-Dist: furo>=2025.9.25; extra == 'docs' -Requires-Dist: proselint>=0.14; extra == 'docs' -Requires-Dist: sphinx-autodoc-typehints>=3.2; extra == 'docs' -Requires-Dist: sphinx>=8.2.3; extra == 'docs' -Provides-Extra: test -Requires-Dist: appdirs==1.4.4; extra == 'test' -Requires-Dist: covdefaults>=2.3; extra == 'test' -Requires-Dist: pytest-cov>=7; extra == 'test' -Requires-Dist: pytest-mock>=3.15.1; extra == 'test' -Requires-Dist: pytest>=8.4.2; extra == 'test' -Provides-Extra: type -Requires-Dist: mypy>=1.18.2; extra == 'type' -Description-Content-Type: text/x-rst +Description-Content-Type: text/markdown -The problem -=========== +# platformdirs -.. image:: https://badge.fury.io/py/platformdirs.svg - :target: https://badge.fury.io/py/platformdirs -.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg - :target: https://pypi.python.org/pypi/platformdirs/ -.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg - :target: https://github.com/platformdirs/platformdirs/actions -.. image:: https://static.pepy.tech/badge/platformdirs/month - :target: https://pepy.tech/project/platformdirs +[](https://badge.fury.io/py/platformdirs) +[](https://pypi.python.org/pypi/platformdirs/) +[](https://github.com/platformdirs/platformdirs/actions) +[](https://pepy.tech/project/platformdirs) -When writing desktop application, finding the right location to store user data -and configuration varies per platform. Even for single-platform apps, there -may by plenty of nuances in figuring out the right location. +A Python package for determining platform-specific directories (e.g. user data, config, cache, logs). Handles the +differences between macOS, Windows, Linux/Unix, and Android so you don't have to. -For example, if running on macOS, you should use:: +## Quick start - ~/Library/Application Support/<AppName> +```python +from platformdirs import PlatformDirs -If on Windows (at least English Win) that should be:: +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 +``` - C:\Users\<User>\Application Data\Local Settings\<AppAuthor>\<AppName> +Convenience functions are also available: -or possibly:: +```python +from platformdirs import user_data_dir, user_config_path - C:\Users\<User>\Application Data\<AppAuthor>\<AppName> +user_data_dir("MyApp", "MyCompany") # returns str +user_config_path("MyApp", "MyCompany") # returns pathlib.Path +``` -for `roaming profiles <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc766489(v=ws.10)>`_ but that is another story. +## Documentation -On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: +Full documentation is available at [platformdirs.readthedocs.io](https://platformdirs.readthedocs.io), including: - ~/.local/share/<AppName> +- [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 -.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - -``platformdirs`` to the rescue -============================== - -This kind of thing is what the ``platformdirs`` package is for. -``platformdirs`` will help you choose an appropriate: - -- user data dir (``user_data_dir``) -- user config dir (``user_config_dir``) -- user cache dir (``user_cache_dir``) -- site data dir (``site_data_dir``) -- site config dir (``site_config_dir``) -- user log dir (``user_log_dir``) -- user documents dir (``user_documents_dir``) -- user downloads dir (``user_downloads_dir``) -- user pictures dir (``user_pictures_dir``) -- user videos dir (``user_videos_dir``) -- user music dir (``user_music_dir``) -- user desktop dir (``user_desktop_dir``) -- user runtime dir (``user_runtime_dir``) - -And also: - -- Is slightly opinionated on the directory names used. Look for "OPINION" in - documentation and code for when an opinion is being applied. - -Example output -============== - -On macOS: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> user_config_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/Users/trentm/Library/Caches/SuperApp' - >>> site_data_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> site_config_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> user_log_dir(appname, appauthor) - '/Users/trentm/Library/Logs/SuperApp' - >>> user_documents_dir() - '/Users/trentm/Documents' - >>> user_downloads_dir() - '/Users/trentm/Downloads' - >>> user_pictures_dir() - '/Users/trentm/Pictures' - >>> user_videos_dir() - '/Users/trentm/Movies' - >>> user_music_dir() - '/Users/trentm/Music' - >>> user_desktop_dir() - '/Users/trentm/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' - -On Windows: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_data_dir(appname, appauthor, roaming=True) - 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' - >>> user_config_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_cache_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' - >>> site_data_dir(appname, appauthor) - 'C:\\ProgramData\\Acme\\SuperApp' - >>> site_config_dir(appname, appauthor) - 'C:\\ProgramData\\Acme\\SuperApp' - >>> user_log_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' - >>> user_documents_dir() - 'C:\\Users\\trentm\\Documents' - >>> user_downloads_dir() - 'C:\\Users\\trentm\\Downloads' - >>> user_pictures_dir() - 'C:\\Users\\trentm\\Pictures' - >>> user_videos_dir() - 'C:\\Users\\trentm\\Videos' - >>> user_music_dir() - 'C:\\Users\\trentm\\Music' - >>> user_desktop_dir() - 'C:\\Users\\trentm\\Desktop' - >>> user_runtime_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp' - -On Linux: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/home/trentm/.local/share/SuperApp' - >>> user_config_dir(appname) - '/home/trentm/.config/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp' - >>> site_data_dir(appname, appauthor) - '/usr/local/share/SuperApp' - >>> site_data_dir(appname, appauthor, multipath=True) - '/usr/local/share/SuperApp:/usr/share/SuperApp' - >>> site_config_dir(appname) - '/etc/xdg/SuperApp' - >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" - >>> site_config_dir(appname, multipath=True) - '/etc/SuperApp:/usr/local/etc/SuperApp' - >>> user_log_dir(appname, appauthor) - '/home/trentm/.local/state/SuperApp/log' - >>> user_documents_dir() - '/home/trentm/Documents' - >>> user_downloads_dir() - '/home/trentm/Downloads' - >>> user_pictures_dir() - '/home/trentm/Pictures' - >>> user_videos_dir() - '/home/trentm/Videos' - >>> user_music_dir() - '/home/trentm/Music' - >>> user_desktop_dir() - '/home/trentm/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/run/user/{os.getuid()}/SuperApp' - -On Android:: - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/data/data/com.myApp/files/SuperApp' - >>> user_config_dir(appname) - '/data/data/com.myApp/shared_prefs/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp' - >>> site_data_dir(appname, appauthor) - '/data/data/com.myApp/files/SuperApp' - >>> site_config_dir(appname) - '/data/data/com.myApp/shared_prefs/SuperApp' - >>> user_log_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp/log' - >>> user_documents_dir() - '/storage/emulated/0/Documents' - >>> user_downloads_dir() - '/storage/emulated/0/Downloads' - >>> user_pictures_dir() - '/storage/emulated/0/Pictures' - >>> user_videos_dir() - '/storage/emulated/0/DCIM/Camera' - >>> user_music_dir() - '/storage/emulated/0/Music' - >>> user_desktop_dir() - '/storage/emulated/0/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp/tmp' - -Note: Some android apps like Termux and Pydroid are used as shells. These -apps are used by the end user to emulate Linux environment. Presence of -``SHELL`` environment variable is used by Platformdirs to differentiate -between general android apps and android apps used as shells. Shell android -apps also support ``XDG_*`` environment variables. - - -``PlatformDirs`` for convenience -================================ - -.. code-block:: pycon - - >>> from platformdirs import PlatformDirs - >>> dirs = PlatformDirs("SuperApp", "Acme") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.user_config_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp' - >>> dirs.site_config_dir - '/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp' - >>> dirs.user_documents_dir - '/Users/trentm/Documents' - >>> dirs.user_downloads_dir - '/Users/trentm/Downloads' - >>> dirs.user_pictures_dir - '/Users/trentm/Pictures' - >>> dirs.user_videos_dir - '/Users/trentm/Movies' - >>> dirs.user_music_dir - '/Users/trentm/Music' - >>> dirs.user_desktop_dir - '/Users/trentm/Desktop' - >>> dirs.user_runtime_dir - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' - -Per-version isolation -===================== - -If you have multiple versions of your app in use that you want to be -able to run side-by-side, then you may want version-isolation for these -dirs:: - - >>> from platformdirs import PlatformDirs - >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.user_config_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp/1.0' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.site_config_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp/1.0' - >>> dirs.user_documents_dir - '/Users/trentm/Documents' - >>> dirs.user_downloads_dir - '/Users/trentm/Downloads' - >>> dirs.user_pictures_dir - '/Users/trentm/Pictures' - >>> dirs.user_videos_dir - '/Users/trentm/Movies' - >>> dirs.user_music_dir - '/Users/trentm/Music' - >>> dirs.user_desktop_dir - '/Users/trentm/Desktop' - >>> dirs.user_runtime_dir - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0' - -Be wary of using this for configuration files though; you'll need to handle -migrating configuration files manually. - -Why this Fork? -============== +## 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. +[ActiveState](https://github.com/ActiveState/appdirs) who created `appdirs`, this package's ancestor. -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. +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. -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. +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. Contributions are most welcome. diff --git a/contrib/python/platformdirs/README.md b/contrib/python/platformdirs/README.md new file mode 100644 index 00000000000..04779d681ff --- /dev/null +++ b/contrib/python/platformdirs/README.md @@ -0,0 +1,53 @@ +# platformdirs + +[](https://badge.fury.io/py/platformdirs) +[](https://pypi.python.org/pypi/platformdirs/) +[](https://github.com/platformdirs/platformdirs/actions) +[](https://pepy.tech/project/platformdirs) + +A Python package for determining platform-specific directories (e.g. user data, config, cache, logs). Handles the +differences between macOS, Windows, Linux/Unix, and Android so you don't have to. + +## Quick start + +```python +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 +``` + +Convenience functions are also available: + +```python +from platformdirs import user_data_dir, user_config_path + +user_data_dir("MyApp", "MyCompany") # returns str +user_config_path("MyApp", "MyCompany") # returns pathlib.Path +``` + +## Documentation + +Full documentation is available at [platformdirs.readthedocs.io](https://platformdirs.readthedocs.io), including: + +- [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 + +## 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. + +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. + +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. + +Contributions are most welcome. diff --git a/contrib/python/platformdirs/README.rst b/contrib/python/platformdirs/README.rst deleted file mode 100644 index 8dc650204d4..00000000000 --- a/contrib/python/platformdirs/README.rst +++ /dev/null @@ -1,307 +0,0 @@ -The problem -=========== - -.. image:: https://badge.fury.io/py/platformdirs.svg - :target: https://badge.fury.io/py/platformdirs -.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg - :target: https://pypi.python.org/pypi/platformdirs/ -.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg - :target: https://github.com/platformdirs/platformdirs/actions -.. image:: https://static.pepy.tech/badge/platformdirs/month - :target: https://pepy.tech/project/platformdirs - -When writing desktop application, finding the right location to store user data -and configuration varies per platform. Even for single-platform apps, there -may by plenty of nuances in figuring out the right location. - -For example, if running on macOS, you should use:: - - ~/Library/Application Support/<AppName> - -If on Windows (at least English Win) that should be:: - - C:\Users\<User>\Application Data\Local Settings\<AppAuthor>\<AppName> - -or possibly:: - - C:\Users\<User>\Application Data\<AppAuthor>\<AppName> - -for `roaming profiles <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc766489(v=ws.10)>`_ but that is another story. - -On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: - - ~/.local/share/<AppName> - -.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - -``platformdirs`` to the rescue -============================== - -This kind of thing is what the ``platformdirs`` package is for. -``platformdirs`` will help you choose an appropriate: - -- user data dir (``user_data_dir``) -- user config dir (``user_config_dir``) -- user cache dir (``user_cache_dir``) -- site data dir (``site_data_dir``) -- site config dir (``site_config_dir``) -- user log dir (``user_log_dir``) -- user documents dir (``user_documents_dir``) -- user downloads dir (``user_downloads_dir``) -- user pictures dir (``user_pictures_dir``) -- user videos dir (``user_videos_dir``) -- user music dir (``user_music_dir``) -- user desktop dir (``user_desktop_dir``) -- user runtime dir (``user_runtime_dir``) - -And also: - -- Is slightly opinionated on the directory names used. Look for "OPINION" in - documentation and code for when an opinion is being applied. - -Example output -============== - -On macOS: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> user_config_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/Users/trentm/Library/Caches/SuperApp' - >>> site_data_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> site_config_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> user_log_dir(appname, appauthor) - '/Users/trentm/Library/Logs/SuperApp' - >>> user_documents_dir() - '/Users/trentm/Documents' - >>> user_downloads_dir() - '/Users/trentm/Downloads' - >>> user_pictures_dir() - '/Users/trentm/Pictures' - >>> user_videos_dir() - '/Users/trentm/Movies' - >>> user_music_dir() - '/Users/trentm/Music' - >>> user_desktop_dir() - '/Users/trentm/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' - -On Windows: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_data_dir(appname, appauthor, roaming=True) - 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' - >>> user_config_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_cache_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' - >>> site_data_dir(appname, appauthor) - 'C:\\ProgramData\\Acme\\SuperApp' - >>> site_config_dir(appname, appauthor) - 'C:\\ProgramData\\Acme\\SuperApp' - >>> user_log_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' - >>> user_documents_dir() - 'C:\\Users\\trentm\\Documents' - >>> user_downloads_dir() - 'C:\\Users\\trentm\\Downloads' - >>> user_pictures_dir() - 'C:\\Users\\trentm\\Pictures' - >>> user_videos_dir() - 'C:\\Users\\trentm\\Videos' - >>> user_music_dir() - 'C:\\Users\\trentm\\Music' - >>> user_desktop_dir() - 'C:\\Users\\trentm\\Desktop' - >>> user_runtime_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp' - -On Linux: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/home/trentm/.local/share/SuperApp' - >>> user_config_dir(appname) - '/home/trentm/.config/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp' - >>> site_data_dir(appname, appauthor) - '/usr/local/share/SuperApp' - >>> site_data_dir(appname, appauthor, multipath=True) - '/usr/local/share/SuperApp:/usr/share/SuperApp' - >>> site_config_dir(appname) - '/etc/xdg/SuperApp' - >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" - >>> site_config_dir(appname, multipath=True) - '/etc/SuperApp:/usr/local/etc/SuperApp' - >>> user_log_dir(appname, appauthor) - '/home/trentm/.local/state/SuperApp/log' - >>> user_documents_dir() - '/home/trentm/Documents' - >>> user_downloads_dir() - '/home/trentm/Downloads' - >>> user_pictures_dir() - '/home/trentm/Pictures' - >>> user_videos_dir() - '/home/trentm/Videos' - >>> user_music_dir() - '/home/trentm/Music' - >>> user_desktop_dir() - '/home/trentm/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/run/user/{os.getuid()}/SuperApp' - -On Android:: - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/data/data/com.myApp/files/SuperApp' - >>> user_config_dir(appname) - '/data/data/com.myApp/shared_prefs/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp' - >>> site_data_dir(appname, appauthor) - '/data/data/com.myApp/files/SuperApp' - >>> site_config_dir(appname) - '/data/data/com.myApp/shared_prefs/SuperApp' - >>> user_log_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp/log' - >>> user_documents_dir() - '/storage/emulated/0/Documents' - >>> user_downloads_dir() - '/storage/emulated/0/Downloads' - >>> user_pictures_dir() - '/storage/emulated/0/Pictures' - >>> user_videos_dir() - '/storage/emulated/0/DCIM/Camera' - >>> user_music_dir() - '/storage/emulated/0/Music' - >>> user_desktop_dir() - '/storage/emulated/0/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp/tmp' - -Note: Some android apps like Termux and Pydroid are used as shells. These -apps are used by the end user to emulate Linux environment. Presence of -``SHELL`` environment variable is used by Platformdirs to differentiate -between general android apps and android apps used as shells. Shell android -apps also support ``XDG_*`` environment variables. - - -``PlatformDirs`` for convenience -================================ - -.. code-block:: pycon - - >>> from platformdirs import PlatformDirs - >>> dirs = PlatformDirs("SuperApp", "Acme") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.user_config_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp' - >>> dirs.site_config_dir - '/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp' - >>> dirs.user_documents_dir - '/Users/trentm/Documents' - >>> dirs.user_downloads_dir - '/Users/trentm/Downloads' - >>> dirs.user_pictures_dir - '/Users/trentm/Pictures' - >>> dirs.user_videos_dir - '/Users/trentm/Movies' - >>> dirs.user_music_dir - '/Users/trentm/Music' - >>> dirs.user_desktop_dir - '/Users/trentm/Desktop' - >>> dirs.user_runtime_dir - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' - -Per-version isolation -===================== - -If you have multiple versions of your app in use that you want to be -able to run side-by-side, then you may want version-isolation for these -dirs:: - - >>> from platformdirs import PlatformDirs - >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.user_config_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp/1.0' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.site_config_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp/1.0' - >>> dirs.user_documents_dir - '/Users/trentm/Documents' - >>> dirs.user_downloads_dir - '/Users/trentm/Downloads' - >>> dirs.user_pictures_dir - '/Users/trentm/Pictures' - >>> dirs.user_videos_dir - '/Users/trentm/Movies' - >>> dirs.user_music_dir - '/Users/trentm/Music' - >>> dirs.user_desktop_dir - '/Users/trentm/Desktop' - >>> dirs.user_runtime_dir - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0' - -Be wary of using this for configuration files though; you'll need to handle -migrating configuration files manually. - -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. - -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. - -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. - -Contributions are most welcome. diff --git a/contrib/python/platformdirs/platformdirs/__init__.py b/contrib/python/platformdirs/platformdirs/__init__.py index 02daa5914a8..7896bf67b04 100644 --- a/contrib/python/platformdirs/platformdirs/__init__.py +++ b/contrib/python/platformdirs/platformdirs/__init__.py @@ -1,6 +1,9 @@ """ Utilities for determining application-specific dirs. +Provides convenience functions (e.g. :func:`user_data_dir`, :func:`user_config_path`), a :data:`PlatformDirs` class +that auto-detects the current platform, and the :class:`~platformdirs.api.PlatformDirsABC` base class. + See <https://github.com/platformdirs/platformdirs> for details and usage. """ @@ -85,7 +88,7 @@ def site_data_dir( :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 multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`. + :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. :returns: data directory shared by users """ @@ -133,9 +136,9 @@ def site_config_dir( :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 multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`. + :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. - :returns: config directory shared by the users + :returns: config directory shared by users """ return PlatformDirs( appname=appname, @@ -157,7 +160,7 @@ def user_cache_dir( :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 `roaming <platformdirs.api.PlatformDirsABC.opinion>`. + :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. :returns: cache directory tied to the user """ @@ -183,7 +186,7 @@ def site_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>`. - :returns: cache directory tied to the user + :returns: cache directory shared by users """ return PlatformDirs( appname=appname, @@ -229,7 +232,7 @@ def user_log_dir( :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 `roaming <platformdirs.api.PlatformDirsABC.opinion>`. + :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. :returns: log directory tied to the user """ @@ -403,9 +406,9 @@ def site_config_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 multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`. + :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. - :returns: config path shared by the users + :returns: config path shared by users """ return PlatformDirs( appname=appname, @@ -429,7 +432,7 @@ def site_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>`. - :returns: cache directory tied to the user + :returns: cache path shared by users """ return PlatformDirs( appname=appname, @@ -451,7 +454,7 @@ def user_cache_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 `roaming <platformdirs.api.PlatformDirsABC.opinion>`. + :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. :returns: cache path tied to the user """ @@ -499,7 +502,7 @@ def user_log_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 `roaming <platformdirs.api.PlatformDirsABC.opinion>`. + :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`. :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. :returns: log path tied to the user """ @@ -513,7 +516,7 @@ def user_log_path( def user_documents_path() -> Path: - """:returns: documents a path tied to the user""" + """:returns: documents path tied to the user""" return PlatformDirs().user_documents_path diff --git a/contrib/python/platformdirs/platformdirs/_xdg.py b/contrib/python/platformdirs/platformdirs/_xdg.py new file mode 100644 index 00000000000..59765ebdf2c --- /dev/null +++ b/contrib/python/platformdirs/platformdirs/_xdg.py @@ -0,0 +1,124 @@ +"""XDG environment variable mixin for Unix and macOS.""" + +from __future__ import annotations + +import os + +from .api import PlatformDirsABC + + +class XDGMixin(PlatformDirsABC): + """Mixin that checks XDG environment variables, falling back to platform-specific defaults via ``super()``.""" + + @property + def user_data_dir(self) -> str: + """:return: data directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default""" + if path := os.environ.get("XDG_DATA_HOME", "").strip(): + return self._append_app_name_and_version(path) + return super().user_data_dir + + @property + def _site_data_dirs(self) -> list[str]: + if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip(): + return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()] + return super()._site_data_dirs # type: ignore[misc] + + @property + def site_data_dir(self) -> str: + """:return: data directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default""" + dirs = self._site_data_dirs + return os.pathsep.join(dirs) if self.multipath else dirs[0] + + @property + def user_config_dir(self) -> str: + """:return: config directory tied to the user, from ``$XDG_CONFIG_HOME`` if set, else platform default""" + if path := os.environ.get("XDG_CONFIG_HOME", "").strip(): + return self._append_app_name_and_version(path) + return super().user_config_dir + + @property + def _site_config_dirs(self) -> list[str]: + if xdg_dirs := os.environ.get("XDG_CONFIG_DIRS", "").strip(): + return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()] + return super()._site_config_dirs # type: ignore[misc] + + @property + def site_config_dir(self) -> str: + """:return: config directories shared by users, from ``$XDG_CONFIG_DIRS`` if set, else platform default""" + dirs = self._site_config_dirs + return os.pathsep.join(dirs) if self.multipath else dirs[0] + + @property + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user, from ``$XDG_CACHE_HOME`` if set, else platform default""" + if path := os.environ.get("XDG_CACHE_HOME", "").strip(): + return self._append_app_name_and_version(path) + return super().user_cache_dir + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, from ``$XDG_STATE_HOME`` if set, else platform default""" + if path := os.environ.get("XDG_STATE_HOME", "").strip(): + return self._append_app_name_and_version(path) + return super().user_state_dir + + @property + def user_runtime_dir(self) -> str: + """:return: runtime directory tied to the user, from ``$XDG_RUNTIME_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_RUNTIME_DIR", "").strip(): + return self._append_app_name_and_version(path) + return super().user_runtime_dir + + @property + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users, from ``$XDG_RUNTIME_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_RUNTIME_DIR", "").strip(): + return self._append_app_name_and_version(path) + return super().site_runtime_dir + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user, from ``$XDG_DOCUMENTS_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_DOCUMENTS_DIR", "").strip(): + return os.path.expanduser(path) # noqa: PTH111 + return super().user_documents_dir + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user, from ``$XDG_DOWNLOAD_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_DOWNLOAD_DIR", "").strip(): + return os.path.expanduser(path) # noqa: PTH111 + return super().user_downloads_dir + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user, from ``$XDG_PICTURES_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_PICTURES_DIR", "").strip(): + return os.path.expanduser(path) # noqa: PTH111 + return super().user_pictures_dir + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user, from ``$XDG_VIDEOS_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_VIDEOS_DIR", "").strip(): + return os.path.expanduser(path) # noqa: PTH111 + return super().user_videos_dir + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user, from ``$XDG_MUSIC_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_MUSIC_DIR", "").strip(): + return os.path.expanduser(path) # noqa: PTH111 + return super().user_music_dir + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user, from ``$XDG_DESKTOP_DIR`` if set, else platform default""" + if path := os.environ.get("XDG_DESKTOP_DIR", "").strip(): + return os.path.expanduser(path) # noqa: PTH111 + return super().user_desktop_dir + + +__all__ = [ + "XDGMixin", +] diff --git a/contrib/python/platformdirs/platformdirs/android.py b/contrib/python/platformdirs/platformdirs/android.py index 92efc852d38..dec1e5eb044 100644 --- a/contrib/python/platformdirs/platformdirs/android.py +++ b/contrib/python/platformdirs/platformdirs/android.py @@ -13,10 +13,14 @@ from .api import PlatformDirsABC class Android(PlatformDirsABC): """ - Follows the guidance `from here <https://android.stackexchange.com/a/216132>`_. + Platform directories for Android. + + Follows the guidance `from here <https://android.stackexchange.com/a/216132>`_. Directories are typically located + under the app's private storage (``/data/user/<userid>/<packagename>/``). Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`, `version - <platformdirs.api.PlatformDirsABC.version>`, `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. + <platformdirs.api.PlatformDirsABC.version>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, + `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. """ @@ -40,7 +44,7 @@ class Android(PlatformDirsABC): @property def site_config_dir(self) -> str: - """:return: config directory shared by the users, same as `user_config_dir`""" + """:return: config directory shared by users, same as `user_config_dir`""" return self.user_config_dir @property @@ -135,7 +139,7 @@ def _android_folder() -> str | None: # noqa: C901 try: # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful # result... - from jnius import autoclass # noqa: PLC0415 + from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import] context = autoclass("android.content.Context") result = context.getFilesDir().getParentFile().getAbsolutePath() @@ -169,7 +173,7 @@ def _android_documents_folder() -> str: """:return: documents folder for the Android OS""" # Get directories with pyjnius try: - from jnius import autoclass # noqa: PLC0415 + from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import] context = autoclass("android.content.Context") environment = autoclass("android.os.Environment") @@ -185,7 +189,7 @@ def _android_downloads_folder() -> str: """:return: downloads folder for the Android OS""" # Get directories with pyjnius try: - from jnius import autoclass # noqa: PLC0415 + from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import] context = autoclass("android.content.Context") environment = autoclass("android.os.Environment") @@ -201,7 +205,7 @@ def _android_pictures_folder() -> str: """:return: pictures folder for the Android OS""" # Get directories with pyjnius try: - from jnius import autoclass # noqa: PLC0415 + from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import] context = autoclass("android.content.Context") environment = autoclass("android.os.Environment") @@ -217,7 +221,7 @@ def _android_videos_folder() -> str: """:return: videos folder for the Android OS""" # Get directories with pyjnius try: - from jnius import autoclass # noqa: PLC0415 + from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import] context = autoclass("android.content.Context") environment = autoclass("android.os.Environment") @@ -233,7 +237,7 @@ def _android_music_folder() -> str: """:return: music folder for the Android OS""" # Get directories with pyjnius try: - from jnius import autoclass # noqa: PLC0415 + from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import] context = autoclass("android.content.Context") environment = autoclass("android.os.Environment") diff --git a/contrib/python/platformdirs/platformdirs/api.py b/contrib/python/platformdirs/platformdirs/api.py index 251600e6d1b..1e038d8b5d2 100644 --- a/contrib/python/platformdirs/platformdirs/api.py +++ b/contrib/python/platformdirs/platformdirs/api.py @@ -13,7 +13,15 @@ if TYPE_CHECKING: class PlatformDirsABC(ABC): # noqa: PLR0904 - """Abstract base class for platform directories.""" + """ + Abstract base class defining all platform directory properties, their :class:`~pathlib.Path` variants, and + iterators. + + Platform-specific subclasses (e.g. :class:`~platformdirs.windows.Windows`, + :class:`~platformdirs.macos.MacOS`, :class:`~platformdirs.unix.Unix`) implement the abstract + properties to return the appropriate paths for each operating system. + + """ def __init__( # noqa: PLR0913, PLR0917 self, @@ -37,7 +45,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904 :param ensure_exists: See `ensure_exists`. """ - self.appname = appname #: The name of application. + self.appname = appname #: The name of the application. self.appauthor = appauthor """ The name of the app author or distributing body for this application. @@ -66,10 +74,18 @@ class PlatformDirsABC(ABC): # noqa: PLR0904 """ An optional parameter which indicates that the entire list of data dirs should be returned. - By default, the first item would only be returned. + By default, the first item would only be returned. Only affects ``site_data_dir`` and ``site_config_dir`` on + Unix and macOS. + + """ + self.opinion = opinion + """ + Whether to use opinionated values. + + When enabled, appends an additional subdirectory for certain directories: e.g. ``Cache`` for cache and ``Logs`` + for logs on Windows, ``log`` for logs on Unix. """ - self.opinion = opinion #: A flag to indicating to use opinionated values. self.ensure_exists = ensure_exists """ Optionally create the directory (and any missing parents) upon access if it does not exist. @@ -116,7 +132,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904 @property @abstractmethod def site_config_dir(self) -> str: - """:return: config directory shared by the users""" + """:return: config directory shared by users""" @property @abstractmethod @@ -195,7 +211,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904 @property def site_config_path(self) -> Path: - """:return: config path shared by the users""" + """:return: config path shared by users""" return Path(self.site_config_dir) @property @@ -220,7 +236,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904 @property def user_documents_path(self) -> Path: - """:return: documents a path tied to the user""" + """:return: documents path tied to the user""" return Path(self.user_documents_dir) @property diff --git a/contrib/python/platformdirs/platformdirs/macos.py b/contrib/python/platformdirs/platformdirs/macos.py index 30ab3689130..80a6c7348f0 100644 --- a/contrib/python/platformdirs/platformdirs/macos.py +++ b/contrib/python/platformdirs/platformdirs/macos.py @@ -6,22 +6,20 @@ import os.path import sys from typing import TYPE_CHECKING +from ._xdg import XDGMixin from .api import PlatformDirsABC if TYPE_CHECKING: from pathlib import Path -class MacOS(PlatformDirsABC): +class _MacOSDefaults(PlatformDirsABC): """ - Platform directories for the macOS operating system. + Default platform directories for macOS without XDG environment variable overrides. Follows the guidance from - `Apple documentation <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_. - Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`, - `version <platformdirs.api.PlatformDirsABC.version>`, - `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. - + `Apple's File System Programming Guide <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_. + The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`. """ @property @@ -30,22 +28,12 @@ class MacOS(PlatformDirsABC): return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111 @property - def site_data_dir(self) -> str: - """ - :return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``. - If we're using a Python binary managed by `Homebrew <https://brew.sh>`_, the directory - will be under the Homebrew prefix, e.g. ``$homebrew_prefix/share/$appname/$version``. - If `multipath <platformdirs.api.PlatformDirsABC.multipath>` is enabled, and we're in Homebrew, - the response is a multi-path string separated by ":", e.g. - ``$homebrew_prefix/share/$appname/$version:/Library/Application Support/$appname/$version`` - """ + def _site_data_dirs(self) -> list[str]: is_homebrew = "/opt/python" in sys.prefix homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/share")] if is_homebrew else [] path_list.append(self._append_app_name_and_version("/Library/Application Support")) - if self.multipath: - return os.pathsep.join(path_list) - return path_list[0] + return path_list @property def site_data_path(self) -> Path: @@ -58,9 +46,8 @@ class MacOS(PlatformDirsABC): return self.user_data_dir @property - def site_config_dir(self) -> str: - """:return: config directory shared by the users, same as `site_data_dir`""" - return self.site_data_dir + def _site_config_dirs(self) -> list[str]: + return self._site_data_dirs @property def user_cache_dir(self) -> str: @@ -141,6 +128,21 @@ class MacOS(PlatformDirsABC): return self.user_runtime_dir +class MacOS(XDGMixin, _MacOSDefaults): + """ + Platform directories for the macOS operating system. + + Follows the guidance from + `Apple documentation <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_. + Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`, + `version <platformdirs.api.PlatformDirsABC.version>`, + `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`. + + XDG environment variables (e.g. ``$XDG_DATA_HOME``) are supported and take precedence over macOS defaults. + + """ + + __all__ = [ "MacOS", ] diff --git a/contrib/python/platformdirs/platformdirs/unix.py b/contrib/python/platformdirs/platformdirs/unix.py index fc75d8d0747..d0cb6719d9e 100644 --- a/contrib/python/platformdirs/platformdirs/unix.py +++ b/contrib/python/platformdirs/platformdirs/unix.py @@ -6,8 +6,10 @@ import os import sys from configparser import ConfigParser from pathlib import Path +from tempfile import gettempdir from typing import TYPE_CHECKING, NoReturn +from ._xdg import XDGMixin from .api import PlatformDirsABC if TYPE_CHECKING: @@ -23,17 +25,11 @@ else: from os import getuid -class Unix(PlatformDirsABC): # noqa: PLR0904 +class _UnixDefaults(PlatformDirsABC): """ - On Unix/Linux, we follow the `XDG Basedir Spec <https://specifications.freedesktop.org/basedir-spec/basedir-spec- - latest.html>`_. - - The spec allows overriding directories with environment variables. The examples shown are the default values, - alongside the name of the environment variable that overrides them. Makes use of the `appname - <platformdirs.api.PlatformDirsABC.appname>`, `version <platformdirs.api.PlatformDirsABC.version>`, `multipath - <platformdirs.api.PlatformDirsABC.multipath>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, `ensure_exists - <platformdirs.api.PlatformDirsABC.ensure_exists>`. + Default directories for Unix/Linux without XDG environment variable overrides. + The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`. """ @property @@ -42,30 +38,11 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or ``$XDG_DATA_HOME/$appname/$version`` """ - path = os.environ.get("XDG_DATA_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.local/share") # noqa: PTH111 - return self._append_app_name_and_version(path) + return self._append_app_name_and_version(os.path.expanduser("~/.local/share")) # noqa: PTH111 @property def _site_data_dirs(self) -> list[str]: - path = os.environ.get("XDG_DATA_DIRS", "") - if not path.strip(): - path = f"/usr/local/share{os.pathsep}/usr/share" - return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] - - @property - def site_data_dir(self) -> str: - """ - :return: data directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>` is - enabled and ``XDG_DATA_DIRS`` is set and a multi path the response is also a multi path separated by the - OS path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version`` - """ - # XDG default for $XDG_DATA_DIRS; only first, if multipath is False - dirs = self._site_data_dirs - if not self.multipath: - return dirs[0] - return os.pathsep.join(dirs) + return [self._append_app_name_and_version("/usr/local/share"), self._append_app_name_and_version("/usr/share")] @property def user_config_dir(self) -> str: @@ -73,41 +50,19 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or ``$XDG_CONFIG_HOME/$appname/$version`` """ - path = os.environ.get("XDG_CONFIG_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.config") # noqa: PTH111 - return self._append_app_name_and_version(path) + return self._append_app_name_and_version(os.path.expanduser("~/.config")) # noqa: PTH111 @property def _site_config_dirs(self) -> list[str]: - path = os.environ.get("XDG_CONFIG_DIRS", "") - if not path.strip(): - path = "/etc/xdg" - return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] - - @property - def site_config_dir(self) -> str: - """ - :return: config directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>` - is enabled and ``XDG_CONFIG_DIRS`` is set and a multi path the response is also a multi path separated by - the OS path separator), e.g. ``/etc/xdg/$appname/$version`` - """ - # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False - dirs = self._site_config_dirs - if not self.multipath: - return dirs[0] - return os.pathsep.join(dirs) + return [self._append_app_name_and_version("/etc/xdg")] @property def user_cache_dir(self) -> str: """ :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or - ``~/$XDG_CACHE_HOME/$appname/$version`` + ``$XDG_CACHE_HOME/$appname/$version`` """ - path = os.environ.get("XDG_CACHE_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.cache") # noqa: PTH111 - return self._append_app_name_and_version(path) + return self._append_app_name_and_version(os.path.expanduser("~/.cache")) # noqa: PTH111 @property def site_cache_dir(self) -> str: @@ -120,10 +75,7 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or ``$XDG_STATE_HOME/$appname/$version`` """ - path = os.environ.get("XDG_STATE_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.local/state") # noqa: PTH111 - return self._append_app_name_and_version(path) + return self._append_app_name_and_version(os.path.expanduser("~/.local/state")) # noqa: PTH111 @property def user_log_dir(self) -> str: @@ -167,21 +119,18 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 @property def user_runtime_dir(self) -> str: """ - :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or - ``$XDG_RUNTIME_DIR/$appname/$version``. + :return: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``. - For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if - exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR`` - is not set. + 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. """ - path = os.environ.get("XDG_RUNTIME_DIR", "") - if not path.strip(): - if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): - path = f"/var/run/user/{getuid()}" - if not Path(path).exists(): - path = f"/tmp/runtime-{getuid()}" # noqa: S108 - else: - path = f"/run/user/{getuid()}" + if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): + path = f"/var/run/user/{getuid()}" + else: + path = f"/run/user/{getuid()}" + if not os.access(path, os.W_OK): + path = f"{gettempdir()}/runtime-{getuid()}" return self._append_app_name_and_version(path) @property @@ -198,12 +147,10 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set. """ - path = os.environ.get("XDG_RUNTIME_DIR", "") - if not path.strip(): - if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): - path = "/var/run" - else: - path = "/run" + if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): + path = "/var/run" + else: + path = "/run" return self._append_app_name_and_version(path) @property @@ -213,7 +160,7 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 @property def site_config_path(self) -> Path: - """:return: config path shared by the users, returns the first item, even if ``multipath`` is set to ``True``""" + """:return: config path shared by users, returns the first item, even if ``multipath`` is set to ``True``""" return self._first_item_as_path_if_multipath(self.site_config_dir) @property @@ -232,14 +179,22 @@ class Unix(PlatformDirsABC): # noqa: PLR0904 yield from self._site_data_dirs -def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str: - media_dir = _get_user_dirs_folder(env_var) - if media_dir is None: - media_dir = os.environ.get(env_var, "").strip() - if not media_dir: - media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111 +class Unix(XDGMixin, _UnixDefaults): + """ + On Unix/Linux, we follow the `XDG Basedir Spec <https://specifications.freedesktop.org/basedir/latest/>`_. + + The spec allows overriding directories with environment variables. The examples shown are the default values, + alongside the name of the environment variable that overrides them. Makes use of the `appname + <platformdirs.api.PlatformDirsABC.appname>`, `version <platformdirs.api.PlatformDirsABC.version>`, `multipath + <platformdirs.api.PlatformDirsABC.multipath>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, `ensure_exists + <platformdirs.api.PlatformDirsABC.ensure_exists>`. + """ + - return media_dir +def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str: + if media_dir := _get_user_dirs_folder(env_var): + return media_dir + return os.path.expanduser(fallback_tilde_path) # noqa: PTH111 def _get_user_dirs_folder(key: str) -> str | None: @@ -249,19 +204,17 @@ def _get_user_dirs_folder(key: str) -> str | None: See https://freedesktop.org/wiki/Software/xdg-user-dirs/. """ - user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs" + user_dirs_config_path = Path(os.path.expanduser("~/.config")) / "user-dirs.dirs" # noqa: PTH111 if user_dirs_config_path.exists(): parser = ConfigParser() with user_dirs_config_path.open() as stream: - # Add fake section header, so ConfigParser doesn't complain parser.read_string(f"[top]\n{stream.read()}") if key not in parser["top"]: return None path = parser["top"][key].strip('"') - # Handle relative home paths return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111 return None diff --git a/contrib/python/platformdirs/platformdirs/version.py b/contrib/python/platformdirs/platformdirs/version.py index fcf6f03a1cc..f00ce0cd04a 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.5.1' -__version_tuple__ = version_tuple = (4, 5, 1) +__version__ = version = '4.7.0' +__version_tuple__ = version_tuple = (4, 7, 0) __commit_id__ = commit_id = None diff --git a/contrib/python/platformdirs/platformdirs/windows.py b/contrib/python/platformdirs/platformdirs/windows.py index 8d523a9c665..838e07fad60 100644 --- a/contrib/python/platformdirs/platformdirs/windows.py +++ b/contrib/python/platformdirs/platformdirs/windows.py @@ -63,7 +63,7 @@ class Windows(PlatformDirsABC): @property def site_config_dir(self) -> str: - """:return: config directory shared by the users, same as `site_data_dir`""" + """:return: config directory shared by users, same as `site_data_dir`""" return self.site_data_dir @property @@ -216,53 +216,85 @@ def get_win_folder_from_registry(csidl_name: str) -> str: return str(directory) +_KNOWN_FOLDER_GUIDS: dict[str, str] = { + "CSIDL_APPDATA": "{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}", + "CSIDL_COMMON_APPDATA": "{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}", + "CSIDL_LOCAL_APPDATA": "{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}", + "CSIDL_PERSONAL": "{FDD39AD0-238F-46AF-ADB4-6C85480369C7}", + "CSIDL_MYPICTURES": "{33E28130-4E1E-4676-835A-98395C3BC3BB}", + "CSIDL_MYVIDEO": "{18989B1D-99B5-455B-841C-AB7C74E4DDFC}", + "CSIDL_MYMUSIC": "{4BD8D571-6D19-48D3-BE97-422220080E43}", + "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}", + "CSIDL_DESKTOPDIRECTORY": "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}", +} + + def get_win_folder_via_ctypes(csidl_name: str) -> str: - """Get folder with ctypes.""" - # There is no 'CSIDL_DOWNLOADS'. - # Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead. - # https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + """ + Get folder via :func:`SHGetKnownFolderPath`. - import ctypes # noqa: PLC0415 + See https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath. - csidl_const = { - "CSIDL_APPDATA": 26, - "CSIDL_COMMON_APPDATA": 35, - "CSIDL_LOCAL_APPDATA": 28, - "CSIDL_PERSONAL": 5, - "CSIDL_MYPICTURES": 39, - "CSIDL_MYVIDEO": 14, - "CSIDL_MYMUSIC": 13, - "CSIDL_DOWNLOADS": 40, - "CSIDL_DESKTOPDIRECTORY": 16, - }.get(csidl_name) - if csidl_const is None: + """ + if sys.platform != "win32": # only needed for type checker to know that this code runs only on Windows + raise NotImplementedError + from ctypes import HRESULT, POINTER, Structure, WinDLL, byref, create_unicode_buffer, wintypes # noqa: PLC0415 + + class _GUID(Structure): + _fields_ = [ + ("Data1", wintypes.DWORD), + ("Data2", wintypes.WORD), + ("Data3", wintypes.WORD), + ("Data4", wintypes.BYTE * 8), + ] + + ole32 = WinDLL("ole32") + ole32.CLSIDFromString.restype = HRESULT + ole32.CLSIDFromString.argtypes = [wintypes.LPCOLESTR, POINTER(_GUID)] + ole32.CoTaskMemFree.restype = None + ole32.CoTaskMemFree.argtypes = [wintypes.LPVOID] + + shell32 = WinDLL("shell32") + shell32.SHGetKnownFolderPath.restype = HRESULT + shell32.SHGetKnownFolderPath.argtypes = [POINTER(_GUID), wintypes.DWORD, wintypes.HANDLE, POINTER(wintypes.LPWSTR)] + + kernel32 = WinDLL("kernel32") + kernel32.GetShortPathNameW.restype = wintypes.DWORD + kernel32.GetShortPathNameW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR, wintypes.DWORD] + + folder_guid = _KNOWN_FOLDER_GUIDS.get(csidl_name) + if folder_guid is None: msg = f"Unknown CSIDL name: {csidl_name}" raise ValueError(msg) - buf = ctypes.create_unicode_buffer(1024) - windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker - windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + guid = _GUID() + ole32.CLSIDFromString(folder_guid, byref(guid)) - # Downgrade to short path name if it has high-bit chars. - if any(ord(c) > 255 for c in buf): # noqa: PLR2004 - buf2 = ctypes.create_unicode_buffer(1024) - if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): - buf = buf2 + path_ptr = wintypes.LPWSTR() + shell32.SHGetKnownFolderPath(byref(guid), 0, None, byref(path_ptr)) + result = path_ptr.value + ole32.CoTaskMemFree(path_ptr) - if csidl_name == "CSIDL_DOWNLOADS": - return os.path.join(buf.value, "Downloads") # noqa: PTH118 + if result is None: + msg = f"SHGetKnownFolderPath returned NULL for {csidl_name}" + raise ValueError(msg) - return buf.value + if any(ord(c) > 255 for c in result): # noqa: PLR2004 + buf = create_unicode_buffer(1024) + if kernel32.GetShortPathNameW(result, buf, 1024): + result = buf.value + + return result def _pick_get_win_folder() -> Callable[[str], str]: + """Select the best method to resolve Windows folder paths: ctypes, then registry, then environment variables.""" try: - import ctypes # noqa: PLC0415 + import ctypes # noqa: PLC0415, F401 except ImportError: pass else: - if hasattr(ctypes, "windll"): - return get_win_folder_via_ctypes + return get_win_folder_via_ctypes try: import winreg # noqa: PLC0415, F401 except ImportError: diff --git a/contrib/python/platformdirs/ya.make b/contrib/python/platformdirs/ya.make index b9fb583dc84..e763ebaa093 100644 --- a/contrib/python/platformdirs/ya.make +++ b/contrib/python/platformdirs/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.5.1) +VERSION(4.7.0) LICENSE(MIT) @@ -12,6 +12,7 @@ PY_SRCS( TOP_LEVEL platformdirs/__init__.py platformdirs/__main__.py + platformdirs/_xdg.py platformdirs/android.py platformdirs/api.py platformdirs/macos.py |
