milieux.cli.distro
1from dataclasses import dataclass, field 2from datetime import datetime 3from typing import Any, Optional, Union 4 5from fancy_dataclass.cli import CLIDataclass 6 7from milieux.distro import Distro 8from milieux.errors import DistroExistsError 9from milieux.utils import NonemptyPrompt, distro_sty 10 11 12def _get_name_field(required: bool) -> Any: 13 # make 'name' a positional argument 14 metadata = {'args': ['name'], 'help': 'name of distro'} 15 if not required: 16 metadata['nargs'] = '?' 17 return field(default=None, metadata=metadata) 18 19_force_field = field( 20 default=False, 21 metadata={ 22 'args': ['-f', '--force'], 23 'help': 'force overwrite of distro if it exists' 24 } 25) 26 27 28@dataclass 29class DistroList(CLIDataclass, command_name='list'): 30 """List all distros.""" 31 32 def run(self) -> None: 33 Distro.list() 34 35 36@dataclass 37class DistroLock(CLIDataclass, command_name='lock'): 38 """Lock dependencies in a distro.""" 39 name: str = _get_name_field(required=True) 40 new: Optional[str] = field(default=None, metadata={'nargs': '?', 'const': '', 'help': 'name of new locked distro'}) 41 force: bool = _force_field 42 annotate: bool = field(default=False, metadata={'help': 'include comment annotations indicating the source of each package'}) 43 44 def run(self) -> None: 45 distro = Distro(self.name) 46 if self.new is None: 47 new_name = None 48 else: 49 if self.new == '': 50 now = datetime.now() 51 new_name = self.name + '.' + now.strftime('%Y%m%d') 52 else: 53 new_name = self.new 54 if (not self.force) and Distro(new_name).exists(): 55 raise DistroExistsError(f'Distro {distro_sty(new_name)} already exists') 56 output = distro.lock(annotate=self.annotate) 57 if self.new is None: # print new file to stdout 58 print(output) 59 else: 60 assert new_name is not None 61 Distro.new(new_name, packages=output.splitlines(), force=self.force) 62 63 64@dataclass 65class DistroNew(CLIDataclass, command_name='new'): 66 """Create a new distro.""" 67 name: str = _get_name_field(required=False) 68 packages: list[str] = field( 69 default_factory=list, 70 metadata={'nargs': '+', 'args': ['-p', '--packages'], 'help': 'list of packages to include in the distro'} 71 ) 72 requirements: list[str] = field( 73 default_factory=list, 74 metadata={'nargs': '+', 'args': ['-r', '--requirements'], 'help': 'requirements file(s) listing packages'} 75 ) 76 distros: list[str] = field( 77 default_factory=list, 78 metadata={'nargs': '+', 'args': ['-d', '--distros'], 'help': 'existing distro name(s) to include'} 79 ) 80 force: bool = _force_field 81 82 def run(self) -> None: 83 name = self.name or NonemptyPrompt.ask('Name of distro') 84 if (not self.packages) and (not self.requirements) and (not self.distros): 85 packages_str = NonemptyPrompt.ask('Packages to include (comma-separated)') 86 packages = [tok.strip() for tok in packages_str.split(',')] 87 else: 88 packages = self.packages 89 Distro.new(name, packages=packages, requirements=self.requirements, distros=self.distros, force=self.force) 90 91 92@dataclass 93class DistroRemove(CLIDataclass, command_name='remove'): 94 """Remove a distro.""" 95 name: str = _get_name_field(required=True) 96 97 def run(self) -> None: 98 Distro(self.name).remove() 99 100 101@dataclass 102class DistroShow(CLIDataclass, command_name='show'): 103 """Show the contents of a distro.""" 104 name: str = _get_name_field(required=True) 105 106 def run(self) -> None: 107 Distro(self.name).show() 108 109 110@dataclass 111class DistroCmd(CLIDataclass, command_name='distro'): 112 """Manage distros.""" 113 subcommand: Union[ 114 DistroList, 115 DistroLock, 116 DistroNew, 117 DistroRemove, 118 DistroShow, 119 ] = field(metadata={'subcommand': True})
29@dataclass 30class DistroList(CLIDataclass, command_name='list'): 31 """List all distros.""" 32 33 def run(self) -> None: 34 Distro.list()
List all distros.
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.
37@dataclass 38class DistroLock(CLIDataclass, command_name='lock'): 39 """Lock dependencies in a distro.""" 40 name: str = _get_name_field(required=True) 41 new: Optional[str] = field(default=None, metadata={'nargs': '?', 'const': '', 'help': 'name of new locked distro'}) 42 force: bool = _force_field 43 annotate: bool = field(default=False, metadata={'help': 'include comment annotations indicating the source of each package'}) 44 45 def run(self) -> None: 46 distro = Distro(self.name) 47 if self.new is None: 48 new_name = None 49 else: 50 if self.new == '': 51 now = datetime.now() 52 new_name = self.name + '.' + now.strftime('%Y%m%d') 53 else: 54 new_name = self.new 55 if (not self.force) and Distro(new_name).exists(): 56 raise DistroExistsError(f'Distro {distro_sty(new_name)} already exists') 57 output = distro.lock(annotate=self.annotate) 58 if self.new is None: # print new file to stdout 59 print(output) 60 else: 61 assert new_name is not None 62 Distro.new(new_name, packages=output.splitlines(), force=self.force)
Lock dependencies in a distro.
45 def run(self) -> None: 46 distro = Distro(self.name) 47 if self.new is None: 48 new_name = None 49 else: 50 if self.new == '': 51 now = datetime.now() 52 new_name = self.name + '.' + now.strftime('%Y%m%d') 53 else: 54 new_name = self.new 55 if (not self.force) and Distro(new_name).exists(): 56 raise DistroExistsError(f'Distro {distro_sty(new_name)} already exists') 57 output = distro.lock(annotate=self.annotate) 58 if self.new is None: # print new file to stdout 59 print(output) 60 else: 61 assert new_name is not None 62 Distro.new(new_name, packages=output.splitlines(), force=self.force)
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.
65@dataclass 66class DistroNew(CLIDataclass, command_name='new'): 67 """Create a new distro.""" 68 name: str = _get_name_field(required=False) 69 packages: list[str] = field( 70 default_factory=list, 71 metadata={'nargs': '+', 'args': ['-p', '--packages'], 'help': 'list of packages to include in the distro'} 72 ) 73 requirements: list[str] = field( 74 default_factory=list, 75 metadata={'nargs': '+', 'args': ['-r', '--requirements'], 'help': 'requirements file(s) listing packages'} 76 ) 77 distros: list[str] = field( 78 default_factory=list, 79 metadata={'nargs': '+', 'args': ['-d', '--distros'], 'help': 'existing distro name(s) to include'} 80 ) 81 force: bool = _force_field 82 83 def run(self) -> None: 84 name = self.name or NonemptyPrompt.ask('Name of distro') 85 if (not self.packages) and (not self.requirements) and (not self.distros): 86 packages_str = NonemptyPrompt.ask('Packages to include (comma-separated)') 87 packages = [tok.strip() for tok in packages_str.split(',')] 88 else: 89 packages = self.packages 90 Distro.new(name, packages=packages, requirements=self.requirements, distros=self.distros, force=self.force)
Create a new distro.
83 def run(self) -> None: 84 name = self.name or NonemptyPrompt.ask('Name of distro') 85 if (not self.packages) and (not self.requirements) and (not self.distros): 86 packages_str = NonemptyPrompt.ask('Packages to include (comma-separated)') 87 packages = [tok.strip() for tok in packages_str.split(',')] 88 else: 89 packages = self.packages 90 Distro.new(name, packages=packages, requirements=self.requirements, distros=self.distros, force=self.force)
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.
93@dataclass 94class DistroRemove(CLIDataclass, command_name='remove'): 95 """Remove a distro.""" 96 name: str = _get_name_field(required=True) 97 98 def run(self) -> None: 99 Distro(self.name).remove()
Remove a distro.
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.
102@dataclass 103class DistroShow(CLIDataclass, command_name='show'): 104 """Show the contents of a distro.""" 105 name: str = _get_name_field(required=True) 106 107 def run(self) -> None: 108 Distro(self.name).show()
Show the contents of a distro.
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.
111@dataclass 112class DistroCmd(CLIDataclass, command_name='distro'): 113 """Manage distros.""" 114 subcommand: Union[ 115 DistroList, 116 DistroLock, 117 DistroNew, 118 DistroRemove, 119 DistroShow, 120 ] = field(metadata={'subcommand': True})
Manage distros.