4

I have a class B defined by multiple inheritance.

class A:
    def __init__(self, x, y):
      self.x = x
      self.y = y

class AMixin:
    def __init__(self, *args, **kwargs):
      # do stuff
      super().__init__(*args, **kwargs)

class B(AMixin, A):
    pass

Basically the mixin class overrides the __init__ method, but from the point of view of users using class B, class B's signature is same as A.

Since I am using *args and **kwargs in the mixin class, then B's initialization is based on A's constructor (functionally).

However, linters won't know that and they would think that B's signature is args and kwargs, which is kind of unhelpful.

I think this is he same problem as letting inspect.signature return A's signature (instead of AMixin) when inspecting B, but now here is what I get when I inspect B:

from inspect import signature
signature(B).parameters.keys()
# odict_keys(['args', 'kwargs'])

How to make it return ['x', 'y'] instead?

Gerges
  • 6,269
  • 2
  • 22
  • 44
  • Faced a very similar problem today. I look forward to the answers! – Cedric H. Jan 03 '19 at 20:40
  • Maybe you could write a metaclass that walks up the mro and builds an appropriate signature when the type is being created. I'm not sure how you would store that signature so that `inspect` would access it though. – Patrick Haugh Jan 03 '19 at 21:24

2 Answers2

1

Class B __init__ is inherited from AMixin class. Even though AMixin is calling super() with **args and **kwargs, its __init__ function can execute any logic as you want. It doesn't make sense for linter to expand on what's being run inside a function.

webh
  • 329
  • 4
  • 6
0

So after some research, and with the help of this post, I came up with a solution by decorating class B as such:

from functools import wraps

def BaseSignature(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    # Override signature
    wrapper.__signature__ = signature(f.__mro__[2])
    return wrapper

So then B is defined as:

@BaseSignature
class B(AMixin, A):
    pass

and now signature(B) gives <Signature (x, y)>, and the linter is also working nicely.

Although it works, it is still not ideal for me, because I have tens of those, and not looking forward to add the same decorator to all of them.

Gerges
  • 6,269
  • 2
  • 22
  • 44