Search code examples
pythonoopfunctional-programming

How does one export classmethods to a module as module methods in a dynamic fashion in python?


Assuming python3.10, suppose I have a class in some file:

class Foo(metaclass=ABCMeta):
  @classmethod
  @abstractmethod
  def do_fn(cls, yada, yada_):
    pass

  @classmethod
  @abstractmethod
  def that_fn(cls, this, that):
    pass


SpecializedFoo(Foo):

  @classmethod
  def do_fn(cls, yada, yada_):
    logger.info("doing it")

  @classmethod
  def that_fn(cls, this, that):
    logger.info("this or that")

And a system interface module that expects do_fn and that_fn:

# interface.py
from functools import partial

from specialized_foo import SpecializedFoo

##### A Foo Interface #####
# Optionally leverage the system integration
# Read the docs: https://yankee.doodle.etc/callbacks

do_fn = partial(SpecializedFoo.do_fn)
that_fn = partial(SpecializedFoo.that_fn)

### Done: Foo Interface

Is there a clean or at least automatic and stable way I can define a method in SpecializedFoo or abstract parent class Foo such that the system interface interface.py can simply call:

SpecializedFoo.export_interface()

And the method functions will be exported to the module's list of available module methods as if they were defined in the exact same fashion, e.g.:

do_fn = partial(SpecializedFoo.do_fn)
that_fn = partial(SpecializedFoo.that_fn)

Solution

  • The answer involves a small function added to the parent class:

    class Foo(metaclass=ABCMeta):
        @classmethod
        def export_interface(cls, module_name: str):
            module = sys.modules[module_name]
    
            # this can be handled dynamically by matching the `_fn` w/ re
            module.do_fn = cls.do_fn
            module.that_fn = cls.that_fn
    

    And a single call in the interface.py:

    SpecializedFoo.export_interface(__name__)
    

    The user is responsible for avoiding collisions in the interface.py, but a system interface constructed this way (using modules as if they are classes) that imposes those kind of duties on the developer anyhow.