ipython / ipython/traitlets

in get_type_hints(Appliction), TypeError: <class 'traitlets.traitlets.Dict'> is not a generic class

Aperta
#904 1 commento 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Python
Stelle
653
Fork
217
Merge medio
2g 21h
PR unite (30g)
2

Descrizione

The expression `get_type_hints(Application)` raises an exception, given the class `traitlets.config.application.Application` or a subclass.

### Minimal Example

file `traitlets_debug_00.py`
```python
from traitlets.config.application import Application
from typing_extensions import get_type_hints

if __name__ == "__main__":
hints = get_type_hints(Application)
```

Traceback from `__main__`:
```
Traceback (most recent call last):
File ".../traitlets_debug_00.py", line 6, in
hints = get_type_hints(Application)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/typing.py", line 2305, in get_type_hints
value = _eval_type(value, base_globals, base_locals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/typing.py", line 359, in _eval_type
return t._evaluate(globalns, localns, recursive_guard)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/typing.py", line 857, in _evaluate
eval(self.__forward_code__, globalns, localns),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "", line 1, in
File "/usr/lib64/python3.11/typing.py", line 344, in inner
return func(*args, **kwds)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/typing.py", line 1807, in __class_getitem__
_check_generic(cls, params, len(cls.__parameters__))
File "/usr/lib64/python3.11/typing.py", line 271, in _check_generic
raise TypeError(f"{cls} is not a generic class")
TypeError: is not a generic class
```

### Analysis

in [`traitlets.traitlets` line 3842](https://github.com/ipython/traitlets/blob/5cf34f6c42b6e39aa1e53394443efca3d7329e3b/traitlets/traitlets.py#L3842C1-L3842C36), the `Dict` TraitType is defined like as follows:
```py
class Dict(Instance["dict[K, V]"]):
...
```

In [`traitlets.config.application`, line 403](https://github.com/ipython/traitlets/blob/5cf34f6c42b6e39aa1e53394443efca3d7329e3b/traitlets/config/application.py#L403), the type is applied as:
```py
subcommands: dict[str, t.Any] | Dict[str, t.Any] = Dict()
```

The `Dict` TraitType has not been defined as a subscriptable type, however.

### Patch?

At an instance scope, the value of the `subcommands` attribute should generally be a Python dict, while at a class scope, the attribute would be represented with a TraitType descriptor, namely an instance of the Dict TraitType.

As per the note about the syntax of the `subcommands` value in the source code where the attr is defined, the `subcommands` attr could be defined as follows:
```py
subcommands: dict[str, tuple[Callable, str]] = Dict()
```

In effect, this would remove the class-scoped Dict TraitType descriptor from the type hint for the instance-scoped `subcommands` attribute value, also providing some more detail about the syntax of the attribute value.

The initialization of the `subcommands` descriptor object would not be changed with this definition.

### Workarounds?

As one approach, the following can be used to patch the annotation in the base Application class, while providing an `Application`-like class with the patched type hint:

```python
from traitlets.config.application import Application
from traitlets.traitlets import Dict
from collections.abc import Callable
import typing as t

class ApplicationShim(Application):
subcommands: dict[str, tuple[t.Union[str, Callable[[t.Self], t.Self]], str]] = Dict()

Application.__annotations__['subcommands'] = ApplicationShim.__annotations__['subcommands']
```

The type hint in this patch is referenced to how the attribute value is applied in `Application.initialize_subcommand()` with examples for how the `subcommands` value is defined as a class variable, in the following:
- `IPython.core.profileapp.ProfileApp`
- `jupyter_client.kernelspecapp.KernelSpecApp`
- `ipykernel.kernelapp.IPKernelApp`
- `ipyparallel.cluster.app.IPCluster`

To apply the patched type hint insofar as may be visible within editor environments for instance, the class `ApplicationShim` could be used as a base class in lieu of `Application`. The patch onto the base `subcommands` annotation would be the main thing, however, insofar as for application with `get_type_hints()`

### Impacts?

This exception is encountered when calling `get_type_hints(Application)`.

This may affect some reflection-oriented user code and could possibly affect type analysis for the Application class

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.