From 8146bc4efa73dd50d202a341178ba664c942092c Mon Sep 17 00:00:00 2001 From: codomposer Date: Thu, 30 Oct 2025 14:34:59 -0400 Subject: [PATCH 1/2] add type hint for >3.9 --- patterns/behavioral/chain_of_responsibility.py | 15 +++++++-------- patterns/behavioral/command.py | 7 +++---- patterns/behavioral/memento.py | 4 ++-- patterns/behavioral/observer.py | 3 +-- patterns/behavioral/registry.py | 5 +---- patterns/behavioral/specification.py | 9 ++++----- patterns/behavioral/strategy.py | 4 ++-- patterns/behavioral/visitor.py | 5 ++--- patterns/creational/abstract_factory.py | 5 ++--- patterns/creational/borg.py | 5 +---- patterns/creational/factory.py | 4 ++-- patterns/creational/lazy_evaluation.py | 4 ++-- patterns/creational/pool.py | 7 +++---- patterns/fundamental/delegation_pattern.py | 2 +- patterns/other/graph_search.py | 18 +++++++++--------- patterns/structural/3-tier.py | 4 ++-- patterns/structural/bridge.py | 3 +-- patterns/structural/composite.py | 3 +-- patterns/structural/mvc.py | 2 +- patterns/structural/proxy.py | 4 +--- 20 files changed, 48 insertions(+), 65 deletions(-) diff --git a/patterns/behavioral/chain_of_responsibility.py b/patterns/behavioral/chain_of_responsibility.py index 9d46c4a8..72bb73e4 100644 --- a/patterns/behavioral/chain_of_responsibility.py +++ b/patterns/behavioral/chain_of_responsibility.py @@ -19,11 +19,10 @@ """ from abc import ABC, abstractmethod -from typing import Optional, Tuple class Handler(ABC): - def __init__(self, successor: Optional["Handler"] = None): + def __init__(self, successor: "Handler | None" = None): self.successor = successor def handle(self, request: int) -> None: @@ -39,7 +38,7 @@ def handle(self, request: int) -> None: self.successor.handle(request) @abstractmethod - def check_range(self, request: int) -> Optional[bool]: + def check_range(self, request: int) -> bool | None: """Compare passed value to predefined interval""" @@ -49,7 +48,7 @@ class ConcreteHandler0(Handler): """ @staticmethod - def check_range(request: int) -> Optional[bool]: + def check_range(request: int) -> bool | None: if 0 <= request < 10: print(f"request {request} handled in handler 0") return True @@ -61,7 +60,7 @@ class ConcreteHandler1(Handler): start, end = 10, 20 - def check_range(self, request: int) -> Optional[bool]: + def check_range(self, request: int) -> bool | None: if self.start <= request < self.end: print(f"request {request} handled in handler 1") return True @@ -71,7 +70,7 @@ def check_range(self, request: int) -> Optional[bool]: class ConcreteHandler2(Handler): """... With helper methods.""" - def check_range(self, request: int) -> Optional[bool]: + def check_range(self, request: int) -> bool | None: start, end = self.get_interval_from_db() if start <= request < end: print(f"request {request} handled in handler 2") @@ -79,13 +78,13 @@ def check_range(self, request: int) -> Optional[bool]: return None @staticmethod - def get_interval_from_db() -> Tuple[int, int]: + def get_interval_from_db() -> tuple[int, int]: return (20, 30) class FallbackHandler(Handler): @staticmethod - def check_range(request: int) -> Optional[bool]: + def check_range(request: int) -> bool | None: print(f"end of chain, no handler for {request}") return False diff --git a/patterns/behavioral/command.py b/patterns/behavioral/command.py index a88ea8be..949f29b2 100644 --- a/patterns/behavioral/command.py +++ b/patterns/behavioral/command.py @@ -20,7 +20,6 @@ https://docs.djangoproject.com/en/2.1/ref/request-response/#httprequest-objects """ -from typing import List, Union class HideFileCommand: @@ -30,7 +29,7 @@ class HideFileCommand: def __init__(self) -> None: # an array of files hidden, to undo them as needed - self._hidden_files: List[str] = [] + self._hidden_files: list[str] = [] def execute(self, filename: str) -> None: print(f"hiding {filename}") @@ -48,7 +47,7 @@ class DeleteFileCommand: def __init__(self) -> None: # an array of deleted files, to undo them as needed - self._deleted_files: List[str] = [] + self._deleted_files: list[str] = [] def execute(self, filename: str) -> None: print(f"deleting {filename}") @@ -64,7 +63,7 @@ class MenuItem: The invoker class. Here it is items in a menu. """ - def __init__(self, command: Union[HideFileCommand, DeleteFileCommand]) -> None: + def __init__(self, command: HideFileCommand | DeleteFileCommand) -> None: self._command = command def on_do_press(self, filename: str) -> None: diff --git a/patterns/behavioral/memento.py b/patterns/behavioral/memento.py index 4d072833..069e8458 100644 --- a/patterns/behavioral/memento.py +++ b/patterns/behavioral/memento.py @@ -6,7 +6,7 @@ """ from copy import copy, deepcopy -from typing import Callable, List +from typing import Callable def memento(obj: Any, deep: bool = False) -> Callable: @@ -26,7 +26,7 @@ class Transaction: """ deep = False - states: List[Callable[[], None]] = [] + states: list[Callable[[], None]] = [] def __init__(self, deep: bool, *targets: Any) -> None: self.deep = deep diff --git a/patterns/behavioral/observer.py b/patterns/behavioral/observer.py index c9184be1..f7e49de0 100644 --- a/patterns/behavioral/observer.py +++ b/patterns/behavioral/observer.py @@ -12,7 +12,6 @@ # observer.py from __future__ import annotations -from typing import List class Observer: def update(self, subject: Subject) -> None: @@ -26,7 +25,7 @@ def update(self, subject: Subject) -> None: class Subject: - _observers: List[Observer] + _observers: list[Observer] def __init__(self) -> None: """ diff --git a/patterns/behavioral/registry.py b/patterns/behavioral/registry.py index 60cae019..1288c1f6 100644 --- a/patterns/behavioral/registry.py +++ b/patterns/behavioral/registry.py @@ -1,8 +1,5 @@ -from typing import Dict - - class RegistryHolder(type): - REGISTRY: Dict[str, "RegistryHolder"] = {} + REGISTRY: dict[str, "RegistryHolder"] = {} def __new__(cls, name, bases, attrs): new_cls = type.__new__(cls, name, bases, attrs) diff --git a/patterns/behavioral/specification.py b/patterns/behavioral/specification.py index 10d22689..027e0a7f 100644 --- a/patterns/behavioral/specification.py +++ b/patterns/behavioral/specification.py @@ -6,7 +6,6 @@ """ from abc import abstractmethod -from typing import Union class Specification: @@ -44,7 +43,7 @@ def __init__(self, one: "Specification", other: "Specification") -> None: self._one: Specification = one self._other: Specification = other - def is_satisfied_by(self, candidate: Union["User", str]) -> bool: + def is_satisfied_by(self, candidate: "User" | str) -> bool: return bool( self._one.is_satisfied_by(candidate) and self._other.is_satisfied_by(candidate) @@ -56,7 +55,7 @@ def __init__(self, one: "Specification", other: "Specification") -> None: self._one: Specification = one self._other: Specification = other - def is_satisfied_by(self, candidate: Union["User", str]): + def is_satisfied_by(self, candidate: "User" | str): return bool( self._one.is_satisfied_by(candidate) or self._other.is_satisfied_by(candidate) @@ -67,7 +66,7 @@ class NotSpecification(CompositeSpecification): def __init__(self, wrapped: "Specification"): self._wrapped: Specification = wrapped - def is_satisfied_by(self, candidate: Union["User", str]): + def is_satisfied_by(self, candidate: "User" | str): return bool(not self._wrapped.is_satisfied_by(candidate)) @@ -77,7 +76,7 @@ def __init__(self, super_user: bool = False) -> None: class UserSpecification(CompositeSpecification): - def is_satisfied_by(self, candidate: Union["User", str]) -> bool: + def is_satisfied_by(self, candidate: "User" | str) -> bool: return isinstance(candidate, User) diff --git a/patterns/behavioral/strategy.py b/patterns/behavioral/strategy.py index 000ff2ad..1c2ff8b7 100644 --- a/patterns/behavioral/strategy.py +++ b/patterns/behavioral/strategy.py @@ -29,7 +29,7 @@ def validate(obj: Order, value: Callable) -> bool: def __set_name__(self, owner, name: str) -> None: self.private_name = f"_{name}" - def __set__(self, obj: Order, value: Callable = None) -> None: + def __set__(self, obj: Order, value: Callable | None = None) -> None: if value and self.validate(obj, value): setattr(obj, self.private_name, value) else: @@ -42,7 +42,7 @@ def __get__(self, obj: object, objtype: type = None): class Order: discount_strategy = DiscountStrategyValidator() - def __init__(self, price: float, discount_strategy: Callable = None) -> None: + def __init__(self, price: float, discount_strategy: Callable | None = None) -> None: self.price: float = price self.discount_strategy = discount_strategy diff --git a/patterns/behavioral/visitor.py b/patterns/behavioral/visitor.py index aa10b58c..1297eb86 100644 --- a/patterns/behavioral/visitor.py +++ b/patterns/behavioral/visitor.py @@ -14,7 +14,6 @@ which is then being used e.g. in tools like `pyflakes`. - `Black` formatter tool implements it's own: https://github.com/ambv/black/blob/master/black.py#L718 """ -from typing import Union class Node: @@ -34,7 +33,7 @@ class C(A, B): class Visitor: - def visit(self, node: Union[A, C, B], *args, **kwargs) -> None: + def visit(self, node: A | C | B, *args, **kwargs) -> None: meth = None for cls in node.__class__.__mro__: meth_name = "visit_" + cls.__name__ @@ -49,7 +48,7 @@ def visit(self, node: Union[A, C, B], *args, **kwargs) -> None: def generic_visit(self, node: A, *args, **kwargs) -> None: print("generic_visit " + node.__class__.__name__) - def visit_B(self, node: Union[C, B], *args, **kwargs) -> None: + def visit_B(self, node: C | B, *args, **kwargs) -> None: print("visit_B " + node.__class__.__name__) diff --git a/patterns/creational/abstract_factory.py b/patterns/creational/abstract_factory.py index 15e5d67f..2362d8fe 100644 --- a/patterns/creational/abstract_factory.py +++ b/patterns/creational/abstract_factory.py @@ -31,7 +31,6 @@ """ import random -from typing import Type class Pet: @@ -64,7 +63,7 @@ def __str__(self) -> str: class PetShop: """A pet shop""" - def __init__(self, animal_factory: Type[Pet]) -> None: + def __init__(self, animal_factory: type[Pet]) -> None: """pet_factory is our abstract factory. We can set it at will.""" self.pet_factory = animal_factory @@ -91,7 +90,7 @@ def main() -> None: if __name__ == "__main__": animals = [Dog, Cat] - random_animal: Type[Pet] = random.choice(animals) + random_animal: type[Pet] = random.choice(animals) shop = PetShop(random_animal) import doctest diff --git a/patterns/creational/borg.py b/patterns/creational/borg.py index edd0589d..c9d6da07 100644 --- a/patterns/creational/borg.py +++ b/patterns/creational/borg.py @@ -33,11 +33,8 @@ Provides singleton-like behavior sharing state between instances. """ -from typing import Dict - - class Borg: - _shared_state: Dict[str, str] = {} + _shared_state: dict[str, str] = {} def __init__(self) -> None: self.__dict__ = self._shared_state diff --git a/patterns/creational/factory.py b/patterns/creational/factory.py index e5372ca5..76d7f0f6 100644 --- a/patterns/creational/factory.py +++ b/patterns/creational/factory.py @@ -22,7 +22,7 @@ Creates objects without having to specify the exact class. """ -from typing import Dict, Protocol, Type +from typing import Protocol class Localizer(Protocol): @@ -49,7 +49,7 @@ def localize(self, msg: str) -> str: def get_localizer(language: str = "English") -> Localizer: """Factory""" - localizers: Dict[str, Type[Localizer]] = { + localizers: dict[str, type[Localizer]] = { "English": EnglishLocalizer, "Greek": GreekLocalizer, } diff --git a/patterns/creational/lazy_evaluation.py b/patterns/creational/lazy_evaluation.py index 1f8db6bd..d8129dfd 100644 --- a/patterns/creational/lazy_evaluation.py +++ b/patterns/creational/lazy_evaluation.py @@ -20,7 +20,7 @@ """ import functools -from typing import Callable, Type +from typing import Callable class lazy_property: @@ -28,7 +28,7 @@ def __init__(self, function: Callable) -> None: self.function = function functools.update_wrapper(self, function) - def __get__(self, obj: "Person", type_: Type["Person"]) -> str: + def __get__(self, obj: "Person", type_: type["Person"]) -> str: if obj is None: return self val = self.function(obj) diff --git a/patterns/creational/pool.py b/patterns/creational/pool.py index 02f61791..ad8c348b 100644 --- a/patterns/creational/pool.py +++ b/patterns/creational/pool.py @@ -29,7 +29,6 @@ """ from queue import Queue from types import TracebackType -from typing import Union class ObjectPool: @@ -44,9 +43,9 @@ def __enter__(self) -> str: def __exit__( self, - Type: Union[type[BaseException], None], - value: Union[BaseException, None], - traceback: Union[TracebackType, None], + Type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, ) -> None: if self.item is not None: self._queue.put(self.item) diff --git a/patterns/fundamental/delegation_pattern.py b/patterns/fundamental/delegation_pattern.py index f7a7c2f5..0a4a37a5 100644 --- a/patterns/fundamental/delegation_pattern.py +++ b/patterns/fundamental/delegation_pattern.py @@ -33,7 +33,7 @@ class Delegator: def __init__(self, delegate: Delegate) -> None: self.delegate = delegate - def __getattr__(self, name: str) -> Any | Callable: + def __getattr__(self, name: str) -> Any: attr = getattr(self.delegate, name) if not callable(attr): diff --git a/patterns/other/graph_search.py b/patterns/other/graph_search.py index 6e3cdffb..a2eb1545 100644 --- a/patterns/other/graph_search.py +++ b/patterns/other/graph_search.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any class GraphSearch: @@ -8,12 +8,12 @@ class GraphSearch: dfs stands for Depth First Search bfs stands for Breadth First Search""" - def __init__(self, graph: Dict[str, List[str]]) -> None: + def __init__(self, graph: dict[str, list[str]]) -> None: self.graph = graph def find_path_dfs( - self, start: str, end: str, path: Optional[List[str]] = None - ) -> Optional[List[str]]: + self, start: str, end: str, path: list[str] | None = None + ) -> list[str] | None: path = path or [] path.append(start) @@ -26,8 +26,8 @@ def find_path_dfs( return newpath def find_all_paths_dfs( - self, start: str, end: str, path: Optional[List[str]] = None - ) -> List[Union[List[str], Any]]: + self, start: str, end: str, path: list[str] | None = None + ) -> list[list[str] | Any]: path = path or [] path.append(start) if start == end: @@ -40,8 +40,8 @@ def find_all_paths_dfs( return paths def find_shortest_path_dfs( - self, start: str, end: str, path: Optional[List[str]] = None - ) -> Optional[List[str]]: + self, start: str, end: str, path: list[str] | None = None + ) -> list[str] | None: path = path or [] path.append(start) @@ -56,7 +56,7 @@ def find_shortest_path_dfs( shortest = newpath return shortest - def find_shortest_path_bfs(self, start: str, end: str) -> Optional[List[str]]: + def find_shortest_path_bfs(self, start: str, end: str) -> list[str] | None: """ Finds the shortest path between two nodes in a graph using breadth-first search. diff --git a/patterns/structural/3-tier.py b/patterns/structural/3-tier.py index 287badaf..2554b7f8 100644 --- a/patterns/structural/3-tier.py +++ b/patterns/structural/3-tier.py @@ -3,7 +3,7 @@ Separates presentation, application processing, and data management functions. """ -from typing import Dict, KeysView, Optional, Union +from typing import KeysView class Data: @@ -30,7 +30,7 @@ def product_list(self) -> KeysView[str]: def product_information( self, product: str - ) -> Optional[Dict[str, Union[int, float]]]: + ) -> dict[str, int | float] | None: return self.data["products"].get(product, None) diff --git a/patterns/structural/bridge.py b/patterns/structural/bridge.py index 1575cb53..534e3eb7 100644 --- a/patterns/structural/bridge.py +++ b/patterns/structural/bridge.py @@ -5,7 +5,6 @@ *TL;DR Decouples an abstraction from its implementation. """ -from typing import Union # ConcreteImplementor 1/2 @@ -23,7 +22,7 @@ def draw_circle(self, x: int, y: int, radius: float) -> None: # Refined Abstraction class CircleShape: def __init__( - self, x: int, y: int, radius: int, drawing_api: Union[DrawingAPI2, DrawingAPI1] + self, x: int, y: int, radius: int, drawing_api: DrawingAPI2 | DrawingAPI1 ) -> None: self._x = x self._y = y diff --git a/patterns/structural/composite.py b/patterns/structural/composite.py index a4bedc1d..ee0520c6 100644 --- a/patterns/structural/composite.py +++ b/patterns/structural/composite.py @@ -27,7 +27,6 @@ """ from abc import ABC, abstractmethod -from typing import List class Graphic(ABC): @@ -38,7 +37,7 @@ def render(self) -> None: class CompositeGraphic(Graphic): def __init__(self) -> None: - self.graphics: List[Graphic] = [] + self.graphics: list[Graphic] = [] def render(self) -> None: for graphic in self.graphics: diff --git a/patterns/structural/mvc.py b/patterns/structural/mvc.py index 27765fb7..59010024 100644 --- a/patterns/structural/mvc.py +++ b/patterns/structural/mvc.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod from ProductModel import Price -from typing import Dict, List, Union, Any +from typing import Any from inspect import signature from sys import argv diff --git a/patterns/structural/proxy.py b/patterns/structural/proxy.py index 3ef74ec0..c12fb051 100644 --- a/patterns/structural/proxy.py +++ b/patterns/structural/proxy.py @@ -15,8 +15,6 @@ without changing its interface. """ -from typing import Union - class Subject: """ @@ -59,7 +57,7 @@ def do_the_job(self, user: str) -> None: print("[log] I can do the job just for `admins`.") -def client(job_doer: Union[RealSubject, Proxy], user: str) -> None: +def client(job_doer: RealSubject | Proxy, user: str) -> None: job_doer.do_the_job(user) From 06dd09c14451ec3dda2f8600835c5778eef5dc0c Mon Sep 17 00:00:00 2001 From: codomposer Date: Thu, 30 Oct 2025 22:17:18 -0400 Subject: [PATCH 2/2] fix typo --- patterns/behavioral/chain_of_responsibility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/behavioral/chain_of_responsibility.py b/patterns/behavioral/chain_of_responsibility.py index 72bb73e4..8cd196f1 100644 --- a/patterns/behavioral/chain_of_responsibility.py +++ b/patterns/behavioral/chain_of_responsibility.py @@ -22,7 +22,7 @@ class Handler(ABC): - def __init__(self, successor: "Handler | None" = None): + def __init__(self, successor: "Handler" | None = None): self.successor = successor def handle(self, request: int) -> None: