Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to keep a context between multiple hooks? #13

Open
Sofahamster opened this issue Oct 19, 2023 · 3 comments
Open

How to keep a context between multiple hooks? #13

Sofahamster opened this issue Oct 19, 2023 · 3 comments

Comments

@Sofahamster
Copy link

Sorry if this is obvious, but async python is new to me. How do I keep a context for a single email when I have multiple hooks? i.e. I want to check the daemon_name macro in the on_connect hook, and then check if one or more header values are present in the hook_on_header callback and only then decide to do something (i.e. add a header or reject the message).

Is there some kind of state variable or context where I can keep information / state about an email between hooks?

@gertvdijk
Copy link
Owner

It's not possible currently, and it should be a new API I think. See #7 (comment)

Another API design that I like is using a context manager, as used in Kilter (https://code.kodo.org.uk/kilter/kilter.service).

But anyway, I think it just needs a whole refactor, I'm afraid. Help appreciated, as I probably am lacking time the coming months to pick that up.

@wbolster
Copy link
Collaborator

the contextvars module has asyncio support and i think it could help here: https://docs.python.org/3/library/contextvars.html#asyncio-support

@jschwindt
Copy link

As @wbolster said, using contextvars seems to work well. For example:

from contextvars import ContextVar

# at the global scope:
message_store: ContextVar[str] = ContextVar("message_store", default="")
premium_domain: ContextVar[bool] = ContextVar("premium_domain", default=False)

# Then in the "on_" handlers, for example:

async def on_mail_from(cmd: MailFrom) -> None:
    _, domain = cmd.address.split("@")
    if await redis.sismember("premium_domains", domain):
        premium_domain.set(True)

async def on_body_chunk(cmd: BodyChunk) -> None:
    message_store.set(message_store.get() + cmd.data_raw.decode())

async def on_end_of_message(cmd: EndOfMessage) -> Continue:
    if premium_domain.get():
        logger.info(f"Premium user, skipping. ID={cmd.macros['i']}")
        return Continue()
    message = add_powered_by(message_store.get())
    logger.info(f"On end of message with Powered by. ID={cmd.macros['i']}")
    return Continue(manipulations=[ReplaceBodyChunk(chunk=message.encode())])

@gertvdijk am I missing anything?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants