pylint-dev / pylint-dev/astroid

How to type ``NodeNG.parent``

Open
#2,017 3 comments 0 reactions 0 assignees View on GitHub
Maintenance
Dominant language
Python
Stars
582
Forks
357
Avg merge
1d 1h
Merged PRs (30d)
23

Description

```python
from __future__ import annotations

from typing import Generic

from typing_extensions import TypeVar

_NodeParent = TypeVar(
"_NodeParent", bound="LocalsDictNode | None", default="LocalsDictNode"
)

class Node(Generic[_NodeParent]):
def __init__(self, parent: _NodeParent) -> None:
self.parent = parent

class LocalsDictNode(Node["LocalsDictNode" | None]):
...

class Module(LocalsDictNode):
def __init__(self) -> None:
super().__init__(None)
self.parent: None = None
reveal_type(self.parent)

def method(self) -> None:
reveal_type(self.parent)

class FunctionDef(LocalsDictNode):
def method(self) -> None:
if isinstance(self.parent, Module):
reveal_type(self.parent.parent)
else:
reveal_type(self.parent.parent)

class Arguments(Node[FunctionDef]):
def __init__(self, parent: FunctionDef) -> None:
super().__init__(parent)
reveal_type(self.parent)

class AssignName(Node[LocalsDictNode]):
def __init__(self, parent: LocalsDictNode) -> None:
super().__init__(parent)
reveal_type(self.parent)

def method(self, attr: Node) -> None:
reveal_type(attr)
```

The above code almost meets all requirements we would have for `.parent` except for one. I'll list them in the hopes of getting any good ideas on how to fix this. Note that this uses the proposed [PEP 696](https://peps.python.org/pep-0696/#generic-typealiases) as I saw no other way to even get this far without.

1. `Node` itself should not need any type parameters (because of its genericness) and should be useable without any
2. Unless otherwise specified `NodeNG.parent` should be a `LocalsDictNode`
3. A `LocalsDictNode.parent` should also be a `LocalsDictNode`
4. Except for `Module.parent`. That should be `None`.

The above design almost meets that requirement except for that in `FunctionDef.method the second `reveal_type` shows `LocalsDictNode | None`, which should be `LocalsDictNode` as we know that `node.parent` isn't a `Module` and therefore `node.parent.parent` should be `LocalsDictNode`.

/CC @cdce8p as you might have a good idea for this. Hopefully...

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.