Skip to content
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0e3d511
Infer type of self as typing.Self in method body
Glyphack Jun 4, 2025
6913d99
Handle type var in SuperOwnerKind::try_from_type
Glyphack Jun 5, 2025
466c93e
Update class super test
Glyphack Jun 10, 2025
3f2c487
Mark failing tests
Glyphack Jun 11, 2025
fd732c5
Don't emit diagnostic for assigning to self
Glyphack Jul 22, 2025
b6449d3
Don't unwrap the self type carelessly
Glyphack Jun 11, 2025
deb7a96
Mark attributes failing tests as todo
Glyphack Jun 14, 2025
3586737
Fix trait upcasting coercion is experimental
Glyphack Jun 14, 2025
c21827d
Fix too many iterations on comtypes
Glyphack Aug 28, 2025
495278d
Apply comments
Glyphack Jul 17, 2025
854ddee
Temp fix completions
Glyphack Jul 17, 2025
6bf8513
Add apprise to bad.txt
Glyphack Jul 17, 2025
2c24356
Remove apprise from good.txt
Glyphack Jun 23, 2025
9a6ae4f
Link issue
Glyphack Jun 24, 2025
ce1d951
Remove upcast
Glyphack Jul 9, 2025
e9aec22
Update TODO mdtests
Glyphack Jul 17, 2025
49c310a
Update snapshots
Glyphack Jul 9, 2025
c3d7304
Move discord.py to bad.txt
Glyphack Jul 17, 2025
053f651
Increase max_diagnostics
Glyphack Jul 9, 2025
1f71b74
Disallow complex exprs in constraint builder
Glyphack Jul 22, 2025
2acdbc7
Add test for cycle count
Glyphack Jul 22, 2025
4818f33
Revert falling back to Ambiguous for complex expressions
Glyphack Jul 24, 2025
ae79edf
Use infer_expression_types instead of infer_expression_types in Reach…
Glyphack Jul 24, 2025
08e8695
Remove gridout test case
Glyphack Aug 28, 2025
241bf63
Set correct typevar_binding_context
Glyphack Aug 29, 2025
d0acd44
Decrease MAX_UNION_LITERALS to avoid panic
Glyphack Aug 29, 2025
f67cc04
Handle both NonInferableTypeVar in super args
Glyphack Aug 30, 2025
5054fda
Update snapshots
Glyphack Aug 30, 2025
e877537
Update mdtest assertion
Glyphack Aug 30, 2025
58b02a2
Increase diagnostic count
Glyphack Aug 30, 2025
4205dbf
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Sep 3, 2025
0df523f
Minor review comments
sharkdp Sep 3, 2025
f24f495
Move failing projects to bad.txt
sharkdp Sep 3, 2025
d08f955
Treat __new__ as a static method
sharkdp Sep 3, 2025
a37ee79
Disable sympy in walltime benchmarks
sharkdp Sep 3, 2025
8305105
Disable prefect in memory statistics
sharkdp Sep 3, 2025
51640f2
Bring back TODO comment
sharkdp Sep 3, 2025
45421b6
Remove comment
sharkdp Sep 3, 2025
347892f
Add comment why benchmark was disabled
sharkdp Sep 3, 2025
5be3a5a
Disable pandas as a walltime benchmark
sharkdp Sep 3, 2025
baf30b4
Minor adjustments to special_first_method_parameter_type
sharkdp Sep 3, 2025
2a62a7d
Increase another threshold
sharkdp Sep 3, 2025
921119a
Merge branch 'main' into typing-self-function-scope
MichaReiser Sep 23, 2025
a02b03f
Discard changes to playground/ty/src/Editor/Editor.tsx
MichaReiser Sep 23, 2025
9caa105
Revert mypy primer changes
MichaReiser Sep 23, 2025
c0043d2
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Sep 30, 2025
32be31d
Update Salsa
MichaReiser Sep 30, 2025
56db0b9
Revert "Update Salsa"
sharkdp Oct 7, 2025
0056416
Remove FIXME comment
sharkdp Oct 7, 2025
c1cab1f
Minor rewording/renaming
sharkdp Oct 7, 2025
da56e21
Use new typing_self helper
sharkdp Oct 7, 2025
fa83f8f
Remove confusing TODO annotations
sharkdp Oct 7, 2025
b696145
Minor comment cleanup
sharkdp Oct 7, 2025
13457d2
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Oct 7, 2025
1b36000
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Oct 16, 2025
a4d6a2a
Update tests
sharkdp Oct 16, 2025
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
5 changes: 3 additions & 2 deletions crates/ruff_benchmark/benches/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ fn attrs(criterion: &mut Criterion) {
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
100,
110,
);

bench_project(&benchmark, criterion);
Expand All @@ -684,7 +684,8 @@ fn anyio(criterion: &mut Criterion) {
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
100,
// Increased after implicit self was added
150,
);

bench_project(&benchmark, criterion);
Expand Down
38 changes: 19 additions & 19 deletions crates/ruff_benchmark/benches/ty_walltime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,20 +188,20 @@ static PYDANTIC: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::
)
});

static SYMPY: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "sympy",
repository: "https://github.com/sympy/sympy",
commit: "22fc107a94eaabc4f6eb31470b39db65abb7a394",
paths: vec![SystemPath::new("sympy")],
dependencies: vec!["mpmath"],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
13000,
)
});
// static SYMPY: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
// Benchmark::new(
// RealWorldProject {
// name: "sympy",
// repository: "https://github.com/sympy/sympy",
// commit: "22fc107a94eaabc4f6eb31470b39db65abb7a394",
// paths: vec![SystemPath::new("sympy")],
// dependencies: vec!["mpmath"],
// max_dep_date: "2025-06-17",
// python_version: PythonVersion::PY312,
// },
// 13000,
// )
// });

static TANJUN: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
Expand All @@ -214,7 +214,7 @@ static TANJUN: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::ne
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
100,
320,
)
});

Expand Down Expand Up @@ -255,10 +255,10 @@ fn medium(bencher: Bencher, benchmark: &Benchmark) {
run_single_threaded(bencher, benchmark);
}

#[bench(args=[&*SYMPY], sample_size=1, sample_count=2)]
fn large(bencher: Bencher, benchmark: &Benchmark) {
run_single_threaded(bencher, benchmark);
}
// #[bench(args=[&*SYMPY], sample_size=1, sample_count=2)]
// fn large(bencher: Bencher, benchmark: &Benchmark) {
// run_single_threaded(bencher, benchmark);
// }

#[bench(args=[&*PYDANTIC], sample_size=3, sample_count=8)]
fn multithreaded(bencher: Bencher, benchmark: &Benchmark) {
Expand Down
6 changes: 6 additions & 0 deletions crates/ruff_python_ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3030,6 +3030,12 @@ impl Parameters {
.find(|arg| arg.parameter.name.as_str() == name)
}

/// Returns the index of the parameter with the given name
pub fn index(&self, name: &str) -> Option<usize> {
self.iter_non_variadic_params()
.position(|arg| arg.parameter.name.as_str() == name)
}

/// Returns an iterator over all parameters included in this [`Parameters`] node.
pub fn iter(&self) -> ParametersIterator<'_> {
ParametersIterator::new(self)
Expand Down
26 changes: 25 additions & 1 deletion crates/ty_ide/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1623,7 +1623,31 @@ class Quux:
// of available attributes.
//
// See: https://github.com/astral-sh/ty/issues/159
assert_snapshot!(test.completions_without_builtins(), @"<No completions found>");
assert_snapshot!(test.completions_without_builtins(), @r"
__annotations__
__class__
__delattr__
__dict__
__dir__
__doc__
__eq__
__format__
__getattribute__
__getstate__
__hash__
__init__
__init_subclass__
__module__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
");
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions crates/ty_ide/src/semantic_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1803,12 +1803,12 @@ class BoundedContainer[T: int, U = str]:
"get_first" @ 642..651: Method [definition]
"self" @ 652..656: SelfParameter
"T" @ 661..662: TypeParameter
"self" @ 679..683: Variable
"self" @ 679..683: TypeParameter
"value1" @ 684..690: Variable
"get_second" @ 700..710: Method [definition]
"self" @ 711..715: SelfParameter
"U" @ 720..721: TypeParameter
"self" @ 738..742: Variable
"self" @ 738..742: TypeParameter
"value2" @ 743..749: Variable
"BoundedContainer" @ 798..814: Class [definition]
"T" @ 815..816: TypeParameter [definition]
Expand Down
34 changes: 22 additions & 12 deletions crates/ty_python_semantic/resources/mdtest/annotations/self.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ class Shape:
inner(self)

def implicit_self(self) -> Self:
# TODO: first argument in a method should be considered as "typing.Self"
reveal_type(self) # revealed: Unknown
reveal_type(self) # revealed: Self@implicit_self
return self

reveal_type(Shape().nested_type()) # revealed: list[Shape]
Expand Down Expand Up @@ -83,14 +82,14 @@ class Shape:

@classmethod
def bar(cls: type[Self]) -> Self:
# TODO: type[Shape]
# TODO(cls): type[Shape]
reveal_type(cls) # revealed: @Todo(unsupported type[X] special form)
return cls()

class Circle(Shape): ...

reveal_type(Shape().foo()) # revealed: Shape
# TODO: Shape
# TODO(cls): Shape
reveal_type(Shape.bar()) # revealed: Unknown
```

Expand All @@ -115,6 +114,20 @@ class LinkedList:
reveal_type(LinkedList().next()) # revealed: LinkedList
```

Or attributes can have a type var type:

```py
from typing import Generic, TypeVar

T = TypeVar("T")

class Bar(Generic[T]):
foo: T
def bar(self) -> T:
reveal_type(self) # revealed: Self@bar
return self.foo
```

## Generic Classes

```py
Expand Down Expand Up @@ -164,31 +177,28 @@ b: Self

# TODO: "Self" cannot be used in a function with a `self` or `cls` parameter that has a type annotation other than "Self"
class Foo:
# TODO: rejected Self because self has a different type
# TODO: should be rejected Self because self has a different type
def has_existing_self_annotation(self: T) -> Self:
return self # error: [invalid-return-type]

def return_concrete_type(self) -> Self:
# TODO: tell user to use "Foo" instead of "Self"
# TODO: tell user to annotate with "Foo" instead of "Self"
# error: [invalid-return-type]
return Foo()

@staticmethod
# TODO: reject because of staticmethod
# TODO: should be reject because of staticmethod
def make() -> Self:
# error: [invalid-return-type]
return Foo()

class Bar(Generic[T]):
foo: T
def bar(self) -> T:
return self.foo
class Bar(Generic[T]): ...

# error: [invalid-type-form]
class Baz(Bar[Self]): ...

class MyMetaclass(type):
# TODO: rejected
# TODO: reject the Self usage. because self cannot be used within a metaclass.
def __new__(cls) -> Self:
return super().__new__(cls)
```
Expand Down
22 changes: 16 additions & 6 deletions crates/ty_python_semantic/resources/mdtest/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ class C:
c_instance = C(1)

reveal_type(c_instance.inferred_from_value) # revealed: Unknown | Literal[1, "a"]

# TODO: Same here. This should be `Unknown | Literal[1, "a"]`
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown | Literal[1, "a"]

# There is no special handling of attributes that are (directly) assigned to a declared parameter,
# which means we union with `Unknown` here, since the attribute itself is not declared. This is
Expand Down Expand Up @@ -177,8 +175,7 @@ c_instance = C(1)

reveal_type(c_instance.inferred_from_value) # revealed: Unknown | Literal[1, "a"]

# TODO: Should be `Unknown | Literal[1, "a"]`
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown | Literal[1, "a"]

reveal_type(c_instance.inferred_from_param) # revealed: Unknown | int | None

Expand Down Expand Up @@ -399,9 +396,19 @@ class TupleIterable:

class C:
def __init__(self) -> None:
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
[... for self.a in IntIterable()]
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
# error: [unresolved-attribute]
[... for (self.b, self.c) in TupleIterable()]
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
# error: [unresolved-attribute]
[... for self.d in IntIterable() for self.e in IntIterable()]
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
[[... for self.f in IntIterable()] for _ in IntIterable()]
[[... for self.g in IntIterable()] for self in [D()]]

Expand Down Expand Up @@ -598,6 +605,8 @@ class C:
self.c = c
if False:
def set_e(self, e: str) -> None:
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
self.e = e

# TODO: this would ideally be `Unknown | Literal[1]`
Expand Down Expand Up @@ -685,7 +694,7 @@ class C:
pure_class_variable2: ClassVar = 1

def method(self):
# TODO: this should be an error
# error: [invalid-attribute-access] "Cannot assign to ClassVar `pure_class_variable1` from an instance of type `Self@method`"
self.pure_class_variable1 = "value set through instance"

reveal_type(C.pure_class_variable1) # revealed: str
Expand Down Expand Up @@ -1759,6 +1768,7 @@ def external_getattribute(name) -> int:

class ThisFails:
def __init__(self):
# error: [invalid-assignment] "Implicit shadowing of function `__getattribute__`"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few new "implicit shadowing of function …" diagnostics in the diff. This seems sort-of fine, but maybe worth looking into.

self.__getattribute__ = external_getattribute

# error: [unresolved-attribute]
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/resources/mdtest/call/dunder.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class C:
return str(key)

def f(self):
# TODO: This should emit an `invalid-assignment` diagnostic once we understand the type of `self`
# error: [invalid-assignment] "Implicit shadowing of function `__getitem__`"
self.__getitem__ = None

# This is still fine, and simply calls the `__getitem__` method on the class
Expand Down
15 changes: 8 additions & 7 deletions crates/ty_python_semantic/resources/mdtest/class/super.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,12 @@ class A:

class B(A):
def __init__(self, a: int):
# TODO: Once `Self` is supported, this should be `<super: <class 'B'>, B>`
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'B'>, B>
super().__init__(a)

@classmethod
def f(cls):
# TODO: Once `Self` is supported, this should be `<super: <class 'B'>, <class 'B'>>`
# TODO: Once `cls` is supported, this should be `<super: <class 'B'>, <class 'B'>>`
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
super().f()

Expand Down Expand Up @@ -149,15 +148,15 @@ from __future__ import annotations

class A:
def test(self):
reveal_type(super()) # revealed: <super: <class 'A'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'A'>, A>

class B:
def test(self):
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'B'>, B>

class C(A.B):
def test(self):
reveal_type(super()) # revealed: <super: <class 'C'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'C'>, C>

def inner(t: C):
reveal_type(super()) # revealed: <super: <class 'B'>, C>
Expand Down Expand Up @@ -279,6 +278,8 @@ class A[T]:

class B[T](A[T]):
def f(self, b: T) -> T:
# TODO: https://github.com/astral-sh/ty/issues/697
# error: [invalid-super-argument] "`Self@f` is not an instance or subclass of `<class 'B'>` in `super(<class 'B'>, Self@f)` call"
return super().f(b)
```

Expand Down Expand Up @@ -399,7 +400,7 @@ class A:
class B(A):
def __init__(self, a: int):
super().__init__(a)
# TODO: Once `Self` is supported, this should raise `unresolved-attribute` error
# error: [unresolved-attribute] "Type `<super: <class 'B'>, B>` has no attribute `a`"
super().a

# error: [unresolved-attribute] "Type `<super: <class 'B'>, B>` has no attribute `a`"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def f1(flag: bool):
attr = DataDescriptor()

def f(self):
# error: [invalid-assignment] "Invalid assignment to data descriptor attribute `attr` on type `Self@f` with custom `__set__` method"
self.attr = "normal"

reveal_type(C1().attr) # revealed: Unknown | Literal["data", "normal"]
Expand Down
3 changes: 1 addition & 2 deletions crates/ty_python_semantic/resources/mdtest/named_tuple.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ class SuperUser(User):
def now_called_robert(self):
self.name = "Robert" # fine because overridden with a mutable attribute

# TODO: this should cause us to emit an error as we're assigning to a read-only property
# inherited from the `NamedTuple` superclass (requires https://github.com/astral-sh/ty/issues/159)
# error: 9 [invalid-assignment] "Cannot assign to read-only property `nickname` on object of type `Self@now_called_robert`"
self.nickname = "Bob"

james = SuperUser(0, "James", 42, "Jimmy")
Expand Down
3 changes: 2 additions & 1 deletion crates/ty_python_semantic/resources/mdtest/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,8 @@ class Foo(Protocol):
self.b: int = 128 # TODO: should emit diagnostic

def non_init_method(self) -> None:
self.y = 64 # fine
# TODO: should be fine
self.y = 64 # error: [invalid-assignment] "Object of type `Literal[64]` is not assignable to attribute `y` of type `str`"
self.c = 72 # TODO: should emit diagnostic

# Note: the list of members does not include `a`, `b` or `c`,
Expand Down
Loading
Loading