Repository navigation
Expand file tree
/
Copy pathpython_floor.py
More file actions
119 lines (102 loc) · 4.98 KB
/
Copy pathpython_floor.py
File metadata and controls
119 lines (102 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"""python_floor.py -- stop at start-up on a Python older than the pins need.
requirements.txt was resolved on Python 3.12.13 (the repo venv, and
installed_versions.txt is its `pip freeze`). scipy==1.18.1 and
nautilus_trader==1.229.0 have no release for 3.11, so on 3.11 pip installs
nothing and the failure surfaces later as an unrelated ImportError.
Call require() before any third-party import. This module imports only the
standard library so that it runs on the interpreter it is meant to reject.
THE DRIVE CHECK, require_drive(), was environment_check.py until 2026-10-18 (the
file last exists at aebf1da; its require() is renamed so the two do not collide):
macOS writes a "._" AppleDouble file beside every file it copies to an exFAT or
FAT drive. Inside site-packages those files break installed packages, so a venv
there fails later with an unrelated import or decode error. The repository
itself may sit on such a drive: every directory listing in the code skips "._"
files (paths.list_dir), so that is reported as a note and the run continues.
Call require_drive() before any third-party import. Standard library only, so it
runs on an environment too broken to import pandas.
"""
import os
import re
import subprocess
import sys
import sysconfig
from pathlib import Path
REQUIRED = (3, 12)
RESOLVED_ON = "3.12.13"
def require(script):
"""Exit with one message if the running Python is older than REQUIRED."""
if sys.version_info[:2] >= REQUIRED:
return
need = "%d.%d" % REQUIRED
have = "%d.%d.%d" % sys.version_info[:3]
sys.exit(
"%s needs Python %s or newer; this is Python %s (%s).\n"
"The pinned requirements were resolved on Python %s. Build the venv with it:\n"
" python%s -m venv venv\n"
" ./venv/bin/python -m pip install -r requirements.txt -c installed_versions.txt\n"
"then run ./venv/bin/python %s"
% (script, need, have, sys.executable, RESOLVED_ON, need, script)
)
FAT_TYPES = {"exfat", "msdos", "vfat", "fat", "fat12", "fat16", "fat32"}
def fs_type(path):
"""The file system type of the volume holding `path`, lower case, or None."""
p = Path(path).resolve()
if sys.platform == "win32":
import ctypes
root = p.anchor
buf = ctypes.create_unicode_buffer(64)
ok = ctypes.windll.kernel32.GetVolumeInformationW(
ctypes.c_wchar_p(root), None, 0, None, None, None, buf, len(buf))
return buf.value.lower() if ok else None
try:
out = subprocess.run(["mount"], capture_output=True, text=True, check=True).stdout
except (OSError, subprocess.CalledProcessError):
return None
best, kind = None, None
for line in out.splitlines():
# macOS: "/dev/disk4s1 on /Volumes/T7 (exfat, local, ...)"
# Linux: "/dev/sda1 on /mnt/usb type vfat (rw, ...)"
m = re.match(r"^.+? on (.+?) (?:\((\w+)|type (\w+))", line)
if not m:
continue
mp = Path(m.group(1))
if (p == mp or mp in p.parents) and (best is None or len(mp.parts) > len(best.parts)):
best, kind = mp, (m.group(2) or m.group(3)).lower()
return kind
def first_appledouble(root):
"""The first "._" file under `root`, or None."""
for dirpath, _, files in os.walk(root):
for f in files:
if f.startswith("._"):
return Path(dirpath) / f
return None
def require_drive(script, repo):
"""Exit with one message if the running environment is on an exFAT or FAT
drive or has "._" files in site-packages. Print a note if only `repo` is."""
env = Path(sys.prefix)
env_fs = fs_type(env)
site = sorted({sysconfig.get_paths()[k] for k in ("purelib", "platlib")})
stray = next((x for x in map(first_appledouble, site) if x), None)
repo_fs = fs_type(repo)
repo_line = (f"The repository folder {repo} is on a drive formatted as {repo_fs}. That alone is fine:\n"
f"the code skips \"._\" files."
if repo_fs in FAT_TYPES else
f"The repository folder {repo} is not on an exFAT or FAT drive.")
if env_fs in FAT_TYPES or stray:
found = []
if env_fs in FAT_TYPES:
found.append(f"the environment {env} is on a drive formatted as {env_fs}")
if stray:
found.append(f"site-packages holds \"._\" files, for example {stray}")
sys.exit(
f"{script} cannot start: {' and '.join(found)}.\n"
"The venv is on an external drive. macOS writes \"._\" files on exFAT and FAT\n"
"drives, and in site-packages they break installed packages. Create the venv\n"
"on the internal disk and run with it:\n"
" python3.12 -m venv ~/venvs/algo_trading\n"
" ~/venvs/algo_trading/bin/python -m pip install -r requirements.txt "
"-c installed_versions.txt\n"
f" ~/venvs/algo_trading/bin/python {script}\n"
f"{repo_line}")
if repo_fs in FAT_TYPES:
print(f"NOTE: {repo_line}")