From 2e225082877dd83329594933a69301b8c5b9246e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sun, 26 Dec 2021 17:12:46 +0000 Subject: [PATCH] Bump linters, drop codecov and python 3.6 (#52) --- .github/workflows/check.yml | 123 ++++++++++++++++++++------------ .pre-commit-config.yaml | 48 ++++++++----- CHANGES.rst | 4 ++ README.rst | 2 +- docs/conf.py | 2 + setup.cfg | 7 +- setup.py | 2 + src/platformdirs/__init__.py | 104 ++++++++++++++------------- src/platformdirs/__main__.py | 2 + src/platformdirs/android.py | 12 ++-- src/platformdirs/api.py | 9 +-- src/platformdirs/macos.py | 2 + src/platformdirs/unix.py | 5 +- src/platformdirs/windows.py | 6 +- tests/conftest.py | 6 +- tests/test_android.py | 16 +++-- tests/test_api.py | 5 +- tests/test_comp_with_appdirs.py | 6 +- tests/test_main.py | 5 +- tests/test_unix.py | 4 +- tox.ini | 36 ++++++++-- 21 files changed, 254 insertions(+), 152 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ad8fedc..5f3577f 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,13 +1,7 @@ name: check on: push: - branches: - - main - tags: - - "*" pull_request: - branches: - - main schedule: - cron: "0 8 * * *" @@ -16,53 +10,56 @@ concurrency: cancel-in-progress: true jobs: - pre_commit: + lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.0 + - uses: pre-commit/action@v2.0.3 test: name: test ${{ matrix.py }} - ${{ matrix.os }} - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: - - Ubuntu - - Windows - - MacOs py: - - "3.10.0-rc.1" + - "3.10" + - "pypy-3.7-v7.3.7" # ahead to start it earlier because takes longer - "3.9" - "3.8" - "3.7" - - "3.6" - - "pypy-3.6" - - "pypy-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.9" + python-version: "3.10" + - name: Install tox + run: python -m pip install tox - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Install tox - run: python -m pip install tox - name: Setup python for test ${{ matrix.py }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.py }} - name: Pick environment to run run: | - import json + import codecs import os - import subprocess - major, minor, impl = json.loads(subprocess.check_output(["python", "-c", "import json; import sys; import platform; print(json.dumps([sys.version_info[0], sys.version_info[1], platform.python_implementation()]));"], universal_newlines=True)) - with open(os.environ['GITHUB_ENV'], 'a') as file_handler: - file_handler.write('TOXENV=' + ("py" if impl == "CPython" else "pypy") + ("{}{}".format(major, minor) if impl == "CPython" else "3") + "\n") + 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 @@ -70,63 +67,95 @@ jobs: run: tox --skip-pkg-install env: PYTEST_ADDOPTS: "-vv --durations=20" + CI_RUN: "yes" - name: Rename coverage report file - run: | - import os; os.rename('.tox/coverage.{}.xml'.format(os.environ['TOXENV']), '.tox/coverage.xml') + run: import os; import sys; os.rename(f".tox/.coverage.{os.environ['TOXENV']}", f".tox/.coverage.{os.environ['TOXENV']}-{sys.platform}") shell: python - - uses: codecov/codecov-action@v1 + - 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: - file: ./.tox/coverage.xml - flags: tests - name: ${{ matrix.py }} - ${{ matrix.os }} + 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: check ${{ matrix.tox_env }} - ${{ matrix.os }} - runs-on: ${{ matrix.os }}-latest + name: ${{ matrix.tox_env }} - ${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: - - Windows - - Ubuntu + - ubuntu-latest + - windows-latest tox_env: - dev - - pkg_check - type - docs + - readme exclude: - - { os: windows, tox_env: pkg_check } + - { os: windows-latest, tox_env: readme } steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Setup Python 3.9 + - name: Setup Python "3.10" uses: actions/setup-python@v2 with: - python-version: "3.9" + python-version: "3.10" - name: Install tox run: python -m pip install tox - - name: Run check for ${{ matrix.tox_env }} - run: tox -e ${{ matrix.tox_env }} - env: - UPGRADE_ADVISORY: "yes" + - 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, test, pre_commit ] + needs: [ check, coverage, lint ] runs-on: ubuntu-latest steps: - name: Setup python to build package uses: actions/setup-python@v2 with: - python-version: "3.9" + python-version: "3.10" - name: Install build run: python -m pip install build - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Build package - run: pyproject-build . -o dist + - 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: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9fdc29..83caccc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,44 +1,58 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + 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.25.0 + rev: v2.29.1 hooks: - id: pyupgrade + args: [ "--py36-plus" ] - repo: https://github.com/PyCQA/isort - rev: 5.9.3 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/psf/black - rev: 21.8b0 + rev: 21.12b0 hooks: - id: black - args: [--safe] - - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.17.0 + args: [ --safe ] + - repo: https://github.com/asottile/blacken-docs + rev: v1.12.0 hooks: - - id: setup-cfg-fmt - args: ["--max-py-version", "3.10"] + - id: blacken-docs + additional_dependencies: [ black==21.12b0 ] + - 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.1" 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: "3.9.2" + rev: 4.0.1 hooks: - id: flake8 - additional_dependencies: [ - "flake8-bugbear == 21.4.3", - "flake8-unused-arguments == 0.0.6", - "flake8-comprehensions == 3.5.0", - "flake8-spellcheck == 0.24.0", - "flake8-pytest-style == 1.5.0", - ] + additional_dependencies: + - flake8-bugbear==21.11.29 + - flake8-comprehensions==3.7 + - 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 diff --git a/CHANGES.rst b/CHANGES.rst index 19f2924..cc1111f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,10 @@ platformdirs Changelog ====================== +platformdirs 2.3.1 +------------------ +- Drop python 3.6 support + platformdirs 2.3.0 ------------------ - Add ``user_runtime_dir`` and its path-returning equivalent (#37) diff --git a/README.rst b/README.rst index 0ac81e3..25e1735 100644 --- a/README.rst +++ b/README.rst @@ -117,7 +117,7 @@ On Linux: '/run/user/{os.getuid()}/SuperApp' >>> site_config_dir(appname) '/etc/xdg/SuperApp' - >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc' + >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" >>> site_config_dir(appname, multipath=True) '/etc/SuperApp:/usr/local/etc/SuperApp' diff --git a/docs/conf.py b/docs/conf.py index 5d01d20..0810918 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from platformdirs.version import __version__ author = "The platformdirs team" diff --git a/setup.cfg b/setup.cfg index c56ca83..1a4ab63 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -32,7 +31,7 @@ project_urls = [options] packages = find: -python_requires = >=3.6 +python_requires = >=3.7 package_dir = =src zip_safe = True @@ -76,6 +75,10 @@ source = .tox\*\Lib\site-packages\ */src *\src +other = + . + */platformdirs + *\platformdirs [coverage:run] branch = true diff --git a/setup.py b/setup.py index 6068493..a03590f 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import setup setup() diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py index 26032a3..46b1157 100644 --- a/src/platformdirs/__init__.py +++ b/src/platformdirs/__init__.py @@ -2,11 +2,13 @@ Utilities for determining application-specific dirs. See for details and usage. """ +from __future__ import annotations + import importlib import os import sys from pathlib import Path -from typing import TYPE_CHECKING, Optional, Type, Union +from typing import TYPE_CHECKING if TYPE_CHECKING: from typing_extensions import Literal # pragma: no cover @@ -15,7 +17,7 @@ from .version import __version__, __version_info__ -def _set_platform_dir_class() -> Type[PlatformDirsABC]: +def _set_platform_dir_class() -> type[PlatformDirsABC]: if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": module, name = "platformdirs.android", "Android" elif sys.platform == "win32": @@ -24,7 +26,7 @@ def _set_platform_dir_class() -> Type[PlatformDirsABC]: module, name = "platformdirs.macos", "MacOS" else: module, name = "platformdirs.unix", "Unix" - result: Type[PlatformDirsABC] = getattr(importlib.import_module(module), name) + result: type[PlatformDirsABC] = getattr(importlib.import_module(module), name) return result @@ -33,9 +35,9 @@ def _set_platform_dir_class() -> Type[PlatformDirsABC]: def user_data_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, ) -> str: """ @@ -49,9 +51,9 @@ def user_data_dir( def site_data_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, multipath: bool = False, ) -> str: """ @@ -65,9 +67,9 @@ def site_data_dir( def user_config_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, ) -> str: """ @@ -81,9 +83,9 @@ def user_config_dir( def site_config_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, multipath: bool = False, ) -> str: """ @@ -97,9 +99,9 @@ def site_config_dir( def user_cache_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, opinion: bool = True, ) -> str: """ @@ -113,9 +115,9 @@ def user_cache_dir( def user_state_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, ) -> str: """ @@ -129,9 +131,9 @@ def user_state_dir( def user_log_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, opinion: bool = True, ) -> str: """ @@ -152,9 +154,9 @@ def user_documents_dir() -> str: def user_runtime_dir( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, opinion: bool = True, ) -> str: """ @@ -168,9 +170,9 @@ def user_runtime_dir( def user_data_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, ) -> Path: """ @@ -184,9 +186,9 @@ def user_data_path( def site_data_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, multipath: bool = False, ) -> Path: """ @@ -200,9 +202,9 @@ def site_data_path( def user_config_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, ) -> Path: """ @@ -216,9 +218,9 @@ def user_config_path( def site_config_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, multipath: bool = False, ) -> Path: """ @@ -232,9 +234,9 @@ def site_config_path( def user_cache_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, opinion: bool = True, ) -> Path: """ @@ -248,9 +250,9 @@ def user_cache_path( def user_state_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, ) -> Path: """ @@ -264,9 +266,9 @@ def user_state_path( def user_log_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, opinion: bool = True, ) -> Path: """ @@ -287,9 +289,9 @@ def user_documents_path() -> Path: def user_runtime_path( - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, opinion: bool = True, ) -> Path: """ diff --git a/src/platformdirs/__main__.py b/src/platformdirs/__main__.py index ad22937..0fc1edd 100644 --- a/src/platformdirs/__main__.py +++ b/src/platformdirs/__main__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from platformdirs import PlatformDirs, __version__ PROPS = ( diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index 7599045..a684058 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import re import sys @@ -80,9 +82,9 @@ def _android_folder() -> str: """:return: base folder for the Android OS""" try: # First try to get path to android app via pyjnius - from jnius import autoclass # noqa: SC200 + from jnius import autoclass - Context = autoclass("android.content.Context") # noqa: SC200 + Context = autoclass("android.content.Context") # noqa: N806 result: str = Context.getFilesDir().getParentFile().getAbsolutePath() except Exception: # if fails find an android folder looking path on the sys.path @@ -101,10 +103,10 @@ def _android_documents_folder() -> str: """:return: documents folder for the Android OS""" # Get directories with pyjnius try: - from jnius import autoclass # noqa: SC200 + from jnius import autoclass - Context = autoclass("android.content.Context") # noqa: SC200 - Environment = autoclass("android.os.Environment") + 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" diff --git a/src/platformdirs/api.py b/src/platformdirs/api.py index ebd96af..6f6e2c2 100644 --- a/src/platformdirs/api.py +++ b/src/platformdirs/api.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import os import sys from abc import ABC, abstractmethod from pathlib import Path -from typing import Optional, Union if sys.version_info >= (3, 8): # pragma: no branch from typing import Literal # pragma: no cover @@ -15,9 +16,9 @@ class PlatformDirsABC(ABC): def __init__( self, - appname: Optional[str] = None, - appauthor: Union[str, None, "Literal[False]"] = None, - version: Optional[str] = None, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, roaming: bool = False, multipath: bool = False, opinion: bool = True, diff --git a/src/platformdirs/macos.py b/src/platformdirs/macos.py index fffd1a7..a01337c 100644 --- a/src/platformdirs/macos.py +++ b/src/platformdirs/macos.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from .api import PlatformDirsABC diff --git a/src/platformdirs/unix.py b/src/platformdirs/unix.py index 688b279..2fbd4d4 100644 --- a/src/platformdirs/unix.py +++ b/src/platformdirs/unix.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import os import sys from configparser import ConfigParser from pathlib import Path -from typing import Optional from .api import PlatformDirsABC @@ -154,7 +155,7 @@ def _first_item_as_path_if_multipath(self, directory: str) -> Path: return Path(directory) -def _get_user_dirs_folder(key: str) -> Optional[str]: +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): diff --git a/src/platformdirs/windows.py b/src/platformdirs/windows.py index c75cf99..ef972bd 100644 --- a/src/platformdirs/windows.py +++ b/src/platformdirs/windows.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import ctypes import os from functools import lru_cache -from typing import Callable, Optional +from typing import Callable from .api import PlatformDirsABC @@ -27,7 +29,7 @@ def user_data_dir(self) -> str: path = os.path.normpath(get_win_folder(const)) return self._append_parts(path) - def _append_parts(self, path: str, *, opinion_value: Optional[str] = None) -> str: + def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str: params = [] if self.appname: if self.appauthor is not False: diff --git a/tests/conftest.py b/tests/conftest.py index 8d49f81..34f808f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,6 @@ -from typing import Tuple, cast +from __future__ import annotations + +from typing import cast import pytest from _pytest.fixtures import SubRequest @@ -29,5 +31,5 @@ def func_path(request: SubRequest) -> str: @pytest.fixture() -def props() -> Tuple[str, ...]: +def props() -> tuple[str, ...]: return PROPS diff --git a/tests/test_android.py b/tests/test_android.py index 90983db..8f98e69 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import sys -from typing import Any, Dict +from typing import Any from unittest.mock import MagicMock import pytest @@ -26,7 +28,7 @@ "app_name_author_version_false_opinion", ], ) -def test_android(mocker: MockerFixture, params: Dict[str, Any], func: str) -> None: +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) @@ -58,7 +60,7 @@ def test_android(mocker: MockerFixture, params: Dict[str, Any], func: str) -> No def test_android_folder_from_jnius(mocker: MockerFixture) -> None: from platformdirs import PlatformDirs - from platformdirs.android import _android_folder # noqa + from platformdirs.android import _android_folder _android_folder.cache_clear() @@ -68,8 +70,8 @@ def test_android_folder_from_jnius(mocker: MockerFixture) -> None: 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 + 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() @@ -92,7 +94,7 @@ def test_android_folder_from_jnius(mocker: MockerFixture) -> None: 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 # noqa + from platformdirs.android import _android_folder _android_folder.cache_clear() monkeypatch.setattr(sys, "path", ["/A", "/B", path]) @@ -104,7 +106,7 @@ def test_android_folder_from_sys_path(mocker: MockerFixture, path: str, monkeypa 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 # noqa + from platformdirs.android import _android_folder _android_folder.cache_clear() monkeypatch.setattr(sys, "path", []) diff --git a/tests/test_api.py b/tests/test_api.py index cd9bf67..1a4e6a7 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import inspect from pathlib import Path -from typing import Optional import pytest from _pytest.monkeypatch import MonkeyPatch @@ -50,7 +51,7 @@ def test_function_interface_is_in_sync(func: str) -> None: @pytest.mark.parametrize("root", ["A", "/system", None]) @pytest.mark.parametrize("data", ["D", "/data", None]) -def test_android_active(monkeypatch: MonkeyPatch, root: Optional[str], data: Optional[str]) -> None: +def test_android_active(monkeypatch: MonkeyPatch, root: str | None, data: str | None) -> None: for env_var, value in {"ANDROID_DATA": data, "ANDROID_ROOT": root}.items(): if value is None: monkeypatch.delenv(env_var, raising=False) diff --git a/tests/test_comp_with_appdirs.py b/tests/test_comp_with_appdirs.py index 7190762..c4b4c65 100644 --- a/tests/test_comp_with_appdirs.py +++ b/tests/test_comp_with_appdirs.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import sys from inspect import getmembers, isfunction -from typing import Any, Dict +from typing import Any import appdirs import pytest @@ -47,7 +49,7 @@ def test_has_all_properties() -> None: "app_name_author_version", ], ) -def test_compatibility(params: Dict[str, Any], func: str) -> None: +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`") diff --git a/tests/test_main.py b/tests/test_main.py index ed597c7..d09e18a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,12 +1,13 @@ +from __future__ import annotations + import sys from subprocess import check_output -from typing import Tuple from platformdirs import __version__ from platformdirs.__main__ import PROPS -def test_props_same_as_test(props: Tuple[str, ...]) -> None: +def test_props_same_as_test(props: tuple[str, ...]) -> None: assert PROPS == props diff --git a/tests/test_unix.py b/tests/test_unix.py index b13d658..272c28e 100644 --- a/tests/test_unix.py +++ b/tests/test_unix.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import importlib import os import sys @@ -49,7 +51,7 @@ class XDGVariable(typing.NamedTuple): default_value: str -def _func_to_path(func: str) -> typing.Optional[XDGVariable]: +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"), diff --git a/tox.ini b/tox.ini index 984975a..e1b4e46 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,14 @@ [tox] envlist = + fix py310 py39 py38 py37 pypy3 - fix type - pkg_check + coverage + readme isolated_build = true skip_missing_interpreters = true minversion = 3.21 @@ -50,16 +51,41 @@ description = run type check on code base setenv = {tty:MYPY_FORCE_COLOR = 1} deps = - mypy==0.910 + mypy==0.930 commands = mypy --strict src mypy --strict tests -[testenv:pkg_check] +[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 = * -basepython = python3.9 skip_install = true deps = build>=0.5