Python: is it possible to compare function import origin? - python

I have 4 python files, the first two is the function itself, the second is functions dictionary, and the third is kind of a 'definition' parser
function_1
def increment(obj):
return obj+1
#another function
function_2
def decrement(obj):
return obj-1
#another function
function_dictionary
import fucntion1
import function2
FUNC_DICT = {
'increment': function1.increment,
'decrement': function2.decrement,
#another function
}
definition_parser
from function_dictionary import FUNC_DICT
def get_definition():
result = {}
for key, value in FUNC_DICT.items():
#check if value is from function_1 or function_2
#result[key] = 'function_1' or 'function_2', depends on its origin
return result
is it possible to compare function import? I tried it with is_in_function_1 = value is in function_1, doesn't work.
if it is not, what are the way around without much repetition?

You can get the module of functions via the __module__ property.
from function_dictionary import FUNC_DICT
def get_definition():
result = {}
for key, value in FUNC_DICT.items():
result[key] = value.__module__
return result
The output would look like the following:
{
'increment': 'function_1',
'decrement': 'function_2'
}

You could use the inspect module like so:
import inspect
print(inspect.getmodule(SequenceMatcher))
for example, if I inspect SequenceMatcher, the output is:
<module 'difflib' from 'C:\ProgramData\Anaconda2\lib\difflib.py'>
So to compare the origin of two functions, you could simply do this:
if inspect.getmodule(increment) == inspect.getmodule(decrement):
do stuff

Related

Mocking functions in python called from a dictionary

I have a problem with the code below. I would like to mock the functions in different file for unit testing in the FUNCTION_MAPPING part.
import module.module2 as module_name
FUNCTION_MAPPING = {
1: module_name.foo,
2: module_name.foo2,
3: module_name.foo3
}
def my_func(number):
function_call = FUNCTION_MAPPING[number]
result = function_call()
return result
For some reason I can not mock those functions. I have tried every possible way that i had knowledge about. If possible i would like not to change the code above.
foo, foo2 and foo3 inner code can be anything print(1), print(2) etc
Code of the unit test:
#patch("module_of_the_code_above.module_name.foo",return_value="Test")
def test_my_func(self,mocked_foo):
result = my_func(1)
nose_tools.assert_equal(result,"Test")
Simple two options if you use MagicMock:
The issue with this case is that as you import your module the dictionary with the lookups is created and the references to the "module_name.foo" is made, so any patching/mocking at a global level will not affect that explicit mapping, you have to replace/wrap it on that structure.
....
# in you test manually replace the function mapping with either new
# functions you define, but I prefer MagicMock as it allows you to get
# all kinds of goodies.
# This is necessary as the dict for the function lookup is probably already
# initialize before any mocking can take place (as you include the module)
# and so even patching the function globally will not affect that lookup
# You can use MagicMock so that your original function is not used..
FUNCTION_MAPPING['1'] = mock.MagicMock()
FUNCTION_MAPPING['2'] = mock.MagicMock()
FUNCTION_MAPPING['3'] = mock.MagicMock()
# Or
# if you just want to spy/keep stats but still call the original function, you will
# need to do something like
FUNCTION_MAPPING['1'] = mock.Mock(wraps=FUNCTION_MAPPING['1')
# then you can all the great things that you can do with mock.
assert FUNCTION_MAPPING['1'].call_counts == 3
PS: did not have time to test/vet the this for exact syntax but hope this points you in the right direction.
You should assign the function(without brackets) and not the result of the fucntion - {1: func}
def hello():
print('Hello!')
def text(text):
print(text)
function_map = {
1: hello,
2: text
}
func1 = function_map[1]
func1()
func2 = function_map[2]
func2('ABC123')
Output:
Hello!
ABC123
When working with imports:
import math
function_map = {
'factorial': math.factorial,
'gcd': math.gcd
}
func1 = function_map['factorial']
print(func1(5))
func2 = function_map['gcd']
print(func2(5, 25))

Call specific variable in a Python function

I have a Python script called module.py with several functions in it, like this:
def foo_1():
var = 1
return
def foo_2():
var = 2
return
def foo_3():
var = 3
return
def foo_4():
var = 4
return
Each of the functions (foo_1, foo_2, ...) defines a unique value for the variable named var, and I am looking for a way to grab this value from each of the functions. Is this possible in Python? It is crucial to my project that each of the variables var have the same local name in their respective function.
I have already been able to grab a list of all the functions as follows:
from inspect import getmembers, isfunction
import module
function_list = [pair[0] for pair in getmembers(module, isfunction)]
Which returns a list of all the functions names [foo_1, foo_2, foo_3, foo_4]. Now, I am looking for a way to iterate through all the functions and grab the value of var in each one — something like this:
var_value_tuples = []
for function in function_list:
var_values.append((function, function.var))
Thank you in advance for any help.
A different way to achieve this, that is less of an anti-pattern and does not require introspection (also does not impose any specific function signature):
vars = { "foo_1": 1, "foo_2": 2, "foo_3": 3, "foo_4": 4 }
def foo_1():
var = vars["foo_1"]
return
def foo_2():
var = vars["foo_2"]
return
def foo_3():
var = vars["foo_3"]
return
def foo_4():
var = vars["foo_4"]
return
Some things to consider:
do these all need to be distinct functions if they have such similar implementations
since the functions seem to have a one to one mapping, can you use a list or some other data structure to map from vars to functions
Ultimately this is an XY Problem. You should add more context or outline the overall problem you are trying to solve.
you can try something like
foo_1.__code__.co_varnames gives list of all local variables in the function
foo_1.__code__.co_consts gives all the constant values,first one being the default return value i.e. None
def foo_1():
var = 1
var2 = 2
return
variables = dict(zip(foo_1.__code__.co_varnames, foo_1.__code__.co_consts[1:]))
print(variables)
{'var': 1, 'var2': 2}

Create a dictionary with all functions from a file

Say I have a file functions.py containing
def some_function():
return "something"
def some_other_function():
return "something else"
I'm trying to create a dictionary in another file, load_functions.py, with keys being the function-names from functions.py and the items being the actual function e.g:
import functions
useable_functions = [f for f in dir(functions) if "__" not in f] #Get all functions to be used
function_dict = TO_BE_IMPLEMENTED
print(function_dict)
#{"some_function":some_function,
#"some_other_function":some_other_function}
func = function_dict["some_function"]
func()
# "something"
Is it by all means doable?
Take a look at the inspect module.
Say you have your functions.py module.
To get all functions from it, use this:
import inspect
import functions # your module
elements = inspect.getmembers(functions, inspect.isfunction)
element_dict = dict(elements)
The call will return all the function members of the functions.py module as a list of (name, value) pairs sorted by name. Note it will also include lambdas.
Then, I use dict() to convert the (name, value) pairs to a dict.
https://docs.python.org/3/library/inspect.html

Get a list of the names of all functions in a Python module in the order they appear in the file?

The globals function returns a dictionary that contains the functions in a module, and the dir function returns a list that contains the names of the functions in a module, but they are in alphabetical order or are in a dictionary.
Is there a way to get the names of all the functions in a module in the order they appear in a file?
Here is my solution. Read in the source file as a string, filter out lines that begin with def, strip leading whitespace and extract the substring between the first space and first left paren.
def extract_fns(filename):
with open(filename) as f:
lines = f.readlines()
return [line.split(' ', 1)[1].split('(')[0] for line in lines
if line.strip().startswith('def')]
When I had a need like this, I used a decorator.
def remember_order(func, counter=[0]):
func._order = counter[0]
counter[0] += 1
return func
#remember_order
def foo():
print "foo"
#remember_order
def bar():
print "bar"
#remember_order
def baz():
print "baz"
Yes, you have to decorate each function individually. Explicit is better than implicit, as they say, and because you are doing something unnatural it's good to call it out as plainly as possible.
Now you want to get all the decorated functions in the order they were defined?
import sys
module = sys.modules[__name__] # get reference to current module
# you could also use a reference to some other module where the functions are
# get the functions in the order defined
funcs = sorted((func for func in
(getattr(module, name) for name in dir(module))
if callable(func) and hasattr(func, "_order")),
key = lambda func: func._order)
# call them in that order
for func in funcs:
func()
But it'd be easier to just give them names in alphabetical order...
My solution was to parse the ast. As it turns out elements in the ast tree are in the order they are parsed. Hence functions are in the order they appear in the file.
import inspect
import ast
def get_function_names_in_order_of_appearence(module_name):
this_source = inspect.getsource(module_name)
tree = ast.parse(this_source)
functions = []
for elem in tree.body:
if type(elem) is ast.FunctionDef:
this_func_name = elem.name
functions.append(this_func_name)
return functions
Assuming the module that is to be analyzed is:
# fancy_module
def z_func(x):
return x+1
def a_func(a, b):
pass
def b_func(mystr, **kw):
pass
Will give:
func_list = get_function_names_in_order_of_appearence(fancy_module)
for func in func_list:
print(func)
# -------
# output:
z_func
a_func
b_func
The advantage is you can add other stuff from the ast tree, like the signatures of the functions.

Go through a number of functions in Python

I have an unknown number of functions in my python script (well, it is known, but not constant) that start with site_...
I was wondering if there's a way to go through all of these functions in some main function that calls for them.
something like:
foreach function_that_has_site_ as coolfunc
if coolfunc(blabla,yada) == true:
return coolfunc(blabla,yada)
so it would go through them all until it gets something that's true.
thanks!
The inspect module, already mentioned in other answers, is especially handy because you get to easily filter the names and values of objects you care about. inspect.getmembers takes two arguments: the object whose members you're exploring, and a predicate (a function returning bool) which will accept (return True for) only the objects you care about.
To get "the object that is this module" you need the following well-known idiom:
import sys
this_module = sys.modules[__name__]
In your predicate, you want to select only objects which are functions and have names that start with site_:
import inspect
def function_that_has_site(f):
return inspect.isfunction(f) and f.__name__.startswith('site_')
With these two items in hand, your loop becomes:
for n, coolfunc in inspect.getmembers(this_module, function_that_has_site):
result = coolfunc(blabla, yada)
if result: return result
I have also split the loop body so that each function is called only once (which both saves time and is a safer approach, avoiding possible side effects)... as well as rewording it in Python;-)
Have you tried using the inspect module?
http://docs.python.org/library/inspect.html
The following will return the methods:
inspect.getmembers
Then you could invoke with:
methodobjToInvoke = getattr(classObj, methodName)
methodobj("arguments")
This method goes through all properties of the current module and executes all functions it finds with a name starting with site_:
import sys
import types
for elm in dir():
f = getattr(sys.modules[__name__], elm)
if isinstance(f, types.FunctionType) and f.__name__[:5] == "site_":
f()
The function-type check is unnecessary if only functions are have names starting with site_.
def run():
for f_name, f in globals().iteritems():
if not f_name.startswith('site_'):
continue
x = f()
if x:
return x
It's best to use a decorator to enumerate the functions you care about:
_funcs = []
def enumfunc(func):
_funcs.append(func)
return func
#enumfunc
def a():
print 'foo'
#enumfunc
def b():
print 'bar'
#enumfunc
def c():
print 'baz'
if __name__ == '__main__':
for f in _funcs:
f()
Try dir(), globals() or locals(). Or inspect module (as mentioned above).
def site_foo():
pass
def site_bar():
pass
for name, f in globals().items():
if name.startswith("site_"):
print name, f()

Categories

Resources