Skip to content

remove current_mode() #598

Description

@fohrloop

While working on #589 I noticed that now there are three APIs for entering and exiting wakepy modes, but the current_mode() does not always make sense:

Context Manager syntax

  • current_mode() does not make any sense with the context manager syntax

Example: Plain context manager

from wakepy import keep

# Does not make sense
with keep.running():
    mode = current_mode()
    print(mode.active_method)

# Better: Use explicit "with X as Y" syntax instead
with keep.running() as mode:
    print(mode.active_method)

Example 2: Context manager + subfunction

from wakepy import keep

def do_something()
    with keep.running():
        long_task()

def long_task():
    mode = current_mode()
    print(mode.active_method)

this does not make sense since you could as well do this (Example 3)

from wakepy import keep

def do_something()
    with keep.running() as mode:
        long_task(mode)

def long_task(mode):
    print(mode.active_method)

This is much nicer as this is explicit (which is better than implicit). In addition, it's explicit that this function needs a mode as the input. Note that in Example 2

If there are no Modes active in the call stack, raises a NoCurrentModeError

and in reality, you would need to do something like:

def long_task():
    try:
        mode = current_mode()
        print(mode.active_method)
    except NoCurrentModeError:
        pass

so better would be:

def long_task(mode=None):
    if mode:
        print(mode.active_method)

In addition to guiding towards better code, dropping support for current_mode() for context manager syntax makes the wakepy source code much simpler, and less likely to have bugs in some corner cases (say, including async.Tasks/threads). Less to test and less to worry about.

Explicit Mode.activate() or Mode.enter()

It's also clear that with explicit Mode.activate() or Mode.enter() syntax, there is no need for current_mode().

from wakepy import keep

mode = keep.running()
mode.enter()
mode = current_mode() # Not possible (or technically could be, but not implemented) + does not make any sense.
print(mode.active_method)
mode.exit()

Decorator syntax

The decorator syntax is more interesting, and it is basically the reason why I added current_mode() to wakepy 1.0.0.

from wakepy import keep

# Only place where current_mode() could make sense
@keep.running
def somefunc()
    mode = current_mode()
    print(mode.active_method)

but also here guaranteeing that the correct mode in case if nested functions, async.Tasks and threads might not be so simple.

Alternative 1: pass mode as argument when asked. Similar to bind=True in Celery or pass_context in Click.

from wakepy import keep

@keep.running(pass_mode=True)
def somefunc(mode)
    print(mode.active_method)

I thought about the syntax in Alternative 1, but I'm not sure if this is the most intuitive way of handling the problem. The reader should understand that "pass_mode" means literally that the somefunc should be able to take an extra argument. It's not clear should the argument be the first or the last. When using the decorated function, one should not pass anything for "mode", but skip the parameter. Also the typing might become a challenge.

Alternative 2: Using lifecycle hooks.

  • First: What is the motivation of someone getting the Mode handle? It's most likely for logging / messaging purposes. This can be achieved also using lifecycle hooks.
  • Second: The code using the mode (for reacting to lifecycle events) can be taken away from somefunc, and somefunc can be 100% business logic.
  • Third: No need to have extra input parameters / change the function signature of the decorated function.
def on_enter(mode):
    print(mode.active_method)

@keep.running(on_enter=on_enter)
def somefunc():
    # business logic only

-> New ticket for lifecycle hooks: #599

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions