# @extends / @copy\_signature to add arguments to an overriden method of a derived class

**URL:** <https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333>\
**Category:** Typing\
**Created:** [April 18, 2024, 10:27pm UTC](https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333 "2024-04-18T22:27:59Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![BrunoBelucci](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brunobelucci/32/19314_2.png) [@BrunoBelucci](https://discuss.python.org/u/BrunoBelucci)\
**Post date:** [April 18, 2024, 10:27pm UTC](https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333/1 "2024-04-18T22:27:59Z")

</div>

It has been a while since I started looking for a way to implement this. I have already come across several discussions and ideas about it, with the most relevant/recent one being in this topic:

> [@@extends decorator to explicitly marking an overriden function to accept additional variables](https://discuss.python.org/t/extends-decorator-to-explicitly-marking-an-overriden-function-to-accept-additional-variables/22538):
>
> Hey there smiley I’m not sure if there is already an existing solution or proposal for this (the closest thing I saw was [https://peps.python.org/pep-0698/](https://PEP-698) but it is not quite the thing I need. I also, tried to achieve something with ParamSpec and Concatenate but it didn’t quite work for me either), but i encounter this problem quite a lot. Whenever I create a class or a function, I like to have full documentation and annotations for each parameter to ease the work of users when using it. The…

The topic clearly explains what we are trying to implement, but I would like to expand upon the example given there. I believe that the `@dataclass` decorator already solves the example concerning the ` __init__ ` function. My proposal is a decorator that functions as follows:

```python
class A:
    def fn(self, param1: int, param2: str):
        """
        :param param1: Documentation for param1
        :param param2: Documentation for param2
        """
        # do something

class B(A):
    @extends(A.fn) # or only @extends
    def fn(self, param3: int, *args, **kwargs):
        """
        :param param3: Documentation for param3
        """
        super().fn(*args, **kwargs) # args/kwargs should contain param1 and param2

```

The `fn` method in `B` expects to receive exactly the same arguments as `fn` in `A`, plus the specific arguments added in `B`. This would even be a generalization of what is discussed on this topic:

> [@Taking the argument signature from a different function](https://discuss.python.org/t/taking-the-argument-signature-from-a-different-function/42618):
>
> First let me make clear: I’m not writing a decorator. This is a real issue from my cattrs library. I have a class with a certain \_\_init\_\_ signature. I have many functions, in different modules, essentially wrapping this \_\_init\_\_. These functions all take \*args, \*\*kwargs and use them to call this \_\_init\_\_. I want all of these functions to have the same input parameters as this \_\_init\_\_ at type-checking time, and not \*args: Any, \*\*kwargs: Any. One solution is just to copy/paste the arguments and…

The `@copy_signature` that is discussed there would only be the case when no extra arguments are added.

Currently, we can implement this to work in runtime environments, like Jupyter Notebooks, by manipulating the signature of each function. However, this approach does not generalize well to static type checkers in IDEs.

As I mentioned, it seems to me that the `@dataclass` decorator has successfully implemented this behavior. For example:

```python
@dataclass
class A:
    param1: int = 1
    param2: float = 0.4

@dataclass
class B(A):
    param3: int = 2

```

An instance of class `B` will have `self.param1`, `self.param2`, and `self.param3` as attributes, and when instantiating it in an IDE, it will correctly show that it takes `param1`, `param2`, and `param3` to be instantiated. Unfortunately, it appears that the documentation is still not fully functional, but avoiding the need to copy/paste some parameters is already a significant improvement!

So, as of today, do we have any workaround for this pattern? Do you think we could implement something similar to the `@dataclass` decorator?

---

<div class="post-metadata">

**Author:** ![alicederyn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/alicederyn/32/10282_2.png) [@alicederyn](https://discuss.python.org/u/alicederyn)\
**Post date:** [April 19, 2024, 6:05am UTC](https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333/2 "2024-04-19T06:05:15Z")

</div>

> [@BrunoBelucci](#):
>
> `def fn(self, param3: int, *args, **kwargs):`

This doesn’t look like it is a valid subclass implementation: `foo.fn(param1, param2)` **must** be a valid call by Liskov substitutability, but it is not. `param3` would need a default.

As a user, I would also expect the new parameter to come last, not first, i.e. for `foo.fn(param1, param2, param3)` to work. Is the decorator intended to make this work, i.e. reorder the unnamed input arguments, and work out how they line up with any keyword arguments?

---

<div class="post-metadata">

**Author:** ![BrunoBelucci](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brunobelucci/32/19314_2.png) [@BrunoBelucci](https://discuss.python.org/u/BrunoBelucci)\
**Post date:** [April 19, 2024, 12:23pm UTC](https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333/3 "2024-04-19T12:23:21Z")

</div>

Yes, you are right, I think I got a little bit ahead of myself trying to generalize too much. I think that this pattern mostly emerges when we are exclusively extending method with \*\*kwargs.

ActualIy, I can think of two distinct cases:

- adding some functionality with optional \*\*kwargs to a parent method
- forwarding all the required\_parameters / args / kwargs (or an entire tuple/dict) from a function to another

I think that both cases are fairly common and could be addressed by some kind of decorator, but you are right that we should be more careful when using it to extend a method.

An example of the first case that I think should be addressed is when we have something like:

```python
class A:
    def fn(self, param1: int, param2: str):
        """
        :param param1: Documentation for param1
        :param param2: Documentation for param2
        """
        # do something

class B(A):
    def fn(self, param1: int, param2: str, param3: int=2):
        """'
        :param param1: Documentation for param1 (that is the same as before)
        :param param2: Documentation for param2 (that is the same as before)
        :param param3: Documentation for param3 (new)
        """
        # do something and possibly call super().fn(param1, param2), but not necessarily

```

which with this decorator would be refactored to

```python
class B(A):
    @extends(A.fn)
    def fn(self, *args, param3: int=2):
        """'
        :param param3: Documentation for param3 (new)
        """
        # do something and possibly call super().fn(*args), but not necessarily

```

This decorator that I have in mind should be able to tell the function being extended that the \*args are the parameters from A.fn. Sure, there are still some cases we have to consider, like, how do we distinguish between the required parameters, \*args and \*\*kwargs from A.fn? Maybe, by telling explicitly to the decorator which one is which? Maybe something like

```python
`@extends(A.fn, required_args, args, kwargs)`
def fn(required_args, args, kwargs, param3: int=2):

```

While I am still unsure, I believe it is crucial to avoid the copy-and-paste scenario. This would allow one to modify A.fn (or have the function modified when we are not responsible for the code base where A lives) without the need to modify B.fn by adding/removing the new parameters from A.fn.

The second example that I have mentioned is when we have some function that is forwarding all the kwargs or an entire dict to another function, which is also extremely common, for example when using `matplotlib`:

```python
import matplotlib.pyplot as plt

def fn(x, y, **kwargs):
    # do something
    plt.plot(x, y, **kwargs)

```

Here we usually do not have the documentation for the \*\*kwargs part, we usually have to tell the user to refer to the documentation of the function that is using the \*\*kwargs. The decorator would be able to tell `fn` that the kwargs used there are the kwargs from plt.plot, for example by writing:

```python
@extends(plt.plot, kwargs=kwargs)
def fn(x, y, kwargs):

```

Although these are two distinct usage cases, it seems to me that both could be addressed by the same decorator which is responsible for telling the new function that it is extending another function that has required\_args, args and kwargs that are the same being used in this new function.

---

<div class="post-metadata">

**Author:** ![Liz](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/liz/32/15021_2.png) [@Liz](https://discuss.python.org/u/Liz)\
**Post date:** [April 19, 2024, 10:20pm UTC](https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333/4 "2024-04-19T22:20:49Z")

</div>

> [@BrunoBelucci](#):
>
> which with this decorator would be refactored to
> 
> ```python
> class B(A):
> @extends(A.fn)
> def fn(self, *args, param3: int=2):
> """'
> :param param3: Documentation for param3 (new)
> """
> # do something and possibly call super().fn(*args), but not necessarily
> 
> ```

This also breaks LSP. It’s no longer valid to pass the passed through args by name.

For kwargs at least you can type it as `(**kwargs: Unpack[SomeTypedDict])` and reuse the typed dict.

---

<div class="post-metadata">

**Author:** ![BrunoBelucci](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brunobelucci/32/19314_2.png) [@BrunoBelucci](https://discuss.python.org/u/BrunoBelucci)\
**Post date:** [April 19, 2024, 11:21pm UTC](https://discuss.python.org/t/extends-copy-signature-to-add-arguments-to-an-overriden-method-of-a-derived-class/51333/5 "2024-04-19T23:21:07Z")

</div>

We can do

```python
@extends(A.fn)
def fn(self, *args, param3: int=2, **kwargs):
        """'
        :param param3: Documentation for param3 (new)
        """

```

and what the decorator does is tell the function that the \*args and \*\*kwargs of the new function are the \*args and \*\*kwargs of the extended function. We will not be able to use new \*args and \*\*kwargs for the new function, but I think that still covers the pattern. The original function can have its args and kwargs, and we are just extending it with some functionality. We may need to always use \*args and \*\*kwargs when using the decorator, but I do not think that this is too much of a trouble.
