From d8a214e4f72f8d43f6b5d0c4eec9b8f6ed57e95b Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka Date: Wed, 9 Feb 2022 11:24:07 +0300 Subject: [PATCH 1/6] Fix the behavior of the OS definition on Android --- src/platformdirs/__init__.py | 11 ++++++++--- src/platformdirs/android.py | 4 ++-- tests/test_android.py | 3 +-- tests/test_api.py | 11 +++++++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py index 46b1157..d66256d 100644 --- a/src/platformdirs/__init__.py +++ b/src/platformdirs/__init__.py @@ -18,14 +18,19 @@ 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": + if sys.platform == "win32": module, name = "platformdirs.windows", "Windows" elif sys.platform == "darwin": module, name = "platformdirs.macos", "MacOS" else: module, name = "platformdirs.unix", "Unix" + + if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": + from platformdirs.android import _android_folder + + if _android_folder() is not None: + module, name = "platformdirs.android", "Android" + result: type[PlatformDirsABC] = getattr(importlib.import_module(module), name) return result diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index a684058..27dd573 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -78,7 +78,7 @@ def user_runtime_dir(self) -> str: @lru_cache(maxsize=1) -def _android_folder() -> str: +def _android_folder() -> str | None: """:return: base folder for the Android OS""" try: # First try to get path to android app via pyjnius @@ -94,7 +94,7 @@ def _android_folder() -> str: result = path.split("/files")[0] break else: - raise OSError("Cannot find path to android app folder") + result = None return result diff --git a/tests/test_android.py b/tests/test_android.py index 8f98e69..51dd09b 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -110,5 +110,4 @@ def test_android_folder_not_found(mocker: MockerFixture, monkeypatch: MonkeyPatc _android_folder.cache_clear() monkeypatch.setattr(sys, "path", []) - with pytest.raises(OSError, match="Cannot find path to android app folder"): - _android_folder() + assert _android_folder() is None diff --git a/tests/test_api.py b/tests/test_api.py index 1a4e6a7..03c1eff 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,6 +1,7 @@ from __future__ import annotations import inspect +import sys from pathlib import Path import pytest @@ -51,14 +52,20 @@ 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: str | None, data: str | None) -> 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) - expected = root == "/system" and data == "/data" + 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() if expected: assert platformdirs._set_platform_dir_class() is Android else: From bd70a1156f686036b6349888a3bc115f5e17daed Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka Date: Wed, 9 Feb 2022 11:39:12 +0300 Subject: [PATCH 2/6] Fix returned type of _android_folder --- src/platformdirs/__init__.py | 2 +- src/platformdirs/android.py | 6 +++--- tests/test_android.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py index d66256d..d2e1dea 100644 --- a/src/platformdirs/__init__.py +++ b/src/platformdirs/__init__.py @@ -28,7 +28,7 @@ def _set_platform_dir_class() -> type[PlatformDirsABC]: if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": from platformdirs.android import _android_folder - if _android_folder() is not None: + if _android_folder(): module, name = "platformdirs.android", "Android" result: type[PlatformDirsABC] = getattr(importlib.import_module(module), name) diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index 27dd573..ada3d0b 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -78,8 +78,8 @@ def user_runtime_dir(self) -> str: @lru_cache(maxsize=1) -def _android_folder() -> str | None: - """:return: base folder for the Android OS""" +def _android_folder() -> str: + """:return: base folder for the Android OS. If such cannot be found, an empty string is returned""" try: # First try to get path to android app via pyjnius from jnius import autoclass @@ -94,7 +94,7 @@ def _android_folder() -> str | None: result = path.split("/files")[0] break else: - result = None + result = "" return result diff --git a/tests/test_android.py b/tests/test_android.py index 51dd09b..8e00458 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -110,4 +110,4 @@ def test_android_folder_not_found(mocker: MockerFixture, monkeypatch: MonkeyPatc _android_folder.cache_clear() monkeypatch.setattr(sys, "path", []) - assert _android_folder() is None + assert not _android_folder() From aa60af328f905fb24272c182870b1b794165ca1d Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka Date: Wed, 9 Feb 2022 12:16:43 +0300 Subject: [PATCH 3/6] Revert "Fix returned type of _android_folder" --- src/platformdirs/__init__.py | 2 +- src/platformdirs/android.py | 14 +++++++------- tests/test_android.py | 2 +- tests/test_api.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/platformdirs/__init__.py b/src/platformdirs/__init__.py index d2e1dea..d66256d 100644 --- a/src/platformdirs/__init__.py +++ b/src/platformdirs/__init__.py @@ -28,7 +28,7 @@ def _set_platform_dir_class() -> type[PlatformDirsABC]: if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": from platformdirs.android import _android_folder - if _android_folder(): + if _android_folder() is not None: module, name = "platformdirs.android", "Android" result: type[PlatformDirsABC] = getattr(importlib.import_module(module), name) diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index ada3d0b..fe7f2dd 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -18,7 +18,7 @@ class Android(PlatformDirsABC): @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(_android_folder(), "files") + return self._append_app_name_and_version(_android_folder() or "", "files") @property def site_data_dir(self) -> str: @@ -30,7 +30,7 @@ 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(_android_folder(), "shared_prefs") + return self._append_app_name_and_version(_android_folder() or "", "shared_prefs") @property def site_config_dir(self) -> str: @@ -40,7 +40,7 @@ def site_config_dir(self) -> str: @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(_android_folder(), "cache") + return self._append_app_name_and_version(_android_folder() or "", "cache") @property def user_state_dir(self) -> str: @@ -78,14 +78,14 @@ def user_runtime_dir(self) -> str: @lru_cache(maxsize=1) -def _android_folder() -> str: - """:return: base folder for the Android OS. If such cannot be found, an empty string is returned""" +def _android_folder() -> str | None: + """:return: base folder for the Android OS. If such cannot be found, `None` is returned""" try: # First try to get path to android app via pyjnius from jnius import autoclass Context = autoclass("android.content.Context") # noqa: N806 - result: str = Context.getFilesDir().getParentFile().getAbsolutePath() + 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") @@ -94,7 +94,7 @@ def _android_folder() -> str: result = path.split("/files")[0] break else: - result = "" + result = None return result diff --git a/tests/test_android.py b/tests/test_android.py index 8e00458..51dd09b 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -110,4 +110,4 @@ def test_android_folder_not_found(mocker: MockerFixture, monkeypatch: MonkeyPatc _android_folder.cache_clear() monkeypatch.setattr(sys, "path", []) - assert not _android_folder() + assert _android_folder() is None diff --git a/tests/test_api.py b/tests/test_api.py index 03c1eff..96ac24b 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -65,7 +65,7 @@ def test_android_active(monkeypatch: MonkeyPatch, root: str | None, data: str | _android_folder.cache_clear() monkeypatch.setattr(sys, "path", ["/A", "/B", path]) - expected = root == "/system" and data == "/data" and _android_folder() + expected = root == "/system" and data == "/data" and _android_folder() is not None if expected: assert platformdirs._set_platform_dir_class() is Android else: From 60c3b4cabcb8c0c0a96f14b8e6c3de870fcd8c1c Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka Date: Wed, 9 Feb 2022 12:28:52 +0300 Subject: [PATCH 4/6] Add changelog entry --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1fb666c..ca019fe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,10 @@ platformdirs Changelog ====================== +platformdirs 2.4.2 +------------------ +- Fix OSError when platformdirs is runned from subsystem in Termux + platformdirs 2.4.1 ------------------ - Drop python 3.6 support From 9c49bcc45034eb3efbcde338491fc94e34ee2191 Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka Date: Wed, 9 Feb 2022 12:35:10 +0300 Subject: [PATCH 5/6] Documentation notes --- CHANGES.rst | 4 ++-- src/platformdirs/android.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index ca019fe..d50cbb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,9 @@ platformdirs Changelog ====================== -platformdirs 2.4.2 +platformdirs 2.5.0 ------------------ -- Fix OSError when platformdirs is runned from subsystem in Termux +- Add support for Termux subsystems platformdirs 2.4.1 ------------------ diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index fe7f2dd..7518f37 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -79,7 +79,7 @@ def user_runtime_dir(self) -> str: @lru_cache(maxsize=1) def _android_folder() -> str | None: - """:return: base folder for the Android OS. If such cannot be found, `None` is returned""" + """: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 From d9eb3c980488121eeb7db51b9c2ed5ded172d719 Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka Date: Wed, 9 Feb 2022 12:47:38 +0300 Subject: [PATCH 6/6] Change "or" to typing.cast" --- src/platformdirs/android.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/platformdirs/android.py b/src/platformdirs/android.py index 7518f37..eda8093 100644 --- a/src/platformdirs/android.py +++ b/src/platformdirs/android.py @@ -4,6 +4,7 @@ import re import sys from functools import lru_cache +from typing import cast from .api import PlatformDirsABC @@ -18,7 +19,7 @@ class Android(PlatformDirsABC): @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(_android_folder() or "", "files") + return self._append_app_name_and_version(cast(str, _android_folder()), "files") @property def site_data_dir(self) -> str: @@ -30,7 +31,7 @@ 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(_android_folder() or "", "shared_prefs") + return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs") @property def site_config_dir(self) -> str: @@ -40,7 +41,7 @@ def site_config_dir(self) -> str: @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(_android_folder() or "", "cache") + return self._append_app_name_and_version(cast(str, _android_folder()), "cache") @property def user_state_dir(self) -> str: