Skip to content

Instantly share code, notes, and snippets.

@kodekracker
Created April 9, 2025 09:32
Show Gist options
  • Save kodekracker/33d2c0951895777fcb446715ac5bd1a1 to your computer and use it in GitHub Desktop.
Save kodekracker/33d2c0951895777fcb446715ac5bd1a1 to your computer and use it in GitHub Desktop.
Cross Platform app data and bin directory setup utilities
# 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