1+ import tomllib
2+ from pathlib import Path
3+ import yaml
4+ import os
5+
6+ def parse_pyproject (path : str , optional_sections_to_skip = None ):
7+ with open (path , "rb" ) as f :
8+ data = tomllib .load (f )
9+
10+ deps = set ()
11+
12+ # project.dependencies (PEP 621)
13+ for dep in data .get ("project" , {}).get ("dependencies" , []):
14+ if "numpy" in dep :
15+ # numpy is also listed in build requirements with a higher version number
16+ # so we skip it here to avoid conflicts.
17+ continue
18+ deps .add (dep )
19+
20+ # optional dependencies (PEP 621)
21+ if optional_sections_to_skip is None :
22+ optional_sections_to_skip = []
23+ for group , group_deps in data .get ("project" , {}).get ("optional-dependencies" , {}).items ():
24+ if group in optional_sections_to_skip :
25+ print ("Skipping optional dependency group:" , group )
26+ continue
27+ deps .update (group_deps )
28+
29+ deps .discard ("geoana[all]" )
30+ deps .discard ("geoana[doc,all]" )
31+ deps .discard ("geoana[plot,extras,jittable]" )
32+
33+ if "matplotlib" in deps :
34+ deps .remove ("matplotlib" )
35+ deps .add ("matplotlib-base" )
36+ return deps
37+
38+ def create_env_yaml (deps , name = "env" , python_version = None , free_threaded = False ):
39+ conda_pkgs = []
40+ pip_pkgs = []
41+
42+ for dep in deps :
43+ # crude split: try to detect conda vs pip-only packages
44+ if any (dep .startswith (pip_only ) for pip_only in ["git+" , "http:" , "https:" , "file:" ]):
45+ pip_pkgs .append (dep )
46+ else :
47+ conda_pkgs .append (dep )
48+
49+ dependencies = conda_pkgs
50+ if pip_pkgs :
51+ dependencies .append ({"pip" : pip_pkgs })
52+
53+ if python_version :
54+ if free_threaded :
55+ dependencies .insert (0 , f"python-freethreading={ python_version } " )
56+ else :
57+ dependencies .insert (0 , f"python={ python_version } " )
58+
59+ return {
60+ "name" : name ,
61+ "channels" : ["conda-forge" ],
62+ "dependencies" : dependencies ,
63+ }
64+
65+ if __name__ == "__main__" :
66+ pyproject_path = Path ("pyproject.toml" )
67+
68+ py_vers = os .environ .get ("PYTHON_VERSION" , "3.11" )
69+ mkl_vers = os .environ .get ("MKL_VERSION" , "2024" )
70+ is_free_threaded = os .environ .get ("FREE_THREADED" , "false" ).lower () == "true"
71+ is_coverage = os .environ .get ("DO_COVERAGE" , "false" ).lower () == "true"
72+ env_name = os .environ .get ("ENV_NAME" , "pydiso-ci" )
73+
74+ optional_to_skip = []
75+ if not is_coverage :
76+ optional_to_skip .append ("coverage" )
77+
78+ deps = parse_pyproject (pyproject_path , optional_sections_to_skip = optional_to_skip )
79+ deps .add ("mkl-devel" )
80+ deps .add ("pkg-config" )
81+ deps .add (f"mkl={ mkl_vers } " )
82+ env_data = create_env_yaml (deps , name = env_name , python_version = py_vers , free_threaded = is_free_threaded )
83+
84+ out_name = "environment_ci.yml"
85+ with open (out_name , "w" ) as f :
86+ yaml .safe_dump (env_data , f , sort_keys = False )
87+
88+ print ("✅ Generated environment_ci.yml with" , len (deps ), "dependencies" )
0 commit comments