Skip to content

cannot pass class-bound method to context call #313

Description

@jesteria

As described by the documentation, I expect to be able to specify as a call target of Context.call_asyncfn – either a:

  • module-level function
  • static method
  • class method

However, at least in naively passing the Python object reference of either version of a method, mitogen is either unable to find or unable to process the call target.

For example, using staticmethod

#!/usr/bin/env python
import os
import pathlib
import socket

import mitogen.utils

class MyClass:

    @staticmethod
    def my_first_function():
        print('hostname:', socket.gethostname())
        print('remote PID:', os.getpid())
        print('file:', pathlib.Path(__file__))
        return 123

    @classmethod
    def main(cls, router):
        local = router.local()
        result = local.call(cls.my_first_function)
        print(result, socket.gethostname(), os.getpid())

if __name__ == '__main__':
    mitogen.utils.log_to_file()
    mitogen.utils.run_with_router(MyClass.main)

– results in the following error –

Traceback (most recent call last):
  File "./fake_manage_method.py", line 27, in <module>
    mitogen.utils.run_with_router(MyClass.main)
  File "/home/USER/dev/repo/opt/mitogen/mitogen/utils.py", line 100, in run_with_router
    return func(router, *args, **kwargs)
  File "./fake_manage_method.py", line 21, in main
    result = local.call(cls.my_first_function)
  File "/home/USER/dev/repo/opt/mitogen/mitogen/parent.py", line 1016, in call
    return receiver.get().unpickle(throw_dead=False)
  File "/home/USER/dev/repo/opt/mitogen/mitogen/core.py", line 487, in unpickle
    raise obj
mitogen.core.CallError: builtins.AttributeError: module '__main__' has no attribute 'my_first_function'
  File "<stdin>", line 2049, in _dispatch_calls
  File "<stdin>", line 2036, in _dispatch_one

Or, replacing the staticmethod with classmethod:

Traceback (most recent call last):
  File "./fake_manage_method.py", line 27, in <module>
    mitogen.utils.run_with_router(MyClass.main)
  File "/home/USER/dev/repo/opt/mitogen/mitogen/utils.py", line 100, in run_with_router
    return func(router, *args, **kwargs)
  File "./fake_manage_method.py", line 21, in main
    result = local.call(cls.my_first_function)
  File "/home/USER/dev/repo/opt/mitogen/mitogen/parent.py", line 1015, in call
    receiver = self.call_async(fn, *args, **kwargs)
  File "/home/USER/dev/repo/opt/mitogen/mitogen/parent.py", line 1012, in call_async
    return self.send_async(make_call_msg(fn, *args, **kwargs))
  File "/home/USER/dev/repo/opt/mitogen/mitogen/parent.py", line 401, in make_call_msg
    isinstance(fn.im_self, (type, types.ClassType)):
AttributeError: 'function' object has no attribute 'im_self'

In the case of the staticmethod, (unpickling), this may be worked around, for example –

def my_first_function():
    print('hostname:', socket.gethostname())
    print('remote PID:', os.getpid())
    print('file:', pathlib.Path(__file__))
    return 123

class MyClass:

    my_first_function = staticmethod(my_first_function)

    @classmethod
    def main(cls, router):
        local = router.local()
        result = local.call(cls.my_first_function)
        print(result, socket.gethostname(), os.getpid())    

– such that mitogen can now find the call target at the module level; (however, if mitogen intends to support static methods, this is less than ideal).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions