././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1645291648.78334 platformdirs-2.5.1/0000755000175100001710000000000000000000000013565 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1645291648.7793398 platformdirs-2.5.1/.github/0000755000175100001710000000000000000000000015125 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/.github/CODEOWNERS0000644000175100001710000000006000000000000016514 0ustar00runnerdocker* @gaborbernat @ofek @Julian @RonnyPfannschmidt ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1645291648.7793398 platformdirs-2.5.1/.github/workflows/0000755000175100001710000000000000000000000017162 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/.github/workflows/check.yml0000644000175100001710000001107500000000000020766 0ustar00runnerdockername: check on: push: pull_request: schedule: - cron: "0 8 * * *" concurrency: group: check-${{ github.ref }} cancel-in-progress: true jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - uses: pre-commit/action@v2.0.3 test: name: test ${{ matrix.py }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: py: - "3.10" - "pypy-3.7-v7.3.7" # ahead to start it earlier because takes longer - "3.9" - "3.8" - "3.7" os: - ubuntu-20.04 - windows-2022 - macos-10.15 steps: - name: Setup python for tox uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install tox run: python -m pip install tox - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup python for test ${{ matrix.py }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.py }} - name: Pick environment to run run: | import codecs import os import platform import sys cpy = platform.python_implementation() == "CPython" base =("{}{}{}" if cpy else "{}{}").format("py" if cpy else "pypy", *sys.version_info[0:2]) env = "TOXENV={}\n".format(base) print("Picked:\n{}for{}".format(env, sys.version)) with codecs.open(os.environ["GITHUB_ENV"], "a", "utf-8") as file_handler: file_handler.write(env) shell: python - name: Setup test suite run: tox -vv --notest - name: Run test suite run: tox --skip-pkg-install env: PYTEST_ADDOPTS: "-vv --durations=20" CI_RUN: "yes" - name: Rename coverage report file run: import os; import sys; os.rename(f".tox/.coverage.{os.environ['TOXENV']}", f".tox/.coverage.{os.environ['TOXENV']}-{sys.platform}") shell: python - name: Upload coverage data uses: actions/upload-artifact@v2 with: name: coverage-data path: ".tox/.coverage.*" coverage: name: Combine coverage runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install tox run: python -m pip install tox - name: Setup coverage tool run: tox -e coverage --notest - name: Install package builder run: python -m pip install build - name: Build package run: pyproject-build --wheel . - name: Download coverage data uses: actions/download-artifact@v2 with: name: coverage-data path: .tox - name: Combine and report coverage run: tox -e coverage - name: Upload HTML report uses: actions/upload-artifact@v2 with: name: html-report path: .tox/htmlcov check: name: ${{ matrix.tox_env }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: - ubuntu-latest - windows-latest tox_env: - dev - type - docs - readme exclude: - { os: windows-latest, tox_env: readme } steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup Python "3.10" uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install tox run: python -m pip install tox - name: Setup test suite run: tox -vv --notest -e ${{ matrix.tox_env }} - name: Run test suite run: tox --skip-pkg-install -e ${{ matrix.tox_env }} publish: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') needs: [ check, coverage, lint ] runs-on: ubuntu-latest steps: - name: Setup python to build package uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install build run: python -m pip install build - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Build sdist and wheel run: python -m build -s -w . -o dist - name: Publish to PyPi uses: pypa/gh-action-pypi-publish@master with: skip_existing: true user: __token__ password: ${{ secrets.pypi_password }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/.gitignore0000644000175100001710000000013100000000000015550 0ustar00runnerdocker/.idea/ /.vscode/ *.pyc *.egg-info tmp/ build/ dist/ .tox/ /src/platformdirs/version.py ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/.pre-commit-config.yaml0000644000175100001710000000312400000000000020046 0ustar00runnerdockerrepos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: - id: check-ast - id: check-builtin-literals - id: check-docstring-first - id: check-merge-conflict - id: check-yaml - id: check-toml - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade rev: v2.31.0 hooks: - id: pyupgrade args: [ "--py36-plus" ] - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: - id: isort - repo: https://github.com/psf/black rev: 22.1.0 hooks: - id: black args: [ --safe ] - repo: https://github.com/asottile/blacken-docs rev: v1.12.1 hooks: - id: blacken-docs additional_dependencies: [ black==22.1.0 ] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.9.0 hooks: - id: rst-backticks - repo: https://github.com/tox-dev/tox-ini-fmt rev: "0.5.2" hooks: - id: tox-ini-fmt args: [ "-p", "fix" ] - repo: https://github.com/asottile/setup-cfg-fmt rev: v1.20.0 hooks: - id: setup-cfg-fmt args: [ --min-py3-version, "3.7", "--max-py-version", "3.10" ] - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: - id: flake8 additional_dependencies: - flake8-bugbear==22.1.11 - flake8-comprehensions==3.8 - flake8-pytest-style==1.6 - flake8-spellcheck==0.24 - flake8-unused-arguments==0.0.9 - flake8-noqa==1.2.1 - pep8-naming==0.12.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/.readthedocs.yml0000644000175100001710000000041500000000000016653 0ustar00runnerdockerversion: 2 build: image: latest formats: - htmlzip - epub - pdf python: version: 3.7 install: - method: pip path: . extra_requirements: - docs sphinx: builder: html configuration: docs/conf.py fail_on_warning: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/CHANGES.rst0000644000175100001710000001213200000000000015366 0ustar00runnerdockerplatformdirs Changelog ====================== platformdirs 2.5.1 ------------------ - Add native support for nuitka platformdirs 2.5.0 ------------------ - Add support for Termux subsystems platformdirs 2.4.1 ------------------ - Drop python 3.6 support platformdirs 2.4.0 ------------------ - Add ``user_documents_dir`` platformdirs 2.3.0 ------------------ - Add ``user_runtime_dir`` and its path-returning equivalent (#37) platformdirs 2.2.0 ------------------ - Unix: Fallback to default if XDG environment variable is empty platformdirs 2.1.0 ------------------ - Add ``readthedocs.org`` documentation via Sphinx - Modernize project layout - Drop Python 2.7 and 3.5 support - Android support - Add type annotations - Reorganize project layout to platform specific classes, see :class:`PlatformDirsABC ` and it's implementations: :class:`Android `, :class:`MacOS `, :class:`Unix ` and :class:`Windows ` - Add ``*_path`` API, returning :class:`pathlib.Path ` objects instead of :class:`str` (``user_data_path``, ``user_config_path``, ``user_cache_path``, ``user_state_path``, ``user_log_path``, ``site_data_path``, ``site_config_path``) - by `@papr `_ platformdirs 2.0.2 ------------------ - Fix ``__version__`` and ``__version_info__`` platformdirs 2.0.1 ------------------ - Documentation fixes platformdirs 2.0.0 ------------------ - **BREAKING** Name change as part of the friendly fork - **BREAKING** Remove support for end-of-life Pythons 2.6, 3.2, and 3.3 - **BREAKING** Correct the config directory on OSX/macOS - Add Python 3.7, 3.8, and 3.9 support appdirs 1.4.4 ------------- - [PR #92] Don't import appdirs from setup.py which resolves issue #91 Project officially classified as Stable which is important for inclusion in other distros such as ActivePython. appdirs 1.4.3 ------------- - [PR #76] Python 3.6 invalid escape sequence deprecation fixes - Fix for Python 3.6 support appdirs 1.4.2 ------------- - [PR #84] Allow installing without setuptools - [PR #86] Fix string delimiters in setup.py description - Add Python 3.6 support appdirs 1.4.1 ------------- - [issue #38] Fix _winreg import on Windows Py3 - [issue #55] Make appname optional appdirs 1.4.0 ------------- - [PR #42] AppAuthor is now optional on Windows - [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows support requires `JNA `_. - [PR #44] Fix incorrect behaviour of the site_config_dir method appdirs 1.3.0 ------------- - [Unix, issue 16] Conform to XDG standard, instead of breaking it for everybody - [Unix] Removes gratuitous case mangling of the case, since \*nix-es are usually case sensitive, so mangling is not wise - [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result based on XDG_DATA_DIRS and make room for respecting the standard which specifies XDG_DATA_DIRS is a multiple-value variable - [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to XDG specs; on Windows and Mac return the corresponding ``*_data_dir`` appdirs 1.2.0 ------------- - [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more typical. - [issue 9] Make ``unicode`` work on py3k. appdirs 1.1.0 ------------- - [issue 4] Add ``AppDirs.user_log_dir``. - [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec `_. - [Mac, issue 5] Fix ``site_data_dir()`` on Mac. - [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports Python3 now. - [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use ``opinion=False`` option to disable this. - Add ``appdirs.AppDirs`` convenience class. Usage: >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") >>> dirs.user_data_dir '/Users/trentm/Library/Application Support/SuperApp/1.0' - [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short paths if there are high bit chars. - [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g. "~/.superapp/cache". - [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only) and change the default ``user_data_dir`` behaviour to use a *non*-roaming profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because a large roaming profile can cause login speed issues. The "only syncs on logout" behaviour can cause surprises in appdata info. appdirs 1.0.1 (never released) ------------------------------ Started this changelog 27 July 2010. Before that this module originated in the `Komodo `_ product as ``applib.py`` and then as `applib/location.py `_ (used by `PyPM `_ in `ActivePython `_). This is basically a fork of applib.py 1.0.1 and applib/location.py 1.0.1. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/LICENSE.txt0000644000175100001710000000211000000000000015402 0ustar00runnerdocker# This is the MIT license Copyright (c) 2010 ActiveState Software Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1645291648.78334 platformdirs-2.5.1/PKG-INFO0000644000175100001710000002101400000000000014660 0ustar00runnerdockerMetadata-Version: 2.1 Name: platformdirs Version: 2.5.1 Summary: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". Home-page: https://github.com/platformdirs/platformdirs Maintainer: Bernát Gábor, Julian Berman, Ofek Lev, Ronny Pfannschmidt Maintainer-email: gaborjbernat@gmail.com, Julian@GrayVines.com, oss@ofek.dev, opensource@ronnypfannschmidt.de License: MIT Project-URL: Source, https://github.com/platformdirs/platformdirs Project-URL: Tracker, https://github.com/platformdirs/platformdirs/issues Project-URL: Documentation, https://platformdirs.readthedocs.io/ Keywords: application directory log cache user Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.7 Description-Content-Type: text/x-rst Provides-Extra: docs Provides-Extra: test License-File: LICENSE.txt The problem =========== .. image:: https://github.com/platformdirs/platformdirs/workflows/Test/badge.svg :target: https://github.com/platformdirs/platformdirs/actions?query=workflow%3ATest 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/ If on Windows (at least English Win XP) that should be:: C:\Documents and Settings\\Application Data\Local Settings\\ or possibly:: C:\Documents and Settings\\Application Data\\ for `roaming profiles `_ but that is another story. On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: ~/.local/share/ .. _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`` module 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 runtime dir (``user_runtime_dir``) And also: - Is a single module so other Python packages can vendor their own private copy. - 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' >>> site_data_dir(appname, appauthor) '/Library/Application Support/SuperApp' >>> user_cache_dir(appname, appauthor) '/Users/trentm/Library/Caches/SuperApp' >>> user_log_dir(appname, appauthor) '/Users/trentm/Library/Logs/SuperApp' >>> user_documents_dir() '/Users/trentm/Documents' >>> user_runtime_dir(appname, appauthor) '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' On Windows 7: .. 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_cache_dir(appname, appauthor) 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' >>> user_log_dir(appname, appauthor) 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' >>> user_documents_dir() 'C:\\Users\\trentm\\Documents' >>> 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' >>> site_data_dir(appname, appauthor) '/usr/local/share/SuperApp' >>> site_data_dir(appname, appauthor, multipath=True) '/usr/local/share/SuperApp:/usr/share/SuperApp' >>> user_cache_dir(appname, appauthor) '/home/trentm/.cache/SuperApp' >>> user_log_dir(appname, appauthor) '/home/trentm/.cache/SuperApp/log' >>> user_config_dir(appname) '/home/trentm/.config/SuperApp' >>> user_documents_dir() '/home/trentm/Documents' >>> user_runtime_dir(appname, appauthor) '/run/user/{os.getuid()}/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' On Android:: >>> from platformdirs import * >>> appname = "SuperApp" >>> appauthor = "Acme" >>> user_data_dir(appname, appauthor) '/data/data/com.termux/files/SuperApp' >>> user_cache_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp' >>> user_log_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp/log' >>> user_config_dir(appname) '/data/data/com.termux/shared_prefs/SuperApp' >>> user_documents_dir() '/storage/emulated/0/Documents' >>> user_runtime_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp/tmp' ``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.site_data_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_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.site_data_dir '/Library/Application Support/SuperApp/1.0' >>> dirs.user_cache_dir '/Users/trentm/Library/Caches/SuperApp/1.0' >>> dirs.user_log_dir '/Users/trentm/Library/Logs/SuperApp/1.0' >>> dirs.user_documents_dir '/Users/trentm/Documents' >>> 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 `_ 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 `_, this fork was created. Contributions are most welcome. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/README.rst0000644000175100001710000001574500000000000015270 0ustar00runnerdockerThe problem =========== .. image:: https://github.com/platformdirs/platformdirs/workflows/Test/badge.svg :target: https://github.com/platformdirs/platformdirs/actions?query=workflow%3ATest 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/ If on Windows (at least English Win XP) that should be:: C:\Documents and Settings\\Application Data\Local Settings\\ or possibly:: C:\Documents and Settings\\Application Data\\ for `roaming profiles `_ but that is another story. On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: ~/.local/share/ .. _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`` module 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 runtime dir (``user_runtime_dir``) And also: - Is a single module so other Python packages can vendor their own private copy. - 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' >>> site_data_dir(appname, appauthor) '/Library/Application Support/SuperApp' >>> user_cache_dir(appname, appauthor) '/Users/trentm/Library/Caches/SuperApp' >>> user_log_dir(appname, appauthor) '/Users/trentm/Library/Logs/SuperApp' >>> user_documents_dir() '/Users/trentm/Documents' >>> user_runtime_dir(appname, appauthor) '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' On Windows 7: .. 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_cache_dir(appname, appauthor) 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' >>> user_log_dir(appname, appauthor) 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' >>> user_documents_dir() 'C:\\Users\\trentm\\Documents' >>> 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' >>> site_data_dir(appname, appauthor) '/usr/local/share/SuperApp' >>> site_data_dir(appname, appauthor, multipath=True) '/usr/local/share/SuperApp:/usr/share/SuperApp' >>> user_cache_dir(appname, appauthor) '/home/trentm/.cache/SuperApp' >>> user_log_dir(appname, appauthor) '/home/trentm/.cache/SuperApp/log' >>> user_config_dir(appname) '/home/trentm/.config/SuperApp' >>> user_documents_dir() '/home/trentm/Documents' >>> user_runtime_dir(appname, appauthor) '/run/user/{os.getuid()}/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' On Android:: >>> from platformdirs import * >>> appname = "SuperApp" >>> appauthor = "Acme" >>> user_data_dir(appname, appauthor) '/data/data/com.termux/files/SuperApp' >>> user_cache_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp' >>> user_log_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp/log' >>> user_config_dir(appname) '/data/data/com.termux/shared_prefs/SuperApp' >>> user_documents_dir() '/storage/emulated/0/Documents' >>> user_runtime_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp/tmp' ``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.site_data_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_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.site_data_dir '/Library/Application Support/SuperApp/1.0' >>> dirs.user_cache_dir '/Users/trentm/Library/Caches/SuperApp/1.0' >>> dirs.user_log_dir '/Users/trentm/Library/Logs/SuperApp/1.0' >>> dirs.user_documents_dir '/Users/trentm/Documents' >>> 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 `_ 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 `_, this fork was created. Contributions are most welcome. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/codecov.yml0000644000175100001710000000013100000000000015725 0ustar00runnerdockercoverage: status: patch: default: informational: true comment: false ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1645291648.7793398 platformdirs-2.5.1/docs/0000755000175100001710000000000000000000000014515 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/docs/api.rst0000644000175100001710000000364000000000000016023 0ustar00runnerdockerAPI === User directories ~~~~~~~~~~~~~~~~ These are user-specific (and, generally, user-writeable) directories. User data directory ------------------- .. autofunction:: platformdirs.user_data_dir .. autofunction:: platformdirs.user_data_path User config directory --------------------- .. autofunction:: platformdirs.user_config_dir .. autofunction:: platformdirs.user_config_path Cache directory ------------------- .. autofunction:: platformdirs.user_cache_dir .. autofunction:: platformdirs.user_cache_path State directory ------------------- .. autofunction:: platformdirs.user_state_dir .. autofunction:: platformdirs.user_state_path Logs directory ------------------- .. autofunction:: platformdirs.user_log_dir .. autofunction:: platformdirs.user_log_path User documents directory ------------------------ .. autofunction:: platformdirs.user_documents_dir .. autofunction:: platformdirs.user_documents_path Runtime directory ------------------- .. autofunction:: platformdirs.user_runtime_dir .. autofunction:: platformdirs.user_runtime_path Shared directories ~~~~~~~~~~~~~~~~~~ These are system-wide (and, generally, read-only) directories. Shared data directory --------------------- .. autofunction:: platformdirs.site_data_dir .. autofunction:: platformdirs.site_data_path Shared config directory ----------------------- .. autofunction:: platformdirs.site_config_dir .. autofunction:: platformdirs.site_config_path Platforms ~~~~~~~~~ ABC --- .. autoclass:: platformdirs.api.PlatformDirsABC :members: :special-members: __init__ Android ------- .. autoclass:: platformdirs.android.Android :members: :show-inheritance: macOS ----- .. autoclass:: platformdirs.macos.MacOS :members: :show-inheritance: Unix (Linux) ------------ .. autoclass:: platformdirs.unix.Unix :members: :show-inheritance: Windows ------- .. autoclass:: platformdirs.windows.Windows :members: :show-inheritance: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/docs/changelog.rst0000644000175100001710000000003400000000000017173 0ustar00runnerdocker.. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/docs/conf.py0000644000175100001710000000116100000000000016013 0ustar00runnerdockerfrom __future__ import annotations from platformdirs.version import __version__ author = "The platformdirs team" project = "platformdirs" copyright = "2021, The platformdirs team" release = __version__ version = release extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosectionlabel", "sphinx.ext.viewcode", "sphinx.ext.intersphinx", # "sphinx_autodoc_typehints", ] html_theme = "furo" autodoc_default_options = { "member-order": "bysource", "undoc-members": True, } default_role = "any" autodoc_typehints = "signature" intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/docs/index.rst0000644000175100001710000000075300000000000016363 0ustar00runnerdockerplatformdirs's documentation ============================ ``platformdirs`` is a library to determine platform-specific system directories. This includes directories where to place cache files, user data, configuration, etc. The source code and issue tracker are both hosted on `GitHub`_. .. _GitHub: https://github.com/platformdirs/platformdirs .. toctree:: :maxdepth: 3 api changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/pyproject.toml0000644000175100001710000000064400000000000016505 0ustar00runnerdocker[build-system] requires = ["setuptools >= 44", "wheel >= 0.30", "setuptools_scm[toml]>=5"] build-backend = "setuptools.build_meta" [tool.black] line-length = 120 [tool.isort] profile = "black" known_first_party = ["platformdirs"] [tool.setuptools_scm] write_to = "src/platformdirs/version.py" write_to_template = """ \"\"\" Version information \"\"\" __version__ = "{version}" __version_info__ = {version_tuple} """ ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1645291648.78334 platformdirs-2.5.1/setup.cfg0000644000175100001710000000472500000000000015416 0ustar00runnerdocker[metadata] name = platformdirs description = A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". long_description = file: README.rst long_description_content_type = text/x-rst url = https://github.com/platformdirs/platformdirs maintainer = Bernát Gábor, Julian Berman, Ofek Lev, Ronny Pfannschmidt maintainer_email = gaborjbernat@gmail.com, Julian@GrayVines.com, oss@ofek.dev, opensource@ronnypfannschmidt.de license = MIT license_file = LICENSE.txt classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy Topic :: Software Development :: Libraries :: Python Modules keywords = application directory log cache user project_urls = Source=https://github.com/platformdirs/platformdirs Tracker=https://github.com/platformdirs/platformdirs/issues Documentation=https://platformdirs.readthedocs.io/ [options] packages = find: python_requires = >=3.7 package_dir = =src zip_safe = True [options.packages.find] where = src [options.extras_require] docs = Sphinx>=4 furo>=2021.7.5b38 proselint>=0.10.2 sphinx-autodoc-typehints>=1.12 test = appdirs==1.4.4 pytest>=6 pytest-cov>=2.7 pytest-mock>=3.6 [options.package_data] platformdirs = py.typed [flake8] max-line-length = 120 dictionaries = en_US,python,technical [coverage:report] show_missing = True exclude_lines = \#\s*pragma: no cover ^\s*raise AssertionError\b ^\s*raise NotImplementedError\b ^\s*raise$ ^if __name__ == ['"]__main__['"]:$ [coverage:paths] source = src .tox/*/lib/python*/site-packages .tox/pypy*/site-packages .tox\*\Lib\site-packages\ */src *\src other = . */platformdirs *\platformdirs [coverage:run] branch = true parallel = true dynamic_context = test_function source = ${_COVERAGE_SRC} [coverage:html] show_contexts = true skip_covered = false skip_empty = true [mypy] python_version = 3.9 warn_unused_ignores = False [mypy-appdirs.*] ignore_missing_imports = True [mypy-jnius.*] ignore_missing_imports = True [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/setup.py0000644000175100001710000000011200000000000015271 0ustar00runnerdockerfrom __future__ import annotations from setuptools import setup setup() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1645291648.7793398 platformdirs-2.5.1/src/0000755000175100001710000000000000000000000014354 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1645291648.7793398 platformdirs-2.5.1/src/platformdirs/0000755000175100001710000000000000000000000017062 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/__init__.py0000644000175100001710000003062000000000000021174 0ustar00runnerdocker""" Utilities for determining application-specific dirs. See for details and usage. """ from __future__ import annotations import os import sys from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: from typing_extensions import Literal # pragma: no cover from .api import PlatformDirsABC from .version import __version__, __version_info__ def _set_platform_dir_class() -> type[PlatformDirsABC]: if sys.platform == "win32": from platformdirs.windows import Windows as Result elif sys.platform == "darwin": from platformdirs.macos import MacOS as Result else: from platformdirs.unix import Unix as Result if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": from platformdirs.android import _android_folder if _android_folder() is not None: from platformdirs.android import Android return Android # return to avoid redefinition of result return Result PlatformDirs = _set_platform_dir_class() #: Currently active platform AppDirs = PlatformDirs #: Backwards compatibility with appdirs def user_data_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param roaming: See `roaming `. :returns: data directory tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_dir def site_data_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, multipath: bool = False, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param multipath: See `roaming `. :returns: data directory shared by users """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_dir def user_config_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param roaming: See `roaming `. :returns: config directory tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_dir def site_config_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, multipath: bool = False, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param multipath: See `roaming `. :returns: config directory shared by the users """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_dir def user_cache_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, opinion: bool = True, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param opinion: See `roaming `. :returns: cache directory tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_dir def user_state_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param roaming: See `roaming `. :returns: state directory tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_dir def user_log_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, opinion: bool = True, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param opinion: See `roaming `. :returns: log directory tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir def user_documents_dir() -> str: """ :returns: documents directory tied to the user """ return PlatformDirs().user_documents_dir def user_runtime_dir( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, opinion: bool = True, ) -> str: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param opinion: See `opinion `. :returns: runtime directory tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_dir def user_data_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param roaming: See `roaming `. :returns: data path tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path def site_data_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, multipath: bool = False, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param multipath: See `multipath `. :returns: data path shared by users """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path def user_config_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param roaming: See `roaming `. :returns: config path tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path def site_config_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, multipath: bool = False, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param multipath: See `roaming `. :returns: config path shared by the users """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path def user_cache_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, opinion: bool = True, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param opinion: See `roaming `. :returns: cache path tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path def user_state_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param roaming: See `roaming `. :returns: state path tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path def user_log_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, opinion: bool = True, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param opinion: See `roaming `. :returns: log path tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path def user_documents_path() -> Path: """ :returns: documents path tied to the user """ return PlatformDirs().user_documents_path def user_runtime_path( appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, opinion: bool = True, ) -> Path: """ :param appname: See `appname `. :param appauthor: See `appauthor `. :param version: See `version `. :param opinion: See `opinion `. :returns: runtime path tied to the user """ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_path __all__ = [ "__version__", "__version_info__", "PlatformDirs", "AppDirs", "PlatformDirsABC", "user_data_dir", "user_config_dir", "user_cache_dir", "user_state_dir", "user_log_dir", "user_documents_dir", "user_runtime_dir", "site_data_dir", "site_config_dir", "user_data_path", "user_config_path", "user_cache_path", "user_state_path", "user_log_path", "user_documents_path", "user_runtime_path", "site_data_path", "site_config_path", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/__main__.py0000644000175100001710000000221400000000000021153 0ustar00runnerdockerfrom __future__ import annotations from platformdirs import PlatformDirs, __version__ PROPS = ( "user_data_dir", "user_config_dir", "user_cache_dir", "user_state_dir", "user_log_dir", "user_documents_dir", "user_runtime_dir", "site_data_dir", "site_config_dir", ) def main() -> None: app_name = "MyApp" app_author = "MyCompany" print(f"-- platformdirs {__version__} --") print("-- app dirs (with optional 'version')") dirs = PlatformDirs(app_name, app_author, version="1.0") for prop in PROPS: print(f"{prop}: {getattr(dirs, prop)}") print("\n-- app dirs (without optional 'version')") dirs = PlatformDirs(app_name, app_author) for prop in PROPS: print(f"{prop}: {getattr(dirs, prop)}") print("\n-- app dirs (without optional 'appauthor')") dirs = PlatformDirs(app_name) for prop in PROPS: print(f"{prop}: {getattr(dirs, prop)}") print("\n-- app dirs (with disabled 'appauthor')") dirs = PlatformDirs(app_name, appauthor=False) for prop in PROPS: print(f"{prop}: {getattr(dirs, prop)}") if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/android.py0000644000175100001710000000774400000000000021070 0ustar00runnerdockerfrom __future__ import annotations import os import re import sys from functools import lru_cache from typing import cast from .api import PlatformDirsABC class Android(PlatformDirsABC): """ Follows the guidance `from here `_. Makes use of the `appname ` and `version `. """ @property def user_data_dir(self) -> str: """:return: data directory tied to the user, e.g. ``/data/user///files/``""" return self._append_app_name_and_version(cast(str, _android_folder()), "files") @property def site_data_dir(self) -> str: """:return: data directory shared by users, same as `user_data_dir`""" return self.user_data_dir @property def user_config_dir(self) -> str: """ :return: config directory tied to the user, e.g. ``/data/user///shared_prefs/`` """ return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs") @property def site_config_dir(self) -> str: """:return: config directory shared by the users, same as `user_config_dir`""" return self.user_config_dir @property def user_cache_dir(self) -> str: """:return: cache directory tied to the user, e.g. e.g. ``/data/user///cache/``""" return self._append_app_name_and_version(cast(str, _android_folder()), "cache") @property def user_state_dir(self) -> str: """:return: state directory tied to the user, same as `user_data_dir`""" return self.user_data_dir @property def user_log_dir(self) -> str: """ :return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it, e.g. ``/data/user///cache//log`` """ path = self.user_cache_dir if self.opinion: path = os.path.join(path, "log") return path @property def user_documents_dir(self) -> str: """ :return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents`` """ return _android_documents_folder() @property def user_runtime_dir(self) -> str: """ :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, e.g. ``/data/user///cache//tmp`` """ path = self.user_cache_dir if self.opinion: path = os.path.join(path, "tmp") return path @lru_cache(maxsize=1) def _android_folder() -> str | None: """:return: base folder for the Android OS or None if cannot be found""" try: # First try to get path to android app via pyjnius from jnius import autoclass Context = autoclass("android.content.Context") # noqa: N806 result: str | None = Context.getFilesDir().getParentFile().getAbsolutePath() except Exception: # if fails find an android folder looking path on the sys.path pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files") for path in sys.path: if pattern.match(path): result = path.split("/files")[0] break else: result = None return result @lru_cache(maxsize=1) def _android_documents_folder() -> str: """:return: documents folder for the Android OS""" # Get directories with pyjnius try: from jnius import autoclass Context = autoclass("android.content.Context") # noqa: N806 Environment = autoclass("android.os.Environment") # noqa: N806 documents_dir: str = Context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() except Exception: documents_dir = "/storage/emulated/0/Documents" return documents_dir __all__ = [ "Android", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/api.py0000644000175100001710000001145600000000000020214 0ustar00runnerdockerfrom __future__ import annotations import os import sys from abc import ABC, abstractmethod from pathlib import Path if sys.version_info >= (3, 8): # pragma: no branch from typing import Literal # pragma: no cover class PlatformDirsABC(ABC): """ Abstract base class for platform directories. """ def __init__( self, appname: str | None = None, appauthor: str | None | Literal[False] = None, version: str | None = None, roaming: bool = False, multipath: bool = False, opinion: bool = True, ): """ Create a new platform directory. :param appname: See `appname`. :param appauthor: See `appauthor`. :param version: See `version`. :param roaming: See `roaming`. :param multipath: See `multipath`. :param opinion: See `opinion`. """ self.appname = appname #: The name of application. self.appauthor = appauthor """ The name of the app author or distributing body for this application. Typically, it is the owning company name. Defaults to `appname`. You may pass ``False`` to disable it. """ self.version = version """ An optional version path element to append to the path. You might want to use this if you want multiple versions of your app to be able to run independently. If used, this would typically be ``.``. """ self.roaming = roaming """ Whether to use the roaming appdata directory on Windows. That means that for users on a Windows network setup for roaming profiles, this user data will be synced on login (see `here `_). """ self.multipath = multipath """ An optional parameter only applicable to Unix/Linux which indicates that the entire list of data dirs should be returned. By default, the first item would only be returned. """ self.opinion = opinion #: A flag to indicating to use opinionated values. def _append_app_name_and_version(self, *base: str) -> str: params = list(base[1:]) if self.appname: params.append(self.appname) if self.version: params.append(self.version) return os.path.join(base[0], *params) @property @abstractmethod def user_data_dir(self) -> str: """:return: data directory tied to the user""" @property @abstractmethod def site_data_dir(self) -> str: """:return: data directory shared by users""" @property @abstractmethod def user_config_dir(self) -> str: """:return: config directory tied to the user""" @property @abstractmethod def site_config_dir(self) -> str: """:return: config directory shared by the users""" @property @abstractmethod def user_cache_dir(self) -> str: """:return: cache directory tied to the user""" @property @abstractmethod def user_state_dir(self) -> str: """:return: state directory tied to the user""" @property @abstractmethod def user_log_dir(self) -> str: """:return: log directory tied to the user""" @property @abstractmethod def user_documents_dir(self) -> str: """:return: documents directory tied to the user""" @property @abstractmethod def user_runtime_dir(self) -> str: """:return: runtime directory tied to the user""" @property def user_data_path(self) -> Path: """:return: data path tied to the user""" return Path(self.user_data_dir) @property def site_data_path(self) -> Path: """:return: data path shared by users""" return Path(self.site_data_dir) @property def user_config_path(self) -> Path: """:return: config path tied to the user""" return Path(self.user_config_dir) @property def site_config_path(self) -> Path: """:return: config path shared by the users""" return Path(self.site_config_dir) @property def user_cache_path(self) -> Path: """:return: cache path tied to the user""" return Path(self.user_cache_dir) @property def user_state_path(self) -> Path: """:return: state path tied to the user""" return Path(self.user_state_dir) @property def user_log_path(self) -> Path: """:return: log path tied to the user""" return Path(self.user_log_dir) @property def user_documents_path(self) -> Path: """:return: documents path tied to the user""" return Path(self.user_documents_dir) @property def user_runtime_path(self) -> Path: """:return: runtime path tied to the user""" return Path(self.user_runtime_dir) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/macos.py0000644000175100001710000000513700000000000020544 0ustar00runnerdockerfrom __future__ import annotations import os from .api import PlatformDirsABC class MacOS(PlatformDirsABC): """ Platform directories for the macOS operating system. Follows the guidance from `Apple documentation `_. Makes use of the `appname ` and `version `. """ @property def user_data_dir(self) -> str: """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``""" return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support/")) @property def site_data_dir(self) -> str: """:return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``""" return self._append_app_name_and_version("/Library/Application Support") @property def user_config_dir(self) -> str: """:return: config directory tied to the user, e.g. ``~/Library/Preferences/$appname/$version``""" return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences/")) @property def site_config_dir(self) -> str: """:return: config directory shared by the users, e.g. ``/Library/Preferences/$appname``""" return self._append_app_name_and_version("/Library/Preferences") @property def user_cache_dir(self) -> str: """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``""" return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches")) @property def user_state_dir(self) -> str: """:return: state directory tied to the user, same as `user_data_dir`""" return self.user_data_dir @property def user_log_dir(self) -> str: """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``""" return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) @property def user_documents_dir(self) -> str: """:return: documents directory tied to the user, e.g. ``~/Documents``""" return os.path.expanduser("~/Documents") @property def user_runtime_dir(self) -> str: """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``""" return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) __all__ = [ "MacOS", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/py.typed0000644000175100001710000000000000000000000020547 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/unix.py0000644000175100001710000001537600000000000020433 0ustar00runnerdockerfrom __future__ import annotations import os import sys from configparser import ConfigParser from pathlib import Path from .api import PlatformDirsABC if sys.platform.startswith("linux"): # pragma: no branch # no op check, only to please the type checker from os import getuid else: def getuid() -> int: raise RuntimeError("should only be used on Linux") class Unix(PlatformDirsABC): """ On Unix/Linux, we follow the `XDG Basedir Spec `_. The spec allows overriding directories with environment variables. The examples show are the default values, alongside the name of the environment variable that overrides them. Makes use of the `appname `, `version `, `multipath `, `opinion `. """ @property def user_data_dir(self) -> str: """ :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") return self._append_app_name_and_version(path) @property def site_data_dir(self) -> str: """ :return: data directories shared by users (if `multipath ` is enabled and ``XDG_DATA_DIR`` 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 path = os.environ.get("XDG_DATA_DIRS", "") if not path.strip(): path = f"/usr/local/share{os.pathsep}/usr/share" return self._with_multi_path(path) def _with_multi_path(self, path: str) -> str: path_list = path.split(os.pathsep) if not self.multipath: path_list = path_list[0:1] path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list] return os.pathsep.join(path_list) @property def user_config_dir(self) -> str: """ :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") return self._append_app_name_and_version(path) @property def site_config_dir(self) -> str: """ :return: config directories shared by users (if `multipath ` is enabled and ``XDG_DATA_DIR`` 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 path = os.environ.get("XDG_CONFIG_DIRS", "") if not path.strip(): path = "/etc/xdg" return self._with_multi_path(path) @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`` """ path = os.environ.get("XDG_CACHE_HOME", "") if not path.strip(): path = os.path.expanduser("~/.cache") return self._append_app_name_and_version(path) @property def user_state_dir(self) -> str: """ :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") return self._append_app_name_and_version(path) @property def user_log_dir(self) -> str: """ :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``log`` in it """ path = self.user_cache_dir if self.opinion: path = os.path.join(path, "log") return path @property def user_documents_dir(self) -> str: """ :return: documents directory tied to the user, e.g. ``~/Documents`` """ documents_dir = _get_user_dirs_folder("XDG_DOCUMENTS_DIR") if documents_dir is None: documents_dir = os.environ.get("XDG_DOCUMENTS_DIR", "").strip() if not documents_dir: documents_dir = os.path.expanduser("~/Documents") return documents_dir @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`` """ path = os.environ.get("XDG_RUNTIME_DIR", "") if not path.strip(): path = f"/run/user/{getuid()}" return self._append_app_name_and_version(path) @property def site_data_path(self) -> Path: """:return: data path shared by users. Only return first item, even if ``multipath`` is set to ``True``""" return self._first_item_as_path_if_multipath(self.site_data_dir) @property def site_config_path(self) -> Path: """:return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``""" return self._first_item_as_path_if_multipath(self.site_config_dir) def _first_item_as_path_if_multipath(self, directory: str) -> Path: if self.multipath: # If multipath is True, the first path is returned. directory = directory.split(os.pathsep)[0] return Path(directory) def _get_user_dirs_folder(key: str) -> str | None: """Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/""" user_dirs_config_path = os.path.join(Unix().user_config_dir, "user-dirs.dirs") if os.path.exists(user_dirs_config_path): parser = ConfigParser() with open(user_dirs_config_path) 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 path = path.replace("$HOME", os.path.expanduser("~")) return path return None __all__ = [ "Unix", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291648.0 platformdirs-2.5.1/src/platformdirs/version.py0000644000175100001710000000012000000000000021112 0ustar00runnerdocker""" Version information """ __version__ = "2.5.1" __version_info__ = (2, 5, 1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/src/platformdirs/windows.py0000644000175100001710000001444700000000000021140 0ustar00runnerdockerfrom __future__ import annotations import ctypes import os from functools import lru_cache from typing import Callable from .api import PlatformDirsABC class Windows(PlatformDirsABC): """`MSDN on where to store app data files `_. Makes use of the `appname `, `appauthor `, `version `, `roaming `, `opinion `.""" @property def user_data_dir(self) -> str: """ :return: data directory tied to the user, e.g. ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming) """ const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA" path = os.path.normpath(get_win_folder(const)) return self._append_parts(path) def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str: params = [] if self.appname: if self.appauthor is not False: author = self.appauthor or self.appname params.append(author) params.append(self.appname) if opinion_value is not None and self.opinion: params.append(opinion_value) if self.version: params.append(self.version) return os.path.join(path, *params) @property def site_data_dir(self) -> str: """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``""" path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) return self._append_parts(path) @property def user_config_dir(self) -> str: """:return: config directory tied to the user, same as `user_data_dir`""" 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 @property def user_cache_dir(self) -> str: """ :return: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g. ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version`` """ path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA")) return self._append_parts(path, opinion_value="Cache") @property def user_state_dir(self) -> str: """:return: state directory tied to the user, same as `user_data_dir`""" return self.user_data_dir @property def user_log_dir(self) -> str: """ :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it """ path = self.user_data_dir if self.opinion: path = os.path.join(path, "Logs") return path @property def user_documents_dir(self) -> str: """ :return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents`` """ return os.path.normpath(get_win_folder("CSIDL_PERSONAL")) @property def user_runtime_dir(self) -> str: """ :return: runtime directory tied to the user, e.g. ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname`` """ path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) return self._append_parts(path) def get_win_folder_from_env_vars(csidl_name: str) -> str: """Get folder from environment variables.""" if csidl_name == "CSIDL_PERSONAL": # does not have an environment name return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") env_var_name = { "CSIDL_APPDATA": "APPDATA", "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE", "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA", }.get(csidl_name) if env_var_name is None: raise ValueError(f"Unknown CSIDL name: {csidl_name}") result = os.environ.get(env_var_name) if result is None: raise ValueError(f"Unset environment variable: {env_var_name}") return result def get_win_folder_from_registry(csidl_name: str) -> str: """Get folder from the registry. This is a fallback technique at best. I'm not sure if using the registry for this guarantees us the correct answer for all CSIDL_* names. """ shell_folder_name = { "CSIDL_APPDATA": "AppData", "CSIDL_COMMON_APPDATA": "Common AppData", "CSIDL_LOCAL_APPDATA": "Local AppData", "CSIDL_PERSONAL": "Personal", }.get(csidl_name) if shell_folder_name is None: raise ValueError(f"Unknown CSIDL name: {csidl_name}") import winreg key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") directory, _ = winreg.QueryValueEx(key, shell_folder_name) return str(directory) def get_win_folder_via_ctypes(csidl_name: str) -> str: """Get folder with ctypes.""" csidl_const = { "CSIDL_APPDATA": 26, "CSIDL_COMMON_APPDATA": 35, "CSIDL_LOCAL_APPDATA": 28, "CSIDL_PERSONAL": 5, }.get(csidl_name) if csidl_const is None: raise ValueError(f"Unknown CSIDL name: {csidl_name}") 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) # Downgrade to short path name if it has highbit chars. if any(ord(c) > 255 for c in buf): buf2 = ctypes.create_unicode_buffer(1024) if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): buf = buf2 return buf.value def _pick_get_win_folder() -> Callable[[str], str]: if hasattr(ctypes, "windll"): return get_win_folder_via_ctypes try: import winreg # noqa: F401 except ImportError: return get_win_folder_from_env_vars else: return get_win_folder_from_registry get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder()) __all__ = [ "Windows", ] ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1645291648.78334 platformdirs-2.5.1/src/platformdirs.egg-info/0000755000175100001710000000000000000000000020554 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291648.0 platformdirs-2.5.1/src/platformdirs.egg-info/PKG-INFO0000644000175100001710000002101400000000000021647 0ustar00runnerdockerMetadata-Version: 2.1 Name: platformdirs Version: 2.5.1 Summary: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". Home-page: https://github.com/platformdirs/platformdirs Maintainer: Bernát Gábor, Julian Berman, Ofek Lev, Ronny Pfannschmidt Maintainer-email: gaborjbernat@gmail.com, Julian@GrayVines.com, oss@ofek.dev, opensource@ronnypfannschmidt.de License: MIT Project-URL: Source, https://github.com/platformdirs/platformdirs Project-URL: Tracker, https://github.com/platformdirs/platformdirs/issues Project-URL: Documentation, https://platformdirs.readthedocs.io/ Keywords: application directory log cache user Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.7 Description-Content-Type: text/x-rst Provides-Extra: docs Provides-Extra: test License-File: LICENSE.txt The problem =========== .. image:: https://github.com/platformdirs/platformdirs/workflows/Test/badge.svg :target: https://github.com/platformdirs/platformdirs/actions?query=workflow%3ATest 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/ If on Windows (at least English Win XP) that should be:: C:\Documents and Settings\\Application Data\Local Settings\\ or possibly:: C:\Documents and Settings\\Application Data\\ for `roaming profiles `_ but that is another story. On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: ~/.local/share/ .. _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`` module 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 runtime dir (``user_runtime_dir``) And also: - Is a single module so other Python packages can vendor their own private copy. - 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' >>> site_data_dir(appname, appauthor) '/Library/Application Support/SuperApp' >>> user_cache_dir(appname, appauthor) '/Users/trentm/Library/Caches/SuperApp' >>> user_log_dir(appname, appauthor) '/Users/trentm/Library/Logs/SuperApp' >>> user_documents_dir() '/Users/trentm/Documents' >>> user_runtime_dir(appname, appauthor) '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' On Windows 7: .. 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_cache_dir(appname, appauthor) 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' >>> user_log_dir(appname, appauthor) 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' >>> user_documents_dir() 'C:\\Users\\trentm\\Documents' >>> 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' >>> site_data_dir(appname, appauthor) '/usr/local/share/SuperApp' >>> site_data_dir(appname, appauthor, multipath=True) '/usr/local/share/SuperApp:/usr/share/SuperApp' >>> user_cache_dir(appname, appauthor) '/home/trentm/.cache/SuperApp' >>> user_log_dir(appname, appauthor) '/home/trentm/.cache/SuperApp/log' >>> user_config_dir(appname) '/home/trentm/.config/SuperApp' >>> user_documents_dir() '/home/trentm/Documents' >>> user_runtime_dir(appname, appauthor) '/run/user/{os.getuid()}/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' On Android:: >>> from platformdirs import * >>> appname = "SuperApp" >>> appauthor = "Acme" >>> user_data_dir(appname, appauthor) '/data/data/com.termux/files/SuperApp' >>> user_cache_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp' >>> user_log_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp/log' >>> user_config_dir(appname) '/data/data/com.termux/shared_prefs/SuperApp' >>> user_documents_dir() '/storage/emulated/0/Documents' >>> user_runtime_dir(appname, appauthor) '/data/data/com.termux/cache/SuperApp/tmp' ``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.site_data_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_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.site_data_dir '/Library/Application Support/SuperApp/1.0' >>> dirs.user_cache_dir '/Users/trentm/Library/Caches/SuperApp/1.0' >>> dirs.user_log_dir '/Users/trentm/Library/Logs/SuperApp/1.0' >>> dirs.user_documents_dir '/Users/trentm/Documents' >>> 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 `_ 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 `_, this fork was created. Contributions are most welcome. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291648.0 platformdirs-2.5.1/src/platformdirs.egg-info/SOURCES.txt0000644000175100001710000000154200000000000022442 0ustar00runnerdocker.gitignore .pre-commit-config.yaml .readthedocs.yml CHANGES.rst LICENSE.txt README.rst codecov.yml pyproject.toml setup.cfg setup.py tox.ini whitelist.txt .github/CODEOWNERS .github/workflows/check.yml docs/api.rst docs/changelog.rst docs/conf.py docs/index.rst src/platformdirs/__init__.py src/platformdirs/__main__.py src/platformdirs/android.py src/platformdirs/api.py src/platformdirs/macos.py src/platformdirs/py.typed src/platformdirs/unix.py src/platformdirs/version.py src/platformdirs/windows.py src/platformdirs.egg-info/PKG-INFO src/platformdirs.egg-info/SOURCES.txt src/platformdirs.egg-info/dependency_links.txt src/platformdirs.egg-info/requires.txt src/platformdirs.egg-info/top_level.txt src/platformdirs.egg-info/zip-safe tests/conftest.py tests/test_android.py tests/test_api.py tests/test_comp_with_appdirs.py tests/test_main.py tests/test_unix.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291648.0 platformdirs-2.5.1/src/platformdirs.egg-info/dependency_links.txt0000644000175100001710000000000100000000000024622 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291648.0 platformdirs-2.5.1/src/platformdirs.egg-info/requires.txt0000644000175100001710000000022700000000000023155 0ustar00runnerdocker [docs] Sphinx>=4 furo>=2021.7.5b38 proselint>=0.10.2 sphinx-autodoc-typehints>=1.12 [test] appdirs==1.4.4 pytest>=6 pytest-cov>=2.7 pytest-mock>=3.6 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291648.0 platformdirs-2.5.1/src/platformdirs.egg-info/top_level.txt0000644000175100001710000000001500000000000023302 0ustar00runnerdockerplatformdirs ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291647.0 platformdirs-2.5.1/src/platformdirs.egg-info/zip-safe0000644000175100001710000000000100000000000022204 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1645291648.78334 platformdirs-2.5.1/tests/0000755000175100001710000000000000000000000014727 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tests/conftest.py0000644000175100001710000000124100000000000017124 0ustar00runnerdockerfrom __future__ import annotations from typing import cast import pytest from _pytest.fixtures import SubRequest PROPS = ( "user_data_dir", "user_config_dir", "user_cache_dir", "user_state_dir", "user_log_dir", "user_documents_dir", "user_runtime_dir", "site_data_dir", "site_config_dir", ) @pytest.fixture(params=PROPS) def func(request: SubRequest) -> str: return cast(str, request.param) @pytest.fixture(params=PROPS) def func_path(request: SubRequest) -> str: prop = cast(str, request.param) prop = prop.replace("_dir", "_path") return prop @pytest.fixture() def props() -> tuple[str, ...]: return PROPS ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tests/test_android.py0000644000175100001710000000774600000000000017776 0ustar00runnerdockerfrom __future__ import annotations import sys from typing import Any from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch from pytest_mock import MockerFixture from platformdirs.android import Android @pytest.mark.parametrize( "params", [ {}, {"appname": "foo"}, {"appname": "foo", "appauthor": "bar"}, {"appname": "foo", "appauthor": "bar", "version": "v1.0"}, {"appname": "foo", "appauthor": "bar", "version": "v1.0", "opinion": False}, ], ids=[ "no_args", "app_name", "app_name_with_app_author", "app_name_author_version", "app_name_author_version_false_opinion", ], ) def test_android(mocker: MockerFixture, params: dict[str, Any], func: str) -> None: mocker.patch("platformdirs.android._android_folder", return_value="/data/data/com.example", autospec=True) mocker.patch("platformdirs.android.os.path.join", lambda *args: "/".join(args)) result = getattr(Android(**params), func) suffix_elements = [] if "appname" in params: suffix_elements.append(params["appname"]) if "version" in params: suffix_elements.append(params["version"]) if suffix_elements: suffix_elements.insert(0, "") suffix = "/".join(suffix_elements) expected_map = { "user_data_dir": f"/data/data/com.example/files{suffix}", "site_data_dir": f"/data/data/com.example/files{suffix}", "user_config_dir": f"/data/data/com.example/shared_prefs{suffix}", "site_config_dir": f"/data/data/com.example/shared_prefs{suffix}", "user_cache_dir": f"/data/data/com.example/cache{suffix}", "user_state_dir": f"/data/data/com.example/files{suffix}", "user_log_dir": f"/data/data/com.example/cache{suffix}{'' if params.get('opinion', True) is False else '/log'}", "user_documents_dir": "/storage/emulated/0/Documents", "user_runtime_dir": f"/data/data/com.example/cache{suffix}{'' if not params.get('opinion', True) else '/tmp'}", } expected = expected_map[func] assert result == expected def test_android_folder_from_jnius(mocker: MockerFixture) -> None: from platformdirs import PlatformDirs from platformdirs.android import _android_folder _android_folder.cache_clear() if PlatformDirs is Android: import jnius # pragma: no cover autoclass = mocker.spy(jnius, "autoclass") # pragma: no cover else: parent = MagicMock(return_value=MagicMock(getAbsolutePath=MagicMock(return_value="/A"))) # pragma: no cover context = MagicMock(getFilesDir=MagicMock(return_value=MagicMock(getParentFile=parent))) # pragma: no cover autoclass = MagicMock(return_value=context) # pragma: no cover mocker.patch.dict(sys.modules, {"jnius": MagicMock(autoclass=autoclass)}) # pragma: no cover result = _android_folder() assert result == "/A" assert autoclass.call_count == 1 assert autoclass.call_args[0] == ("android.content.Context",) assert _android_folder() is result assert autoclass.call_count == 1 @pytest.mark.parametrize( "path", [ "/data/user/1/a/files", "/data/data/a/files", ], ) def test_android_folder_from_sys_path(mocker: MockerFixture, path: str, monkeypatch: MonkeyPatch) -> None: mocker.patch.dict(sys.modules, {"jnius": MagicMock(autoclass=MagicMock(side_effect=ModuleNotFoundError))}) from platformdirs.android import _android_folder _android_folder.cache_clear() monkeypatch.setattr(sys, "path", ["/A", "/B", path]) result = _android_folder() assert result == path[: -len("/files")] def test_android_folder_not_found(mocker: MockerFixture, monkeypatch: MonkeyPatch) -> None: mocker.patch.dict(sys.modules, {"jnius": MagicMock(autoclass=MagicMock(side_effect=ModuleNotFoundError))}) from platformdirs.android import _android_folder _android_folder.cache_clear() monkeypatch.setattr(sys, "path", []) assert _android_folder() is None ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tests/test_api.py0000644000175100001710000000464500000000000017122 0ustar00runnerdockerfrom __future__ import annotations import inspect import sys from pathlib import Path import pytest from _pytest.monkeypatch import MonkeyPatch import platformdirs from platformdirs.android import Android def test_package_metadata() -> None: assert hasattr(platformdirs, "__version__") assert hasattr(platformdirs, "__version_info__") def test_method_result_is_str(func: str) -> None: method = getattr(platformdirs, func) result = method() assert isinstance(result, str) def test_property_result_is_str(func: str) -> None: dirs = platformdirs.PlatformDirs("MyApp", "MyCompany", version="1.0") result = getattr(dirs, func) assert isinstance(result, str) def test_method_result_is_path(func_path: str) -> None: method = getattr(platformdirs, func_path) result = method() assert isinstance(result, Path) def test_property_result_is_path(func_path: str) -> None: dirs = platformdirs.PlatformDirs("MyApp", "MyCompany", version="1.0") result = getattr(dirs, func_path) assert isinstance(result, Path) def test_function_interface_is_in_sync(func: str) -> None: function_dir = getattr(platformdirs, func) function_path = getattr(platformdirs, func.replace("_dir", "_path")) assert inspect.isfunction(function_dir) assert inspect.isfunction(function_path) function_dir_signature = inspect.Signature.from_callable(function_dir) function_path_signature = inspect.Signature.from_callable(function_path) assert function_dir_signature.parameters == function_path_signature.parameters @pytest.mark.parametrize("root", ["A", "/system", None]) @pytest.mark.parametrize("data", ["D", "/data", None]) @pytest.mark.parametrize("path", ["/data/data/a/files", "/C"]) def test_android_active(monkeypatch: MonkeyPatch, root: str | None, data: str | None, path: str) -> None: for env_var, value in {"ANDROID_DATA": data, "ANDROID_ROOT": root}.items(): if value is None: monkeypatch.delenv(env_var, raising=False) else: monkeypatch.setenv(env_var, value) from platformdirs.android import _android_folder _android_folder.cache_clear() monkeypatch.setattr(sys, "path", ["/A", "/B", path]) expected = root == "/system" and data == "/data" and _android_folder() is not None if expected: assert platformdirs._set_platform_dir_class() is Android else: assert platformdirs._set_platform_dir_class() is not Android ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tests/test_comp_with_appdirs.py0000644000175100001710000000422100000000000022052 0ustar00runnerdockerfrom __future__ import annotations import sys from inspect import getmembers, isfunction from typing import Any import appdirs import pytest import platformdirs def test_has_backward_compatible_class() -> None: from platformdirs import AppDirs assert AppDirs is platformdirs.PlatformDirs def test_has_all_functions() -> None: # Get all public function names from appdirs appdirs_function_names = [f[0] for f in getmembers(appdirs, isfunction) if not f[0].startswith("_")] # Exception will be raised if any appdirs functions aren't in platformdirs. for function_name in appdirs_function_names: getattr(platformdirs, function_name) def test_has_all_properties() -> None: # Get names of all the properties of appdirs.AppDirs appdirs_property_names = [p[0] for p in getmembers(appdirs.AppDirs, lambda member: isinstance(member, property))] # Exception will be raised if any appdirs.AppDirs properties aren't in platformdirs.AppDirs for property_name in appdirs_property_names: getattr(platformdirs.AppDirs, property_name) @pytest.mark.parametrize( "params", [ {}, {"appname": "foo"}, {"appname": "foo", "appauthor": "bar"}, {"appname": "foo", "appauthor": "bar", "version": "v1.0"}, ], ids=[ "no_args", "app_name", "app_name_with_app_author", "app_name_author_version", ], ) def test_compatibility(params: dict[str, Any], func: str) -> None: # Only test functions that are part of appdirs if getattr(appdirs, func, None) is None: pytest.skip(f"`{func}` does not exist in `appdirs`") if sys.platform == "darwin": msg = { # pragma: no cover "user_log_dir": "without appname produces NoneType error", "site_config_dir": "ignores the version argument", "user_config_dir": "uses Library/Preferences instead Application Support", } if func in msg: # pragma: no cover pytest.skip(f"`appdirs.{func}` {msg[func]} on macOS") # pragma: no cover new = getattr(platformdirs, func)(*params) old = getattr(appdirs, func)(*params) assert new == old ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tests/test_main.py0000644000175100001710000000075000000000000017266 0ustar00runnerdockerfrom __future__ import annotations import sys from subprocess import check_output from platformdirs import __version__ from platformdirs.__main__ import PROPS def test_props_same_as_test(props: tuple[str, ...]) -> None: assert PROPS == props def test_run_as_module() -> None: out = check_output([sys.executable, "-m", "platformdirs"], universal_newlines=True) assert out.startswith(f"-- platformdirs {__version__} --") for prop in PROPS: assert prop in out ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tests/test_unix.py0000644000175100001710000001005700000000000017326 0ustar00runnerdockerfrom __future__ import annotations import importlib import os import sys import typing import pytest from _pytest.monkeypatch import MonkeyPatch from pytest_mock import MockerFixture from platformdirs.unix import Unix def test_user_documents_dir(mocker: MockerFixture) -> None: example_path = "/home/example/ExampleDocumentsFolder" mock = mocker.patch("platformdirs.unix._get_user_dirs_folder") mock.return_value = example_path assert Unix().user_documents_dir == example_path def test_user_documents_dir_env_var(mocker: MockerFixture) -> None: # Mock documents dir not being in user-dirs.dirs file mock = mocker.patch("platformdirs.unix._get_user_dirs_folder") mock.return_value = None example_path = "/home/example/ExampleDocumentsFolder" mocker.patch.dict(os.environ, {"XDG_DOCUMENTS_DIR": example_path}) assert Unix().user_documents_dir == example_path def test_user_documents_dir_default(mocker: MockerFixture) -> None: # Mock documents dir not being in user-dirs.dirs file mock = mocker.patch("platformdirs.unix._get_user_dirs_folder") mock.return_value = None # Mock no XDG_DOCUMENTS_DIR env variable being set mocker.patch.dict(os.environ, {"XDG_DOCUMENTS_DIR": ""}) # Mock home directory mocker.patch.dict(os.environ, {"HOME": "/home/example"}) # Mock home directory for running the test on Windows mocker.patch.dict(os.environ, {"USERPROFILE": "/home/example"}) assert Unix().user_documents_dir == "/home/example/Documents" class XDGVariable(typing.NamedTuple): name: str default_value: str def _func_to_path(func: str) -> XDGVariable | None: mapping = { "user_data_dir": XDGVariable("XDG_DATA_HOME", "~/.local/share"), "site_data_dir": XDGVariable("XDG_DATA_DIRS", f"/usr/local/share{os.pathsep}/usr/share"), "user_config_dir": XDGVariable("XDG_CONFIG_HOME", "~/.config"), "site_config_dir": XDGVariable("XDG_CONFIG_DIRS", "/etc/xdg"), "user_cache_dir": XDGVariable("XDG_CACHE_HOME", "~/.cache"), "user_state_dir": XDGVariable("XDG_STATE_HOME", "~/.local/state"), "user_log_dir": XDGVariable("XDG_CACHE_HOME", "~/.cache"), "user_runtime_dir": XDGVariable("XDG_RUNTIME_DIR", "/run/user/1234"), } return mapping.get(func) @pytest.fixture() def dirs_instance() -> Unix: return Unix(multipath=True, opinion=False) @pytest.fixture() def _getuid(mocker: MockerFixture) -> None: mocker.patch("platformdirs.unix.getuid", return_value=1234) @pytest.mark.usefixtures("_getuid") def test_xdg_variable_not_set(monkeypatch: MonkeyPatch, dirs_instance: Unix, func: str) -> None: xdg_variable = _func_to_path(func) if xdg_variable is None: return monkeypatch.delenv(xdg_variable.name, raising=False) result = getattr(dirs_instance, func) assert result == os.path.expanduser(xdg_variable.default_value) @pytest.mark.usefixtures("_getuid") def test_xdg_variable_empty_value(monkeypatch: MonkeyPatch, dirs_instance: Unix, func: str) -> None: xdg_variable = _func_to_path(func) if xdg_variable is None: return monkeypatch.setenv(xdg_variable.name, "") result = getattr(dirs_instance, func) assert result == os.path.expanduser(xdg_variable.default_value) @pytest.mark.usefixtures("_getuid") def test_xdg_variable_custom_value(monkeypatch: MonkeyPatch, dirs_instance: Unix, func: str) -> None: xdg_variable = _func_to_path(func) if xdg_variable is None: return monkeypatch.setenv(xdg_variable.name, "/tmp/custom-dir") result = getattr(dirs_instance, func) assert result == "/tmp/custom-dir" def test_platform_non_linux(monkeypatch: MonkeyPatch) -> None: from platformdirs import unix try: with monkeypatch.context() as context: context.setattr(sys, "platform", "magic") monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False) importlib.reload(unix) with pytest.raises(RuntimeError, match="should only be used on Linux"): unix.Unix().user_runtime_dir finally: importlib.reload(unix) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/tox.ini0000644000175100001710000000543500000000000015107 0ustar00runnerdocker[tox] envlist = fix py310 py39 py38 py37 pypy3 type coverage readme isolated_build = true skip_missing_interpreters = true minversion = 3.21 [testenv] description = run the unit tests with pytest under {basepython} passenv = ANDROID_DATA ANDROID_ROOT setenv = COVERAGE_FILE = {toxworkdir}/.coverage.{envname} COVERAGE_PROCESS_START = {toxinidir}/setup.cfg _COVERAGE_SRC = {envsitepackagesdir}/platformdirs extras = test commands = pytest {tty:--color=yes} {posargs: \ --junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}platformdirs \ --cov {toxinidir}{/}tests \ --cov-config=setup.cfg --no-cov-on-fail --cov-report term-missing:skip-covered --cov-context=test \ --cov-report html:{envtmpdir}{/}htmlcov --cov-report xml:{toxworkdir}{/}coverage.{envname}.xml \ tests} package = wheel wheel_build_env = .pkg [testenv:fix] description = run static analysis and style check using flake8 passenv = HOMEPATH PROGRAMDATA skip_install = true deps = pre-commit>=2 commands = pre-commit run --all-files --show-diff-on-failure [testenv:type] description = run type check on code base setenv = {tty:MYPY_FORCE_COLOR = 1} deps = mypy==0.931 commands = mypy --strict src mypy --strict tests [testenv:coverage] description = combine coverage files and generate diff (against DIFF_AGAINST defaulting to origin/main) passenv = DIFF_AGAINST setenv = COVERAGE_FILE = {toxworkdir}/.coverage skip_install = true deps = covdefaults>=2.1 coverage>=6.2 diff-cover>=6.4 extras = parallel_show_output = true commands = coverage combine coverage report --skip-covered --show-missing coverage xml -o {toxworkdir}/coverage.xml coverage html -d {toxworkdir}/htmlcov diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}/coverage.xml depends = py310 py39 py38 py37 pypy3 [testenv:readme] description = check that the long description is valid passenv = * skip_install = true deps = build>=0.5 twine>=3.2 changedir = {toxinidir} commands = python -m build -o {envtmpdir} . twine check {envtmpdir}/* [testenv:dev] description = generate a DEV environment usedevelop = true extras = test commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' [testenv:docs] extras = docs commands = python -c 'import glob; import subprocess; subprocess.call(["proselint"] + glob.glob("docs/*.rst"))' sphinx-build -d "{envtmpdir}/doctree" docs "{toxworkdir}/docs_out" --color -b html {posargs} python -c 'import pathlib; print("documentation available under \{0\}".format((pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html").as_uri()))' [pytest] junit_family = xunit2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645291641.0 platformdirs-2.5.1/whitelist.txt0000644000175100001710000000045300000000000016344 0ustar00runnerdockerappauthor appdirs appname autoclass autodoc buf buf2 const csidl delenv dirs func getmembers getuid highbit hkey intersphinx isfunction jnius kernel32 lru macos multipath normpath ord param params pathlib pathsep platformdirs pyjnius runtime setenv shell32 typehints unittest usefixtures winreg xdg