Skip to content

Mutating __code__ attribute

Detects setting the __code__ attribute of a function, which can bypass type checking and lead to runtime type errors.

The __code__ attribute contains the compiled bytecode of a function. When you modify this attribute directly, you can completely change the function's behavior, including its parameter types and return type, without the type checker being aware of the change.

What gets flagged

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def foo(x: int) -> int:
    return 1

def bar(x: str) -> str:
    return "!"

foo.__code__ = bar.__code__

# Type checker things `result` is an `int`, but it is a string
result = foo(10)
Unsoundness Checker Output
Text Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
error[mutating-function-code-attribute]: Mutating `__code__` attribute on a function may lead to runtime type errors.
 --> main.py:7:1
  |
5 |     return "!"
6 |
7 | foo.__code__ = bar.__code__
  | ^^^^^^^^^^^^
8 |
9 | # Type checker things `result` is an `int`, but it is a string
  |
info: rule `mutating-function-code-attribute` was selected in the configuration file

Mypy: No Diagnostic Emitted

Pyright: No Diagnostic Emitted

Ty: No Diagnostic Emitted

What we can't catch

Sometimes users will mutate __code__ in a type safe way:

Python
1
2
3
4
5
6
7
8
9
def foo(x: int) -> int:
    return 1

def bar(x: int) -> int:
    return 1

foo.__code__ = bar.__code__

result = foo(10)
Unsoundness Checker Output
Text Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
error[mutating-function-code-attribute]: Mutating `__code__` attribute on a function may lead to runtime type errors.
 --> main.py:7:1
  |
5 |     return 1
6 |
7 | foo.__code__ = bar.__code__
  | ^^^^^^^^^^^^
8 |
9 | result = foo(10)
  |
info: rule `mutating-function-code-attribute` was selected in the configuration file

Mypy: No Diagnostic Emitted

Pyright: No Diagnostic Emitted

Ty: No Diagnostic Emitted

This is very hard to validate, and therefor we only emit a warning that you probably shouldn't be messing with the __code__ attribute.