Convert string into a function call withing the same class - python

How would I go about converting a string into a function call for a function that is within the same class? I used this question to help a bit but I think it has something to do with "self.".
ran_test_opt = choice(test_options)
ran_test_func = globals()[ran_test_opt]
ran_test_func()
where test_options is a list of the names of the functions available in string format. With the above code I get the error
KeyError: 'random_aoi'

Don't use globals() (the functions are not in the global symbol table), just use getattr:
ran_test_func = getattr(self, ran_test_opt)

globals() is a function that you should use very, very rarely, it smells of mixing code and data. Calling an instance method by a name found in a string is similar, but slightly less hacky. Use getattr:
ran_test_func = getattr(self, ran_test_opt)
ran_test_func()

Related

How can I call a method from a name string in Python?

I am using an API to call specific information from a website. I need to be able to parse through the list to utilize the functions. Example:
list = ['doThis','doThat']
for item in list:
sampleobject.item
The issue is when I use this, I get an error saying "sampleobject has no attribute 'item'".
Is there a way that I can pull the quote out of the string to do this?
Try:
methods = ['doThis','doThat']
for method_name in methods:
method = getattr(sampleobject, method_name)
method()
Though it would be easier to do:
sampleobject.doThis()
sampleobject.doThat()
You can call getattr(sampleobject, item) to get the content of a property with the name equal to what is stored in item, which is an element from your list.
I think the problem is not about quotes at all. The problem is that syntax object.member means: evaluate a property named member that is stored in a variable named object. And you expect it to mean: evaluated a property with the name stored in member.

Mapping a returned string to a specific function

I have router that is deciding which function to call based upon user input (uses ConfigParser) and then tries to decide which function to call.
def SomethingElse():
print 'hello'
def UploadDirectory():
print 'hi'
def router(config):
if config.has_option('job', 'Some_Task'):
taskName = config.get('job', 'Some_Task')
# taskName is now 'UploadDirectory'
###execute the UploadDirectory function
###execute something else if something else, etc
So what is the way to write this in python? If I prebuilt a map of functions to strings, can i execute them that way?
How would you write this?
Yep, building a map of strings to function names is perfectly valid:
task_map = {
'upload': UploadDirectory,
'something': SomethingElse,
}
And execute as:
task_map[task_name]()
Aside: try to follow PEP-8, the python style guide; it helps make your code more readable to all other Python programmers. Specifically in this case, prefer underscore_separated function names instead of LeadingCaps.
Another hacky way to do it is to use globals(), if you're not running just by importing the router function:
globals()[taskName]()

Getting KeyError while iterating object names using locals()

I have 5 objects, mac5_le(), mac4_le and so on. I am trying to extract some value from each of the objects as follows,
for i in range(5,-1,-1):
m = locals()['self.mac'+str(i)+'_le.text()']
print m
I am getting the error as KeyError: 'self.mac5_le.text()'.
Any Idea?
What the what?
m = getattr(self, 'mac%d_le' % i).text()
Not sure why you would want to munge objects around that way, but you've definitely got your syntax wrong:
locals()['self.mac'+str(i)+'_le'].text()
should "work".
I see a few things wrong with what you're attempting. First, self.name variables are not local scope. They're either part of the instance, or part of the class. Locals are variables that are accessible from your current function scope, but not the global scope. For instance, in the code below, you would see foo and bar, but not baz:
baz = 1
def silly():
# These two variables are local
foo = 2
bar = 3
print locals()
Calling silly():
>>> silly()
{'foo': 2, 'bar': 3}
Secondly, the locals() and globals() dictionaries don't resolve the dot operator, nor will they call functions.
What you want to do is use something like getattr, or create an api that works better for you (the code you're trying to write isn't very idiomatic Python). Here's what is might look like with getattr:
for i in range(5,-1,-1):
m = getattr(self, 'mac'+str(i)+'_le').text()
print m
getattr is will do all the right lookups underneath the hood to find macN_le. Once you have a reference to the object, then you can call .text() on it.
Hope that helps!

python inspect.getargspect

Imports a module and then goes through the module's namespace to find any functions (you may assume any object with a call() method is a function) and print the names of the functions and their arguments, in the same way as it might appear in a def statement.
My problem is after I have loop through the module and gotten all the function I can not pass the function name to inspect.getfullargspec() because it is a string.How do i make the string callable?
import inspect
from smtplib import SMTP
from pprint import pprint
def func(x):
for item in inspect.getmembers(x):
lst = inspect.getmembers(x, inspect.isfunction)
for items in lst:
func_names = items[0] #names of functions
f = r"%s.%s" % (x.__name__, func_names)
arg = inspect.getargspec(f)
print(f)
if __name__ == '__main__':
func(SMTP)
You've got a few mistakes in this. The quick answer, though, is that that you don't want to make string callable, you just need to know that inspect.getmembers returns a list of ('func_name', <function object>) pairs, and inspect.getargspec expects a function object.
So you could make your for-loop look like this:
for name, fun in lst:
long_name = r"%s.%s" % (x.__name__, name)
argspec = inspect.getargspec(fun)
print(long_name)
As a separate issue, you rvariable names are mostly nondescriptive and occasionally incorrect. For example, what you call func_names is always exactly one name, and the variable lst would be more usefully named members, and item should be member. Naming a function func is not normally good practice, especially when that function needs several variables inside of that should also, more appropriately, be named func.
lst here is a tuple of function name and function object, you do not really need to do all the string manipulation. Below simple method will do the job:
def func(module):
for lst in inspect.getmembers(module, inspect.isfunction):
if inspect.isfunction(lst[1]):# Doing an additional check, may not be actually required
print(lst[0]+inspect.formatargspec(*inspect.getfullargspec(lst[1])))

Passing a list to eval()

Is there a way to pass a list as a function argument to eval() Or do I have to convert it to a string and then parse it as a list in the function?
My simple example looks like:
eval("func1(\'" + fArgs + "\')")
I'm just not sure if there is a better way of taking fArgs as a list instead of a string
Note:
The list is provided from a JSON response
EDIT: Ok here's a bit more of my class so there's a better understanding of how I'm using eval
def test(arg):
print arg
#Add all allowed functions to this list to be mapped to a dictionary
safe_list = ['test']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
class Validate:
def __init__(self, Value, fName, fArgs):
eval(fName + "(\'" + fArgs + "\')", {"__builtins__":None},safe_dict)
I may be wrong in thinking this, but to my understanding this is a safe use of eval because the only functions that can be called are the ones that are listed in the safe_list dictionary. The function to be run and the arguments for that function are being extracted out of a JSON object. The arguments are to be structured as a list, Will joining the list together with ", " be interpreted as actual arguments or just a single argument?
If you're using Python 2.6.x, then you should be able to use the json module (see py doc 19.2). If not, then there is python-json available through the python package index. Both of these packages will provide a reader for parsing JSON data into an appropriate Python data type.
For your second problem of calling a function determined by a message, you can do the following:
def foo():
print 'I am foo!'
def bar():
pass
def baz():
pass
funcs = {'func_a':foo, 'func_b':bar, 'func_c':baz}
funcs['func_a']()
This approach can be a bit more secure than eval because it prevents 'unsafe' python library functions from being injected into the JSON. However, you still need to be cautious that the data supplied to your functions can't be manipulated to cause problems.
Specifying parameters the following way works:
root#parrot$ more test.py
def func1(*args):
for i in args:
print i
l = [1,'a',9.1]
func1(*l)
root#parrot$ python test.py
1
a
9.1
so, no direct need for eval(), unless I'm misunderstanding something.
Using a library to parse JSON input may be a better approach than eval, something like:
import json
func1(json.loads(fArgs))
Assert-ing that user input is correct would be a good idea, too.
The others have a good point, that you shouldn't be using eval. But, if you must:
eval("func1(%s)" % ", ".join(fArgs))
will call the function with all the arguments in the list. This:
eval("func1([%s])" % ", ".join(fArgs))
will call it with the list of arguments in just one argument. Maybe you even want this?
eval("func1([%s])" % ", ".join(map(eval, fArgs)))
which would eval the arguments as well?

Categories

Resources