diff --git a/patterns/behavioral/catalog.py b/patterns/behavioral/catalog.py index 5f69c9e6..11a730c3 100644 --- a/patterns/behavioral/catalog.py +++ b/patterns/behavioral/catalog.py @@ -12,7 +12,6 @@ class Catalog: """ def __init__(self, param: str) -> None: - # dictionary that will be used to determine which static method is # to be executed but that will be also used to store possible param # value diff --git a/patterns/behavioral/memento.py b/patterns/behavioral/memento.py index 1714e8de..4d072833 100644 --- a/patterns/behavioral/memento.py +++ b/patterns/behavioral/memento.py @@ -9,10 +9,10 @@ from typing import Callable, List -def memento(obj, deep=False): +def memento(obj: Any, deep: bool = False) -> Callable: state = deepcopy(obj.__dict__) if deep else copy(obj.__dict__) - def restore(): + def restore() -> None: obj.__dict__.clear() obj.__dict__.update(state) @@ -28,15 +28,15 @@ class Transaction: deep = False states: List[Callable[[], None]] = [] - def __init__(self, deep, *targets): + def __init__(self, deep: bool, *targets: Any) -> None: self.deep = deep self.targets = targets self.commit() - def commit(self): + def commit(self) -> None: self.states = [memento(target, self.deep) for target in self.targets] - def rollback(self): + def rollback(self) -> None: for a_state in self.states: a_state() @@ -48,29 +48,39 @@ def Transactional(method): :param method: The function to be decorated. """ - def transaction(obj, *args, **kwargs): - state = memento(obj) - try: - return method(obj, *args, **kwargs) - except Exception as e: - state() - raise e + def __init__(self, method: Callable) -> None: + self.method = method + + def __get__(self, obj: Any, T: Type) -> Callable: + """ + A decorator that makes a function transactional. + + :param method: The function to be decorated. + """ + + def transaction(*args, **kwargs): + state = memento(obj) + try: + return self.method(obj, *args, **kwargs) + except Exception as e: + state() + raise e return transaction class NumObj: - def __init__(self, value): + def __init__(self, value: int) -> None: self.value = value - def __repr__(self): + def __repr__(self) -> str: return f"<{self.__class__.__name__}: {self.value!r}>" - def increment(self): + def increment(self) -> None: self.value += 1 @Transactional - def do_stuff(self): + def do_stuff(self) -> None: self.value = "1111" # <- invalid value self.increment() # <- will fail and rollback diff --git a/patterns/behavioral/registry.py b/patterns/behavioral/registry.py index d44a992e..60cae019 100644 --- a/patterns/behavioral/registry.py +++ b/patterns/behavioral/registry.py @@ -2,7 +2,6 @@ class RegistryHolder(type): - REGISTRY: Dict[str, "RegistryHolder"] = {} def __new__(cls, name, bases, attrs): diff --git a/patterns/behavioral/specification.py b/patterns/behavioral/specification.py index 303ee513..10d22689 100644 --- a/patterns/behavioral/specification.py +++ b/patterns/behavioral/specification.py @@ -6,6 +6,7 @@ """ from abc import abstractmethod +from typing import Union class Specification: @@ -28,22 +29,22 @@ class CompositeSpecification(Specification): def is_satisfied_by(self, candidate): pass - def and_specification(self, candidate): + def and_specification(self, candidate: "Specification") -> "AndSpecification": return AndSpecification(self, candidate) - def or_specification(self, candidate): + def or_specification(self, candidate: "Specification") -> "OrSpecification": return OrSpecification(self, candidate) - def not_specification(self): + def not_specification(self) -> "NotSpecification": return NotSpecification(self) class AndSpecification(CompositeSpecification): - def __init__(self, one, other): + def __init__(self, one: "Specification", other: "Specification") -> None: self._one: Specification = one self._other: Specification = other - def is_satisfied_by(self, candidate): + def is_satisfied_by(self, candidate: Union["User", str]) -> bool: return bool( self._one.is_satisfied_by(candidate) and self._other.is_satisfied_by(candidate) @@ -51,11 +52,11 @@ def is_satisfied_by(self, candidate): class OrSpecification(CompositeSpecification): - def __init__(self, one, other): + def __init__(self, one: "Specification", other: "Specification") -> None: self._one: Specification = one self._other: Specification = other - def is_satisfied_by(self, candidate): + def is_satisfied_by(self, candidate: Union["User", str]): return bool( self._one.is_satisfied_by(candidate) or self._other.is_satisfied_by(candidate) @@ -63,25 +64,25 @@ def is_satisfied_by(self, candidate): class NotSpecification(CompositeSpecification): - def __init__(self, wrapped): + def __init__(self, wrapped: "Specification"): self._wrapped: Specification = wrapped - def is_satisfied_by(self, candidate): + def is_satisfied_by(self, candidate: Union["User", str]): return bool(not self._wrapped.is_satisfied_by(candidate)) class User: - def __init__(self, super_user=False): + def __init__(self, super_user: bool = False) -> None: self.super_user = super_user class UserSpecification(CompositeSpecification): - def is_satisfied_by(self, candidate): + def is_satisfied_by(self, candidate: Union["User", str]) -> bool: return isinstance(candidate, User) class SuperUserSpecification(CompositeSpecification): - def is_satisfied_by(self, candidate): + def is_satisfied_by(self, candidate: "User") -> bool: return getattr(candidate, "super_user", False) diff --git a/patterns/behavioral/visitor.py b/patterns/behavioral/visitor.py index 2e11a583..aa10b58c 100644 --- a/patterns/behavioral/visitor.py +++ b/patterns/behavioral/visitor.py @@ -14,6 +14,7 @@ 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: @@ -33,7 +34,7 @@ class C(A, B): class Visitor: - def visit(self, node, *args, **kwargs): + def visit(self, node: Union[A, C, B], *args, **kwargs) -> None: meth = None for cls in node.__class__.__mro__: meth_name = "visit_" + cls.__name__ @@ -45,11 +46,11 @@ def visit(self, node, *args, **kwargs): meth = self.generic_visit return meth(node, *args, **kwargs) - def generic_visit(self, node, *args, **kwargs): - return "generic_visit " + node.__class__.__name__ + def generic_visit(self, node: A, *args, **kwargs) -> None: + print("generic_visit " + node.__class__.__name__) - def visit_B(self, node, *args, **kwargs): - return "visit_B " + node.__class__.__name__ + def visit_B(self, node: Union[C, B], *args, **kwargs) -> None: + print("visit_B " + node.__class__.__name__) def main(): diff --git a/patterns/creational/lazy_evaluation.py b/patterns/creational/lazy_evaluation.py index b56daf0c..1f8db6bd 100644 --- a/patterns/creational/lazy_evaluation.py +++ b/patterns/creational/lazy_evaluation.py @@ -20,14 +20,15 @@ """ import functools +from typing import Callable, Type class lazy_property: - def __init__(self, function): + def __init__(self, function: Callable) -> None: self.function = function functools.update_wrapper(self, function) - def __get__(self, obj, type_): + def __get__(self, obj: "Person", type_: Type["Person"]) -> str: if obj is None: return self val = self.function(obj) @@ -35,7 +36,7 @@ def __get__(self, obj, type_): return val -def lazy_property2(fn): +def lazy_property2(fn: Callable) -> property: """ A lazy property decorator. @@ -54,19 +55,19 @@ def _lazy_property(self): class Person: - def __init__(self, name, occupation): + def __init__(self, name: str, occupation: str) -> None: self.name = name self.occupation = occupation self.call_count2 = 0 @lazy_property - def relatives(self): + def relatives(self) -> str: # Get all relatives, let's assume that it costs much time. relatives = "Many relatives." return relatives @lazy_property2 - def parents(self): + def parents(self) -> str: self.call_count2 += 1 return "Father and mother" diff --git a/patterns/creational/pool.py b/patterns/creational/pool.py index 1d70ea69..02f61791 100644 --- a/patterns/creational/pool.py +++ b/patterns/creational/pool.py @@ -27,24 +27,32 @@ *TL;DR Stores a set of initialized objects kept ready to use. """ +from queue import Queue +from types import TracebackType +from typing import Union class ObjectPool: - def __init__(self, queue, auto_get=False): + def __init__(self, queue: Queue, auto_get: bool = False) -> None: self._queue = queue self.item = self._queue.get() if auto_get else None - def __enter__(self): + def __enter__(self) -> str: if self.item is None: self.item = self._queue.get() return self.item - def __exit__(self, Type, value, traceback): + def __exit__( + self, + Type: Union[type[BaseException], None], + value: Union[BaseException, None], + traceback: Union[TracebackType, None], + ) -> None: if self.item is not None: self._queue.put(self.item) self.item = None - def __del__(self): + def __del__(self) -> None: if self.item is not None: self._queue.put(self.item) self.item = None diff --git a/patterns/other/graph_search.py b/patterns/other/graph_search.py index 262a6f08..6e3cdffb 100644 --- a/patterns/other/graph_search.py +++ b/patterns/other/graph_search.py @@ -1,3 +1,6 @@ +from typing import Any, Dict, List, Optional, Union + + class GraphSearch: """Graph search emulation in python, from source http://www.python.org/doc/essays/graphs/ @@ -5,10 +8,12 @@ class GraphSearch: dfs stands for Depth First Search bfs stands for Breadth First Search""" - def __init__(self, graph): + def __init__(self, graph: Dict[str, List[str]]) -> None: self.graph = graph - def find_path_dfs(self, start, end, path=None): + def find_path_dfs( + self, start: str, end: str, path: Optional[List[str]] = None + ) -> Optional[List[str]]: path = path or [] path.append(start) @@ -20,7 +25,9 @@ def find_path_dfs(self, start, end, path=None): if newpath: return newpath - def find_all_paths_dfs(self, start, end, path=None): + def find_all_paths_dfs( + self, start: str, end: str, path: Optional[List[str]] = None + ) -> List[Union[List[str], Any]]: path = path or [] path.append(start) if start == end: @@ -32,7 +39,9 @@ def find_all_paths_dfs(self, start, end, path=None): paths.extend(newpaths) return paths - def find_shortest_path_dfs(self, start, end, path=None): + def find_shortest_path_dfs( + self, start: str, end: str, path: Optional[List[str]] = None + ) -> Optional[List[str]]: path = path or [] path.append(start) @@ -47,7 +56,7 @@ def find_shortest_path_dfs(self, start, end, path=None): shortest = newpath return shortest - def find_shortest_path_bfs(self, start, end): + def find_shortest_path_bfs(self, start: str, end: str) -> Optional[List[str]]: """ 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 ecc04243..287badaf 100644 --- a/patterns/structural/3-tier.py +++ b/patterns/structural/3-tier.py @@ -16,7 +16,6 @@ class Data: } def __get__(self, obj, klas): - print("(Fetching from Data Store)") return {"products": self.products} diff --git a/patterns/structural/bridge.py b/patterns/structural/bridge.py index feddb675..1575cb53 100644 --- a/patterns/structural/bridge.py +++ b/patterns/structural/bridge.py @@ -5,34 +5,37 @@ *TL;DR Decouples an abstraction from its implementation. """ +from typing import Union # ConcreteImplementor 1/2 class DrawingAPI1: - def draw_circle(self, x, y, radius): + def draw_circle(self, x: int, y: int, radius: float) -> None: print(f"API1.circle at {x}:{y} radius {radius}") # ConcreteImplementor 2/2 class DrawingAPI2: - def draw_circle(self, x, y, radius): + def draw_circle(self, x: int, y: int, radius: float) -> None: print(f"API2.circle at {x}:{y} radius {radius}") # Refined Abstraction class CircleShape: - def __init__(self, x, y, radius, drawing_api): + def __init__( + self, x: int, y: int, radius: int, drawing_api: Union[DrawingAPI2, DrawingAPI1] + ) -> None: self._x = x self._y = y self._radius = radius self._drawing_api = drawing_api # low-level i.e. Implementation specific - def draw(self): + def draw(self) -> None: self._drawing_api.draw_circle(self._x, self._y, self._radius) # high-level i.e. Abstraction specific - def scale(self, pct): + def scale(self, pct: float) -> None: self._radius *= pct diff --git a/patterns/structural/flyweight.py b/patterns/structural/flyweight.py index fad17a8b..68b6f43c 100644 --- a/patterns/structural/flyweight.py +++ b/patterns/structural/flyweight.py @@ -36,7 +36,7 @@ class Card: # when there are no other references to it. _pool: weakref.WeakValueDictionary = weakref.WeakValueDictionary() - def __new__(cls, value, suit): + def __new__(cls, value: str, suit: str): # If the object exists in the pool - just return it obj = cls._pool.get(value + suit) # otherwise - create new one (and add it to the pool) @@ -52,7 +52,7 @@ def __new__(cls, value, suit): # def __init__(self, value, suit): # self.value, self.suit = value, suit - def __repr__(self): + def __repr__(self) -> str: return f"" diff --git a/patterns/structural/mvc.py b/patterns/structural/mvc.py index 5726b089..27765fb7 100644 --- a/patterns/structural/mvc.py +++ b/patterns/structural/mvc.py @@ -4,9 +4,10 @@ """ from abc import ABC, abstractmethod +from ProductModel import Price +from typing import Dict, List, Union, Any from inspect import signature from sys import argv -from typing import Any class Model(ABC):