Skip to content
This repository was archived by the owner on Jun 2, 2026. It is now read-only.

Commit c9bd075

Browse files
fopina-cifopina
authored andcommitted
Fix CLI type annotations on Python 3.9
1 parent 5cd5bac commit c9bd075

2 files changed

Lines changed: 49 additions & 6 deletions

File tree

defectdojo_api_generated/cli/commands/apis.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ def _get_click_type(
196196
raise TypeError(f'Unsupported CLI parameter type{param_name}: {current!r}. Only primitive types are supported.')
197197

198198

199+
def _get_class_annotation(click_type: Any) -> type:
200+
# Classyclick inspects annotations at import time and expects a runtime type.
201+
if click_type is Any:
202+
return str
203+
if isinstance(click_type, click.Path):
204+
return Path
205+
return click_type
206+
207+
199208
def _iter_command_parameters(api_class: type, target_method: str):
200209
signature = inspect.signature(getattr(api_class, target_method))
201210

@@ -286,7 +295,7 @@ def __call__(self):
286295
}
287296

288297
for field_name, field, click_type, multiple, _converter in required_fields:
289-
namespace['__annotations__'][field_name] = click_type
298+
namespace['__annotations__'][field_name] = _get_class_annotation(click_type)
290299
option_kwargs = {
291300
'help': _get_model_field_help(field),
292301
'required': True,
@@ -306,7 +315,7 @@ def __call__(self):
306315
namespace['__annotations__']['jq'] = str
307316

308317
for field_name, field, click_type, multiple, _converter in optional_fields:
309-
namespace['__annotations__'][field_name] = click_type
318+
namespace['__annotations__'][field_name] = _get_class_annotation(click_type)
310319
option_kwargs = {
311320
'help': _get_model_field_help(field),
312321
'default': field.default,
@@ -519,7 +528,7 @@ def __call__(self):
519528

520529
for name, parameter, click_type, multiple, _converter in required_parameters:
521530
# required request-body models are passed as JSON strings and converted before the API call
522-
namespace['__annotations__'][name] = click_type
531+
namespace['__annotations__'][name] = _get_class_annotation(click_type)
523532
option_kwargs = {
524533
'help': _get_help_from_annotation(parameter.annotation),
525534
'required': True,
@@ -539,7 +548,7 @@ def __call__(self):
539548
namespace['__annotations__']['jq'] = str
540549

541550
for name, parameter, click_type, multiple, _converter in optional_parameters:
542-
namespace['__annotations__'][name] = click_type
551+
namespace['__annotations__'][name] = _get_class_annotation(click_type)
543552
option_kwargs = {
544553
'help': _get_help_from_annotation(parameter.annotation),
545554
'default': parameter.default,

tests/unit/test_cli.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import unittest
44
from importlib.metadata import version
55
from pathlib import Path
6-
from typing import Optional
6+
from typing import Any, Optional, Tuple, Union
77
from unittest import mock
88

99
from click.testing import CliRunner
10-
from pydantic import Field
10+
from pydantic import BaseModel, Field
1111
from typing_extensions import Annotated
1212

1313
from defectdojo_api_generated.api.findings_api import FindingsApi
@@ -287,6 +287,40 @@ def test_required_request_body_parameters_become_field_flags(self):
287287
payload = json.loads(run_result.output)
288288
self.assertEqual(payload['name'], 'Example')
289289

290+
def test_request_model_fields_typed_as_any_register_without_import_errors(self):
291+
class AnyRequest(BaseModel):
292+
payload: Any = None
293+
294+
class AnyRequestApi:
295+
def __init__(self, api_client):
296+
self.api_client = api_client
297+
298+
def create(self, any_request: AnyRequest):
299+
return any_request.model_dump()
300+
301+
command = make_api_group('any_request_api', AnyRequestApi).click
302+
option = next(param for param in command.params if getattr(param, 'name', None) == 'payload')
303+
304+
self.assertEqual(option.type.name, 'text')
305+
306+
def test_request_model_file_fields_annotate_as_path(self):
307+
class FileRequest(BaseModel):
308+
file: Optional[Union[bytes, str, Tuple[str, bytes]]] = None
309+
310+
class FileRequestApi:
311+
def __init__(self, api_client):
312+
self.api_client = api_client
313+
314+
def create(self, file_request: FileRequest):
315+
return file_request.model_dump()
316+
317+
command_class = make_api_group('file_request_api', FileRequestApi)
318+
command = command_class.click
319+
option = next(param for param in command.params if getattr(param, 'name', None) == 'file')
320+
321+
self.assertIs(command_class.__annotations__['file'], Path)
322+
self.assertEqual(option.type.name, 'file')
323+
290324
def test_bad_request_exception_uses_detail_message(self):
291325
runner = CliRunner()
292326

0 commit comments

Comments
 (0)