Problem 575082 · easy · Level 05 Advanced Algorithms & Graphs

Commands That Sign Themselves Up

py-decorators · py-classes · py-dicts · registration · dispatch

A small command-line tool keeps a big if chain that maps each command word to its function, and every new command means editing that chain. Instead, each function should sign itself up where it is defined:

cli = Registry()

@cli.command("add")
def add(a, b):
    return int(a) + int(b)

cli.run("add 2 3")      # 5

Write a class Registry with these methods:

  • command(name=None) returns a decorator. The decorator records the function under name, or under the function's own __name__ when no name is given (@cli.command()), and returns the same function object, unchanged, so it can still be called directly.
  • Registering a name that is already in use raises ValueError, and the first registration stays.
  • run(line) splits line on whitespace. The first word is the command name; the other words are passed to the function as string arguments, in order. run returns what the function returns. An unknown command raises KeyError.
  • names() returns the registered names as a sorted list.

Each Registry has its own commands. Setup helpers you can use with Run: calculator(lines), unchanged(), duplicate_name(), two_registries() and raises(fn, *args), which returns the name of the exception a call raises (or None).

Examples

Input:  calculator(["add 2 3", "neg 7", "shout hello there", "count", "mul 2 3"])
Output: (["add", "count", "neg", "shout"], [5, -7, "HELLO THERE!", 4, "KeyError"])

Input:  unchanged()
Output: (True, 1.5, 3.0)

Constraints

  • Command names contain no spaces; run receives at least one word.
  • Arguments that a function does not accept make it raise TypeError, which run lets through.

Goals

  • Write a decorator factory: a method that takes a setting and returns a decorator
  • Use a decorator to record a function somewhere instead of wrapping it
  • Dispatch a text command to the function registered under its first word
Starting Python…