milieux.cli.scaffold
1from abc import ABC, abstractmethod 2from dataclasses import dataclass, field 3from pathlib import Path 4import subprocess 5from typing import Literal 6 7from fancy_dataclass.cli import CLIDataclass 8 9from milieux import logger 10from milieux.utils import run_command 11 12 13# utility for scaffolding 14ScaffoldUtility = Literal['hatch', 'uv'] 15 16 17class Scaffolder(ABC): 18 """Class for setting up a project scaffold.""" 19 20 @abstractmethod 21 def make_scaffold(self, base_dir: Path, project_name: str) -> None: 22 """Creates a scaffold for a new project in the given base directory.""" 23 24 25class HatchScaffolder(Scaffolder): 26 """Project scaffolder that uses the 'hatch' command-line tool.""" 27 28 def make_scaffold(self, base_dir: Path, project_name: str) -> None: # noqa: D102 29 cmd = ['hatch', 'config', 'find'] 30 config_path = subprocess.check_output(cmd, text=True).rstrip('\n') 31 logger.info(f'Using hatch configurations in {config_path}') 32 location = base_dir / project_name 33 cmd = ['hatch', 'new', project_name, str(location)] 34 run_command(cmd) 35 36 37class UVScaffolder(Scaffolder): 38 """Project scaffolder that uses the 'uv' command-line tool.""" 39 40 def make_scaffold(self, base_dir: Path, project_name: str) -> None: # noqa: D102 41 location = base_dir / project_name 42 if not location.exists(): 43 logger.info(f'mkdir {location}') 44 location.mkdir() 45 cmd = ['uv', 'init', '--directory', str(location), '--verbose'] 46 run_command(cmd) 47 48 49SCAFFOLDERS = { 50 'hatch': HatchScaffolder, 51 'uv': UVScaffolder, 52} 53 54 55@dataclass 56class ScaffoldCmd(CLIDataclass, command_name='scaffold'): 57 """Set up a project scaffold.""" 58 project_name: str = field(metadata={'help': 'name of project'}) 59 utility: ScaffoldUtility = field(default='hatch', metadata={'help': 'utility for creating the scaffold'}) 60 61 def run(self) -> None: # noqa: D102 62 cls = SCAFFOLDERS[self.utility] 63 scaffolder = cls() 64 logger.info(f'Creating new project {self.project_name!r} with {self.utility!r} utility') 65 scaffolder.make_scaffold(Path.cwd(), self.project_name)
ScaffoldUtility =
typing.Literal['hatch', 'uv']
class
Scaffolder(abc.ABC):
18class Scaffolder(ABC): 19 """Class for setting up a project scaffold.""" 20 21 @abstractmethod 22 def make_scaffold(self, base_dir: Path, project_name: str) -> None: 23 """Creates a scaffold for a new project in the given base directory."""
Class for setting up a project scaffold.
@abstractmethod
def
make_scaffold(self, base_dir: pathlib.Path, project_name: str) -> None:
21 @abstractmethod 22 def make_scaffold(self, base_dir: Path, project_name: str) -> None: 23 """Creates a scaffold for a new project in the given base directory."""
Creates a scaffold for a new project in the given base directory.
26class HatchScaffolder(Scaffolder): 27 """Project scaffolder that uses the 'hatch' command-line tool.""" 28 29 def make_scaffold(self, base_dir: Path, project_name: str) -> None: # noqa: D102 30 cmd = ['hatch', 'config', 'find'] 31 config_path = subprocess.check_output(cmd, text=True).rstrip('\n') 32 logger.info(f'Using hatch configurations in {config_path}') 33 location = base_dir / project_name 34 cmd = ['hatch', 'new', project_name, str(location)] 35 run_command(cmd)
Project scaffolder that uses the 'hatch' command-line tool.
def
make_scaffold(self, base_dir: pathlib.Path, project_name: str) -> None:
29 def make_scaffold(self, base_dir: Path, project_name: str) -> None: # noqa: D102 30 cmd = ['hatch', 'config', 'find'] 31 config_path = subprocess.check_output(cmd, text=True).rstrip('\n') 32 logger.info(f'Using hatch configurations in {config_path}') 33 location = base_dir / project_name 34 cmd = ['hatch', 'new', project_name, str(location)] 35 run_command(cmd)
Creates a scaffold for a new project in the given base directory.
38class UVScaffolder(Scaffolder): 39 """Project scaffolder that uses the 'uv' command-line tool.""" 40 41 def make_scaffold(self, base_dir: Path, project_name: str) -> None: # noqa: D102 42 location = base_dir / project_name 43 if not location.exists(): 44 logger.info(f'mkdir {location}') 45 location.mkdir() 46 cmd = ['uv', 'init', '--directory', str(location), '--verbose'] 47 run_command(cmd)
Project scaffolder that uses the 'uv' command-line tool.
def
make_scaffold(self, base_dir: pathlib.Path, project_name: str) -> None:
41 def make_scaffold(self, base_dir: Path, project_name: str) -> None: # noqa: D102 42 location = base_dir / project_name 43 if not location.exists(): 44 logger.info(f'mkdir {location}') 45 location.mkdir() 46 cmd = ['uv', 'init', '--directory', str(location), '--verbose'] 47 run_command(cmd)
Creates a scaffold for a new project in the given base directory.
SCAFFOLDERS =
{'hatch': <class 'HatchScaffolder'>, 'uv': <class 'UVScaffolder'>}
@dataclass
class
ScaffoldCmd56@dataclass 57class ScaffoldCmd(CLIDataclass, command_name='scaffold'): 58 """Set up a project scaffold.""" 59 project_name: str = field(metadata={'help': 'name of project'}) 60 utility: ScaffoldUtility = field(default='hatch', metadata={'help': 'utility for creating the scaffold'}) 61 62 def run(self) -> None: # noqa: D102 63 cls = SCAFFOLDERS[self.utility] 64 scaffolder = cls() 65 logger.info(f'Creating new project {self.project_name!r} with {self.utility!r} utility') 66 scaffolder.make_scaffold(Path.cwd(), self.project_name)
Set up a project scaffold.
def
run(self) -> None:
62 def run(self) -> None: # noqa: D102 63 cls = SCAFFOLDERS[self.utility] 64 scaffolder = cls() 65 logger.info(f'Creating new project {self.project_name!r} with {self.utility!r} utility') 66 scaffolder.make_scaffold(Path.cwd(), self.project_name)
Runs the main body of the program.
Subclasses should implement this to provide custom behavior.
If the class has a subcommand defined, and it is an instance of CLIDataclass
, the default implementation of run
will be to call the subcommand's own implementation.