88 lines
2.6 KiB
Python
88 lines
2.6 KiB
Python
import asyncio
|
|
import inspect
|
|
import re
|
|
from typing import Any, Callable, ForwardRef, List, Set
|
|
|
|
from django.urls import register_converter
|
|
from django.urls.converters import UUIDConverter
|
|
from pydantic._internal._typing_extra import eval_type_lenient as evaluate_forwardref
|
|
|
|
from ninja.types import DictStrAny
|
|
|
|
__all__ = [
|
|
"get_typed_signature",
|
|
"get_typed_annotation",
|
|
"make_forwardref",
|
|
"get_path_param_names",
|
|
"is_async",
|
|
]
|
|
|
|
|
|
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
|
|
"Finds call signature and resolves all forwardrefs"
|
|
signature = inspect.signature(call)
|
|
globalns = getattr(call, "__globals__", {})
|
|
typed_params = [
|
|
inspect.Parameter(
|
|
name=param.name,
|
|
kind=param.kind,
|
|
default=param.default,
|
|
annotation=get_typed_annotation(param, globalns),
|
|
)
|
|
for param in signature.parameters.values()
|
|
]
|
|
typed_signature = inspect.Signature(typed_params)
|
|
return typed_signature
|
|
|
|
|
|
def get_typed_annotation(param: inspect.Parameter, globalns: DictStrAny) -> Any:
|
|
annotation = param.annotation
|
|
if isinstance(annotation, str):
|
|
annotation = make_forwardref(annotation, globalns)
|
|
return annotation
|
|
|
|
|
|
def make_forwardref(annotation: str, globalns: DictStrAny) -> Any:
|
|
# NOTE: in future versions of pydantic, the import may be changed to:
|
|
# from pydantic._internal._typing_extra import try_eval_type
|
|
# usage:
|
|
# result, _ = try_eval_type(forward_ref, globalns, globalns)
|
|
forward_ref = ForwardRef(annotation)
|
|
return evaluate_forwardref(forward_ref, globalns, globalns)
|
|
|
|
|
|
def get_path_param_names(path: str) -> Set[str]:
|
|
"""turns path string like /foo/{var}/path/{int:another}/end to set {'var', 'another'}"""
|
|
return {item.strip("{}").split(":")[-1] for item in re.findall("{[^}]*}", path)}
|
|
|
|
|
|
def is_async(callable: Callable[..., Any]) -> bool:
|
|
return asyncio.iscoroutinefunction(callable)
|
|
|
|
|
|
def has_kwargs(func: Callable[..., Any]) -> bool:
|
|
for param in inspect.signature(func).parameters.values():
|
|
if param.kind == param.VAR_KEYWORD:
|
|
return True
|
|
return False
|
|
|
|
|
|
def get_args_names(func: Callable[..., Any]) -> List[str]:
|
|
"returns list of function argument names"
|
|
return list(inspect.signature(func).parameters.keys())
|
|
|
|
|
|
class NinjaUUIDConverter:
|
|
"""Return a path converted UUID as a str instead of the standard UUID"""
|
|
|
|
regex = UUIDConverter.regex
|
|
|
|
def to_python(self, value: str) -> str:
|
|
return value
|
|
|
|
def to_url(self, value: Any) -> str:
|
|
return str(value)
|
|
|
|
|
|
register_converter(NinjaUUIDConverter, "uuid")
|