functools.update_wrapper() will not accept descriptor (decorator) as wrapper
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 5.1k
- Fork
- 2.1k
- Merge trung bình
- 1 ngày 19 giờ
- Pull request đã merge (30 ngày)
- 82
Mô tả
Description:
The type stub for functools.update_wrapper() does not permit a descriptor for the wrapper (first) argument, a legitimate case arising when defining a descriptor-type decorator (like @property is).
https://github.com/python/typeshed/blob/main/stdlib/functools.pyi#L83
@erictraut While perhaps not exactly a regression, this used to work up until this commit of yours here (last week): https://github.com/python/typeshed/commit/9c4bfd5d11b47677e128452baa8ac7eeb1903a8e#diff-02c51ed45b741b6765c365fb5d6578c586510bbbd57b10a1dba10aeba37ae157L70-R84. I say not exactly a regression because the prior constraint introduced here was perhaps overly loose: https://github.com/python/typeshed/pull/4627/files
Example
As an example, the following (pyright) typing error is detected in the lazyproperty descriptor-type decorator defined here: https://gist.github.com/scanny/255f16339b74b2b59e896d67bd459d6b
lazyproperty.py|65 col 34-38 error| Argument of type "Self@lazyproperty[T@lazyproperty]"
cannot be assigned to parameter "wrapper"
of type "(**_PWrapper@update_wrapper) -> _RWapper@update_wrapper"
in function "update_wrapper"
Type "Self@lazyproperty[T@lazyproperty]" cannot be assigned to
type "(**_PWrapper@update_wrapper) -> _RWapper@update_wrapper"
To Reproduce
The gist referenced above has no dependencies other than the stdlib so can be type-checked directly (using pyright v1.1.296):
$ pyright lazyproperty.py
Analysis
The type definition at work constrains wrapper to be a Callable[_PWrapper, _RWapper]. I believe it also needs to permit Descriptor, although I'm not completely sure what that type definition is. My best guess is something like this:
class HasDunderGet(Protocol):
def __get__(self, obj: Any, type: type = None) -> Any: ...
class HasDunderSet(Protocol):
def __set__(self, obj: Any, value: Any) -> None: ...
class HasDunderDelete(Protocol):
def __delete__(self, obj: Any) -> None: ...
Descriptor = Union[HasDunderGet, HasDunderSet, HasDunderDelete]
Expected behavior
As far as I can tell, this is a legitimate usage of functools.update_wrapper() and decorators that produce a descriptor (like @property does) are also legitimate Python. It has certainly worked for a long time and also even typechecked with pyright previously (v1.1.273), maybe six months ago. So I would expect it to be accepted as written.
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu với stdlib/functools.pyi, quanh các dòng 82-87, và so sánh chú thích của wrapper với cách sử dụng descriptor trong gist lazyproperty.py được liên kết. Chạy pyright v1.1.296 trên lazyproperty.py để tái hiện lỗi, sau đó kiểm tra commit typeshed được tham chiếu và pull request trước đó để hiểu bối cảnh. Công việc được xem là hoàn tất khi decorator descriptor hợp lệ vượt qua kiểm tra kiểu mà không làm yếu việc định kiểu update_wrapper ở những phần không liên quan.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- developer-experience, tooling
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Đình trệ
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 35/100