summaryrefslogtreecommitdiffstats
path: root/contrib/python/platformdirs
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-02-28 22:22:15 +0300
committerrobot-piglet <[email protected]>2026-02-28 22:47:08 +0300
commit4327fd33b91e71935a178c932880f9a575603a33 (patch)
treeebed7608d3a05218dcf972c376e1574226e67a3a /contrib/python/platformdirs
parent09594345cca8209c70479f9a34e7880f09d323fc (diff)
Intermediate changes
commit_hash:a01a1ccee8f63c56eaa87c462b96e7ee22f7afae
Diffstat (limited to 'contrib/python/platformdirs')
-rw-r--r--contrib/python/platformdirs/.dist-info/METADATA343
-rw-r--r--contrib/python/platformdirs/README.md53
-rw-r--r--contrib/python/platformdirs/README.rst307
-rw-r--r--contrib/python/platformdirs/platformdirs/__init__.py27
-rw-r--r--contrib/python/platformdirs/platformdirs/_xdg.py124
-rw-r--r--contrib/python/platformdirs/platformdirs/android.py22
-rw-r--r--contrib/python/platformdirs/platformdirs/api.py30
-rw-r--r--contrib/python/platformdirs/platformdirs/macos.py46
-rw-r--r--contrib/python/platformdirs/platformdirs/unix.py133
-rw-r--r--contrib/python/platformdirs/platformdirs/version.py4
-rw-r--r--contrib/python/platformdirs/platformdirs/windows.py96
-rw-r--r--contrib/python/platformdirs/ya.make3
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
+[![PyPI version](https://badge.fury.io/py/platformdirs.svg)](https://badge.fury.io/py/platformdirs)
+[![Python versions](https://img.shields.io/pypi/pyversions/platformdirs.svg)](https://pypi.python.org/pypi/platformdirs/)
+[![CI](https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg)](https://github.com/platformdirs/platformdirs/actions)
+[![Downloads](https://static.pepy.tech/badge/platformdirs/month)](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
+
+[![PyPI version](https://badge.fury.io/py/platformdirs.svg)](https://badge.fury.io/py/platformdirs)
+[![Python versions](https://img.shields.io/pypi/pyversions/platformdirs.svg)](https://pypi.python.org/pypi/platformdirs/)
+[![CI](https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg)](https://github.com/platformdirs/platformdirs/actions)
+[![Downloads](https://static.pepy.tech/badge/platformdirs/month)](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