Accessing __defaults__ attribute
Detects accessing the __defaults__ attribute of a function, which can bypass type checking and lead to runtime type errors.
The __defaults__ attribute stores the default values for a function's parameters. When you modify this attribute directly, type checkers cannot verify that the new defaults match the expected parameter types, potentially causing type errors at runtime.
What gets flagged
| Python |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | def foo(x: int = 1) -> int:
return x
foo.__defaults__ = ("string",)
foo.__defaults__ = None
def bar(x: int = 1, y: str = "string", z: bool = True) -> int:
return x
bar.__defaults__ = (1, "string", "string")
bar.__defaults__ = None
bar.__defaults__ = (1, "string")
|
Unsoundness Checker Output
| Text Only |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 | error[invalid-function-defaults]: Setting `__defaults__` to an object of type `tuple[Literal["string"]]` on a function may lead to runtime type errors.
--> main.py:4:1
|
2 | return x
3 |
4 | foo.__defaults__ = ("string",)
| ^^^^^^^^^^^^^^^^
5 |
6 | foo.__defaults__ = None
|
info: rule `invalid-function-defaults` was selected in the configuration file
error[invalid-function-defaults]: Setting `__defaults__` to an object of type `None` on a function may lead to runtime type errors.
--> main.py:6:1
|
4 | foo.__defaults__ = ("string",)
5 |
6 | foo.__defaults__ = None
| ^^^^^^^^^^^^^^^^
7 |
8 | def bar(x: int = 1, y: str = "string", z: bool = True) -> int:
|
info: rule `invalid-function-defaults` was selected in the configuration file
error[invalid-function-defaults]: Setting `__defaults__` to an object of type `tuple[Literal[1], Literal["string"], Literal["string"]]` on a function may lead to runtime type errors.
--> main.py:11:1
|
9 | return x
10 |
11 | bar.__defaults__ = (1, "string", "string")
| ^^^^^^^^^^^^^^^^
12 |
13 | bar.__defaults__ = None
|
info: rule `invalid-function-defaults` was selected in the configuration file
error[invalid-function-defaults]: Setting `__defaults__` to an object of type `None` on a function may lead to runtime type errors.
--> main.py:13:1
|
11 | bar.__defaults__ = (1, "string", "string")
12 |
13 | bar.__defaults__ = None
| ^^^^^^^^^^^^^^^^
14 |
15 | bar.__defaults__ = (1, "string")
|
info: rule `invalid-function-defaults` was selected in the configuration file
error[invalid-function-defaults]: Setting `__defaults__` to an object of type `tuple[Literal[1], Literal["string"]]` on a function may lead to runtime type errors.
--> main.py:15:1
|
13 | bar.__defaults__ = None
14 |
15 | bar.__defaults__ = (1, "string")
| ^^^^^^^^^^^^^^^^
|
info: rule `invalid-function-defaults` was selected in the configuration file
|
Mypy: No Diagnostic Emitted
Pyright: No Diagnostic Emitted
Ty: No Diagnostic Emitted
What is okay
We do not emit errors if the types of the new defaults match the expected parameter types.
| Python |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | def foo(x: str = "string") -> str:
return x
foo.__defaults__ = ("another_string",)
foo.__defaults__ = ("another_string", "another_string")
def bar(x: int): ...
bar.__defaults__ = (1,)
def baz(x: int): ...
baz.__defaults__ = None
|
Unsoundness Checker: No Diagnostic Emitted
Mypy: No Diagnostic Emitted
Pyright: No Diagnostic Emitted
Ty: No Diagnostic Emitted