Modes

Modes are global state that can be used to allow esc operations to work in different ways. For instance, the trig operations in the trig plugin distributed with esc use a degrees/radians mode. Modes tend to be confusing to both users and programmers, so it’s best to avoid them when possible, but sometimes it’s difficult to do without them (do you really want to create and select a whole separate set of trig operations depending on whether you’re calculating in degrees or radians?).

Working with modes

Modes are created using the Mode and ModeChange constructors:

esc.commands.Mode(name, default_value, allowable_values=None)[source]

Register a new mode.

Parameters:
  • name – The name of the mode. This is used to refer to it in code. If a mode with this name already exists, a ProgrammingError will be raised.
  • default_value – The value the mode starts at.
  • allowable_values – An optional sequence of possible values for the mode. If defined, if code ever tries to set a different value, a ProgrammingError will be raised.
esc.commands.ModeChange(key, description, menu, mode_name, to_value)[source]

Create a new mode change operation the user can select from a menu. Syntactic sugar for registering an operation.

Parameters:
  • key – The key to press to select the constant from the menu.
  • description – A brief description to show next to the key.
  • menu – A Menu to place this operation on.
  • mode_name – The name of the mode, registered with Mode(), to set.
  • to_value – The value the mode will be set to when this operation is selected.

Aside from defining ModeChange operations, you can view and edit the values of modes using the modes module:

modes.py - Manage calculator state/modes

class esc.modes.Mode(name, value, allowable_values)[source]

esc modes implement basic calculator state like a degrees/radians switch. In esc, they are created and used by menus with families of related operations, where they can also be displayed. They have a name, a current value, and optionally a set of allowable values; if something ever causes the value to be set to a non-allowable value, a ProgrammingError will be raised, hopefully identifying the issue before it leads to wrong results.

Modes are usually created by the esc.commands.Mode() factory function, not by calling this constructor directly.

esc.modes.get(name)[source]

Retrieve the value of a mode with a given name. Return None if no mode by that name has been registered.

esc.modes.register(name, default_value, allowable_values=None)[source]

Create a new mode. If the mode already exists, a ProgrammingError is raised.

Modes should be registered by the esc.commands.Mode() factory function, not by calling this function directly.

esc.modes.set(name, val)[source]

Set a mode to a new value. If the mode doesn’t exist, a KeyError will be raised. If the value is invalid for the mode, a ProgrammingError will be raised.

You’ll probably want to display the value of your mode somewhere – as confusing as modes can be already, they’re even worse when they’re invisible! Typically, this is done by supplying a mode_display callable to the Menu that contains the associated operations. This callable takes no arguments and calls modes.get to determine the display value:

def my_mode_display_1():
    # Assumes the values of the mode are strings.
    # If they're something else, you need to map them to strings here.
    return modes.get('my_mode')

Example

Here’s a complete silly example:

from esc.commands import Operation, Mode, ModeChange, Menu, main_menu, BINOP
from esc import modes

def opposite_mode():
    return "[opposite day]" if modes.get('opposite_day') else ""
bool_menu = Menu('b', 'boolean functions', parent=main_menu, doc="blah",
                 mode_display=opposite_mode)

Mode('opposite_day', False, (True, False))
ModeChange("o", "enable opposite day", menu=bool_menu,
           mode_name='opposite_day', to_value=True)
ModeChange("O", "disable opposite day", menu=bool_menu,
           mode_name='opposite_day', to_value=False)

@Operation('a', menu=bool_menu, push=1,
           description="AND sos and bos",
           log_as=BINOP)
def and_(x, y):
    result = x and y
    return int((not result) if modes.get('opposite_day') else result)

# ...insert other boolean functions here