1

I want to do something, for functions, not classes, like

def function_A(param1, param2 = "default_param_2")

def function_B(__signature of A__)

So that whenever I change the interface of A, this is automatically updated in B. Is there a notion of an "abstract base interface" for functions or a mechanism to do this?

Tommy
  • 12,588
  • 14
  • 59
  • 110

3 Answers3

2

Not sure how useful this is but decorators may give you a least part of what you are looking for. Consider:

from functools import wraps

def abi(f):
    @wraps(f) #preserves name/doc of wrapped function
    def function_A(param1, param2='param2'):
        print(param1, param2)
    return function_A

@abi
def function_B(*args, **kw):
    "I'm function_B but I'm REALLY function_A.  Look there for params."
    pass

Defining *args and **kw for function_B makes it flexible for function_A to change its signature. Any parameters passed into function_B will be mapped to function_A's following the normal rules; too many args, multiple values for argument, etc. will raise TypeErrors.

If you change the signature of function_A, any calls to function_B that still match can be called successfully (assuming the module has been reloaded). The rest will fail obviously and you'll need to update the calls to function_B.

Hope this helps! YMMV.

ksteinbe
  • 21
  • 2
1

I would not do this at runtime, and you cannot change the signature automatically but you can definitely test that in your testing suite using the inspect module.

For instance:

import inspect

def foo(bar, baz=42, **kwargs):
    pass

for param in inspect.signature(foo).parameters.values():
    print('%s is %s' % (param.name, param.kind))

This will print:

bar is POSITIONAL_OR_KEYWORD
baz is POSITIONAL_OR_KEYWORD
kwargs is VAR_KEYWORD

You can use this to compare the signature of functions in your test suite. Check out the inspect module for other things you can introspect.

spectras
  • 13,105
  • 2
  • 31
  • 53
1

Under Python 3 you can easily get the signature:

>>> from inspect import signature
>>> def A(a, b='default'):
...    pass
>>> sig=signature(A) 
>>> print(sig)
(a, b='default')

Problem is, there is not much you can do with that without being really hacky.

You can compare a function's signature to another:

def A(a, b='default'):
    pass

def B(a, b='something else'):
    if signature(A) != signature(B):
        print('Yo, need to update me!')
    else:
        print('all good in B land')    

But you cannot dynamically update B to match A.

dawg
  • 98,345
  • 23
  • 131
  • 206