mirror of
https://github.com/MLSysBook/TinyTorch.git
synced 2026-04-30 10:13:57 -05:00
Remove redundant py_to_notebook tool in favor of Jupytext
- Delete bin/py_to_notebook.py and tito/tools/py_to_notebook.py - Update notebooks command to use Jupytext directly - Jupytext is already configured in all *_dev.py files - Simpler, more standard workflow using established tools - Better integration with NBDev ecosystem Benefits: - Eliminates duplicate conversion tools - Uses industry-standard Jupytext instead of custom tool - Reduces maintenance burden - Better error handling and compatibility
This commit is contained in:
@@ -54,7 +54,7 @@ tito/
|
|||||||
│ └── notebooks.py # Notebooks command
|
│ └── notebooks.py # Notebooks command
|
||||||
└── tools/ # CLI tools
|
└── tools/ # CLI tools
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
└── py_to_notebook.py # Conversion tool
|
└── tito # Main CLI script
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🎯 Design Patterns Applied
|
## 🎯 Design Patterns Applied
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Convert Python files with cell markers to Jupyter notebooks.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py
|
|
||||||
python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py --output custom_name.ipynb
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def convert_py_to_notebook(py_file: Path, output_file: Path = None):
|
|
||||||
"""Convert Python file with cell markers to notebook."""
|
|
||||||
|
|
||||||
if not py_file.exists():
|
|
||||||
print(f"❌ File not found: {py_file}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Read the Python file
|
|
||||||
with open(py_file, 'r') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# Split into cells based on # %% markers
|
|
||||||
cells = re.split(r'^# %%.*$', content, flags=re.MULTILINE)
|
|
||||||
cells = [cell.strip() for cell in cells if cell.strip()]
|
|
||||||
|
|
||||||
# Create notebook structure
|
|
||||||
notebook = {
|
|
||||||
'cells': [],
|
|
||||||
'metadata': {
|
|
||||||
'kernelspec': {
|
|
||||||
'display_name': 'Python 3',
|
|
||||||
'language': 'python',
|
|
||||||
'name': 'python3'
|
|
||||||
},
|
|
||||||
'language_info': {
|
|
||||||
'name': 'python',
|
|
||||||
'version': '3.8.0'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'nbformat': 4,
|
|
||||||
'nbformat_minor': 4
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, cell_content in enumerate(cells):
|
|
||||||
if not cell_content:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if this is a markdown cell
|
|
||||||
if cell_content.startswith('# ') and '\n' in cell_content:
|
|
||||||
lines = cell_content.split('\n')
|
|
||||||
if lines[0].startswith('# ') and not any(line.strip() and not line.startswith('#') for line in lines[:5]):
|
|
||||||
# This looks like a markdown cell
|
|
||||||
cell = {
|
|
||||||
'cell_type': 'markdown',
|
|
||||||
'metadata': {},
|
|
||||||
'source': []
|
|
||||||
}
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if line.startswith('# '):
|
|
||||||
cell['source'].append(line[2:] + '\n')
|
|
||||||
elif line.startswith('#'):
|
|
||||||
cell['source'].append(line[1:] + '\n')
|
|
||||||
elif line.strip() == '':
|
|
||||||
cell['source'].append('\n')
|
|
||||||
|
|
||||||
notebook['cells'].append(cell)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Code cell
|
|
||||||
cell = {
|
|
||||||
'cell_type': 'code',
|
|
||||||
'execution_count': None,
|
|
||||||
'metadata': {},
|
|
||||||
'outputs': [],
|
|
||||||
'source': []
|
|
||||||
}
|
|
||||||
|
|
||||||
for line in cell_content.split('\n'):
|
|
||||||
cell['source'].append(line + '\n')
|
|
||||||
|
|
||||||
# Remove trailing newline from last line
|
|
||||||
if cell['source'] and cell['source'][-1].endswith('\n'):
|
|
||||||
cell['source'][-1] = cell['source'][-1][:-1]
|
|
||||||
|
|
||||||
notebook['cells'].append(cell)
|
|
||||||
|
|
||||||
# Determine output file
|
|
||||||
if output_file is None:
|
|
||||||
output_file = py_file.with_suffix('.ipynb')
|
|
||||||
|
|
||||||
# Write notebook
|
|
||||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
with open(output_file, 'w') as f:
|
|
||||||
json.dump(notebook, f, indent=2)
|
|
||||||
|
|
||||||
print(f"✅ Converted {py_file} → {output_file}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description="Convert Python files to Jupyter notebooks")
|
|
||||||
parser.add_argument('input_file', type=Path, help='Input Python file')
|
|
||||||
parser.add_argument('--output', '-o', type=Path, help='Output notebook file')
|
|
||||||
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
success = convert_py_to_notebook(args.input_file, args.output)
|
|
||||||
|
|
||||||
if not success:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if args.verbose:
|
|
||||||
print("🎉 Conversion complete!")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Notebooks command for building Jupyter notebooks from Python files.
|
Notebooks command for building Jupyter notebooks from Python files using Jupytext.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -15,7 +15,7 @@ from .base import BaseCommand
|
|||||||
from ..core.exceptions import ExecutionError, ModuleNotFoundError
|
from ..core.exceptions import ExecutionError, ModuleNotFoundError
|
||||||
|
|
||||||
class NotebooksCommand(BaseCommand):
|
class NotebooksCommand(BaseCommand):
|
||||||
"""Command to build Jupyter notebooks from Python files."""
|
"""Command to build Jupyter notebooks from Python files using Jupytext."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
@@ -62,33 +62,31 @@ class NotebooksCommand(BaseCommand):
|
|||||||
return dev_files
|
return dev_files
|
||||||
|
|
||||||
def _convert_file(self, dev_file: Path) -> Tuple[bool, str]:
|
def _convert_file(self, dev_file: Path) -> Tuple[bool, str]:
|
||||||
"""Convert a single Python file to notebook."""
|
"""Convert a single Python file to notebook using Jupytext."""
|
||||||
try:
|
try:
|
||||||
py_to_notebook_tool = self.config.bin_dir / "py_to_notebook.py"
|
# Use Jupytext to convert Python file to notebook
|
||||||
result = subprocess.run([
|
result = subprocess.run([
|
||||||
sys.executable, str(py_to_notebook_tool), str(dev_file)
|
"jupytext", "--to", "notebook", str(dev_file)
|
||||||
], capture_output=True, text=True, timeout=30)
|
], capture_output=True, text=True, timeout=30, cwd=dev_file.parent)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
# Extract success message from the tool output
|
notebook_file = dev_file.with_suffix('.ipynb')
|
||||||
output_lines = result.stdout.strip().split('\n')
|
return True, f"{dev_file.name} → {notebook_file.name}"
|
||||||
success_msg = output_lines[-1] if output_lines else f"{dev_file.name} → {dev_file.with_suffix('.ipynb').name}"
|
|
||||||
# Clean up the message
|
|
||||||
clean_msg = success_msg.replace('✅ ', '').replace('Converted ', '')
|
|
||||||
return True, clean_msg
|
|
||||||
else:
|
else:
|
||||||
error_msg = result.stderr.strip() if result.stderr.strip() else "Conversion failed"
|
error_msg = result.stderr.strip() if result.stderr.strip() else "Conversion failed"
|
||||||
return False, error_msg
|
return False, error_msg
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return False, "Conversion timed out"
|
return False, "Conversion timed out"
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False, "Jupytext not found. Install with: pip install jupytext"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, f"Error: {str(e)}"
|
return False, f"Error: {str(e)}"
|
||||||
|
|
||||||
def run(self, args: Namespace) -> int:
|
def run(self, args: Namespace) -> int:
|
||||||
"""Execute the notebooks command."""
|
"""Execute the notebooks command."""
|
||||||
self.console.print(Panel(
|
self.console.print(Panel(
|
||||||
"📓 Building Notebooks from Python Files",
|
"📓 Building Notebooks from Python Files (using Jupytext)",
|
||||||
title="Notebook Generation",
|
title="Notebook Generation",
|
||||||
border_style="bright_cyan"
|
border_style="bright_cyan"
|
||||||
))
|
))
|
||||||
@@ -149,8 +147,8 @@ class NotebooksCommand(BaseCommand):
|
|||||||
summary_text.append("\n💡 Next steps:\n", style="bold yellow")
|
summary_text.append("\n💡 Next steps:\n", style="bold yellow")
|
||||||
summary_text.append(" • Open notebooks with: jupyter lab\n", style="white")
|
summary_text.append(" • Open notebooks with: jupyter lab\n", style="white")
|
||||||
summary_text.append(" • Work interactively in the notebooks\n", style="white")
|
summary_text.append(" • Work interactively in the notebooks\n", style="white")
|
||||||
summary_text.append(" • Export code with: tito sync\n", style="white")
|
summary_text.append(" • Export code with: tito package sync\n", style="white")
|
||||||
summary_text.append(" • Run tests with: tito test\n", style="white")
|
summary_text.append(" • Run tests with: tito module test\n", style="white")
|
||||||
|
|
||||||
border_style = "green" if error_count == 0 else "yellow"
|
border_style = "green" if error_count == 0 else "yellow"
|
||||||
self.console.print(Panel(
|
self.console.print(Panel(
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ CLI Tools package.
|
|||||||
Contains utility tools used by the CLI commands.
|
Contains utility tools used by the CLI commands.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .py_to_notebook import convert_py_to_notebook
|
# No tools currently - py_to_notebook removed in favor of Jupytext
|
||||||
|
|
||||||
__all__ = ['convert_py_to_notebook']
|
__all__ = []
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Convert Python files with cell markers to Jupyter notebooks.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py
|
|
||||||
python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py --output custom_name.ipynb
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def convert_py_to_notebook(py_file: Path, output_file: Path = None):
|
|
||||||
"""Convert Python file with cell markers to notebook."""
|
|
||||||
|
|
||||||
if not py_file.exists():
|
|
||||||
print(f"❌ File not found: {py_file}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Read the Python file
|
|
||||||
with open(py_file, 'r') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# Split into cells based on # %% markers
|
|
||||||
cells = re.split(r'^# %%.*$', content, flags=re.MULTILINE)
|
|
||||||
cells = [cell.strip() for cell in cells if cell.strip()]
|
|
||||||
|
|
||||||
# Create notebook structure
|
|
||||||
notebook = {
|
|
||||||
'cells': [],
|
|
||||||
'metadata': {
|
|
||||||
'kernelspec': {
|
|
||||||
'display_name': 'Python 3',
|
|
||||||
'language': 'python',
|
|
||||||
'name': 'python3'
|
|
||||||
},
|
|
||||||
'language_info': {
|
|
||||||
'name': 'python',
|
|
||||||
'version': '3.8.0'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'nbformat': 4,
|
|
||||||
'nbformat_minor': 4
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, cell_content in enumerate(cells):
|
|
||||||
if not cell_content:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if this is a markdown cell
|
|
||||||
if cell_content.startswith('# ') and '\n' in cell_content:
|
|
||||||
lines = cell_content.split('\n')
|
|
||||||
if lines[0].startswith('# ') and not any(line.strip() and not line.startswith('#') for line in lines[:5]):
|
|
||||||
# This looks like a markdown cell
|
|
||||||
cell = {
|
|
||||||
'cell_type': 'markdown',
|
|
||||||
'metadata': {},
|
|
||||||
'source': []
|
|
||||||
}
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if line.startswith('# '):
|
|
||||||
cell['source'].append(line[2:] + '\n')
|
|
||||||
elif line.startswith('#'):
|
|
||||||
cell['source'].append(line[1:] + '\n')
|
|
||||||
elif line.strip() == '':
|
|
||||||
cell['source'].append('\n')
|
|
||||||
|
|
||||||
notebook['cells'].append(cell)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Code cell
|
|
||||||
cell = {
|
|
||||||
'cell_type': 'code',
|
|
||||||
'execution_count': None,
|
|
||||||
'metadata': {},
|
|
||||||
'outputs': [],
|
|
||||||
'source': []
|
|
||||||
}
|
|
||||||
|
|
||||||
for line in cell_content.split('\n'):
|
|
||||||
cell['source'].append(line + '\n')
|
|
||||||
|
|
||||||
# Remove trailing newline from last line
|
|
||||||
if cell['source'] and cell['source'][-1].endswith('\n'):
|
|
||||||
cell['source'][-1] = cell['source'][-1][:-1]
|
|
||||||
|
|
||||||
notebook['cells'].append(cell)
|
|
||||||
|
|
||||||
# Determine output file
|
|
||||||
if output_file is None:
|
|
||||||
output_file = py_file.with_suffix('.ipynb')
|
|
||||||
|
|
||||||
# Write notebook
|
|
||||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
with open(output_file, 'w') as f:
|
|
||||||
json.dump(notebook, f, indent=2)
|
|
||||||
|
|
||||||
print(f"✅ Converted {py_file} → {output_file}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description="Convert Python files to Jupyter notebooks")
|
|
||||||
parser.add_argument('input_file', type=Path, help='Input Python file')
|
|
||||||
parser.add_argument('--output', '-o', type=Path, help='Output notebook file')
|
|
||||||
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
success = convert_py_to_notebook(args.input_file, args.output)
|
|
||||||
|
|
||||||
if not success:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if args.verbose:
|
|
||||||
print("🎉 Conversion complete!")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user