Setting the file. One moment.
Setup Env · Hf Cloud Python Env Setup · huggingface/skills · Skills Docs
ContentsBack to the top of the page Bundled file Requirements
scripts/ setup_env.py
Python · 143 lines · 5 KB
15
16 import os
17 import shutil
18 import subprocess
19 import sys
20 from pathlib import Path
21
22 # 3.10-3.12 is the safe zone for modern boto3 + awscli. 3.13+ may work but ML
23 # libs lag on wheel availability.
24 SUPPORTED_MIN = ( 3 , 10 )
25 SUPPORTED_MAX = ( 3 , 12 )
26
27 IS_WINDOWS = os.name == "nt"
28
29
30 def log (msg: str ) -> None :
31 print ( f "[setup_env] { msg } " , file = sys.stderr, flush = True )
32
33
34 def parse_version (v: str ) -> tuple[ int , int ]:
35 try :
36 major, minor = ( int (p) for p in v.split( "." )[: 2 ])
37 return (major, minor)
38 except ValueError :
39 log( f "ERROR: invalid Python version ' { v } ' (expected e.g. 3.12)" )
40 sys.exit( 1 )
41
42
43 def venv_python (venv_dir: Path) -> Path:
44 """Path to the interpreter inside a venv, per platform."""
45 if IS_WINDOWS :
46 return venv_dir / "Scripts" / "python.exe"
47 return venv_dir / "bin" / "python"
48
49
50 def interpreter_version (python_path: Path) -> str | None :
51 if not python_path.exists():
52 return None
53 proc = subprocess.run(
54 [ str (python_path), "-c" ,
55 "import sys; print(f' {sys.version_info.major} . {sys.version_info.minor} ')" ],
56 capture_output = True , text = True ,
57 )
58 return proc.stdout.strip() if proc.returncode == 0 else None
59
60
61 def find_base_python (version: str ) -> str | None :
62 """Locate a base interpreter for the requested version for `python -m venv`."""
63 # Try `pythonX.Y` (Unix) / `pythonX.Y.exe`, then the Windows `py` launcher.
64 candidate = shutil.which( f "python { version } " )
65 if candidate:
66 return candidate
67 if IS_WINDOWS and shutil.which( "py" ):
68 probe = subprocess.run([ "py" , f "- { version } " , "--version" ], capture_output = True , text = True )
69 if probe.returncode == 0 :
70 return f "py - { version } " # sentinel; expanded by caller
71 return None
72
73
74 def main () -> int :
75 venv_dir = Path(sys.argv[ 1 ]) if len (sys.argv) > 1 else Path( ".venv" )
76 python_version = sys.argv[ 2 ] if len (sys.argv) > 2 else "3.12"
77
78 ver = parse_version(python_version)
79 if not ( SUPPORTED_MIN <= ver <= SUPPORTED_MAX ):
80 lo = "." .join( map ( str , SUPPORTED_MIN ))
81 hi = "." .join( map ( str , SUPPORTED_MAX ))
82 log( f "ERROR: Python { python_version } outside supported range { lo } - { hi } " )
83 log( "Use 3.10, 3.11, or 3.12." )
84 return 1
85
86 installer = "uv" if shutil.which( "uv" ) else "venv"
87 if installer == "venv" :
88 log( "uv not found — falling back to python + venv. Consider: https://docs.astral.sh/uv/" )
89
90 py = venv_python(venv_dir)
91
92 # Reuse existing env only if Python version matches.
93 current = interpreter_version(py)
94 if current == python_version:
95 log( f "Env exists at { venv_dir } with Python { current } — reusing" )
96 else :
97 if current is not None :
98 log( f "Env at { venv_dir } uses Python { current } (wanted { python_version } ) — recreating" )
99 shutil.rmtree(venv_dir)
100
101 if installer == "uv" :
102 uv = shutil.which( "uv" )
103 if subprocess.run([uv, "venv" , "--python" , python_version, str (venv_dir)]).returncode != 0 :
104 log( "ERROR: `uv venv` failed." )
105 return 1
106 else :
107 base = find_base_python(python_version)
108 if not base:
109 log( f "ERROR: python { python_version } not found on PATH." )
110 log( "Install via pyenv/asdf/brew/system package manager, the Windows installer, or install uv." )
111 return 1
112 base_cmd = base.split() if base.startswith( "py " ) else [base]
113 if subprocess.run([ * base_cmd, "-m" , "venv" , str (venv_dir)]).returncode != 0 :
114 log( "ERROR: `python -m venv` failed." )
115 return 1
116 log( f "Created env at { venv_dir } " )
117
118 requirements = Path( __file__ ).resolve().parent.parent / "requirements.txt"
119 if not requirements.is_file():
120 log( f "ERROR: requirements.txt not found at { requirements } " )
121 return 1
122
123 log( f "Installing from { requirements } " )
124 if installer == "uv" :
125 uv = shutil.which( "uv" )
126 rc = subprocess.run(
127 [uv, "pip" , "install" , "--python" , str (py), "--upgrade" , "-r" , str (requirements)]
128 ).returncode
129 else :
130 subprocess.run([ str (py), "-m" , "pip" , "install" , "--upgrade" , "pip" ])
131 rc = subprocess.run(
132 [ str (py), "-m" , "pip" , "install" , "--upgrade" , "-r" , str (requirements)]
133 ).returncode
134 if rc != 0 :
135 log( "ERROR: dependency install failed." )
136 return 1
137
138 log( f "Done. Invoke directly: { py } <script>" )
139 return 0
140
141
142 if __name__ == "__main__" :
143 sys.exit(main())