How can I get around files not being imported when using a listener pattern in python?

Question:

I want to create an event-based system where functions can be subscribed to handle certain events.

There are a few ways to do this, I’ve chosen decorators:

# decorators.py

EVENT_HANDLERS: dict[str, set[Callable]] = defaultdict(set)


def my_event_listener(event_type: str):
    """
    A decorator to subscribe to events with the given event_type.
    """

    def decorator(callback_fn):
        EVENT_HANDLERS[event_type].add(callback_fn)
        return callback_fn

    return decorator

# events.py

def create_event(event: dict):
    for handler_fn in EVENT_HANDLERS[event[event_type]]:
        handler_fn(event)

# handlers.py

@my_event_listener(event_type="test")
def handle_test_event(event):
    logger.info(f"Test event received with payload {event['payload']}")

This works great! However I run into problems when handlers.py is not imported elsewhere in the codebase. Python only loads files when they’re imported somewhere else, and since in this case handlers.py has no reason to be imported anywhere it is never loaded, so the decorator never runs, and the callback is never registered.

I don’t think this is an issue with the decorator-based approach, as if I were to use a class or whatever I’d have the same problem with imports.

Other than keeping some registry of handlers somewhere, is there a way around this?

Asked By: Tom

||

Answers:

is there a way around this?

No.

If you’re using code to register a thing, the code naturally needs to be run.

You could, for instance,

  • keep a list of modules to import, and then importlib.import_module() them all, or
  • walk the filesystem to find all modules with e.g. a certain name (all handlers.pys?) to import, or
  • walk the filesystem to find some sort of other metadata to figure out what to import, or
  • if you’re planning on making your program pluggable, use e.g. entrypoints mechanism plugins to register themselves with.
Answered By: AKX
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.