diff --git a/nb_cli/handlers/__init__.py b/nb_cli/handlers/__init__.py index 10044ee..e746c20 100644 --- a/nb_cli/handlers/__init__.py +++ b/nb_cli/handlers/__init__.py @@ -29,6 +29,13 @@ # isort: split +# data +from .data import DATA_DIR as DATA_DIR +from .data import CACHE_DIR as CACHE_DIR +from .data import CONFIG_DIR as CONFIG_DIR + +# isort: split + # package from .store import load_module_data as load_module_data from .store import format_package_results as format_package_results diff --git a/nb_cli/handlers/data.py b/nb_cli/handlers/data.py new file mode 100644 index 0000000..6d36eac --- /dev/null +++ b/nb_cli/handlers/data.py @@ -0,0 +1,79 @@ +import os +import sys +from pathlib import Path +from typing import Literal + +WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") + + +# -- Windows support functions -- +def _get_win_folder_from_registry( + csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"] +) -> Path: + """ + 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. + """ + import winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", + ) + directory, _type = winreg.QueryValueEx(key, shell_folder_name) + return Path(directory) + + +def _get_win_folder_with_ctypes( + csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"] +) -> Path: + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = any(ord(c) > 255 for c in buf) + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return Path(buf.value) + + +if WINDOWS: + try: + import ctypes + + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + CACHE_DIR = _get_win_folder("CSIDL_LOCAL_APPDATA") / "nb-cli" / "Cache" + DATA_DIR = _get_win_folder("CSIDL_LOCAL_APPDATA") / "nb-cli" + CONFIG_DIR = DATA_DIR + +elif sys.platform == "darwin": + CACHE_DIR = Path("~/Library/Caches/nb-cli").expanduser() + DATA_DIR = Path("~/Library/Application Support/nb-cli").expanduser() + CONFIG_DIR = DATA_DIR + +else: + CACHE_DIR = Path(os.getenv("XDG_CACHE_HOME", "~/.cache")).expanduser() / "nb-cli" + DATA_DIR = ( + Path(os.getenv("XDG_DATA_HOME", "~/.local/share")).expanduser() / "nb-cli" + ) + CONFIG_DIR = Path(os.getenv("XDG_CONFIG_HOME", "~/.config")).expanduser() / "nb-cli"