154 lines
5.3 KiB
Python
154 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Rich TUI linting interface for Sandpypi."""
|
|
import logging
|
|
import subprocess
|
|
import time
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from rich.console import Console
|
|
from rich.layout import Layout
|
|
from rich.live import Live
|
|
from rich.panel import Panel
|
|
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
|
|
|
|
class LinterTUI:
|
|
"""Main classs for the linting TUI."""
|
|
|
|
def __init__(self):
|
|
self.console = Console()
|
|
self.layout = Layout()
|
|
self.layout.split_column(
|
|
Layout(name="output", ratio=8), Layout(name="progress", ratio=2)
|
|
)
|
|
self.setup_logging()
|
|
|
|
def setup_logging(self):
|
|
"""Logging"""
|
|
self.log_dir = Path("logs")
|
|
self.log_dir.mkdir(exist_ok=True)
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
self.log_file = f"lint_{timestamp}.log"
|
|
logging.basicConfig(
|
|
filename=self.log_dir / self.log_file,
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(levelname)s - %(message)s",
|
|
)
|
|
|
|
def get_target_path(self):
|
|
"""Get thee target path for linting."""
|
|
paths = input(
|
|
"\nEnter paths to lint (comma-separated NO SPACES)\
|
|
or press Enter for default [src,tests]: "
|
|
).strip()
|
|
target_paths = paths.split(",") if paths else ["src", "tests"]
|
|
|
|
# Convert to absolute paths and include subdirectories
|
|
abs_paths = []
|
|
for path in target_paths:
|
|
base_path = Path(path).absolute()
|
|
if base_path.is_dir():
|
|
abs_paths.extend(str(p) for p in base_path.rglob("*.py"))
|
|
else:
|
|
abs_paths.append(str(base_path))
|
|
|
|
return abs_paths
|
|
|
|
def run_linters(self, selected_linters, paths):
|
|
"""Run the linters on the selected paths."""
|
|
progress = Progress(
|
|
SpinnerColumn(),
|
|
TextColumn("[progress.description]{task.description}"),
|
|
BarColumn(),
|
|
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
expand=True,
|
|
)
|
|
|
|
self.layout["progress"].update(progress)
|
|
|
|
with Live(self.layout, refresh_per_second=5, screen=True):
|
|
task = progress.add_task(
|
|
"[cyan]Linting...", total=len(selected_linters)
|
|
)
|
|
|
|
for linter in selected_linters:
|
|
progress.update(task, description=f"[cyan]Running {linter}...")
|
|
|
|
cmd = [linter] + paths
|
|
with subprocess.Popen(
|
|
cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True,
|
|
bufsize=1,
|
|
) as process:
|
|
current_output = []
|
|
linter_name = linter.split()[0]
|
|
while True:
|
|
output = process.stdout.readline()
|
|
if output == "" and process.poll() is not None:
|
|
break
|
|
if output:
|
|
current_output.append(output.strip())
|
|
# Log each line of output
|
|
logging.info("Running %s on %s", linter, paths)
|
|
logging.info("Output: %s", output.strip())
|
|
|
|
self.layout["output"].update(
|
|
Panel(
|
|
"\n".join(current_output[-20:]),
|
|
title=f"[bold green]{linter}\
|
|
{linter_name}",
|
|
border_style="green",
|
|
)
|
|
)
|
|
time.sleep(0.1)
|
|
|
|
# Log any errors
|
|
if not current_output:
|
|
stderr_output = process.stderr.read()
|
|
output_text = stderr_output or "No output"
|
|
self.layout["output"].update(
|
|
Panel(
|
|
output_text,
|
|
title=f"[bold green]{linter} Output",
|
|
border_style="green",
|
|
)
|
|
)
|
|
if stderr_output:
|
|
logging.error("Errors: %s", stderr_output)
|
|
|
|
progress.advance(task)
|
|
|
|
final_message = Panel(
|
|
"[bold green]✨ Linting Complete! ✨\n"
|
|
"[cyan]Check the logs for detailed results.\n"
|
|
f"[green]Logs saved to: {self.log_dir / self.log_file}\n"
|
|
"[yellow]Press Enter to exit...",
|
|
title="[bold blue]Sandpypi Linter",
|
|
border_style="green",
|
|
padding=(2, 4),
|
|
)
|
|
|
|
self.layout["output"].update(final_message)
|
|
self.layout["progress"].update(
|
|
Panel("[bold green]100% Complete!", border_style="green")
|
|
)
|
|
|
|
input()
|
|
|
|
|
|
def main():
|
|
"""Runner for the Linting TUI."""
|
|
tui = LinterTUI()
|
|
linters = ["black", "isort", "mypy", "flake8", "pylint"]
|
|
paths = tui.get_target_path()
|
|
tui.run_linters(linters, paths)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
# scripts\run_tests.py
|
|
# !/usr/bin/env python3
|