0

Calling a wrapped in a decorator method concurrently with tqdm.contrib.concurent.process_map inside a Class raises AttributeError: 'Class' object has no attribute 'wrapper' error.

I understand that I have a potential workaround of the task by means of multiprocessing.pool.map. However, since I found process_map really handy when handling progress bars with concurrency, I was hoping if anyone could help me avoiding this error while keeping process_map and the decorator.

Input

from tqdm.contrib.concurrent import process_map

def error_handle(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as exc:
            print(f"ERROR IN function {func.__name__} with exception {exc}")

    return wrapper

class Foo:
    def __init__(self) -> None:
         pass
    
    def bar_wrapper(self):
        process_map(self.bar, 100*[0], max_workers=10)

    @error_handle
    def bar(self, a):
        pass           

obj = Foo()
obj.bar_wrapper()

Output

[...]
AttributeError: 'Foo' object has no attribute 'wrapper'

I've tried defining the method inside the class itself as a non-static and static class and neither worked out.

  • I've tried replicating the error, and I can't. Can you provide the traceback? – Anony Mous Apr 14 '23 at 10:43
  • @AnonyMous I share the whole traceback [here](https://pastebin.com/yND4QfeP). Hope this helps. – Unai Carbajo Apr 14 '23 at 10:54
  • oooook. So since you're using multiprocessing (I assume), Python likes being annoying when it comes to scopes. Can you try something? Replace the function `error_handle` with something random (like `functionwedontcareabout`). But don't change `@error_handle`. Just an experiment. Get back to be on what it says. – Anony Mous Apr 14 '23 at 11:18
  • Throws: `NameError: name 'error_handle' is not defined` It seems like it actually looks for the decorator. – Unai Carbajo Apr 14 '23 at 12:05
  • not sure if my solution will work. But give it a shot and let me know about the results. – Anony Mous Apr 14 '23 at 13:29

1 Answers1

-1

I'm not very sure if this will work to be completely honest, but give it a shot.

from tqdm.contrib.concurrent import process_map

def error_handle(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as exc:
            print(f"ERROR IN function {func.__name__} with exception {exc}")

    return wrapper

class Foo:
    def __init__(self) -> None:
         pass
    
    def bar_wrapper(self):
        process_map(self.bar, 100*[0], max_workers=10)

    @error_handle
    def bar(self, a):
        pass           


def main() -> None:
    obj = Foo()
    obj.bar_wrapper()


if __name__ == '__main__':
    main()

See if anything different happens if you try this. It's a bit late in my timezone, so I need to rest. Have a read through the answer for Python concurrent.futures: ProcessPoolExecutor fail to work. Hopefully this works!

Anony Mous
  • 345
  • 3
  • 10