From 8f9d5952aa02fcb8d958fdf3df558be29c2f78a6 Mon Sep 17 00:00:00 2001 From: Salmireles Date: Fri, 6 Jan 2023 09:21:09 -0800 Subject: [PATCH 1/2] Add registry pattern --- .../registry_pattern/before/main.py | 32 +++++++++ .../registry_pattern/before/tasks.json | 17 +++++ .../registry_pattern/before/tasks.py | 28 ++++++++ .../registry_pattern/class_based/main.py | 28 ++++++++ .../registry_pattern/class_based/registry.py | 31 ++++++++ .../registry_pattern/class_based/tasks.json | 17 +++++ .../registry_pattern/class_based/tasks.py | 46 ++++++++++++ .../registry_pattern/fn_based/inject.py | 8 +++ .../registry_pattern/fn_based/loader.py | 6 ++ .../registry_pattern/fn_based/main.py | 41 +++++++++++ .../registry_pattern/fn_based/registry.py | 17 +++++ .../registry_pattern/fn_based/tasks.json | 23 ++++++ .../registry_reference/registry_diagram.md | 71 +++++++++++++++++++ .../registry_reference/registry_reference.py | 60 ++++++++++++++++ 14 files changed, 425 insertions(+) create mode 100644 pythonic-patterns/registry_pattern/before/main.py create mode 100644 pythonic-patterns/registry_pattern/before/tasks.json create mode 100644 pythonic-patterns/registry_pattern/before/tasks.py create mode 100644 pythonic-patterns/registry_pattern/class_based/main.py create mode 100644 pythonic-patterns/registry_pattern/class_based/registry.py create mode 100644 pythonic-patterns/registry_pattern/class_based/tasks.json create mode 100644 pythonic-patterns/registry_pattern/class_based/tasks.py create mode 100644 pythonic-patterns/registry_pattern/fn_based/inject.py create mode 100644 pythonic-patterns/registry_pattern/fn_based/loader.py create mode 100644 pythonic-patterns/registry_pattern/fn_based/main.py create mode 100644 pythonic-patterns/registry_pattern/fn_based/registry.py create mode 100644 pythonic-patterns/registry_pattern/fn_based/tasks.json create mode 100644 pythonic-patterns/registry_pattern/registry_reference/registry_diagram.md create mode 100644 pythonic-patterns/registry_pattern/registry_reference/registry_reference.py diff --git a/pythonic-patterns/registry_pattern/before/main.py b/pythonic-patterns/registry_pattern/before/main.py new file mode 100644 index 0000000..f9da6b4 --- /dev/null +++ b/pythonic-patterns/registry_pattern/before/main.py @@ -0,0 +1,32 @@ +import json +from typing import Protocol + +from tasks import Pulse, Recalibrate, Reinforce + + +class Task(Protocol): + def run(self) -> None: + ... + + +def main() -> None: + with open("./tasks.json", encoding="utf-8") as file: + data = json.load(file) + + tasks: list[Task] = [] + + for item in data["tasks"]: + if item["type"] == "pulse": + tasks.append(Pulse(item["strength"])) + elif item["type"] == "recalibrate": + tasks.append(Recalibrate(item["target"])) + elif item["type"] == "reinforce": + tasks.append(Reinforce(item["plating_type"], item["target"])) + + # run the tasks + for task in tasks: + task.run() + + +if __name__ == "__main__": + main() diff --git a/pythonic-patterns/registry_pattern/before/tasks.json b/pythonic-patterns/registry_pattern/before/tasks.json new file mode 100644 index 0000000..99ae68b --- /dev/null +++ b/pythonic-patterns/registry_pattern/before/tasks.json @@ -0,0 +1,17 @@ +{ + "tasks": [ + { + "type": "pulse", + "strength": 190 + }, + { + "type": "recalibrate", + "target": "Thoron subspace transponder" + }, + { + "type": "reinforce", + "plating_type": "biogenic", + "target": "the deflector array" + } + ] +} diff --git a/pythonic-patterns/registry_pattern/before/tasks.py b/pythonic-patterns/registry_pattern/before/tasks.py new file mode 100644 index 0000000..807d226 --- /dev/null +++ b/pythonic-patterns/registry_pattern/before/tasks.py @@ -0,0 +1,28 @@ +from dataclasses import dataclass + + +@dataclass +class Pulse: + strength: int + + def run(self) -> None: + print( + f"Sending a subspace pulse of {self.strength} microPicards to the converter assembly." + ) + + +@dataclass +class Recalibrate: + target: str + + def run(self) -> None: + print(f"Recalibrating the {self.target}.") + + +@dataclass +class Reinforce: + plating_type: str + target: str + + def run(self) -> None: + print(f"Reinforcing {self.plating_type} plating of {self.target}.") diff --git a/pythonic-patterns/registry_pattern/class_based/main.py b/pythonic-patterns/registry_pattern/class_based/main.py new file mode 100644 index 0000000..f86b532 --- /dev/null +++ b/pythonic-patterns/registry_pattern/class_based/main.py @@ -0,0 +1,28 @@ +import json + +from registry import TaskRegistry +from tasks import PulseFactory, RecalibrateFactory, ReinforceFactory + + +def main() -> None: + + # register a couple of tasks + task_registry = TaskRegistry() + task_registry.register("pulse", PulseFactory()) + task_registry.register("recalibrate", RecalibrateFactory()) + task_registry.register("reinforce", ReinforceFactory()) + + # read data from a JSON file + with open("./tasks.json", encoding="utf-8") as file: + data = json.load(file) + + # create the tasks + tasks = [task_registry.create(item) for item in data["tasks"]] + + # run the tasks + for task in tasks: + task.run() + + +if __name__ == "__main__": + main() diff --git a/pythonic-patterns/registry_pattern/class_based/registry.py b/pythonic-patterns/registry_pattern/class_based/registry.py new file mode 100644 index 0000000..a8bfc88 --- /dev/null +++ b/pythonic-patterns/registry_pattern/class_based/registry.py @@ -0,0 +1,31 @@ +from typing import Any, Protocol + + +class Task(Protocol): + def run(self) -> None: + """Run the task.""" + + +class TaskFactory(Protocol): + def create(self, args: dict[str, Any]) -> Task: + """Creates a new task.""" + + +class TaskRegistry: + def __init__(self): + self.registry: dict[str, TaskFactory] = {} + + def register(self, task_type: str, factory: TaskFactory) -> None: + self.registry[task_type] = factory + + def unregister(self, task_type: str) -> None: + self.registry.pop(task_type, None) + + def create(self, args: dict[str, Any]) -> Task: + args_copy = args.copy() + task_type = args_copy.pop("type") + try: + factory = self.registry[task_type] + except KeyError: + raise ValueError(f"Unknown task type: {task_type!r}") from None + return factory.create(args_copy) diff --git a/pythonic-patterns/registry_pattern/class_based/tasks.json b/pythonic-patterns/registry_pattern/class_based/tasks.json new file mode 100644 index 0000000..99ae68b --- /dev/null +++ b/pythonic-patterns/registry_pattern/class_based/tasks.json @@ -0,0 +1,17 @@ +{ + "tasks": [ + { + "type": "pulse", + "strength": 190 + }, + { + "type": "recalibrate", + "target": "Thoron subspace transponder" + }, + { + "type": "reinforce", + "plating_type": "biogenic", + "target": "the deflector array" + } + ] +} diff --git a/pythonic-patterns/registry_pattern/class_based/tasks.py b/pythonic-patterns/registry_pattern/class_based/tasks.py new file mode 100644 index 0000000..f8b0fe9 --- /dev/null +++ b/pythonic-patterns/registry_pattern/class_based/tasks.py @@ -0,0 +1,46 @@ +from dataclasses import dataclass +from typing import Any + +from registry import Task + + +@dataclass +class Pulse: + strength: int + + def run(self) -> None: + print( + f"Sending a subspace pulse of {self.strength} microPicards to the converter assembly." + ) + + +@dataclass +class Recalibrate: + target: str + + def run(self) -> None: + print(f"Recalibrating the {self.target}.") + + +@dataclass +class Reinforce: + plating_type: str + target: str + + def run(self) -> None: + print(f"Reinforcing {self.plating_type} plating of {self.target}.") + + +class PulseFactory: + def create(self, args: dict[str, Any]) -> Task: + return Pulse(**args) + + +class RecalibrateFactory: + def create(self, args: dict[str, Any]) -> Task: + return Recalibrate(**args) + + +class ReinforceFactory: + def create(self, args: dict[str, Any]) -> Task: + return Reinforce(**args) diff --git a/pythonic-patterns/registry_pattern/fn_based/inject.py b/pythonic-patterns/registry_pattern/fn_based/inject.py new file mode 100644 index 0000000..0349dbc --- /dev/null +++ b/pythonic-patterns/registry_pattern/fn_based/inject.py @@ -0,0 +1,8 @@ +from registry import register + + +def inject(material: str, target: str) -> None: + print(f"Injecting {material} into {target}.") + + +register("inject", inject) diff --git a/pythonic-patterns/registry_pattern/fn_based/loader.py b/pythonic-patterns/registry_pattern/fn_based/loader.py new file mode 100644 index 0000000..abe8822 --- /dev/null +++ b/pythonic-patterns/registry_pattern/fn_based/loader.py @@ -0,0 +1,6 @@ +import importlib + + +def load_plugins(plugins: list[str]) -> None: + for plugin in plugins: + importlib.import_module(plugin) diff --git a/pythonic-patterns/registry_pattern/fn_based/main.py b/pythonic-patterns/registry_pattern/fn_based/main.py new file mode 100644 index 0000000..8c2dea7 --- /dev/null +++ b/pythonic-patterns/registry_pattern/fn_based/main.py @@ -0,0 +1,41 @@ +import json + +from loader import load_plugins +from registry import register, run + + +def send_pulse(strength: int) -> None: + print( + f"Sending a subspace pulse of {strength} microPicards to the converter assembly." + ) + + +def recalibrate(target: str) -> None: + print(f"Recalibrating the {target}.") + + +def reinforce(plating_type: str, target: str) -> None: + print(f"Reinforcing {plating_type} plating of {target}.") + + +def main() -> None: + + # register a couple of tasks + register("pulse", send_pulse) + register("recalibrate", recalibrate) + register("reinforce", reinforce) + + # read data from a JSON file + with open("./tasks.json", encoding="utf-8") as file: + data = json.load(file) + + # load the plugins + load_plugins(data["plugins"]) + + # run the tasks + for task in data["tasks"]: + run(task) + + +if __name__ == "__main__": + main() diff --git a/pythonic-patterns/registry_pattern/fn_based/registry.py b/pythonic-patterns/registry_pattern/fn_based/registry.py new file mode 100644 index 0000000..a9a91cd --- /dev/null +++ b/pythonic-patterns/registry_pattern/fn_based/registry.py @@ -0,0 +1,17 @@ +from typing import Any, Callable + +task_functions: dict[str, Callable[..., None]] = {} + + +def register(task_type: str, task_fn: Callable[..., None]) -> None: + task_functions[task_type] = task_fn + + +def unregister(task_type: str) -> None: + task_functions.pop(task_type, None) + + +def run(arguments: dict[str, Any]) -> None: + args_copy = arguments.copy() + task_type = args_copy.pop("type") + task_functions[task_type](**args_copy) diff --git a/pythonic-patterns/registry_pattern/fn_based/tasks.json b/pythonic-patterns/registry_pattern/fn_based/tasks.json new file mode 100644 index 0000000..9578f85 --- /dev/null +++ b/pythonic-patterns/registry_pattern/fn_based/tasks.json @@ -0,0 +1,23 @@ +{ + "plugins": ["inject"], + "tasks": [ + { + "type": "pulse", + "strength": 190 + }, + { + "type": "recalibrate", + "target": "Thoron subspace transponder" + }, + { + "type": "reinforce", + "plating_type": "biogenic", + "target": "the deflector array" + }, + { + "type": "inject", + "material": "tachyons", + "target": "molecular transporter resonator" + } + ] +} diff --git a/pythonic-patterns/registry_pattern/registry_reference/registry_diagram.md b/pythonic-patterns/registry_pattern/registry_reference/registry_diagram.md new file mode 100644 index 0000000..cfa65ed --- /dev/null +++ b/pythonic-patterns/registry_pattern/registry_reference/registry_diagram.md @@ -0,0 +1,71 @@ +```mermaid +classDiagram + + class AbstractFactory { + <> + Product create()* + } + class ConcreteFactory1 { + Product create() + } + class ConcreteFactory2 { + Product create() + } + class Product { + <> + } + class Registry { + registry: dict[str, AbstractFactory] + register(type: str, factory: AbstractFactory) + unregister(type: str) + Product create(type: str) + } + + ConcreteFactory1 --|> AbstractFactory + ConcreteFactory2 --|> AbstractFactory + ConcreteProduct1 --|> Product + ConcreteProduct2 --|> Product + ConcreteFactory1 ..> ConcreteProduct1 + ConcreteFactory2 ..> ConcreteProduct2 + Registry o-- AbstractFactory +``` + +```mermaid +classDiagram + + class TaskFactory { + <> + Task create(args)* + } + class PulseFactory { + Task create(args) + } + class RecalibrateFactory { + Task create(args) + } + class ReinforceFactory { + Task create(args) + } + class Task { + <> + run()* + } + class TaskRegistry { + registry: dict[str, TaskFactory] + register(type: str, factory: TaskFactory) + unregister(type: str) + Task create(type: str) + } + + PulseFactory --|> TaskFactory + RecalibrateFactory --|> TaskFactory + ReinforceFactory --|> TaskFactory + Pulse --|> Task + Recalibrate --|> Task + Reinforce --|> Task + + PulseFactory ..> Pulse + RecalibrateFactory ..> Recalibrate + ReinforceFactory ..> Reinforce + TaskRegistry o-- TaskFactory +``` diff --git a/pythonic-patterns/registry_pattern/registry_reference/registry_reference.py b/pythonic-patterns/registry_pattern/registry_reference/registry_reference.py new file mode 100644 index 0000000..358bfaf --- /dev/null +++ b/pythonic-patterns/registry_pattern/registry_reference/registry_reference.py @@ -0,0 +1,60 @@ +from abc import ABC, abstractmethod + + +class Product(ABC): + pass + + +class Product1(Product): + pass + + +class Product2(Product): + pass + + +class AbstractFactory(ABC): + @abstractmethod + def create(self) -> Product: + pass + + +class ConcreteFactory1(AbstractFactory): + def create(self) -> Product: + return Product1() + + +class ConcreteFactory2(AbstractFactory): + def create(self) -> Product: + return Product2() + + +class Registry: + def __init__(self): + self.registry: dict[str, AbstractFactory] = {} + + def register(self, product_type: str, factory: AbstractFactory) -> None: + self.registry[product_type] = factory + + def unregister(self, product_type: str) -> None: + self.registry.pop(product_type, None) + + def create(self, product_type: str) -> Product: + factory = self.registry[product_type] + return factory.create() + + +def main() -> None: + registry = Registry() + registry.register("product1", ConcreteFactory1()) + registry.register("product2", ConcreteFactory2()) + + print(registry.create("product1")) + print(registry.create("product2")) + + registry.unregister("product1") + registry.unregister("product2") + + +if __name__ == "__main__": + main() From 615d3364152e1c35aede5790dc014602fb5cf218 Mon Sep 17 00:00:00 2001 From: Salmireles Date: Fri, 6 Jan 2023 09:21:58 -0800 Subject: [PATCH 2/2] add video link to readme --- pythonic-patterns/registry_pattern/readme.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pythonic-patterns/registry_pattern/readme.md diff --git a/pythonic-patterns/registry_pattern/readme.md b/pythonic-patterns/registry_pattern/readme.md new file mode 100644 index 0000000..ee3ebaa --- /dev/null +++ b/pythonic-patterns/registry_pattern/readme.md @@ -0,0 +1,4 @@ +# Registry Pattern + +### Summary +- Video Link: https://www.arjancodes.com/products/the-software-designer-mindset-pythonic-patterns/categories/2149946555/posts/2160000778 \ No newline at end of file