typing icon indicating copy to clipboard operation
typing copied to clipboard

How should we annotate functions that forward to their superclass?

Open NeilGirdhar opened this issue 2 years ago • 5 comments

Consider this real code:

from typing import SuperKwargs

class InferenceManager(Generic[T]):
    @override
    def __init__(self,
                 *,
                 default_trajectory: T,
                 progress_manager: None | ProgressManager,
                 wandb_run: None | Run
                 ) -> None:
        super().__init__()
        self._progress_manager = progress_manager
        self._results: list[T] = []
        self._trajectory = default_trajectory
        self._wandb_run = wandb_run


class TrainingInferenceManager(InferenceManager[RLTrainingResult]):
    def __init__(self, training_result: TrainingResult, **kwargs: SuperKwargs):
         self.training_result = training_result
         super().__init__(**kwargs)

If we want full annotations for TrainingInferenceManager.__init__, we currently need to duplicate all of the superclass's parameters. I suggest adding typing.SuperKwargs that stands in place of them.

(This could be made more complicated by allowing the child class to synthesize some of the parameters.)

Related: https://github.com/python/mypy/issues/8769

NeilGirdhar avatar Sep 18 '23 22:09 NeilGirdhar

If you're using a smart editor or language server, it should take little or no effort to duplicate the signature when overriding a method. For example, with pylance installed in VS Code, you can type def __init__, then hit tab, and the entire signature of the base class will be copied along with a call to super().__init__(). I realize that's not quite what you're asking for here, but I mention it in case you weren't aware that this functionality existed.

erictraut avatar Sep 18 '23 23:09 erictraut

Thanks @erictraut, that's a good point. I use NeoVim, which afaik doesn't have this functionality. I'm sure you're aware, but just for any other readers, the limitations of copying the base class signature include:

  • there may be more than one base class,
  • the base classes can change, which leads to churn in all derived class signatures, and
  • as in the example above, the base class is generic, but the derived class isn't, so the editor would have to do the generic type substitution.

NeilGirdhar avatar Sep 18 '23 23:09 NeilGirdhar

@NeilGirdhar Chiming in: I use NeoVim too, and with these plugins, I do get the def __init__ tab completion on a child class

  use {
    "williamboman/mason.nvim",
    "williamboman/mason-lspconfig.nvim",
    "neovim/nvim-lspconfig",
  }

Using mason.nvim I installed pyright with LspInstall pyright. Mason plugs pyright into NeoVim's native LSP framework.

kwsp avatar Sep 20 '23 17:09 kwsp

On this topic, I think there is a need for a way to forward parameters annotations in general, not just for the purpose of inheritance. For example, I often write pure helper functions that are called inside a method that take the same parameters as the method.

kwsp avatar Sep 20 '23 17:09 kwsp

If you're using a smart editor or language server, it should take little or no effort to duplicate the signature when overriding a method

A plain-text copy of another library's source code is a flaky error-prone thing to maintain, even if it is possible to generate one.

You might end up having to import a big pile of weird internal details that the upstream library uses in their type annotations that might break at any time, and are otherwise irrelevant to your application.

For example, consider the case of writing a type stub for geopandas.GeoSeries, which inherits from pandas.Series. The problem is that pandas.Series.__new__ has 6 parameters and 4 overloads, and several of those parameters are themselves complicated internal-only things that have no stability or interface guarantees.

Similarly, imagine a case where I'm writing my own subclass of Series, and all I want to do is define a new method and add an extra optional parameter to __init__. Ideally I'd be able to forward the types of *args, **kwargs to the parent class and not have to maintain my own copy of the entire signature.

I think the above cases are common enough and painful enough that something like SuperKwargs would be very much appreciated by many people.

gwerbin avatar Dec 20 '23 20:12 gwerbin