If I have a reference to a function I can check it's code object f.__code__
, get a signature, then perform later checks against this signature to see if the code changed. This is good.
But what if one of the funciton's dependencies changed? E.g.
def foo(a, b):
return bar(a, b)
Let's say foo
remained the same, but bar
changed. Is there a way I can check foo
's dependencies 'live' via the foo.__code__
object (vs. parsing the text and using AST)?
import functools def calltracker(func): @functools. wraps(func) def wrapper(*args): wrapper. has_been_called = True return func(*args) wrapper. has_been_called = False return wrapper @calltracker def doubler(number): return number * 2 if __name__ == '__main__': if not doubler.
Python help() function is used to get the documentation of specified module, class, function, variables etc. This method is generally used with python interpreter console to get details about python objects.
currentframe() returns the frame at the top of the stack (for the current function). getargvalues() returns a tuple with argument names, the names of the variable arguments, and a dictionary with local values from the frame.
I'm just poking around at the __code__
object here, so I can't say that this would work for sure, but it looks to me like you could use the co_names
tuple to (recursively) traverse the call graph rooted at a particular function, in order to build up some sort of hash of the transitive closure of the functions that could possible be called. (I don't think it'd be possible to include only the functions that will be called for a particular input, but you could be conservative and include every possible call.)
To do this you'd need to maintain some sort of symbol table to be able to look up the names of the functions that get called. But once you start going down this path, it seems like you're basically going to build up your own equivalent of the AST. So, why not just use the AST to start with ?
You may to compare bytecode attributes on code object using method.__code__.co_code
. For example lets define two classes:
>>> class A:
... a = 1
... def b(self, b):
... print(self.a + b)
...
>>> class B:
... a = 1
... def b(self, b):
... print(self.a + b)
...
>>> A().b.__code__.co_code
'|\x00\x00j\x00\x00|\x01\x00\x17GHd\x00\x00S'
>>> B().b.__code__.co_code
'|\x00\x00j\x00\x00|\x01\x00\x17GHd\x00\x00S'
>>> A().b.__code__.co_code == B().b.__code__.co_code
True
and if method b
in class A
is changed:
>>> class A:
... a = 1
... def b(self, b):
... print(b + self.a)
...
>>> A().b.__code__.co_code
'|\x01\x00|\x00\x00j\x00\x00\x17GHd\x00\x00S'
>>> A().b.__code__.co_code == B().b.__code__.co_code
False
or use inspect method inspect.getsource(object)
that:
Return the text of the source code for an object. The argument may be a module, class, method, function, traceback, frame, or code object. The source code is returned as a single string.
And if you want to know whether the code has changed in dynamic you may need to reload your class with importlib
and compare bytecode.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With