2

I'm looking for a means to trace all calls on an instance of a class, including calls to dunder methods like __iter__, __getattribute__ etc. I've tried using sys.setprofile, the autologging package, but failed : it doesn't seem to be able to trace calls to dundermethods, or attributes etc.

My usecase is the following. Python's duck typing is awesome, and I would need to be able to "mock" objects, implementing a class only with the necessary methods (including magic or dunders). As I understand duck typing (tell me if i'm misleading), it's basically the fact that the following code will work for any object instance_of_class as long as it implements the methods necessary for iterables (__iter__ and __next__ for example) and that elements returned implements object.__getattribute__('name') which result has __repr__ method, for the printing

import ExternalClass

ExternalClass = my_awesome_decorator_to_trace_them_all(ExternalClass)
instance_of_class = ExternalClass(...)
for element in instance_of_class:
    print(element.name)

So, I would like to be able to trace variables (or the original class, and then every instances will get traced), using for example a decorator or anything, that would show the list of all methods called. In the previous example for ExternalClass, it would be:

method __init__ called with arguments ...
method __new__ called with arguments ...
method __iter__ called with arguments ...
method __next__ called with arguments ...

Thanks for the help

--- Update with @martineau efficient and elegant solution

From @martineau's working code on How to wrap every method of a class?

from functools import wraps
from types import FunctionType


def wrapper(method):
    @wraps(method)
    def wrapped(*args, **kwargs):
        class_name = args[0].__class__.__name__
        func_name = method.__name__
        print('calling {}.{}()... '.format(class_name, func_name))
        return method(*args, **kwargs)
    return wrapped


class MetaClass(type):
    def __new__(meta, classname, bases, classDict):
        newClassDict = {}
        for attributeName, attribute in classDict.items():
            if isinstance(attribute, FunctionType):
                # replace it with a wrapped version
                attribute = wrapper(attribute)
            newClassDict[attributeName] = attribute
        return type.__new__(meta, classname, bases, newClassDict)

class SimpleList(object, metaclass=MetaClass):
    def __init__(self):
        super().__init__()
        self.list_elem = list(range(1))

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        this_index = self.index
        if this_index >= len(self.list_elem):
            raise StopIteration()
        self.index += 1
        return self.list_elem[this_index]


simple_list = SimpleList()
print(simple_list)
for element in simple_list:
    pass

prints the following

calling SimpleList.__init__()...
<__main__.SimpleList object at 0x1054ce9a0>
calling SimpleList.__iter__()...
calling SimpleList.__next__()...
calling SimpleList.__next__()...
Paje
  • 31
  • 4
  • Hi Thomas, I mentioned I tried settrace, but after your answer, I doublechecked and it works but does not follow a particular object or class : it prints all calls. It definitely is a good one though, I'll try modifying it and edit my question with the results ! thanks – Paje Jan 15 '22 at 17:10
  • See [How to wrap every method of a class?](https://stackoverflow.com/questions/11349183/how-to-wrap-every-method-of-a-class) – martineau Jan 15 '22 at 18:11
  • so efficient and elegant solution, thanks! I didn't know about metaclasses. Editing the question right away – Paje Jan 17 '22 at 16:16
  • 1
    Nice to hear — and you're welcome — but Michael Foord should get most of the credit since my code was based on what's in the [his blog](https://web.archive.org/web/20200124090402id_/http://www.voidspace.org.uk/python/articles/metaclasses.shtml#a-method-decorating-metaclass). – martineau Jan 17 '22 at 18:06
  • Also note that you can up-vote my [answer](https://stackoverflow.com/a/11350487/355230) to the linked question if you found it helpful. – martineau Jan 18 '22 at 00:27
  • will do! thanks for Michael Foord's blog, I did look for it but couldn't find – Paje Jan 18 '22 at 10:31
  • Thanks. There are links to the relevant portions of a blog entry of his titled [Meta-classes Made Easy](https://web.archive.org/web/20200124090402id_/http://www.voidspace.org.uk/python/articles/metaclasses.shtml) in my previous comment and my linked answer, so you don't have to look for them. I think his blog is now discontinued. – martineau Jan 18 '22 at 14:50

0 Answers0