Skip to content

Commit b44d57d

Browse files
authored
Allow working without ctypes (#275)
1 parent 89b6b2b commit b44d57d

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

src/platformdirs/windows.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import ctypes
65
import os
76
import sys
87
from functools import lru_cache
@@ -217,6 +216,8 @@ def get_win_folder_via_ctypes(csidl_name: str) -> str:
217216
# Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead.
218217
# https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
219218

219+
import ctypes # noqa: PLC0415
220+
220221
csidl_const = {
221222
"CSIDL_APPDATA": 26,
222223
"CSIDL_COMMON_APPDATA": 35,
@@ -249,8 +250,13 @@ def get_win_folder_via_ctypes(csidl_name: str) -> str:
249250

250251

251252
def _pick_get_win_folder() -> Callable[[str], str]:
252-
if hasattr(ctypes, "windll"):
253-
return get_win_folder_via_ctypes
253+
try:
254+
import ctypes # noqa: PLC0415
255+
except ImportError:
256+
pass
257+
else:
258+
if hasattr(ctypes, "windll"):
259+
return get_win_folder_via_ctypes
254260
try:
255261
import winreg # noqa: PLC0415, F401
256262
except ImportError:

tests/test_api.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
from __future__ import annotations
22

3+
import builtins
4+
import functools
35
import inspect
46
import sys
57
from pathlib import Path
8+
from typing import TYPE_CHECKING, Any, Callable
69

710
import pytest
811

912
import platformdirs
1013
from platformdirs.android import Android
1114

15+
builtin_import = builtins.__import__
16+
17+
18+
if TYPE_CHECKING:
19+
from types import ModuleType
20+
1221

1322
def test_package_metadata() -> None:
1423
assert hasattr(platformdirs, "__version__")
@@ -80,3 +89,34 @@ def test_android_active( # noqa: PLR0913
8089
assert platformdirs._set_platform_dir_class() is Android # noqa: SLF001
8190
else:
8291
assert platformdirs._set_platform_dir_class() is not Android # noqa: SLF001
92+
93+
94+
def _fake_import(name: str, *args: Any, **kwargs: Any) -> ModuleType: # noqa: ANN401
95+
if name == "ctypes":
96+
raise ModuleNotFoundError("No module named %s" % name)
97+
return builtin_import(name, *args, **kwargs)
98+
99+
100+
def mock_import(func: Callable[[], None]) -> Callable[[], None]:
101+
@functools.wraps(func)
102+
def wrap() -> None:
103+
platformdirs_module_items = [item for item in sys.modules.items() if item[0].startswith("platformdirs")]
104+
try:
105+
builtins.__import__ = _fake_import
106+
for name, _ in platformdirs_module_items:
107+
del sys.modules[name]
108+
return func()
109+
finally:
110+
# restore original modules
111+
builtins.__import__ = builtin_import
112+
for name, module in platformdirs_module_items:
113+
sys.modules[name] = module
114+
115+
return wrap
116+
117+
118+
@mock_import
119+
def test_no_ctypes() -> None:
120+
import platformdirs # noqa: PLC0415
121+
122+
assert platformdirs

0 commit comments

Comments
 (0)