facebook / facebook/pyrefly

`__new__` signature should take precedence over `__init__`

Open
#4,137 0 comments 0 reactions 0 assignees View on GitHub
quansight typechecking
Dominant language
Rust
Stars
7k
Forks
516
PR merge metrics
No merged PRs in 30d

Description

This repro is distilled from the (strange) way that `scipy.sparse.linalg.LinearOperator` implements its `__new__` and `__init__` methods ([src](https://github.com/scipy/scipy/blob/ba8b7d300e3afcd412bc9eb91b03d087c678161e/scipy/sparse/linalg/_interface.py#L207-L227)):

```py
class A:
def __new__(cls, a: int):
if cls is A:
return super().__new__(B)
else:
return super().__new__(cls)

def __init__(self) -> None: ...

class B(A):
def __init__(self, a: int) -> None:
self.a = a

A(1) # ❌ Expected 0 positional arguments, got 1 in function `A.__init__` [bad-argument-count]
```

[sandbox](https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeSIAxlKnHAAQCCiAOuvR-ZjGPQPp90MAO4CAFNTgAaeqkT0I6AC4BKVu06aIvSQobM2mo5wBOMJQFcT7OBeIwTYlYQFDRfMQCEVh4-RhQcDDqfkZmltb0tvaOzq4i4pI%2B6L4c3LwCihBK4kFQYCr0ALQAfPQAcrhC8oS1bGzUtAyeYoxqqVw8-HxZOR55YDJyCsqFpRVVwR2aA4So9AC8svXojGIAjMkgUiBkZmBQpIRKuAC2UBQAxPQACqT7h1EYOAT0lFWQAOZWqEoQVYQ2NcAMowGD0AAWSiUxDgiAA9PC9jxDoRcCZPvCYOh4ZhcJQ4PD3ugvj8-lV4fQwOjZAA3VDQVDYWBvD4Qb4mX7-di4Yjk9BwQHoMhKCFVIq0hxwbmLegsEAAZkI6wATPK2ABtBwmdFwAC6bAs6Agp2I6KUMEwRUwEDMlD%2BktlAHJ2eh0TAnWw3UoimYAI4WW2WooAaxgpCKqEolHgDCWTuEqGsnvQIAAvjsow6YAAxaAwChoLB4IhkdNAA)

But at runtime this works fine:

```pycon
>>> class A:
... def __new__(cls, a: int):
... if cls is A:
... return super().__new__(B)
... else:
... return super().__new__(cls)
...
... def __init__(self) -> None: ...
...
... class B(A):
... def __init__(self, a: int) -> None:
... self.a = a
...
... A(1)
...
<__main__.B object at 0x718a77ee2f90>
```

Mypy also has issue with this (IMO awkward) pattern (https://github.com/python/mypy/issues/17251), which, by the way, is why scipy-stubs can't get 100% type coverage at the moment (https://github.com/scipy/scipy-stubs/blob/2ac5dfb1fc3edf8f43eff922cf94410b6e70a8d2/scipy-stubs/sparse/linalg/_interface.pyi#L199-L217). Pyright accurately models this behavior.

Contributor guide

Open the contributing guide

Research direction

Start by running the linked sandbox repro and compare it with scipy/sparse/linalg/_interface.py and the scipy-stubs sparse/linalg/_interface.pyi references. Trace how the checker chooses between __new__ and __init__; done means the example no longer reports the argument-count error and has regression coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.