Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
3027ee9
[ty] Implement protocol property check
mtshiba Jun 29, 2025
91da947
Refactor
charliermarsh May 22, 2026
c1ec12a
Fix property setter accepting Any; custom descriptor whose __set__ re…
charliermarsh May 22, 2026
cae2951
Fix lack of self binding
charliermarsh May 23, 2026
2c134ea
[ty] Adapt protocol property checks to current main
charliermarsh Jun 23, 2026
ef6d6f7
[ty] Model protocol member access capabilities
charliermarsh Jun 24, 2026
7656148
[ty] Resolve protocol property types lazily
charliermarsh Jun 24, 2026
635be31
[ty] Avoid eager protocol property checks
charliermarsh Jun 24, 2026
a0a372e
[ty] Derive protocol member capabilities from member kind
charliermarsh Jun 24, 2026
945aedd
[ty] Accept class and static method protocol implementations
charliermarsh Jun 24, 2026
d055d09
[ty] Bind explicitly typed protocol property getters
charliermarsh Jun 24, 2026
dae2b1b
[ty] Defer annotated self protocol members
charliermarsh Jun 24, 2026
ee69b08
[ty] Avoid expensive class-object property checks
charliermarsh Jun 24, 2026
3258f62
[ty] Clarify ClassVar protocol member capabilities
charliermarsh Jun 24, 2026
cfe86da
[ty] Document attribute write resolution
charliermarsh Jun 24, 2026
2de21b9
[ty] Simplify protocol attribute write handling
charliermarsh Jun 24, 2026
0930197
[ty] Ignore property setter return types for protocols
charliermarsh Jun 24, 2026
68664b8
[ty] Name protocol member access modes
charliermarsh Jun 24, 2026
3eed75e
[ty] Encode protocol member type states
charliermarsh Jun 24, 2026
1e96f95
[ty] Pre-resolve instance attribute writes
charliermarsh Jun 24, 2026
c483a42
[ty] Restore portable NumPy mdtest expectation
charliermarsh Jun 24, 2026
2a72f76
[ty] Bind Self in protocol member assignments
charliermarsh Jun 24, 2026
63670f3
[ty] Bind Self in property setters
charliermarsh Jun 24, 2026
6c9f05b
[ty] Preserve relations for descriptor writes
charliermarsh Jun 24, 2026
ad8848f
[ty] Preserve relations for setattr writes
charliermarsh Jun 24, 2026
f52e780
[ty] Include mutable attributes in protocol disjointness
charliermarsh Jun 24, 2026
bda6e06
[ty] Avoid duplicate protocol method constraints
charliermarsh Jun 24, 2026
1a9c59e
[ty] Preserve recursive protocol method cycles
charliermarsh Jun 24, 2026
71bbeff
[ty] Widen literals for protocol property writes
charliermarsh Jun 24, 2026
1f76c02
[ty] Avoid writes for unsupported protocol methods
charliermarsh Jun 24, 2026
138b02b
[ty] Expand hydra-zen diagnostic cap
charliermarsh Jun 24, 2026
4b1c95e
[ty] Document protocol member access semantics
charliermarsh Jun 25, 2026
0a8498a
Move attribute assignment validation into builder module
charliermarsh Jun 25, 2026
8f86bcf
[ty] Distribute union writes across descriptor overloads
charliermarsh Jun 25, 2026
d55d1ee
[ty] Support class and static protocol methods
charliermarsh Jun 24, 2026
c61c4be
[ty] Preserve Self in overloaded protocol methods
charliermarsh Jun 24, 2026
b8cced2
[ty] Bind Self through decorated protocol methods
charliermarsh Jun 24, 2026
a34e98e
[ty] Recognize wrapped class and static methods
charliermarsh Jun 24, 2026
cf12531
[ty] Compare class protocol methods as bound
charliermarsh Jun 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/ruff_benchmark/benches/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,7 @@ fn hydra(criterion: &mut Criterion) {
max_dep_date: TY_ECOSYSTEM_PIN,
python_version: SupportedPythonVersion::Py311,
},
510,
520,
);

bench_project(&benchmark, criterion);
Expand Down
5 changes: 3 additions & 2 deletions crates/ty/docs/rules.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ assigned a value in their scope.
A `Final` symbol must be initialized with a value at the time of declaration
or in a subsequent assignment. At module or function scope, the assignment must
occur in the same scope. In a class body, the assignment may occur in `__init__`.
Protocol members are declarations of an interface and do not require a value.

## Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Checks for redundant combinations of the `ClassVar` and `Final` type qualifiers.
An attribute that is marked `Final` in a class body is implicitly a class variable.
Marking it as `ClassVar` is therefore redundant.

Note that this diagnostic is not emitted for dataclass fields, where
`ClassVar[Final[int]]` has a distinct meaning from `Final[int]`.
Note that this diagnostic is not emitted for dataclass fields or protocol members,
where `ClassVar[Final[int]]` has a distinct meaning from `Final[int]`.

## Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,6 @@ If users want to read/write to attributes such as `__qualname__`, they need to c
of the attribute first:

```py
from inspect import getattr_static

def f_okay(c: Callable[[], None]):
if hasattr(c, "__qualname__"):
reveal_type(c.__qualname__) # revealed: object
Expand All @@ -503,10 +501,14 @@ def f_okay(c: Callable[[], None]):
# error: [invalid-assignment] "Object of type `Literal["my_callable"]` is not assignable to attribute `__qualname__` on type `(() -> None) & <Protocol with members '__qualname__'>`"
c.__qualname__ = "my_callable"

result = getattr_static(c, "__qualname__")
reveal_type(result) # revealed: property
if isinstance(result, property) and result.fset:
c.__qualname__ = "my_callable" # okay
# TODO: should we have some way for users to narrow a read-only attribute
# into a writable attribute...? What would that look like? Something like this?
if (
hasattr(type(c), "__qualname__")
and isinstance(type(c).__qualname__, property)
and type(c).__qualname__.fset is not None
):
c.__qualname__ = "my_callable" # error: [invalid-assignment]
```

## From a class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,23 @@ def _(obj: BasicAlias):
obj.a = 3 # error: [invalid-assignment]
```

A converted field satisfies a writable property protocol using the converter input type as its write
type:

```py
from typing import Protocol
from ty_extensions import is_assignable_to, is_subtype_of, static_assert

class HasConvertedField(Protocol):
@property
def a(self) -> int: ...
@a.setter
def a(self, value: str) -> None: ...

static_assert(is_subtype_of(Basic, HasConvertedField))
static_assert(is_assignable_to(Basic, HasConvertedField))
```

The default parameter for a converter field should also be verified against the converter's input
type:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,7 @@ class Foo:
foo = Foo(1)

reveal_type(foo.__dataclass_fields__) # revealed: dict[str, Field[Any]]
reveal_type(type(foo).__dataclass_fields__) # revealed: dict[str, Field[Any]]
reveal_type(fields(Foo)) # revealed: tuple[Field[Any], ...]
reveal_type(asdict(foo)) # revealed: dict[str, Any]
```
Expand All @@ -1801,8 +1802,7 @@ reveal_type(fields(Foo)) # revealed: tuple[Field[Any], ...]
But calling `asdict` on the class object is not allowed:

```py
# TODO: this should be a invalid-argument-type error, but we don't properly check the
# types (and more importantly, the `ClassVar` type qualifier) of protocol members yet.
# error: [invalid-argument-type] "Argument to function `asdict` is incorrect: Expected `DataclassInstance`, found `<class 'Foo'>`"
asdict(Foo)
```

Expand Down
Loading
Loading