Properly handle extra and operands
This commit is contained in:
		
							parent
							
								
									2268f94d1f
								
							
						
					
					
						commit
						cc9f7c7b3f
					
				
					 6 changed files with 71 additions and 26 deletions
				
			
		| 
						 | 
					@ -95,7 +95,6 @@ def main() -> int:
 | 
				
			||||||
    except RuntimeError as e:
 | 
					    except RuntimeError as e:
 | 
				
			||||||
        logging.exception(e)
 | 
					        logging.exception(e)
 | 
				
			||||||
        vt100.error(str(e))
 | 
					        vt100.error(str(e))
 | 
				
			||||||
        cli.usage()
 | 
					 | 
				
			||||||
        return 1
 | 
					        return 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    except KeyboardInterrupt:
 | 
					    except KeyboardInterrupt:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -389,7 +389,7 @@ def _():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BuildArgs(model.TargetArgs):
 | 
					class BuildArgs(model.TargetArgs):
 | 
				
			||||||
    component: str = cli.operand("component", "Component to build")
 | 
					    component: str = cli.operand("component", "Component to build", default="__main__")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@cli.command("b", "builder/build", "Build a component or all components")
 | 
					@cli.command("b", "builder/build", "Build a component or all components")
 | 
				
			||||||
| 
						 | 
					@ -409,16 +409,14 @@ class RunArgs(BuildArgs, shell.DebugArgs, shell.ProfileArgs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@cli.command("r", "builder/run", "Run a component")
 | 
					@cli.command("r", "builder/run", "Run a component")
 | 
				
			||||||
def runCmd(args: RunArgs):
 | 
					def runCmd(args: RunArgs):
 | 
				
			||||||
    componentSpec = args.consumeArg() or "__main__"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args.props |= {"debug": args.debug}
 | 
					    args.props |= {"debug": args.debug}
 | 
				
			||||||
    scope = TargetScope.use(args)
 | 
					    scope = TargetScope.use(args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    component = scope.registry.lookup(
 | 
					    component = scope.registry.lookup(
 | 
				
			||||||
        componentSpec, model.Component, includeProvides=True
 | 
					        args.component, model.Component, includeProvides=True
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    if component is None:
 | 
					    if component is None:
 | 
				
			||||||
        raise RuntimeError(f"Component {componentSpec} not found")
 | 
					        raise RuntimeError(f"Component {args.component} not found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    product = build(scope, component)[0]
 | 
					    product = build(scope, component)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -383,8 +383,8 @@ def arg(
 | 
				
			||||||
    return Field(FieldKind.FLAG, shortName, longName, description, default)
 | 
					    return Field(FieldKind.FLAG, shortName, longName, description, default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def operand(longName: str = "", description: str = "") -> Any:
 | 
					def operand(longName: str = "", description: str = "", default: Any = None) -> Any:
 | 
				
			||||||
    return Field(FieldKind.OPERAND, None, longName, description)
 | 
					    return Field(FieldKind.OPERAND, None, longName, description, default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def extra(longName: str = "", description: str = "") -> Any:
 | 
					def extra(longName: str = "", description: str = "") -> Any:
 | 
				
			||||||
| 
						 | 
					@ -475,8 +475,14 @@ class Schema:
 | 
				
			||||||
                return arg
 | 
					                return arg
 | 
				
			||||||
        raise ValueError(f"Unknown argument '{key}'")
 | 
					        raise ValueError(f"Unknown argument '{key}'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _setOperand(self, tok: OperandToken):
 | 
					    def _setOperand(self, obj: Any, value: Any):
 | 
				
			||||||
        return
 | 
					        if len(self.operands) == 0:
 | 
				
			||||||
 | 
					            raise ValueError(f"Unexpected operand '{value}'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for operand in self.operands:
 | 
				
			||||||
 | 
					            if operand.getAttr(obj) is None or operand.isList():
 | 
				
			||||||
 | 
					                operand.putValue(obj, value)
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _instanciate(self) -> Any:
 | 
					    def _instanciate(self) -> Any:
 | 
				
			||||||
        if self.typ is None:
 | 
					        if self.typ is None:
 | 
				
			||||||
| 
						 | 
					@ -484,6 +490,16 @@ class Schema:
 | 
				
			||||||
        res = self.typ()
 | 
					        res = self.typ()
 | 
				
			||||||
        for arg in self.args:
 | 
					        for arg in self.args:
 | 
				
			||||||
            arg.setDefault(res)
 | 
					            arg.setDefault(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for operand in self.operands:
 | 
				
			||||||
 | 
					            if operand.isList():
 | 
				
			||||||
 | 
					                setattr(res, operand._fieldName, [])
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                setattr(res, operand._fieldName, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.extras:
 | 
				
			||||||
 | 
					            self.extras.setDefault(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse(self, args: list[str]) -> Any:
 | 
					    def parse(self, args: list[str]) -> Any:
 | 
				
			||||||
| 
						 | 
					@ -499,7 +515,7 @@ class Schema:
 | 
				
			||||||
            if stack[0] == "--":
 | 
					            if stack[0] == "--":
 | 
				
			||||||
                if not self.extras:
 | 
					                if not self.extras:
 | 
				
			||||||
                    raise ValueError("Unexpected '--'")
 | 
					                    raise ValueError("Unexpected '--'")
 | 
				
			||||||
                self._setExtra(res, stack.pop(0))
 | 
					                self.extras.putValue(res, stack[1:])
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            toks = parseArg(stack.pop(0))
 | 
					            toks = parseArg(stack.pop(0))
 | 
				
			||||||
| 
						 | 
					@ -508,7 +524,7 @@ class Schema:
 | 
				
			||||||
                    arg = self._lookupArg(tok.key, tok.short)
 | 
					                    arg = self._lookupArg(tok.key, tok.short)
 | 
				
			||||||
                    arg.putValue(res, tok.value, tok.subkey)
 | 
					                    arg.putValue(res, tok.value, tok.subkey)
 | 
				
			||||||
                elif isinstance(tok, OperandToken):
 | 
					                elif isinstance(tok, OperandToken):
 | 
				
			||||||
                    self._setOperand(tok)
 | 
					                    self._setOperand(res, tok.value)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    raise ValueError(f"Unexpected token: {type(tok)}")
 | 
					                    raise ValueError(f"Unexpected token: {type(tok)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -634,10 +650,14 @@ class Command:
 | 
				
			||||||
                    self.callable()
 | 
					                    self.callable()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if self.subcommands:
 | 
					            if self.subcommands:
 | 
				
			||||||
                if len(rest) == 0 and not self.populated:
 | 
					                if len(rest) > 0:
 | 
				
			||||||
                    raise ValueError("Expected subcommand")
 | 
					                    if not self.populated:
 | 
				
			||||||
 | 
					                        raise ValueError("Expected subcommand")
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        self.lookupSubcommand(rest[0]).eval(rest)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    self.lookupSubcommand(rest[0]).eval(rest)
 | 
					                    print("Usage: " + cmd + self.usage(), end="\n\n")
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
            elif len(rest) > 0:
 | 
					            elif len(rest) > 0:
 | 
				
			||||||
                raise ValueError(f"Unknown operand '{rest[0]}'")
 | 
					                raise ValueError(f"Unknown operand '{rest[0]}'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,5 +56,5 @@ class PluginsArgs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def setup(args: PluginsArgs):
 | 
					def setup(args: PluginsArgs):
 | 
				
			||||||
    if args.safemod:
 | 
					    if not args.safemod:
 | 
				
			||||||
        loadAll()
 | 
					        loadAll()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -467,8 +467,11 @@ def _(args: _ProfileArgs):
 | 
				
			||||||
    profile(args.fullCmd(), rate=args.rate, what=args.what)
 | 
					    profile(args.fullCmd(), rate=args.rate, what=args.what)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CompresseArgs:
 | 
					class CompressFormatArg:
 | 
				
			||||||
    format: str = cli.arg(None, "format", "The compression format", default="zstd")
 | 
					    format: str = cli.arg(None, "format", "The compression format", default="zstd")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompresseArgs(CompressFormatArg):
 | 
				
			||||||
    dest: Optional[str] = cli.arg(None, "dest", "The destination file or directory")
 | 
					    dest: Optional[str] = cli.arg(None, "dest", "The destination file or directory")
 | 
				
			||||||
    path: str = cli.operand("path", "The file or directory to compress")
 | 
					    path: str = cli.operand("path", "The file or directory to compress")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -211,15 +211,6 @@ def test_cli_arg_dict_str():
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StrOptArg:
 | 
					 | 
				
			||||||
    value: str | None = cli.arg(None, "value")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_cli_arg_str_opt():
 | 
					 | 
				
			||||||
    assert_equal(extractParse(StrOptArg, []).value, None)
 | 
					 | 
				
			||||||
    assert_equal(extractParse(StrOptArg, ["--value=foo"]).value, "foo")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FooArg:
 | 
					class FooArg:
 | 
				
			||||||
    foo: str = cli.arg(None, "foo")
 | 
					    foo: str = cli.arg(None, "foo")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -237,3 +228,37 @@ def test_cli_arg_inheritance():
 | 
				
			||||||
    assert_equal(res.foo, "foo")
 | 
					    assert_equal(res.foo, "foo")
 | 
				
			||||||
    assert_equal(res.bar, "bar")
 | 
					    assert_equal(res.bar, "bar")
 | 
				
			||||||
    assert_equal(res.baz, "baz")
 | 
					    assert_equal(res.baz, "baz")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ExtraArg:
 | 
				
			||||||
 | 
					    value: str = cli.arg(None, "value")
 | 
				
			||||||
 | 
					    extra: list[str] = cli.extra("extra")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_cli_extra_args():
 | 
				
			||||||
 | 
					    res = extractParse(ExtraArg, ["--value=foo", "--", "bar", "baz"])
 | 
				
			||||||
 | 
					    assert_equal(res.value, "foo")
 | 
				
			||||||
 | 
					    assert_equal(res.extra, ["bar", "baz"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StrOperandArg:
 | 
				
			||||||
 | 
					    value: str = cli.operand(None, "value")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_cli_operand_args():
 | 
				
			||||||
 | 
					    res = extractParse(StrOperandArg, ["foo"])
 | 
				
			||||||
 | 
					    assert_equal(res.value, "foo")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ListOperandArg:
 | 
				
			||||||
 | 
					    value: list[str] = cli.operand(None, "value")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_cli_operand_list_args():
 | 
				
			||||||
 | 
					    res = extractParse(ListOperandArg, ["foo", "bar"])
 | 
				
			||||||
 | 
					    assert_equal(res.value, ["foo", "bar"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_cli_operand_list_args_empty():
 | 
				
			||||||
 | 
					    res = extractParse(ListOperandArg, [])
 | 
				
			||||||
 | 
					    assert_equal(res.value, [])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue