Created
April 9, 2025 09:32
-
-
Save kodekracker/33d2c0951895777fcb446715ac5bd1a1 to your computer and use it in GitHub Desktop.
Cross Platform app data and bin directory setup utilities
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Adopted from https://github.com/python-poetry/install.python-poetry.org/blob/main/install-poetry.py | |
import sys | |
# Eager version check so we fail nicely before possible syntax errors | |
if sys.version_info < (3, 6): # noqa: UP036 | |
sys.stdout.write("Poetry installer requires Python 3.6 or newer to run!\n") | |
sys.exit(1) | |
import argparse | |
import json | |
import os | |
import re | |
import shutil | |
import subprocess | |
import sysconfig | |
import tempfile | |
from contextlib import closing | |
from contextlib import contextmanager | |
from functools import cmp_to_key | |
from io import UnsupportedOperation | |
from pathlib import Path | |
from typing import Optional | |
from urllib.request import Request | |
from urllib.request import urlopen | |
SHELL = os.getenv("SHELL", "") | |
WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") | |
MINGW = sysconfig.get_platform().startswith("mingw") | |
MACOS = sys.platform == "darwin" | |
FOREGROUND_COLORS = { | |
"black": 30, | |
"red": 31, | |
"green": 32, | |
"yellow": 33, | |
"blue": 34, | |
"magenta": 35, | |
"cyan": 36, | |
"white": 37, | |
} | |
BACKGROUND_COLORS = { | |
"black": 40, | |
"red": 41, | |
"green": 42, | |
"yellow": 43, | |
"blue": 44, | |
"magenta": 45, | |
"cyan": 46, | |
"white": 47, | |
} | |
OPTIONS = {"bold": 1, "underscore": 4, "blink": 5, "reverse": 7, "conceal": 8} | |
def style(fg, bg, options): | |
codes = [] | |
if fg: | |
codes.append(FOREGROUND_COLORS[fg]) | |
if bg: | |
codes.append(BACKGROUND_COLORS[bg]) | |
if options: | |
if not isinstance(options, (list, tuple)): | |
options = [options] | |
for option in options: | |
codes.append(OPTIONS[option]) | |
return "\033[{}m".format(";".join(map(str, codes))) | |
STYLES = { | |
"info": style("cyan", None, None), | |
"comment": style("yellow", None, None), | |
"success": style("green", None, None), | |
"error": style("red", None, None), | |
"warning": style("yellow", None, None), | |
"b": style(None, None, ("bold",)), | |
} | |
def is_decorated(): | |
if WINDOWS: | |
return ( | |
os.getenv("ANSICON") is not None | |
or os.getenv("ConEmuANSI") == "ON" # noqa: SIM112 | |
or os.getenv("Term") == "xterm" # noqa: SIM112 | |
) | |
if not hasattr(sys.stdout, "fileno"): | |
return False | |
try: | |
return os.isatty(sys.stdout.fileno()) | |
except UnsupportedOperation: | |
return False | |
def is_interactive(): | |
if not hasattr(sys.stdin, "fileno"): | |
return False | |
try: | |
return os.isatty(sys.stdin.fileno()) | |
except UnsupportedOperation: | |
return False | |
def colorize(style, text): | |
if not is_decorated(): | |
return text | |
return f"{STYLES[style]}{text}\033[0m" | |
def string_to_bool(value): | |
value = value.lower() | |
return value in {"true", "1", "y", "yes"} | |
def data_dir() -> Path: | |
if os.getenv("POETRY_HOME"): | |
return Path(os.getenv("POETRY_HOME")).expanduser() | |
if WINDOWS: | |
base_dir = Path(_get_win_folder("CSIDL_APPDATA")) | |
elif MACOS: | |
base_dir = Path("~/Library/Application Support").expanduser() | |
else: | |
base_dir = Path(os.getenv("XDG_DATA_HOME", "~/.local/share")).expanduser() | |
base_dir = base_dir.resolve() | |
return base_dir / "pypoetry" | |
def bin_dir() -> Path: | |
if os.getenv("POETRY_HOME"): | |
return Path(os.getenv("POETRY_HOME")).expanduser() / "bin" | |
if WINDOWS and not MINGW: | |
return Path(_get_win_folder("CSIDL_APPDATA")) / "Python/Scripts" | |
else: | |
return Path("~/.local/bin").expanduser() | |
def _get_win_folder_from_registry(csidl_name): | |
import winreg as _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", | |
) | |
path, _ = _winreg.QueryValueEx(key, shell_folder_name) | |
return path | |
def _get_win_folder_with_ctypes(csidl_name): | |
import ctypes | |
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 | |
# <http://bugs.activestate.com/show_bug.cgi?id=85099>. | |
has_high_char = False | |
for c in buf: | |
if ord(c) > 255: | |
has_high_char = True | |
break | |
if has_high_char: | |
buf2 = ctypes.create_unicode_buffer(1024) | |
if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): | |
buf = buf2 | |
return buf.value | |
if WINDOWS: | |
try: | |
from ctypes import windll # noqa: F401 | |
_get_win_folder = _get_win_folder_with_ctypes | |
except ImportError: | |
_get_win_folder = _get_win_folder_from_registry |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment