0

I am trying to introduce some automation to a script I'm writing, and I'm having some trouble with calling a function that has parameters from another module. Here's the scenario:

I have two modules: test.py and Strategies.py. I have code that generates a list of all the functions in Strategies.py. From that list, I am using getattr to execute each function.

What I'm having problems with is that some of my functions have parameters. I am getting the following error with a function that has an 'x' argument:

TypeError: buy_test_function() missing 1 required positional argument: 'x'

To make this as clear as possible, here's the relevant code:

call_method = strategy_names[0][y]
call_method = getattr(Strategies, call_method)()

I know the first line above is working fine. I also know that it's the empty parentheses at the end of the second line that's causing the problem. The magic I need is finding a way to dynamically read each function's required arguments and execute the function with the necessary arguments in the parentheses.

I've tried to use inspect.signature(), but it keeps telling me the object is not callable.

I have to believe Python has an elegant solution to this, but I've had little luck on Google. Any assistance is greatly appreciated.

Thank you!

TheoretiCAL
  • 15,550
  • 7
  • 33
  • 61
  • ...but even if you were to find how many arguments it needs, what would you fill the arguments with? – Aplet123 Dec 02 '20 at 23:32
  • Does this answer your question? [How can I find the number of arguments of a Python function?](https://stackoverflow.com/questions/847936/how-can-i-find-the-number-of-arguments-of-a-python-function) – TheoretiCAL Dec 02 '20 at 23:33
  • @TheoretiCAL That's only half the problem. See Aplet123's comment. – Barmar Dec 02 '20 at 23:34
  • Might be better to first define the relevant methods to take variable length argument lists, then focus on the part @Aplet123 mentions where you figure out what the arguments should be. That way you will only have 1 problem to solve, instead of 2 – L.Grozinger Dec 02 '20 at 23:52
  • @Aplet123, I have values to fill all the possible arguments. Essentially, I'm building a test environment for stock investing strategies. So I have several lists of data that contain relevant price, volume, dividend, etc. data. Not all the strategies will require ALL the different types of data for evaluation, though, which is why I'm trying to find a dynamic solution. – thedavincicoder Dec 03 '20 at 00:23
  • @TheoretiCAL, I agree it's only half the problem. It's definitely useful to find the number of arguments, but I'm trying to actually plug them into the statement that executes the function. I suppose if all else fails, I can pass ALL the possible variables to every function I'm working with, but that seems excessive when there could be a simpler solution. – thedavincicoder Dec 03 '20 at 00:23

1 Answers1

0

Assuming the functions in Strategies are not class methods, and you've annotated the function types in the signatures, you can construct default instances of the class types you specified for the arguments and pass them in as params:

from inspect import signature
call_method = strategy_names[0][y]
call_method = getattr(Strategies, call_method)
sig = signature(call_method)
call_method = getattr(Strategies, call_method)(*[param.annotation() for param in sig.parameters.values()])

See for reference:

>>> def test(x:int):
...    return x*2
>>> sig = signature(test)
>>> sig.parameters['x'].annotation
<class 'int'>
>>> sig.parameters['x'].annotation()
0
>>> test(*[param.annotation() for param in sig.parameters.values()])
0

I will also note that if you can define the values you want to use in your call methods ahead of time for a given method and your function names in Strategies are unique, you can prebuild a dictionary that maps the function name to the args you want to use:

args = {'test':[1]}
test(*args[test.__name__])
TheoretiCAL
  • 15,550
  • 7
  • 33
  • 61
  • The argument to `signature` should be `getattr(Strategies, call_method)`. `call_method` is a string. – Barmar Dec 02 '20 at 23:37
  • And if it's a class method, the first argument needs to be an instance of the class, not a string. – Barmar Dec 02 '20 at 23:38
  • @Barmar what about something like the above, placing assumptions on the signatures? – TheoretiCAL Dec 02 '20 at 23:43
  • Probably as good as you're going to get, although it only works for annotated parameters. – Barmar Dec 02 '20 at 23:47
  • Thank you everyone! @TheoretiCAL, a prebuilt dictionary wound up being just the ticket. Don't know why I didn't think of it. I tried the signature method, and was still getting an error with it. I can see, however, that your portion of code was working. I'm just cleaning up an old nasty piece of code and it's still got some issues to work out. Thank you again! – thedavincicoder Dec 03 '20 at 16:21