Skip to main content

Type-safe command pattern implementation in pure Python

Project description

Parts of this readme do not render correctly on pypi, see the repository for a better experience

Command System

A type-safe implementation of the command pattern in Python, designed to handle command execution with a lifecycle that includes deferral and cancellation.

With this package, you can create commands that can be queued and manage their own lifecycle, allowing for interacting with external systems, or building your own systems with complex interactions in a safe, clean, and maintainable way.

Example Usage

Simply subclass Command, CommandArgs, and optionally CommandResponse to create a custom command for your use case.

# SayHelloCommand.py
from command_system import (
    Command,
    CommandArgs,
    CommandResponse,
    ExecutionResponse,
)
from dataclasses import dataclass

@dataclass
class SayHelloArgs(CommandArgs):
    name: str

@dataclass
class SayHelloResponse(CommandResponse):
    message: str = ""

class SayHelloCommand(Command[SayHelloArgs, SayHelloResponse]):
    ARGS = SayHelloArgs
    _response_type = SayHelloResponse

    def execute(self) -> ExecutionResponse:
        if not self.args.name:
            return ExecutionResponse.failure("Name cannot be empty.")
        self.response.message = f"Hello, {self.args.name}!"
        return ExecutionResponse.success()

You can then use the CommandQueue to submit commands, and then queue.process_once() or queue.process_all() to execute them.

# main.py
from command_system import CommandQueue
from SayHelloCommand import SayHelloCommand, SayHelloArgs

queue = CommandQueue()
response = queue.submit(SayHelloCommand(SayHelloArgs(name="Alice")))
print(response.status) # Pending
queue.process_once()
print(response.status) # Completed
print(response.message) # Hello, Alice!

Command Lifecycle

flowchart TD
    A[create command] -->|"queue.submit(command)"| B[responseStatus.CREATED]
    B --> C{"command.should_defer()"}
    C -->|"DeferResponse.defer()"| W["responseStatus.PENDING"]
    W --> C
    C -->|"DeferResponse.proceed()"| D{"command.should_cancel()"}
    D -->|"CancelResponse.cancel()"| E[ResponseStatus.CANCELED]
    D -->|"CancelResponse.proceed()"| F["command.execute()"]
    F -->|"ExecutionResponse.success()"| G["ResponseStatus.COMPLETED"]
    F -->|"ExecutionResponse.failure()"| H["ResponseStatus.FAILED"]

Creating a command

Subclass CommandArgs and add any arguments your command needs. This class will be used to pass parameters to your command.

[!CAUTION] Don't add your arguments directly to the Command class. The args class is required for command chaining to work in a type-safe manner.

Subclass Command and set the ARGS class attribute to the subclass of CommandArgs you created. Implement the execute method to define the command's behavior.

  • You can also override should_defer and should_cancel methods to control the command's lifecycle.

Optionally, you can create a custom response class by subclassing CommandResponse so that your command can return specific type-safe data. If you do this, set the _response_type class attribute of your Command subclass to your custom response class.

Writing your execute method

[!WARNING]
Your execute method should not return your custom response class directly. The self.response attribute is automatically set to an instance of your custom response class, which you should modify instead. Then, return an ExecutionResponse instance to indicate the command's success or failure.

Writing should_defer and should_cancel methods

These methods can be overridden to control the command's lifecycle. They must return a DeferResponse or CancelResponse instance, respectively. You can use them to set conditions for deferring or canceling the command.

Complex Command example

for an example of deferring and canceling commands, see the tests/test_defer_cancel.py file.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

command_system-0.1.1.tar.gz (6.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

command_system-0.1.1-py3-none-any.whl (8.4 kB view details)

Uploaded Python 3

File details

Details for the file command_system-0.1.1.tar.gz.

File metadata

  • Download URL: command_system-0.1.1.tar.gz
  • Upload date:
  • Size: 6.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.7.20

File hashes

Hashes for command_system-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4b69c30ba119fd50cf6406d5dec928a203121ec98151e65c2d9c3e0f7f3f6815
MD5 bd6ae2b0c021d9fd4f9f7e9ea7d2bb7b
BLAKE2b-256 e9d325bfb171708414bad1bc604bb5f25606236618f7fba24b51d4538aaeadcb

See more details on using hashes here.

File details

Details for the file command_system-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for command_system-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0444859fdc748830bb0ab1db2a85f5ba7ca9f582ce6fa5c1744248e8fa3d5bf0
MD5 823727b6f8b142aebb3202eebb426f89
BLAKE2b-256 81dfa300ff418877d73fa8ae284c6d9db44262f917a9355017247de3c642c659

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page