我有下面的装饰器函数(serialize_request
的简化版本,来自api-client-pydantic
),它应该被用在一个函数上,这个函数接受任意数量的pydantic
模型作为参数,它用传递给它的参数初始化这些模型,然后用这些模型作为参数调用 Package 函数。
def serialize(extra_kwargs: Dict[str, Any] = None) -> Callable:
extra_kw = extra_kwargs or {"by_alias": True, "exclude_none": True}
def decorator(func: Callable) -> Callable:
map_schemas = {}
map_params = {}
parameters = []
for arg_name, arg_type in get_type_hints(func).items():
if arg_name == "return":
continue
map_schemas[arg_name] = arg_type
if inspect.isclass(arg_type) and issubclass(arg_type, BaseModel):
# the model's signature contains only aliases
arg_fields = list(arg_type.__fields__.keys())
arg_params = inspect.signature(arg_type).parameters
map_params[arg_name] = set(list(arg_params.keys()) + arg_fields)
parameters.extend(list(arg_params.values()))
@wraps(func)
def wrap(*args,**kwargs):
if map_schemas:
data, origin_kwargs = {}, {}
for arg_name, arg_type in map_schemas.items():
if inspect.isclass(arg_type) and issubclass(arg_type, BaseModel):
arg_kwargs = {
k: v for k, v in kwargs.items() if k in map_params[arg_name]
}
data[arg_name] = parse_obj_as(arg_type, arg_kwargs).dict(
**extra_kw
)
else:
val = kwargs.get(arg_name)
if val is not None:
origin_kwargs[arg_name] = val
new_kwargs = {**origin_kwargs,**data} or kwargs
return func(*args,**new_kwargs)
return func(*args,**kwargs)
# Override signature
if parameters:
sig = inspect.signature(func)
_self_param = sig.parameters.get("self")
self_param = [_self_param] if _self_param else []
sig = sig.replace(parameters=tuple(self_param + parameters))
wrap.__signature__ = sig # type: ignore
return wrap
return decorator
通过覆盖签名,像ipython这样的工具可以识别新的参数,并在弹出的帮助中显示它们。
class ModelA(BaseModel):
a: str
b: int
class ModelB(BaseModel):
one: float
two: Optional[str] = None
@serialize()
def foo(model_a: ModelA, model_b: ModelB):
print(model_a)
print(model_b)
在PyCharm中:x1c 0d1x
在纯ipython中:
但pyright无法识别它们,并显示错误:
我不知道PyCharm在内部使用了什么,但它也不识别新的参数。虽然它没有显示错误,但它只接受任何有效的参数,甚至一个都不接受:
现在,我的问题是,是否有某种方法可以使pyright/PyCharm和类似的工具识别由装饰器设置的那些“新”参数,并使它们的行为就像这些参数是直接在函数上设置的一样。
1条答案
按热度按时间kupeojn61#
不,在Python当前的类型系统中没有这样的方法。
以下是您需要使用的成分:
但是你所描述的远远超出了他们的能力范围。