summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Gonçalves <me@rgoncalves.se>2022-10-05 11:24:22 +0200
committerRomain Gonçalves <me@rgoncalves.se>2022-10-05 11:24:22 +0200
commit955f9c5774ee1805872c8c15be8e83062f8996fa (patch)
tree0deb89a194ed035ec8881a0f893ab97a26e446a3
parentaa1fa5271fa53971587fafe270ac225dc0e79334 (diff)
downloadpydanclick-955f9c5774ee1805872c8c15be8e83062f8996fa.tar.gz
wip: deserialize kwargs back to pydantic parameter
-rw-r--r--pydanclick/core.py41
-rw-r--r--pydanclick/decorators.py60
-rw-r--r--pydanclick/examples/__main__.py18
3 files changed, 90 insertions, 29 deletions
diff --git a/pydanclick/core.py b/pydanclick/core.py
index 25ac9bb..1da29a1 100644
--- a/pydanclick/core.py
+++ b/pydanclick/core.py
@@ -1,10 +1,9 @@
from __future__ import annotations
import click
-import functools
import inspect
-from typing import Any
+from typing import Any, Union
from pydantic import BaseModel
from typing import Callable, Type
@@ -35,29 +34,6 @@ class Command(click.Command):
ctx.invoke(self.callback, **arguments)
-def command(name: str = None, cls: Type = Command, **attrs: Any) -> Callable:
- """
- Pydantic arguments to click command.
-
- >>> @command()
- ... def entrypoint():
- ... pass
- >>> entrypoint
- <Command entrypoint>
- """
-
- def wrapper(entrypoint: Callable) -> Callable:
-
- @functools.wraps(entrypoint)
- def _(*args, **kwargs):
- entrypoint(*args, **kwargs)
-
- options = generate_cli_options(**attrs)(_)
- return click.command(options)
-
- return wrapper
-
-
def is_valid_schema_annotation(annotation: Type) -> bool:
"""
Filter an annotation with typing/pydantic implementation.
@@ -147,6 +123,21 @@ def get_option_arguments(
return arguments
+def get_parameters_from_arguments(
+ entrypoint: Callable,
+ *args: Any,
+ **kwargs: Any
+) -> dict[str, Any]:
+ """
+ Re-construct pydantic object from click kwargs and entrypoint annotation.
+ """
+ final_arguments = {}
+ for name, parameter in get_processable_arguments(entrypoint):
+ final_arguments[name] = parameter.annotation(**kwargs)
+
+ return final_arguments
+
+
def generate_cli_option(
schema: CliSchema,
title: str,
diff --git a/pydanclick/decorators.py b/pydanclick/decorators.py
new file mode 100644
index 0000000..eddb50e
--- /dev/null
+++ b/pydanclick/decorators.py
@@ -0,0 +1,60 @@
+import functools
+import click
+
+from typing import Any, Callable
+from pydanclick.core import (
+ Command,
+ generate_cli_options,
+ get_processable_arguments,
+ get_parameters_from_arguments,
+)
+
+def command(
+ name: str = None,
+ cls: type = Command,
+ **attrs: Any
+) -> Callable:
+ """
+ Pydantic arguments to click command.
+
+ >>> @command()
+ ... def entrypoint():
+ ... pass
+ >>> entrypoint
+ <Command entrypoint>
+ """
+
+ def wrapper(entrypoint: Callable) -> Callable:
+
+ @functools.wraps(entrypoint)
+ def _(*args, **kwargs):
+ entrypoint(
+ **get_parameters_from_arguments(entrypoint, *args, **kwargs)
+ )
+
+ options = generate_cli_options(**attrs)(_)
+ return click.command(options)
+
+ return wrapper
+
+
+def group(
+ name: str = None,
+ cls: type = Command,
+ **attrs: Any
+) -> Callable[[Callable[..., Any]], click.Group]:
+ """
+ Decorator: pydantic arguments to click options for group.
+ """
+ def wrapper(entrypoint: Callable) -> click.Group:
+
+ @functools.wraps(entrypoint)
+ def _(*args, **kwargs):
+ entrypoint(
+ **get_parameters_from_arguments(entrypoint, *args, **kwargs)
+ )
+
+ options = generate_cli_options(**attrs)(_)
+ return click.group(options)
+
+ return wrapper
diff --git a/pydanclick/examples/__main__.py b/pydanclick/examples/__main__.py
index fdaa34f..012f110 100644
--- a/pydanclick/examples/__main__.py
+++ b/pydanclick/examples/__main__.py
@@ -1,9 +1,7 @@
-# from click import command
from enum import Enum
from pydantic import BaseModel, Field
-from pydanclick.core import Command, generate_cli_options
-from pydanclick.core import command
+from pydanclick.decorators import command, group
class MainArguments(BaseModel):
@@ -26,10 +24,22 @@ class MainArguments(BaseModel):
network_type: NetworkEnum
-@command(key_prefix="", a="c")
+class ShowArguments(BaseModel):
+ verbose: bool = Field(description="Verbose output")
+
+
+@group()
def main(parameters: MainArguments) -> None:
print(vars(parameters))
+@command()
+def show(parameters: ShowArguments) -> None:
+ print(vars(parameters))
+
+
+main.add_command(show)
+
+
if __name__ == "__main__":
main()
remember that computers suck.