Get function object from stack (Frame) object - python-3.x

I have written a custom logging class for module logging that I called call. With this class I hope to place it in any function/method and it logs the function name with its arguments and all values the function was called with.
This works fine for class methods
Foo.bar(self, a=1, b=2, c=3, *args=(), **kwargs={'something': 4})
using this minimal example
import logging
import inspect
def call(logger):
fname = [] # Function name including module and class
fargs = [] # Arguments of function including positional and named arguments
parentframe = inspect.stack()[1][0]
module = inspect.getmodule(parentframe)
if module and module.__name__ != "__main__":
fname.append(module.__name__)
codename = parentframe.f_code.co_name
if "self" in parentframe.f_locals:
fname.append(parentframe.f_locals["self"].__class__.__name__)
fobj = getattr(parentframe.f_locals["self"].__class__, codename)
if codename != "<module>":
fname.append(codename)
argspec = inspect.formatargspec(*inspect.getfullargspec(fobj))
args = argspec[1:-1].split(",")
for arg in args:
argkey = arg.strip().replace("*", "").split("=")[0]
if arg == "self":
fargs.append("self")
else:
fargs.append(arg.split("=")[0] + "=" + str(parentframe.f_locals[argkey]))
del parentframe
msg = ".".join(fname) + "(" + ",".join(fargs) + ")"
if logger.isEnabledFor(30):
logger.log(30, msg)
class Foo:
def __init__(self, l):
self.logger = l
def bar(self, a, b, c=3, *args, **kwargs):
call(self.logger)
if __name__ == "__main__":
logging.addLevelName(30, "CALL")
logger = logging.getLogger('blub')
logger.level = 20
f = Foo(logger)
f.bar(1, 2, something=4)
print("done...")
My problem is when I use the same functionality on static methods or simple functions. It fails at the line where I get the function object (fobj = getattr(parentframe.f_locals["self"].__class__, codename)
) using self.
parentframe is the Frame object of the function in questions I presume. I have not yet found a way to get the function object from that object. Is there a way?

Use getattr(module, codename) to get the function-object of functions that are not contained in classes.
Here the full code:
import logging
import inspect
def call(logger):
fname = [] # Function name including module and class
fargs = [] # Arguments of function including positional and named arguments
parentframe = inspect.stack()[1][0]
module = inspect.getmodule(parentframe)
if module and module.__name__ != "__main__":
fname.append(module.__name__)
codename = parentframe.f_code.co_name
if "self" in parentframe.f_locals:
fname.append(parentframe.f_locals["self"].__class__.__name__)
fobj = getattr(parentframe.f_locals["self"].__class__, codename)
else:
fobj = getattr(module, codename)
if codename != "<module>":
fname.append(codename)
argspec = inspect.formatargspec(*inspect.getfullargspec(fobj))
args = argspec[1:-1].split(",")
for arg in args:
argkey = arg.strip().replace("*", "").split("=")[0]
if arg == "self":
fargs.append("self")
else:
fargs.append(arg.split("=")[0] + "=" + str(parentframe.f_locals[argkey]))
del parentframe
msg = ".".join(fname) + "(" + ",".join(fargs) + ")"
if logger.isEnabledFor(30):
logger.log(30, msg)
class Foo:
def __init__(self, l):
self.logger = l
def bar(self, a, b, c=3, *args, **kwargs):
call(self.logger)
def boo(a, b, c=3, *args, **kwargs):
call(logger)
if __name__ == "__main__":
logging.addLevelName(30, "CALL")
logger = logging.getLogger('blub')
logger.level = 20
f = Foo(logger)
f.bar(1, 2, something=4)
boo(1, 2, something=4)
print("done...")

Taking ideas from both, I wrote this function to find the function object from a frame. I'm sure there's some edge cases around inherited staticmethods, and obviously any code not using the cls and self conventions for param names. This also doesn't work for lambdas... but you shouldn't be logging anything out in a lamba anyway :-P
def _get_func_obj(f):
"""
Get function object from a frame. If it can't find it, return None
"""
codename = f.f_code.co_name
fobj = None
try:
if "self" in f.f_locals: # regular method
fobj = getattr(f.f_locals["self"].__class__, codename)
elif "cls" in f.f_locals: # class method
fobj = getattr(f.f_locals["cls"], codename)
else:
module = inspect.getmodule(f) # only fetch module if we need it
if hasattr(module, codename): # regular module level function
fobj = getattr(module, codename)
else: # static method
classes = [
getattr(module, name)
for name in dir(module)
if inspect.isclass(getattr(module, name))
]
for cls in classes:
if (
hasattr(cls, codename)
and getattr(cls, codename).__code__ == f.f_code
):
fobj = getattr(cls, codename)
break
if fobj is None:
"""it's likely some nested function/method or a lambda, who logs in a lambda?"""
return fobj
except Exception:
"""never break logging"""

Related

AttributeError: 'obj_2' object has no attribute 'var_1' - getattr -

I need to get data to second class from first class.
I Used - getattr - function to do it.
class obj_1:
def __init__(self):
self.var_1 = 'Hello'
self.var_2 = 'World'
def get_id(self):
i = self.var_1
j = self.var_2
return i, j
def vw(self):
print('Hello..')
class obj_2:
def __init__(self):
pass
def r_data(self):
print('called r_data')
x, y = getattr(obj_1, "get_id")(self)
print('x > ', x)
print('y > ', y)
def rd(self):
getattr(obj_1, 'vw')(self)
if __name__ == '__main__':
ob = obj_2()
ob.r_data()
It given error as - AttributeError: 'obj_2' object has no attribute 'var_1'
I think you are getting this error since the function get_id uses attributes of the class, i.e self.var_1 self.var_2
and these attributes are never initialized since the __init__ function was never called (and since you cant have attributes without an actual object )
so to fix your code I would create an object of "obj_1" and call the function
"get_id" with that object
class obj_1:
def __init__(self):
self.var_1 = 'Hello'
self.var_2 = 'World'
def get_id(self):
i = self.var_1
j = self.var_2
return i, j
def vw(self):
print('Hello..')
class obj_2:
def __init__(self):
self.o1 = obj_1()
def r_data(self):
print('called r_data')
x, y = self.o1.get_id()
print('x > ', x)
print('y > ', y)
def rd(self):
getattr(obj_1, 'vw')(self)
if __name__ == '__main__':
ob = obj_2()
ob.r_data()
hope i could help, please let me know in the comments if you didn't understand anything.
and if my comment helped you i would relly appreciate marking this comment as the answer :)

Wrapper Class in Python for Linked List

I am absolutely new to Wrapper Classes in Python. I was trying to implement it in a program of linked list where multiple linked list are in use. My code is:
def nodewrap(cls):
class Nodewrap:
def __init__(self):
self.head = None
self.tail = None
return Nodewrap
#nodewrap
class Node:
def __init__(self,data):
self.data = data
self.next = None
class Intersection:
def addnode(self,d):
newnode = Node(d)
if head == None:
head = tail = newnode
else:
tail.next = newnode
tail = newnode
obj1 = Intersection()
obj2 = Intersection()
obj3 = Intersection()
s1 = int(input("Enter size of 1st list : \n"))
for i in range(s1):
obj1.addnode(int(input("Enter the data : \n")))
s2 = int(input("Enter size of 1st list : \n"))
for i in range(s2):
obj2.addnode(int(input("Enter the data : \n")))
temp1 = obj1.head
for i in range(s1):
temp2 = obj2.head
for j in range(s2):
if temp1.data == temp2.data:
obj3.addnode(temp1.data)
break
temp2 = temp2.next
temp1 = temp1.next
print("Intersection is :")
temp = obj3.head
while temp!=None:
print(temp.data,end=" ")
temp = temp.next
I thought of using a wrapper class to wrap the class Node instead of using objects of the class Intersection only with data fields as head, tail. But it is giving me some sort of error with regards to init().
Please help.
I was trying to learn it from here:
https://www.geeksforgeeks.org/wrapper-class-in-python/
I think I understand what you want to do, but I think that you don't want to use a decorator, but you want to inherit from NodeWrap class
class Nodewrap:
head = None
tail = None
class Node(NodeWrap):
def __init__(self,data):
self.data = data
self.next = None
But I don't see any reason why to inherit this way. This should be enough, for a linked list. I have added is_first and is_last property
from __future__ import annotations
class Node:
prev_node = None
next_node = None
def __init__(self, data):
self.data = data
def add_node(self, node: Node) -> None:
if self.prev_node is not None:
raise ValueError('Previous node already defined.')
self.next_node = node
node.prev_node = self
#property
def is_first(self) -> bool:
return self.prev_node is None
#property
def is_last(self) -> bool:
return self.next_node is None
You can implement next, iter and create an Iterator class.
I don't recommend using next as the variable name.
from __future__ import annotations reference here. It's just for self reference annotation.

Class Inheritance for the parent __int__ in Python

My code is provided in the end. I want to let the class general inherit all the variables of the constructor (_ init _) in the class LDA. It seems that when the method X_F in the class general calls the X_c, it just returns the objective rather than the array of np.random.rand(3,2) generated in the main. I tried to let the method X_F print the value of the variable m, but the printed result is a default value of 3, rather than 10.
class LDA:
def __init__(self, X_c, m = 3, K=1):
self.X_c =X_c
self.m =m
self.K = K
def Squared_L2_loss(self):
X_result = general(self).X_F()
return X_result
class general(LDA):
def X_F(self):
X = self.X_c[0]
print(self.m)
return X.T
if __name__ == '__main__':
X_c=np.random.rand(3,2)
X = LDA(X_c, m=10, K=30)

Get module of a function (defined stand-alone)

I have an object with a dict of functions that I need to store (for example, in JSON). Such functions are not bound to an object nor decorated and are defined stand-alone in different modules. Is there a way to programatically get the module where the function is defined?
What I expect:
import importlib
class FunctionBag(object):
#property
def functions(self)
return self._functions
def __init__(self, functions):
self._functions = functions
def to_dict_for_json(self):
result = {}
for key, fn, in self._functions.items():
name = fn.__name__
module = MAGIC(fn)
result[key] = (module, name,)
#classmethod
def FromDictOfJson(cls, dct):
functions = {}
for key, value, in dct.items():
module,name, = value
module = importlib.import_module(module)
functions[key] = getattr(module, name)
return cls(functions)
Is there a way to do what MAGIC() does?
>>> def foo():
... pass
...
>>> foo.__module__
'__main__'
>>> sys.modules[foo.__module__]
<module '__main__' (built-in)>

Call threads with a class

I wan't to do parallel with the files that is in the class DictionaryTagger.
I pass 5 files in the class,
class DictionaryTagger(object):
print_look = threading.Lock()
def __init__(self, dictionary_paths):
print('Hilo:', threading.current_thread().getName())
files = [open(path, 'r') for path in dictionary_paths]
dictionaries = [yaml.load(dict_file) for dict_file in files]
map(lambda x: x.close(), files)
self.dictionary = {}
self.max_key_size = 0
for curr_dict in dictionaries:
#print(curr_dict)
for key in curr_dict:
#print(type(curr_dict))
#self.dictionary =
if key in self.dictionary:
self.dictionary[key].extend(curr_dict[key])
else:
self.dictionary[key] = curr_dict[key]
self.max_key_size = max(self.max_key_size, len(key))
dictt = DictionaryTagger(['dicts/positive22.yml', 'dicts/negative22.yml', 'dicts/increasers.yml', 'dicts/decreasers.yml', 'dicts/inverter.yml'])
When I pass this, I have an error because ´dictionary_paths´ is not defined.
for i in range(3):
t = threading.Thread(target=DictionaryTagger)
t.demond = True
t.start()
init() missing 1 required positional argument: ´'dictionary_paths'´
You're receiving this error because the init function you defined as an argument ('dictionary_paths') without a default value. You can solve this in two different ways:
Add a default value to the init function
class DictionaryTagger(object):
...
def __init__(self, dictionary_paths=[]):
...
Provide an argument when starting your threads:
default_dictionary_paths = []
for i in range(3):
t = threading.Thread(target=DictionaryTagger, args=(default_dictionary_paths))
t.demond = True
t.start()

Resources