From 5bb1105f3859b2456e350e2b6d7d23ed1f4df00b Mon Sep 17 00:00:00 2001 From: Joscha Date: Tue, 4 Nov 2025 02:39:11 +0100 Subject: [PATCH] Add @module annotation --- pasch/__init__.py | 3 ++- pasch/orchestrator.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pasch/__init__.py b/pasch/__init__.py index 489cf58..d0f9577 100644 --- a/pasch/__init__.py +++ b/pasch/__init__.py @@ -1,10 +1,11 @@ from . import file, modules, util -from .orchestrator import Module, Orchestrator +from .orchestrator import Module, Orchestrator, module __all__: list[str] = [ "Module", "Orchestrator", "file", + "module", "modules", "util", ] diff --git a/pasch/orchestrator.py b/pasch/orchestrator.py index 098fb82..917ee87 100644 --- a/pasch/orchestrator.py +++ b/pasch/orchestrator.py @@ -3,6 +3,7 @@ from __future__ import annotations import getpass import socket from abc import ABC, abstractmethod +from typing import Callable, Concatenate from rich import print from rich.console import Console @@ -20,6 +21,36 @@ class Module(ABC): def realize(self) -> None: ... +def _snake_to_camel(s: str) -> str: + return "".join(s.capitalize() for s in s.split("_")) + + +# Annotate a function with @module to turn it into a class implementing Module. +def module[**P]( + func: Callable[Concatenate[Orchestrator, P], None], +) -> Callable[Concatenate[Orchestrator, P], None]: + def __init__( + self, + orchestrator: Orchestrator, + *args: P.args, + **kwargs: P.kwargs, + ) -> None: + super(self.__class__, self).__init__(orchestrator) + self.args = args + self.kwargs = kwargs + + def realize(self) -> None: + # pyrefly: ignore + return func(self.orchestrator, *self.args, **self.kwargs) + + # pyrefly: ignore + return type( + _snake_to_camel(func.__name__), + (Module,), + {"__init__": __init__, "realize": realize}, + ) + + class Orchestrator: def __init__(self, name: str = "pasch", dry_run: bool = False) -> None: self.name = name