Skip to content

Commit 5d46315

Browse files
committed
Refactor
1 parent 49023ee commit 5d46315

File tree

1 file changed

+119
-68
lines changed

1 file changed

+119
-68
lines changed

scripts/generate-pixi-toml.py

Lines changed: 119 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -71,23 +71,13 @@
7171
import subprocess
7272
import sys
7373
from pathlib import Path
74+
from textwrap import dedent
7475
from typing import NamedTuple
7576

7677
import tomlkit
7778
import tomlkit.items
7879

7980

80-
class WorkingDirectoryPaths(NamedTuple):
81-
"""Paths within the temporary working directory"""
82-
83-
working_path: Path
84-
working_pyproject_file: Path
85-
working_environment_file: Path
86-
working_environment_blas_file: Path
87-
pixi_toml_raw_file: Path
88-
gitignore_file: Path
89-
90-
9181
RAW_COMMAND = """
9282
pixi exec conda-lock render-lock-spec \
9383
--channel=conda-forge \
@@ -103,6 +93,27 @@ class WorkingDirectoryPaths(NamedTuple):
10393
PARSED_COMMAND = shlex.split(RAW_COMMAND)
10494

10595

96+
class OriginalPaths(NamedTuple):
97+
"""Paths to the original files"""
98+
99+
project_root: Path # ./
100+
pyproject_file: Path # ./pyproject.toml
101+
environment_file: Path # ./environment.yml
102+
environment_blas_file: Path # ./scripts/environment-blas.yml
103+
pixi_toml_file: Path # ./pixi.toml
104+
105+
106+
class WorkingDirectoryPaths(NamedTuple):
107+
"""Paths within the temporary working directory"""
108+
109+
working_path: Path # ./pixi-working/
110+
pyproject_file: Path # ./pixi-working/pyproject.toml
111+
environment_file: Path # ./pixi-working/environment.yml
112+
environment_blas_file: Path # ./pixi-working/scripts/environment-blas.yml
113+
pixi_toml_file: Path # ./pixi-working/pixi.toml
114+
gitignore_file: Path # ./pixi-working/.gitignore
115+
116+
106117
def main():
107118
parser = argparse.ArgumentParser(
108119
description="Generate pixi.toml from environment files"
@@ -119,100 +130,140 @@ def main():
119130
print("pixi is not installed. See <https://pixi.sh/latest/#installation>") # noqa: T201
120131
sys.exit(1)
121132

122-
current_dir = Path(__file__).parent
123-
assert current_dir.name == "scripts"
124-
project_root = current_dir.parent
125-
126-
pyproject_file = project_root / "pyproject.toml"
127-
128-
pyproject_data = tomlkit.loads(pyproject_file.read_text())
129-
130-
pyproject_data = preprocess_pyproject_data(pyproject_data)
133+
original_paths = get_original_paths()
131134

132135
# Make temporary working directory
133-
working_path = project_root / "pixi-working"
134-
working_path.mkdir(parents=True, exist_ok=True)
135-
gitignore_file = working_path / ".gitignore"
136-
gitignore_file.write_text("*")
136+
working_directory_paths = initialize_working_directory(original_paths)
137137

138-
working_pyproject_file = working_path / "pyproject.toml"
139-
with working_pyproject_file.open("w") as fh:
138+
# Copy the processed pyproject.toml to the working directory
139+
pyproject_data = tomlkit.loads(original_paths.pyproject_file.read_text())
140+
pyproject_data = preprocess_pyproject_data(pyproject_data)
141+
with working_directory_paths.pyproject_file.open("w") as fh:
140142
tomlkit.dump(pyproject_data, fh)
141143

142-
raw_environment_file = project_root / "environment.yml"
143-
raw_environment_data = raw_environment_file.read_text()
144-
144+
# Copy the processed environment file to the working directory
145+
raw_environment_data = original_paths.environment_file.read_text()
145146
environment_data = preprocess_environment_data(raw_environment_data)
146-
147-
working_environment_file = working_path / "environment.yml"
148-
with working_environment_file.open("w") as fh:
147+
with working_directory_paths.environment_file.open("w") as fh:
149148
fh.write(environment_data)
150149

151-
environment_blas_file = project_root / "scripts" / "environment-blas.yml"
152-
working_environment_blas_file = working_path / "scripts" / "environment-blas.yml"
150+
# Copy environment-blas.yml to the working directory
153151
# This is for our exclusive use, so it doesn't need to be preprocessed.
154-
working_environment_blas_file.parent.mkdir(parents=True, exist_ok=True)
155-
shutil.copy(environment_blas_file, working_environment_blas_file)
152+
working_directory_paths.environment_blas_file.parent.mkdir(
153+
parents=True, exist_ok=True
154+
)
155+
shutil.copy(
156+
original_paths.environment_blas_file,
157+
working_directory_paths.environment_blas_file,
158+
)
156159

160+
# Run conda-lock to generate the pixi.toml file
157161
print(f"Running the command:\n{shlex.join(PARSED_COMMAND)}\n") # noqa: T201
158162
result = subprocess.run(
159-
PARSED_COMMAND, check=True, capture_output=True, cwd=working_path
163+
PARSED_COMMAND,
164+
check=True,
165+
capture_output=True,
166+
cwd=working_directory_paths.working_path,
160167
)
161168

162169
warnings = result.stderr.decode("utf-8")
163170
if warnings:
164171
print(f"Warnings: \n{warnings}\n") # noqa: T201
165172

173+
# Write the unprocessed pixi.toml data to the working directory
166174
pixi_toml_raw_data = tomlkit.loads(result.stdout.decode("utf-8"))
167-
168-
pixi_toml_raw_file = working_path / "pixi.toml"
169-
pixi_toml_raw_file.write_text(tomlkit.dumps(pixi_toml_raw_data))
170-
171-
pixi_toml_data = postprocess_pixi_toml_data(pixi_toml_raw_data)
172-
173-
working_directory_paths = WorkingDirectoryPaths(
174-
working_path=working_path,
175-
working_pyproject_file=working_pyproject_file,
176-
working_environment_file=working_environment_file,
177-
working_environment_blas_file=working_environment_blas_file,
178-
pixi_toml_raw_file=pixi_toml_raw_file,
179-
gitignore_file=gitignore_file,
180-
)
175+
pixi_toml_raw_content = tomlkit.dumps(pixi_toml_raw_data)
176+
working_directory_paths.pixi_toml_file.write_text(pixi_toml_raw_content)
181177

182178
# Generate the pixi.toml content
179+
pixi_toml_data = postprocess_pixi_toml_data(pixi_toml_raw_data)
183180
pixi_toml_content = tomlkit.dumps(pixi_toml_data)
184181

185182
if args.verify_only:
186183
# Compare with existing pixi.toml
187-
pixi_toml_file = project_root / "pixi.toml"
188-
if not pixi_toml_file.exists():
189-
print("ERROR: pixi.toml does not exist") # noqa: T201
190-
cleanup_working_directory(working_directory_paths)
191-
sys.exit(1)
192-
193-
existing_content = pixi_toml_file.read_text()
194-
if existing_content != pixi_toml_content:
195-
print("ERROR: pixi.toml is not consistent with environment files") # noqa: T201
196-
print("Run 'python scripts/generate-pixi-toml.py' to update it") # noqa: T201
197-
cleanup_working_directory(working_directory_paths)
184+
existing_pixi_toml_content = original_paths.pixi_toml_file.read_text()
185+
if existing_pixi_toml_content != pixi_toml_content:
186+
message = dedent("""\
187+
Mismatch detected between existing and new pixi.toml content.
188+
189+
New pixi.toml content:
190+
{pixi_toml_content}
191+
192+
Run 'python scripts/generate-pixi-toml.py' to regenerate it.
193+
After updating `pixi.toml`, it's suggested to run the following commands:
194+
195+
pixi lock
196+
git add pixi.toml
197+
git commit -m "Regenerate pixi.toml"
198+
git add pixi.lock
199+
git commit -m "Update pixi lockfile"
200+
201+
ERROR: pixi.toml is not consistent with environment files.
202+
See above for details.
203+
""").format(pixi_toml_content=pixi_toml_content)
204+
print(message) # noqa: T201
198205
sys.exit(1)
199206

200207
print("SUCCESS: pixi.toml is consistent with environment files") # noqa: T201
201208
cleanup_working_directory(working_directory_paths)
202209
sys.exit(0)
203210
else:
204211
# Write the pixi.toml file to the project root
205-
(project_root / "pixi.toml").write_text(pixi_toml_content)
212+
original_paths.pixi_toml_file.write_text(pixi_toml_content)
206213
cleanup_working_directory(working_directory_paths)
207214

208215

216+
def get_original_paths() -> OriginalPaths:
217+
"""Get the paths to the original files"""
218+
current_dir = Path(__file__).parent
219+
assert current_dir.name == "scripts"
220+
project_root = current_dir.parent
221+
pyproject_file = project_root / "pyproject.toml"
222+
environment_file = project_root / "environment.yml"
223+
environment_blas_file = project_root / "scripts" / "environment-blas.yml"
224+
pixi_toml_file = project_root / "pixi.toml"
225+
if not pixi_toml_file.exists():
226+
raise FileNotFoundError(f"pixi.toml does not exist at {pixi_toml_file}")
227+
return OriginalPaths(
228+
project_root=project_root,
229+
pyproject_file=pyproject_file,
230+
environment_file=environment_file,
231+
environment_blas_file=environment_blas_file,
232+
pixi_toml_file=pixi_toml_file,
233+
)
234+
235+
236+
def initialize_working_directory(
237+
original_paths: OriginalPaths,
238+
) -> WorkingDirectoryPaths:
239+
"""Initialize the temporary working directory"""
240+
working_path = original_paths.project_root / "pixi-working"
241+
working_path.mkdir(parents=True, exist_ok=True)
242+
gitignore_file = working_path / ".gitignore"
243+
gitignore_file.write_text("*")
244+
245+
pyproject_file = working_path / "pyproject.toml"
246+
environment_file = working_path / "environment.yml"
247+
environment_blas_file = working_path / "scripts" / "environment-blas.yml"
248+
pixi_toml_file = working_path / "pixi.toml"
249+
250+
return WorkingDirectoryPaths(
251+
working_path=working_path,
252+
pyproject_file=pyproject_file,
253+
environment_file=environment_file,
254+
environment_blas_file=environment_blas_file,
255+
pixi_toml_file=pixi_toml_file,
256+
gitignore_file=gitignore_file,
257+
)
258+
259+
209260
def cleanup_working_directory(working_paths: WorkingDirectoryPaths):
210261
"""Clean up the temporary working directory and files"""
211-
working_paths.working_pyproject_file.unlink()
212-
working_paths.working_environment_file.unlink()
213-
working_paths.working_environment_blas_file.unlink()
214-
working_paths.working_environment_blas_file.parent.rmdir()
215-
working_paths.pixi_toml_raw_file.unlink()
262+
working_paths.pyproject_file.unlink()
263+
working_paths.environment_file.unlink()
264+
working_paths.environment_blas_file.unlink()
265+
working_paths.environment_blas_file.parent.rmdir()
266+
working_paths.pixi_toml_file.unlink()
216267
working_paths.gitignore_file.unlink()
217268
working_paths.working_path.rmdir()
218269

0 commit comments

Comments
 (0)