fix descriptor semantics for stub/interface declarations #3045#3151
fix descriptor semantics for stub/interface declarations #3045#3151shuv-amp wants to merge 1 commit intofacebook:mainfrom
Conversation
|
Hi @shuv-amp! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
337c7e4 to
f3cc153
Compare
) Summary: Stub and interface files can declare descriptor-backed attributes without showing the runtime initializer. We only treated class-body assignments as descriptor definitions, so stub-declared descriptors fell back to the raw descriptor type instead of the `__get__` result. Treat fields modeled as `ClassFieldInitialization::Magic` as class-level for descriptor detection. This restores descriptor semantics for stub and interface declarations while preserving the existing behavior for annotation-only instance attributes in source files. Add a regression test covering an inherited annotation-only descriptor in `.pyi` files.
f3cc153 to
e468758
Compare
|
Diff from mypy_primer, showing the effect of this PR on open source code: pip (https://github.com/pypa/pip)
- ERROR src/pip/_vendor/rich/_win32_console.py:384:39-46: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `row` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
- ERROR src/pip/_vendor/rich/_win32_console.py:384:52-59: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `col` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
- ERROR src/pip/_vendor/rich/_win32_console.py:394:39-52: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `row` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
- ERROR src/pip/_vendor/rich/_win32_console.py:394:58-71: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `col` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
django-stubs (https://github.com/typeddjango/django-stubs)
- ERROR django-stubs/contrib/contenttypes/fields.pyi:66:5-10: Class member `GenericRel.field` overrides parent class `ForeignObjectRel` in an inconsistent manner [bad-override]
- ERROR django-stubs/db/models/fields/files.pyi:52:5-10: Class member `FileDescriptor.field` overrides parent class `DeferredAttribute` in an inconsistent manner [bad-override]
+ ERROR django-stubs/db/models/fields/files.pyi:99:9-16: Class member `FileField.__get__` overrides parent class `Field` in an inconsistent manner [bad-override]
- ERROR django-stubs/db/models/fields/files.pyi:115:5-10: Class member `ImageFileDescriptor.field` overrides parent class `FileDescriptor` in an inconsistent manner [bad-override]
- ERROR django-stubs/db/models/fields/files.pyi:120:5-10: Class member `ImageFieldFile.field` overrides parent class `FieldFile` in an inconsistent manner [bad-override]
- ERROR django-stubs/db/models/fields/related_descriptors.pyi:22:5-10: Class member `ForeignKeyDeferredAttribute.field` overrides parent class `DeferredAttribute` in an inconsistent manner [bad-override]
- ERROR django-stubs/db/models/fields/reverse_related.pyi:95:5-10: Class member `ManyToOneRel.field` overrides parent class `ForeignObjectRel` in an inconsistent manner [bad-override]
- ERROR django-stubs/db/models/fields/reverse_related.pyi:115:5-10: Class member `OneToOneRel.field` overrides parent class `ManyToOneRel` in an inconsistent manner [bad-override]
+ ERROR django-stubs/db/models/fields/tuple_lookups.pyi:13:5-17: Class member `Tuple.output_field` overrides parent class `Func` in an inconsistent manner [bad-override]
asynq (https://github.com/quora/asynq)
- ERROR asynq/decorators.py:193:20-40: Object of class `DecoratorBase` has no attribute `asynq` [missing-attribute]
+ ERROR asynq/decorators.py:193:20-40: Object of class `DecoratorBinder` has no attribute `asynq` [missing-attribute]
- ERROR asynq/decorators.py:195:20-40: Object of class `DecoratorBase` has no attribute `asynq` [missing-attribute]
+ ERROR asynq/decorators.py:195:20-40: Object of class `DecoratorBinder` has no attribute `asynq` [missing-attribute]
- ERROR asynq/decorators.py:199:20-42: Object of class `DecoratorBase` has no attribute `asyncio` [missing-attribute]
+ ERROR asynq/decorators.py:199:20-42: Object of class `DecoratorBinder` has no attribute `asyncio` [missing-attribute]
- ERROR asynq/decorators.py:201:20-42: Object of class `DecoratorBase` has no attribute `asyncio` [missing-attribute]
+ ERROR asynq/decorators.py:201:20-42: Object of class `DecoratorBinder` has no attribute `asyncio` [missing-attribute]
- ERROR asynq/tests/test_tools.py:189:17-77: Object of class `DecoratorBase` has no attribute `__acached_per_instance_cache__` [missing-attribute]
+ ERROR asynq/tests/test_tools.py:189:17-77: Object of class `DecoratorBinder` has no attribute `__acached_per_instance_cache__` [missing-attribute]
- ERROR asynq/tools.py:336:13-33: Object of class `DecoratorBase` has no attribute `dirty` [missing-attribute]
+ ERROR asynq/tools.py:336:13-33: Object of class `DecoratorBinder` has no attribute `dirty` [missing-attribute]
- ERROR asynq/tools.py:338:13-33: Object of class `DecoratorBase` has no attribute `dirty` [missing-attribute]
+ ERROR asynq/tools.py:338:13-33: Object of class `DecoratorBinder` has no attribute `dirty` [missing-attribute]
zulip (https://github.com/zulip/zulip)
- ERROR zerver/tests/test_openapi.py:460:25-54: Object of class `LocaleRegexDescriptor` has no attribute `pattern`
- Object of class `LocaleRegexRouteDescriptor` has no attribute `pattern` [missing-attribute]
pandas (https://github.com/pandas-dev/pandas)
- ERROR pandas/tests/dtypes/test_inference.py:1678:24-43: Object of class `_LengthDescriptor` has no attribute `seconds` [missing-attribute]
+ ERROR pandas/tests/dtypes/test_inference.py:1678:24-43: Object of class `int` has no attribute `seconds` [missing-attribute]
rich (https://github.com/Textualize/rich)
- ERROR rich/_win32_console.py:384:39-46: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `row` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
- ERROR rich/_win32_console.py:384:52-59: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `col` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
- ERROR rich/_win32_console.py:394:39-52: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `row` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
- ERROR rich/_win32_console.py:394:58-71: Argument `_CField[c_short, int, c_short | int]` is not assignable to parameter `col` with type `int` in function `WindowsCoordinates.__new__` [bad-argument-type]
optuna (https://github.com/optuna/optuna)
- ERROR optuna/_gp/batched_lbfgsb.py:54:26-61: Object of class `_ParentDescriptor` has no attribute `switch` [missing-attribute]
+ ERROR optuna/_gp/batched_lbfgsb.py:54:26-61: Object of class `NoneType` has no attribute `switch` [missing-attribute]
- ERROR optuna/_gp/batched_lbfgsb.py:73:9-44: Object of class `_ParentDescriptor` has no attribute `switch` [missing-attribute]
+ ERROR optuna/_gp/batched_lbfgsb.py:73:9-44: Object of class `NoneType` has no attribute `switch` [missing-attribute]
|
Primer Diff Classification✅ 3 improvement(s) | ➖ 3 neutral | 6 project(s) total | +12, -25 errors 3 improvement(s) across pip, django-stubs, rich.
Detailed analysis✅ Improvement (3)pip (-4)
django-stubs (+2, -7)
Per-category reasoning:
rich (-4)
➖ Neutral (3)asynq (+7, -7)
pandas (+1, -1)
optuna (+2, -2)
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (3 heuristic, 3 LLM) |
Summary
Fixes #3045
Stub and interface files can declare descriptor-backed attributes without showing the runtime initializer. Today we only apply descriptor semantics when the field is initialized in the class body, marked as
ClassVar, or covered by a special case. That makes stub-declared descriptors fall back to their raw descriptor type instead of the__get__result.This change treats fields modeled as
ClassFieldInitialization::Magicas class-level for descriptor detection. That restores descriptor semantics for stub and interface declarations while preserving the current behavior for annotation-only instance attributes in source files.The regression test covers an inherited annotation-only descriptor declared in
.pyifiles.Test Plan
python3 test.py --no-test --no-conformance --no-jsonschemacargo test -p pyrefly descriptors --quietcargo test -p pyrefly test_foreign_key_basic --quiet