使 Pyright/PyCharm 识别装饰器设置的参数
我有以下装饰器函数(来自 的
),应该用于采用任意数量的 pydantic 模型作为参数的函数。它使用传递给它的参数初始化这些模型,然后使用这些模型作为参数调用包装函数。serialize_request
的简化版本api-client-pydantic
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)
但是pyright无法识别它们并显示错误:
我不知道 PyCharm 内部使用什么,但它也无法识别新参数。但它不会显示错误,它只接受任何内容作为有效参数,也根本不接受任何内容:
现在,我的问题是是否有某种方法可以使 Pyright/PyCharm 和类似工具识别这些“新的”由装饰器设置的参数并使它们的行为就像直接在函数上设置参数一样。
I have the following decorator function (simplified version of serialize_request
from api-client-pydantic
) that is supposed to be used on a function that takes any number of pydantic
models as parameters. It initializes those models with the arguments passed to it and then calls the wrapped function with these models as arguments.
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
By overwriting the signature, tools like ipython recognize the new arguments and show them in the pop up help. For example, with the below models and function:
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)
But pyright doesn't recognize them and shows an error:
I don't know what PyCharm uses internally, but it also doesn't recognize the new arguments. It doesn't show an error though, it just accepts anything as valid arguments, also none at all:
Now, my question is if there is some way to make pyright/PyCharm and similar tools recognize those "new" arguments set by the decorator and make them behave as though the parameters were set as such on the function directly.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
不,Python 当前的类型系统没有这样的方法来做到这一点。
这些是您需要使用的成分:
但是你所描述的远远超出了他们的能力范围。
No, there is no such way to do this with Python's current typing system.
These are the ingredients that you would need to use:
But what you describe is far beyond their capabilities.