initial import
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import logging
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger("slam.build_tool")
|
||||
|
||||
|
||||
class BuildError(Exception):
|
||||
"""
|
||||
Thrown when a subprocess command fails.
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class BuildTool:
|
||||
"""
|
||||
Thin wrapper around poetry and some dev tools.
|
||||
"""
|
||||
|
||||
poetry: Path = Path("poetry")
|
||||
verbose: bool = False
|
||||
|
||||
def do(self, *command_line) -> bool:
|
||||
"""
|
||||
Execute a poetry subprocess.
|
||||
"""
|
||||
cmdline = [str(self.poetry)] + list(command_line)
|
||||
logger.info(" ".join(cmdline))
|
||||
if self.verbose:
|
||||
result = subprocess.run(cmdline)
|
||||
return result.returncode
|
||||
|
||||
result = subprocess.run(cmdline, capture_output=True)
|
||||
logger.debug(f"{result = }")
|
||||
if result.stdout:
|
||||
# log the output and optional print it
|
||||
logger.info(result.stdout)
|
||||
if self.verbose:
|
||||
print(result.stdout.decode("utf-8"))
|
||||
if result.stderr:
|
||||
# log the error and optionally print it
|
||||
if self.verbose:
|
||||
print(result.stderr.decode("utf-8"))
|
||||
if result.returncode != 0:
|
||||
logger.error(result.stderr)
|
||||
raise BuildError(f"Command Failed: {cmdline}")
|
||||
logger.info(result.stderr)
|
||||
return result.returncode
|
||||
|
||||
def run(self, *command_line):
|
||||
"""
|
||||
Same as do(), but prepend a 'run' subcommand.
|
||||
"""
|
||||
return self.do("run", *command_line)
|
||||
|
||||
def install(self) -> bool:
|
||||
return self.do("install")
|
||||
|
||||
def auto_format(self) -> bool:
|
||||
self.run("isort", "src", "test")
|
||||
self.run("autoflake", "src", "test")
|
||||
self.run("black", "src", "test")
|
||||
return 0
|
||||
|
||||
def test(self, args) -> bool:
|
||||
return self.run("pytest", *args)
|
||||
|
||||
def build(self) -> bool:
|
||||
print("Formatting...")
|
||||
success = self.auto_format()
|
||||
print("Testing...")
|
||||
success += self.test([])
|
||||
print("Installing...")
|
||||
success += self.install()
|
||||
print("Building...")
|
||||
success += self.do("build")
|
||||
return success
|
||||
@@ -0,0 +1,75 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
from rich.logging import RichHandler
|
||||
|
||||
from poetry_slam.build_tool import BuildTool
|
||||
|
||||
app = typer.Typer()
|
||||
app_state = dict()
|
||||
|
||||
logger = logging.getLogger("slam.cli")
|
||||
|
||||
|
||||
@app.callback(invoke_without_command=True)
|
||||
def main(
|
||||
context: typer.Context,
|
||||
verbose: bool = typer.Option(False, help="Enable verbose output."),
|
||||
log_level: str = typer.Option("error", help=" Set the log level."),
|
||||
poetry: Optional[Path] = typer.Option(
|
||||
"poetry",
|
||||
help="Path to the poetry executable; defaults to the first in your path.",
|
||||
),
|
||||
):
|
||||
logging.basicConfig(
|
||||
format="%(message)s",
|
||||
level=getattr(logging, log_level.upper()),
|
||||
handlers=[RichHandler(rich_tracebacks=True, tracebacks_suppress=[typer])],
|
||||
)
|
||||
app_state["build_tool"] = BuildTool(poetry=poetry, verbose=verbose)
|
||||
if context.invoked_subcommand is None:
|
||||
logger.debug("No command specified; defaulting to build.")
|
||||
build()
|
||||
|
||||
|
||||
@app.command()
|
||||
def format():
|
||||
"""
|
||||
Run isort, autoflake, and black on the src/ and test/ directories.
|
||||
"""
|
||||
returncode = app_state["build_tool"].auto_format()
|
||||
print(f"slam format: {'SUCCESS' if returncode == 0 else 'ERROR'}")
|
||||
return returncode
|
||||
|
||||
|
||||
@app.command()
|
||||
def build():
|
||||
"""
|
||||
Calls format, test, and install before invoking 'poetry build'.
|
||||
"""
|
||||
returncode = app_state["build_tool"].build()
|
||||
print(f"slam build: {'SUCCESS' if returncode == 0 else 'ERROR'}")
|
||||
return returncode
|
||||
|
||||
|
||||
@app.command()
|
||||
def install():
|
||||
"""
|
||||
Synonym for 'poetry install'
|
||||
"""
|
||||
returncode = app_state["build_tool"].install()
|
||||
print(f"slam install: {'SUCCESS' if returncode == 0 else 'ERROR'}")
|
||||
return returncode
|
||||
|
||||
|
||||
@app.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": True})
|
||||
def test(context: typer.Context):
|
||||
"""
|
||||
Synonym for 'poetry run pytest' Output is always verbose.
|
||||
"""
|
||||
app_state["build_tool"].verbose = True
|
||||
returncode = app_state["build_tool"].test(context.args)
|
||||
print(f"slam test: {'SUCCESS' if returncode == 0 else 'ERROR'}")
|
||||
return returncode
|
||||
Reference in New Issue
Block a user