summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Gonçalves <me@rgoncalves.se>2022-10-04 23:17:21 +0200
committerRomain Gonçalves <me@rgoncalves.se>2022-10-04 23:17:21 +0200
commit20e6e83183bb4dd1fec54cdc50e462f15c8133cd (patch)
tree2c2b9cc3c2e168089c1a4a59834b15471b58aaad
parenta23450a8f54640d70dd26ed46ebc842693f49ebe (diff)
downloadpydanclick-20e6e83183bb4dd1fec54cdc50e462f15c8133cd.tar.gz
wip: externalize generation of option arguments
-rw-r--r--pydanclick/core.py70
-rw-r--r--pydanclick/examples/__main__.py13
-rw-r--r--pydanclick/schemas.py22
-rw-r--r--tests/conftest.py5
-rw-r--r--tests/test_core.py4
5 files changed, 72 insertions, 42 deletions
diff --git a/pydanclick/core.py b/pydanclick/core.py
index 9ccdda9..25ac9bb 100644
--- a/pydanclick/core.py
+++ b/pydanclick/core.py
@@ -8,7 +8,7 @@ from typing import Any
from pydantic import BaseModel
from typing import Callable, Type
-from pydanclick.schemas import CliSchema
+from pydanclick.schemas import CliSchema, OptionArgumentsSchema
class Command(click.Command):
@@ -38,6 +38,12 @@ class Command(click.Command):
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:
@@ -102,52 +108,60 @@ def get_processable_arguments(
))
-def generate_cli_option(
+def get_option_arguments(
schema: CliSchema,
- title: str,
parameter: CliSchema.PropertySchema,
- *,
- key_prefix: None | str = ""
-) -> Callable:
+ *args,
+ **kwargs
+) -> OptionArgumentsSchema:
"""
- Generate the option information of a click.Command.
+ Generate the option information only of a click.Command.
"""
- is_required = True if title in schema.required else False
- is_flag = False
- option_type: None | Type | object = None
- option_title = title.lower().replace("_", "-")
+
+ arguments = OptionArgumentsSchema(help=parameter.description)
option_min = parameter.minLength or parameter.exclusiveMinimum
option_max = parameter.maxLength or parameter.exlusiveMaximum
- # option_extra = {}
match parameter.type:
case "integer":
- option_type = int
+ arguments.type = int
if option_min is not None or option_max is not None:
- option_type = click.IntRange(option_min, option_max)
+ arguments.type = click.IntRange(option_min, option_max)
case "number":
- option_type = float
+ arguments.type = float
case "string":
- option_type = str
+ arguments.type = str
+ if parameter.enum:
+ arguments.type = click.Choice(parameter.enum)
case "boolean":
- option_type = bool
- is_flag = True
+ arguments.type = bool
+ arguments.is_flag = True
case None:
if parameter.ref:
- typ = schema.get_definition_from_ref(parameter.ref)
- print(typ)
- option_type = click.Choice(typ.enum)
+ definition = schema.get_definition_from_ref(parameter.ref)
+ arguments = get_option_arguments(
+ schema, definition, *args, **kwargs
+ )
+
+ return arguments
+
+
+def generate_cli_option(
+ schema: CliSchema,
+ title: str,
+ parameter: CliSchema.PropertySchema,
+ *,
+ key_prefix: None | str = ""
+) -> Callable:
+ """
+ Generate the option object of a click.Command.
+ """
+ option_title = title.lower().replace("_", "-")
return click.option(
f"--{key_prefix}{option_title}",
- help=parameter.description,
- required=is_required,
- is_flag=is_flag,
- type=option_type,
- show_default=True,
- show_envvar=True,
- # **option_extra,
+ **dict(get_option_arguments(schema, parameter))
)
diff --git a/pydanclick/examples/__main__.py b/pydanclick/examples/__main__.py
index 8a6a9cd..fdaa34f 100644
--- a/pydanclick/examples/__main__.py
+++ b/pydanclick/examples/__main__.py
@@ -13,9 +13,16 @@ class MainArguments(BaseModel):
static = "static"
disconnected = "disconnected"
- filename: str = Field(min_length=10)
- minimum_version: int = Field(gt=0)
- force_download: bool = Field(default=False)
+ filename: str = Field(
+ min_length=10, description="Filename to be used for configuration"
+ )
+ minimum_version: int = Field(
+ gt=0, description="Minimum supported API version."
+ )
+ force_download: bool = Field(
+ default=False,
+ description="Force the download on a different API version"
+ )
network_type: NetworkEnum
diff --git a/pydanclick/schemas.py b/pydanclick/schemas.py
index 5601b2b..7fb79ce 100644
--- a/pydanclick/schemas.py
+++ b/pydanclick/schemas.py
@@ -4,10 +4,6 @@ from pydantic import BaseModel, Field
class CliSchema(BaseModel):
- class DefinitionSchema(BaseModel):
- title: str
- description: str
- enum: list[str]
class PropertySchema(BaseModel):
title: None | str
@@ -17,6 +13,7 @@ class CliSchema(BaseModel):
exlusiveMaximum: None | int
minLength: None | int
maxLength: None | int
+ enum: None | list[str]
ref: None | str = Field(alias="$ref")
class Config:
@@ -26,10 +23,23 @@ class CliSchema(BaseModel):
properties: dict[str, CliSchema.PropertySchema]
required: list[str]
description: None | str
- definitions: None | dict[str, CliSchema.DefinitionSchema]
+ definitions: None | dict[str, CliSchema.PropertySchema]
- def get_definition_from_ref(self, ref: str) -> CliSchema.DefinitionSchema:
+ def get_definition_from_ref(self, ref: str) -> CliSchema.PropertySchema:
if not self.definitions:
raise RuntimeError
return self.definitions[ref.split("/")[-1]]
+
+
+class OptionArgumentsSchema(BaseModel):
+ """
+ Externalised and typed click.Option arguments.
+ """
+
+ type: None | type | object
+ help: None | str
+ required: None | bool
+ show_default: None | bool
+ show_envvar: None | bool
+ is_flag: None | bool
diff --git a/tests/conftest.py b/tests/conftest.py
index 9282809..5871ed8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,6 +1 @@
import pytest
-
-
-@pytest.fixture(autouse=True)
-def add_np(doctest_namespace):
- doctest_namespace["np"] = numpy
diff --git a/tests/test_core.py b/tests/test_core.py
index b755a52..10e3b6c 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1,3 +1,7 @@
import pytest
from pydanclick import core
+
+
+def test_generate_cli_option_ok():
+ assert True
remember that computers suck.