Add public functions to dataclasses to correctly identify methods and features
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Feature or enhancement
Proposal:
There are a few places both in the stdlib and in third party packages where tools attempt to discover specific features of how dataclasses are constructed. Currently these either rely on leaky internals or will behave incorrectly in certain cases, or both.
I'd like to both make it possible to stop leaking those internal details while also making the discovery more accurate.
Proposal:
- Add a
__generated_for_dataclass__attribute to methods generated by dataclasses- This would hold the class the method was generated for
- Note that this would not include shared functions like
__replace__which are attached to dataclasses but are not generated for a specific class - Adding a
True/Falseflag was proposed back in the issue that added pprint repr replacement support, this extends that idea to also identify which class a method was generated for which gives more useful information[^1]
- Add a public
generated_for_dataclass[^2] function to retrieve this attribute- This would return
Noneif the method is not a generated dataclass method
- This would return
- Add an
is_frozenpublic function to detect if a class is actually a frozen dataclass- The current implementation would check that
generated_for_dataclass(cls.method) is clsfor both__setattr__and__delattr__ - This is both a convenience function and future-proofing if the implementation of frozen-ness changes
- The current implementation would check that
In the linked discuss thread I also mentioned adding a requires_decorator to is_dataclass to identify if a class has been decorated and doesn't just inherit from a dataclass along with making _is_dataclass_instance public and adding a matching is_dataclass_type. These additions could be used to simplify some existing checks but aren't necessary to resolve the logic issues. As such I'm leaving them out unless it's considered worthwhile to include them.
Examples of issues this intends to resolve:
- Attempts to detect and replace a dataclass
__repr__:
- pprint
- This relies on dataclasses not cleanly renaming the
__repr__function and leaking the name of the internal__create_fn__function through the recursive_repr wrapper - This prevented dataclasses from renaming the internal function in a previous PR
- A change that would no longer leak the internal name would also break this (I discovered this while testing changes for lazy method generation)
- This relies on dataclasses not cleanly renaming the
- enum
- This only checks if
repr=Trueis in the params and doesn't check if the__repr__method is actually generated by dataclasses and will replace any__repr__method on a dataclass - As a dataclass, defining the
__repr__by hand should be treated the same as settingrepr=Falsebut this isn't the case for enum
- This only checks if
- rich
- This checks for
reprlib.py's path in__code__.co_filename - Any
__repr__function on a dataclass that usesrecursive_reprwill be replaced - Unlike the 2 stdlib cases, this can't rely on being kept in sync with changes to
dataclassesdue to failing stdlib tests
- This checks for
These could all use generated_for_dataclass(cls.__repr__) to both identify if a __repr__ is replaceable and get the correct class to use for retrieving the matching fields.
- Current attempts to detect frozenness:
There are a number of tools that look at cls.__dataclass_params__.frozen to try and detect if a class is a frozen dataclass, here's an example from pytorch and other examples from a search.
This logic will fail if __setattr__ or __delattr__ have been replaced in an undecorated subclass.
from dataclasses import dataclass, is_dataclass
@dataclass(frozen=True)
class Frozen:
a: int = 42
class UnFrozen(Frozen):
__setattr__ = object.__setattr__
__delattr__ = object.__delattr__
ex = UnFrozen()
print(is_dataclass(ex)) # True
print(ex.__dataclass_params__.frozen) # True
ex.a = 1 # Not actually frozen
[^1]: For example if you want to replace the __repr__ function, it tells you which class to use for fields(cls) to generate the correct method
[^2]: Open to a different name if there's a better suggestion
Has this already been discussed elsewhere?
I have already discussed this feature proposal on Discourse
Links to previous discussion of this feature:
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
dataclasses の実装と Lib/pprint.py および Lib/enum.py の検出ロジックを確認し、その後、サードパーティツールによる提案の例と比較してください。生成されたメソッドと本当に frozen なクラスをどのように識別すべきかを、公開 API の命名とスコープを含めて決定してください。合意した API と属性が実装され、説明されている継承および手書きメソッドのケースがカバレッジに含まれていれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- developer-experience
- issue の種類
- 機能追加
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 45/100