Skip to content

Commit 4c0f6d1

Browse files
authored
Merge pull request #1971 from Shashank-Tripathi-07/fix/tito-subprocess-encoding-crash
fix(tinytorch): tito crashes on Windows when a subprocess's output can't be decoded as cp1252
2 parents a6de923 + ba71d5f commit 4c0f6d1

12 files changed

Lines changed: 51 additions & 25 deletions

File tree

tinytorch/tito/commands/dev/preflight.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def _run_command(self, cmd: List[str], cwd: Path, timeout: int = 60, verbose: bo
246246
cmd,
247247
cwd=cwd,
248248
capture_output=True,
249-
text=True,
249+
text=True, encoding="utf-8", errors="replace",
250250
timeout=timeout
251251
)
252252

tinytorch/tito/commands/dev/test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ def _build_package(self, project_root: Path, verbose: bool, ci_mode: bool = Fals
352352
stdout=subprocess.PIPE,
353353
stderr=subprocess.STDOUT,
354354
text=True,
355+
encoding="utf-8",
356+
errors="replace",
355357
bufsize=1
356358
)
357359

@@ -370,6 +372,8 @@ def _build_package(self, project_root: Path, verbose: bool, ci_mode: bool = Fals
370372
cwd=project_root,
371373
capture_output=True,
372374
text=True,
375+
encoding="utf-8",
376+
errors="replace",
373377
timeout=600 # 10 minutes for full build
374378
)
375379
returncode = result.returncode
@@ -464,6 +468,8 @@ def _run_pytest(self, project_root: Path, test_path: str, name: str,
464468
stdout=subprocess.PIPE,
465469
stderr=subprocess.STDOUT,
466470
text=True,
471+
encoding="utf-8",
472+
errors="replace",
467473
bufsize=1
468474
)
469475

@@ -551,6 +557,8 @@ def _run_pytest(self, project_root: Path, test_path: str, name: str,
551557
env=env,
552558
capture_output=True,
553559
text=True,
560+
encoding="utf-8",
561+
errors="replace",
554562
timeout=timeout
555563
)
556564

@@ -658,6 +666,8 @@ def _run_inline_tests(self, project_root: Path, module: Optional[str],
658666
"dev", "export", module_num],
659667
capture_output=True,
660668
text=True,
669+
encoding="utf-8",
670+
errors="replace",
661671
cwd=project_root,
662672
timeout=120 # 2 min for export
663673
)
@@ -690,6 +700,8 @@ def _run_inline_tests(self, project_root: Path, module: Optional[str],
690700
"module", "complete", module_num],
691701
capture_output=True,
692702
text=True,
703+
encoding="utf-8",
704+
errors="replace",
693705
cwd=project_root,
694706
timeout=300 # 5 min per module
695707
)
@@ -917,6 +929,8 @@ def _run_user_journey(self, project_root: Path, args: Namespace) -> TestResult:
917929
"module", "start", module_num, "--no-jupyter"],
918930
capture_output=True,
919931
text=True,
932+
encoding="utf-8",
933+
errors="replace",
920934
cwd=project_root,
921935
timeout=120
922936
)
@@ -951,6 +965,8 @@ def _run_user_journey(self, project_root: Path, args: Namespace) -> TestResult:
951965
"module", "complete", module_num],
952966
capture_output=True,
953967
text=True,
968+
encoding="utf-8",
969+
errors="replace",
954970
cwd=project_root,
955971
timeout=300
956972
)
@@ -1007,6 +1023,8 @@ def _run_user_journey(self, project_root: Path, args: Namespace) -> TestResult:
10071023
"milestone", "run", milestone_id, "--skip-checks"],
10081024
capture_output=True,
10091025
text=True,
1026+
encoding="utf-8",
1027+
errors="replace",
10101028
cwd=project_root,
10111029
timeout=300 # 5 min for heavy milestones (CNN, Transformer)
10121030
)

tinytorch/tito/commands/export_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def convert_py_to_notebook(module_path: Path, venv_path: Path, console) -> bool:
161161
venv_jupytext = get_venv_bin_dir(venv_path) / "jupytext"
162162

163163
if venv_jupytext.exists():
164-
test_result = subprocess.run([str(venv_jupytext), "--version"], capture_output=True, text=True)
164+
test_result = subprocess.run([str(venv_jupytext), "--version"], capture_output=True, text=True, encoding="utf-8", errors="replace")
165165
if test_result.returncode == 0:
166166
jupytext_path = str(venv_jupytext)
167167
console.print(f"[dim]🔧 Using venv jupytext: {venv_jupytext}[/dim]")
@@ -175,7 +175,7 @@ def convert_py_to_notebook(module_path: Path, venv_path: Path, console) -> bool:
175175
result = subprocess.run(
176176
[jupytext_path, "--to", "ipynb", str(dev_file), "--output", str(notebook_file)],
177177
capture_output=True,
178-
text=True,
178+
text=True, encoding="utf-8", errors="replace",
179179
cwd=project_root,
180180
)
181181

tinytorch/tito/commands/milestone.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,7 @@ def _handle_demo_command(self, args: Namespace) -> int:
10051005
result = subprocess.run(
10061006
[sys.executable, str(demo_path)],
10071007
capture_output=False,
1008-
text=True
1008+
text=True, encoding="utf-8", errors="replace"
10091009
)
10101010

10111011
if result.returncode == 0:
@@ -1302,7 +1302,7 @@ def _handle_run_command(self, args: Namespace) -> int:
13021302
result = subprocess.run(
13031303
[sys.executable, script_file],
13041304
capture_output=False,
1305-
text=True
1305+
text=True, encoding="utf-8", errors="replace"
13061306
)
13071307

13081308
console.print("\n" + "━" * 80)

tinytorch/tito/commands/module/test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ def run_inline_tests(
109109
[sys.executable, str(module_file)],
110110
capture_output=True,
111111
text=True,
112+
encoding="utf-8",
113+
errors="replace",
112114
cwd=self.config.project_root,
113115
timeout=300,
114116
)
@@ -161,6 +163,8 @@ def run_module_pytest(
161163
cmd,
162164
capture_output=True,
163165
text=True,
166+
encoding="utf-8",
167+
errors="replace",
164168
cwd=self.config.project_root,
165169
timeout=300,
166170
)
@@ -263,6 +267,8 @@ def run_integration_tests(
263267
cmd,
264268
capture_output=True,
265269
text=True,
270+
encoding="utf-8",
271+
errors="replace",
266272
cwd=self.config.project_root,
267273
timeout=600, # 10 minute timeout for integration tests
268274
)

tinytorch/tito/commands/module/workflow.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,9 @@ def _open_jupyter(self, module_name: str) -> int:
536536
cwd=str(module_dir),
537537
stdout=subprocess.PIPE,
538538
stderr=subprocess.PIPE,
539-
text=True
539+
text=True,
540+
encoding="utf-8",
541+
errors="replace"
540542
)
541543

542544
# Give Jupyter a moment to start and capture the URL

tinytorch/tito/commands/nbgrader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ def _run_external(self, cmd: List[str], *, capture_output: bool = False) -> subp
768768
cwd=self.project_root,
769769
check=False,
770770
capture_output=capture_output,
771-
text=True,
771+
text=True, encoding="utf-8", errors="replace",
772772
)
773773
except FileNotFoundError as exc:
774774
return subprocess.CompletedProcess(cmd, 127, "", str(exc))

tinytorch/tito/commands/setup.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def _check_package_installed(self, package_name: str) -> bool:
120120
try:
121121
result = subprocess.run(
122122
[sys.executable, "-m", "pip", "show", package_name],
123-
capture_output=True, text=True, timeout=10
123+
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10
124124
)
125125
return result.returncode == 0
126126
except Exception:
@@ -178,7 +178,7 @@ def install_packages(self) -> bool:
178178
try:
179179
result = subprocess.run([
180180
sys.executable, "-m", "pip", "install", "-q", pkg_spec
181-
], capture_output=True, text=True, timeout=120)
181+
], capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
182182

183183
if result.returncode == 0:
184184
progress.update(task, description=f"[green]✅ {pkg_name}[/green]")
@@ -215,7 +215,7 @@ def install_packages(self) -> bool:
215215
try:
216216
result = subprocess.run([
217217
sys.executable, "-m", "pip", "install", "-q", "-e", "."
218-
], cwd=self.config.project_root, capture_output=True, text=True, timeout=120)
218+
], cwd=self.config.project_root, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
219219

220220
if result.returncode == 0:
221221
progress.update(task, description="[green]✅ Tiny🔥Torch installed[/green]")
@@ -238,7 +238,7 @@ def install_packages(self) -> bool:
238238
"--user",
239239
"--name", "tinytorch",
240240
"--display-name", "TinyTorch (Python 3)"
241-
], capture_output=True, text=True, timeout=60)
241+
], capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60)
242242

243243
if result.returncode == 0:
244244
self.console.print("[green]✅ Jupyter kernel 'tinytorch' registered[/green]")
@@ -304,7 +304,7 @@ def create_virtual_environment(self, force: bool = False) -> bool:
304304
# Check actual hardware
305305
hw_check = sp.run(
306306
["sysctl", "-n", "machdep.cpu.brand_string"],
307-
capture_output=True, text=True
307+
capture_output=True, text=True, encoding="utf-8", errors="replace"
308308
)
309309
if "Apple" in hw_check.stdout:
310310
self.console.print("[yellow]⚠️ Detected Apple Silicon but Python is running in Rosetta (x86_64)[/yellow]")
@@ -319,12 +319,12 @@ def create_virtual_environment(self, force: bool = False) -> bool:
319319
result = subprocess.run(
320320
f'{python_exe} -m venv {venv_path}',
321321
shell=True,
322-
capture_output=True, text=True
322+
capture_output=True, text=True, encoding="utf-8", errors="replace"
323323
)
324324
else:
325325
result = subprocess.run([
326326
python_exe, "-m", "venv", str(venv_path)
327-
], capture_output=True, text=True)
327+
], capture_output=True, text=True, encoding="utf-8", errors="replace")
328328

329329
if result.returncode != 0:
330330
self.console.print(f"[red]Failed to create virtual environment: {result.stderr}[/red]")
@@ -339,7 +339,7 @@ def create_virtual_environment(self, force: bool = False) -> bool:
339339
if venv_python.exists():
340340
arch_check = subprocess.run(
341341
[str(venv_python), "-c", "import platform; print(platform.machine())"],
342-
capture_output=True, text=True
342+
capture_output=True, text=True, encoding="utf-8", errors="replace"
343343
)
344344
if arch_check.returncode == 0:
345345
venv_arch = arch_check.stdout.strip()
@@ -476,7 +476,7 @@ def check_jupyter_kernel(self) -> bool:
476476
try:
477477
result = subprocess.run(
478478
[sys.executable, "-m", "jupyter", "kernelspec", "list"],
479-
capture_output=True, text=True, timeout=10
479+
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10
480480
)
481481
return result.returncode == 0 and "tinytorch" in result.stdout
482482
except Exception:

tinytorch/tito/commands/system/health.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def _check_jupyter_kernel(self):
257257
try:
258258
result = subprocess.run(
259259
[sys.executable, "-m", "jupyter", "kernelspec", "list"],
260-
capture_output=True, text=True, timeout=10
260+
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10
261261
)
262262
if result.returncode == 0 and "tinytorch" in result.stdout:
263263
return "[green]✅ Registered[/green]", "tinytorch kernel found"
@@ -283,7 +283,7 @@ def _get_kernel_python(self):
283283
for kernel_name in ("tinytorch", "python3"):
284284
result = subprocess.run(
285285
[sys.executable, "-m", "jupyter", "kernelspec", "list", "--json"],
286-
capture_output=True, text=True, timeout=10
286+
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10
287287
)
288288
if result.returncode != 0:
289289
return None

tinytorch/tito/commands/system/update.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def _get_latest_version(self) -> Tuple[Optional[str], Optional[str]]:
104104
result = subprocess.run(
105105
['curl', '-fsSL', '--max-time', '10', self.TAGS_API],
106106
capture_output=True,
107-
text=True
107+
text=True, encoding="utf-8", errors="replace"
108108
)
109109

110110
if result.returncode != 0:
@@ -203,7 +203,7 @@ def _download_latest(self, temp_dir: Path) -> bool:
203203
str(repo_dir)
204204
],
205205
capture_output=True,
206-
text=True
206+
text=True, encoding="utf-8", errors="replace"
207207
)
208208

209209
if result.returncode != 0:
@@ -215,7 +215,7 @@ def _download_latest(self, temp_dir: Path) -> bool:
215215
result = subprocess.run(
216216
['git', 'sparse-checkout', 'set', self.SPARSE_PATH],
217217
capture_output=True,
218-
text=True,
218+
text=True, encoding="utf-8", errors="replace",
219219
cwd=repo_dir
220220
)
221221

0 commit comments

Comments
 (0)