diff --git a/python-circular-import/README.md b/python-circular-import/README.md new file mode 100644 index 0000000000..5992b72ba4 --- /dev/null +++ b/python-circular-import/README.md @@ -0,0 +1 @@ +Materials for `python-circular-import` diff --git a/python-circular-import/entity-relations/people/__init__.py b/python-circular-import/entity-relations/people/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/entity-relations/people/__main__.py b/python-circular-import/entity-relations/people/__main__.py new file mode 100644 index 0000000000..05860161f2 --- /dev/null +++ b/python-circular-import/entity-relations/people/__main__.py @@ -0,0 +1,12 @@ +from people import club, person + + +def main(): + p1 = person.Person("John", 30) + p2 = person.Person("Jane", 29) + c = club.Club("Tennis Club", [p1, p2]) + print(c) + + +if __name__ == "__main__": + main() diff --git a/python-circular-import/entity-relations/people/club.py b/python-circular-import/entity-relations/people/club.py new file mode 100644 index 0000000000..3501ff313e --- /dev/null +++ b/python-circular-import/entity-relations/people/club.py @@ -0,0 +1,17 @@ +# from __future__ import annotations + +# from typing import TYPE_CHECKING + +# if TYPE_CHECKING: +# from people.person import Person + +from people.person import Person + + +class Club: + def __init__(self, name, members: list[Person] = []): + self.name = name + self.members = members + + def __str__(self): + return f"{self.name} ({len(self.members)} members)" diff --git a/python-circular-import/entity-relations/people/person.py b/python-circular-import/entity-relations/people/person.py new file mode 100644 index 0000000000..a4f732e43e --- /dev/null +++ b/python-circular-import/entity-relations/people/person.py @@ -0,0 +1,18 @@ +# from __future__ import annotations + +# from typing import TYPE_CHECKING + +# if TYPE_CHECKING: +# from people.club import Club + +from people.club import Club + + +class Person: + def __init__(self, name, age, clubs: list[Club] = []): + self.name = name + self.age = age + self.clubs = clubs + + def __str__(self): + return f"{self.name} ({self.age})" diff --git a/python-circular-import/entity-relations/people_runtime/__init__.py b/python-circular-import/entity-relations/people_runtime/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/entity-relations/people_runtime/__main__.py b/python-circular-import/entity-relations/people_runtime/__main__.py new file mode 100644 index 0000000000..173bde45ac --- /dev/null +++ b/python-circular-import/entity-relations/people_runtime/__main__.py @@ -0,0 +1,14 @@ +from people_runtime import club, person + + +def main(): + p1 = person.Person("John", 30) + p2 = person.Person("Jane", 29) + c = club.Club("Tennis Club") + c.assign_member(p1) + c.assign_member(p2) + print(c) + + +if __name__ == "__main__": + main() diff --git a/python-circular-import/entity-relations/people_runtime/club.py b/python-circular-import/entity-relations/people_runtime/club.py new file mode 100644 index 0000000000..96571d9559 --- /dev/null +++ b/python-circular-import/entity-relations/people_runtime/club.py @@ -0,0 +1,21 @@ +# from people_runtime.person import Person + + +class Club: + def __init__(self, name): + self.name = name + self.members = set() + + def __str__(self): + return f"{self.name} ({len(self.members)} members)" + + def assign_member(self, person): + from people_runtime.person import Person + + if isinstance(person, Person): + self.members.add(person) + + if self not in person.clubs: + person.join_club(self) + else: + raise ValueError("Can only assign instances of Person") diff --git a/python-circular-import/entity-relations/people_runtime/person.py b/python-circular-import/entity-relations/people_runtime/person.py new file mode 100644 index 0000000000..a3ba80e0f1 --- /dev/null +++ b/python-circular-import/entity-relations/people_runtime/person.py @@ -0,0 +1,21 @@ +# from people_runtime.club import Club + + +class Person: + def __init__(self, name, age): + self.name = name + self.age = age + self.clubs = set() + + def __str__(self): + return f"{self.name} ({self.age})" + + def join_club(self, club): + from people_runtime.club import Club + + if isinstance(club, Club): + self.clubs.add(club) + if self not in club.members: + club.assign_member(self) + else: + raise ValueError("Can only join instances of Club") diff --git a/python-circular-import/entity-relations/pyproject.toml b/python-circular-import/entity-relations/pyproject.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/pyproject.toml b/python-circular-import/layered-architecture/pyproject.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop/__init__.py b/python-circular-import/layered-architecture/shop/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop/__main__.py b/python-circular-import/layered-architecture/shop/__main__.py new file mode 100644 index 0000000000..79df5e4ced --- /dev/null +++ b/python-circular-import/layered-architecture/shop/__main__.py @@ -0,0 +1,5 @@ +from shop.controllers.shop_controller import ShopController + +if __name__ == "__main__": + controller = ShopController() + controller.handle_request() diff --git a/python-circular-import/layered-architecture/shop/controllers/__init__.py b/python-circular-import/layered-architecture/shop/controllers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop/controllers/shop_controller.py b/python-circular-import/layered-architecture/shop/controllers/shop_controller.py new file mode 100644 index 0000000000..c3f4edf35b --- /dev/null +++ b/python-circular-import/layered-architecture/shop/controllers/shop_controller.py @@ -0,0 +1,20 @@ +from shop.services.shop_service import ShopService + + +class ShopController: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(ShopController, cls).__new__( + cls, *args, **kwargs + ) + return cls._instance + + def __init__(self): + if not hasattr(self, "service"): + self.service = ShopService() + + def handle_request(self): + print("Handling request") + self.service.perform_service_task() diff --git a/python-circular-import/layered-architecture/shop/services/__init__.py b/python-circular-import/layered-architecture/shop/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop/services/shop_service.py b/python-circular-import/layered-architecture/shop/services/shop_service.py new file mode 100644 index 0000000000..2ec202208b --- /dev/null +++ b/python-circular-import/layered-architecture/shop/services/shop_service.py @@ -0,0 +1,20 @@ +from shop.controllers.shop_controller import ShopController + + +class ShopService: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(ShopService, cls).__new__( + cls, *args, **kwargs + ) + return cls._instance + + def __init__(self): + if not hasattr(self, "controller"): + self.controller = ShopController() + + def perform_service_task(self): + print("Service task performed") + self.controller.handle_request() diff --git a/python-circular-import/layered-architecture/shop_di/__init__.py b/python-circular-import/layered-architecture/shop_di/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop_di/__main__.py b/python-circular-import/layered-architecture/shop_di/__main__.py new file mode 100644 index 0000000000..9282b02fd0 --- /dev/null +++ b/python-circular-import/layered-architecture/shop_di/__main__.py @@ -0,0 +1,7 @@ +from shop_di.controllers.shop_controller import ShopController +from shop_di.services.shop_service import ShopService + +if __name__ == "__main__": + service = ShopService() + controller = ShopController(service) + controller.handle_request() diff --git a/python-circular-import/layered-architecture/shop_di/controllers/__init__.py b/python-circular-import/layered-architecture/shop_di/controllers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop_di/controllers/shop_controller.py b/python-circular-import/layered-architecture/shop_di/controllers/shop_controller.py new file mode 100644 index 0000000000..86ad9454e6 --- /dev/null +++ b/python-circular-import/layered-architecture/shop_di/controllers/shop_controller.py @@ -0,0 +1,15 @@ +class ShopController: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = object.__new__(cls) + return cls._instance + + def __init__(self, service): + if not hasattr(self, "service"): + self.service = service + + def handle_request(self): + print("Handling request") + self.service.perform_service_task() diff --git a/python-circular-import/layered-architecture/shop_di/services/__init__.py b/python-circular-import/layered-architecture/shop_di/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop_di/services/shop_service.py b/python-circular-import/layered-architecture/shop_di/services/shop_service.py new file mode 100644 index 0000000000..d6b7935b8b --- /dev/null +++ b/python-circular-import/layered-architecture/shop_di/services/shop_service.py @@ -0,0 +1,13 @@ +class ShopService: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = object.__new__(cls) + return cls._instance + + def __init__(self): + pass + + def perform_service_task(self): + print("Service task performed") diff --git a/python-circular-import/layered-architecture/shop_import_recursion/__init__.py b/python-circular-import/layered-architecture/shop_import_recursion/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop_import_recursion/__main__.py b/python-circular-import/layered-architecture/shop_import_recursion/__main__.py new file mode 100644 index 0000000000..fb3277319a --- /dev/null +++ b/python-circular-import/layered-architecture/shop_import_recursion/__main__.py @@ -0,0 +1,5 @@ +from shop_import_recursion.controllers.shop_controller import ShopController + +if __name__ == "__main__": + controller = ShopController() + controller.handle_request() diff --git a/python-circular-import/layered-architecture/shop_import_recursion/controllers/__init__.py b/python-circular-import/layered-architecture/shop_import_recursion/controllers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop_import_recursion/controllers/shop_controller.py b/python-circular-import/layered-architecture/shop_import_recursion/controllers/shop_controller.py new file mode 100644 index 0000000000..1dd9b9b6a4 --- /dev/null +++ b/python-circular-import/layered-architecture/shop_import_recursion/controllers/shop_controller.py @@ -0,0 +1,25 @@ +# from shop_import_recursion.services import shop_service +import shop_import_recursion + + +class ShopController: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(ShopController, cls).__new__( + cls, *args, **kwargs + ) + return cls._instance + + def __init__(self): + if not hasattr(self, "service"): + # self.service = shop_service.ShopService() + print(dir(shop_import_recursion)) + self.service = ( + shop_import_recursion.services.shop_service.ShopService() + ) + + def handle_request(self): + print("Handling request") + self.service.perform_service_task() diff --git a/python-circular-import/layered-architecture/shop_import_recursion/services/__init__.py b/python-circular-import/layered-architecture/shop_import_recursion/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/layered-architecture/shop_import_recursion/services/shop_service.py b/python-circular-import/layered-architecture/shop_import_recursion/services/shop_service.py new file mode 100644 index 0000000000..43a70011b2 --- /dev/null +++ b/python-circular-import/layered-architecture/shop_import_recursion/services/shop_service.py @@ -0,0 +1,24 @@ +# from shop_import_recursion.controllers import shop_controller +import shop_import_recursion + + +class ShopService: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(ShopService, cls).__new__( + cls, *args, **kwargs + ) + return cls._instance + + def __init__(self): + if not hasattr(self, "controller"): + # self.controller = shop_controller.ShopController() + self.controller = ( + shop_import_recursion.controllers.shop_controller.ShopController() + ) + + def perform_service_task(self): + print("Service task performed") + self.controller.handle_request() diff --git a/python-circular-import/state-machine/pyproject.toml b/python-circular-import/state-machine/pyproject.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/state-machine/state_machine/__init__.py b/python-circular-import/state-machine/state_machine/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/state-machine/state_machine/__main__.py b/python-circular-import/state-machine/state_machine/__main__.py new file mode 100644 index 0000000000..b25fc958ea --- /dev/null +++ b/python-circular-import/state-machine/state_machine/__main__.py @@ -0,0 +1,5 @@ +from state_machine.state_a import StateA + +if __name__ == "__main__": + current_state = StateA() + current_state = current_state.on_event("to_b") diff --git a/python-circular-import/state-machine/state_machine/state_a.py b/python-circular-import/state-machine/state_machine/state_a.py new file mode 100644 index 0000000000..09ec321c3c --- /dev/null +++ b/python-circular-import/state-machine/state_machine/state_a.py @@ -0,0 +1,9 @@ +from state_machine.state_b import StateB + + +class StateA: + def on_event(self, event): + if event == "to_b": + return StateB() + else: + return self diff --git a/python-circular-import/state-machine/state_machine/state_b.py b/python-circular-import/state-machine/state_machine/state_b.py new file mode 100644 index 0000000000..1c4261731d --- /dev/null +++ b/python-circular-import/state-machine/state_machine/state_b.py @@ -0,0 +1,9 @@ +from state_machine.state_a import StateA + + +class StateB: + def on_event(self, event): + if event == "to_a": + return StateA() + else: + return self diff --git a/python-circular-import/state-machine/state_machine_base/__init__.py b/python-circular-import/state-machine/state_machine_base/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/state-machine/state_machine_base/__main__.py b/python-circular-import/state-machine/state_machine_base/__main__.py new file mode 100644 index 0000000000..f4d92a9c66 --- /dev/null +++ b/python-circular-import/state-machine/state_machine_base/__main__.py @@ -0,0 +1,5 @@ +from state_machine_base.base_state import BaseState + +if __name__ == "__main__": + current_state = BaseState() + current_state = current_state.on_event("to_b") diff --git a/python-circular-import/state-machine/state_machine_base/base_state.py b/python-circular-import/state-machine/state_machine_base/base_state.py new file mode 100644 index 0000000000..9c4ba8f02f --- /dev/null +++ b/python-circular-import/state-machine/state_machine_base/base_state.py @@ -0,0 +1,12 @@ +class BaseState: + state_registry = {} + + @classmethod + def register_state(cls, name, state_cls): + cls.state_registry[name] = state_cls + + def on_event(self, event): + next_state_cls = self.state_registry.get(event) + if next_state_cls: + return next_state_cls() + return self diff --git a/python-circular-import/state-machine/state_machine_base/state_a.py b/python-circular-import/state-machine/state_machine_base/state_a.py new file mode 100644 index 0000000000..2b25afffdf --- /dev/null +++ b/python-circular-import/state-machine/state_machine_base/state_a.py @@ -0,0 +1,8 @@ +from state_machine_base.base_state import BaseState + + +class StateA(BaseState): + pass + + +BaseState.register_state("to_b", "StateB") diff --git a/python-circular-import/state-machine/state_machine_base/state_b.py b/python-circular-import/state-machine/state_machine_base/state_b.py new file mode 100644 index 0000000000..0c05c6dc52 --- /dev/null +++ b/python-circular-import/state-machine/state_machine_base/state_b.py @@ -0,0 +1,8 @@ +from state_machine_base.base_state import BaseState + + +class StateB(BaseState): + pass + + +BaseState.register_state("to_a", "StateA") diff --git a/python-circular-import/state-machine/state_machine_controller/__init__.py b/python-circular-import/state-machine/state_machine_controller/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/state-machine/state_machine_controller/__main__.py b/python-circular-import/state-machine/state_machine_controller/__main__.py new file mode 100644 index 0000000000..41d3398598 --- /dev/null +++ b/python-circular-import/state-machine/state_machine_controller/__main__.py @@ -0,0 +1,9 @@ +from state_machine_controller.controller import Controller + +if __name__ == "__main__": + controller = Controller() + print(controller.state) + controller.on_event("to_b") + print(controller.state) + controller.on_event("to_a") + print(controller.state) diff --git a/python-circular-import/state-machine/state_machine_controller/controller.py b/python-circular-import/state-machine/state_machine_controller/controller.py new file mode 100644 index 0000000000..da62cf73dc --- /dev/null +++ b/python-circular-import/state-machine/state_machine_controller/controller.py @@ -0,0 +1,16 @@ +from state_machine_controller.state_a import StateA +from state_machine_controller.state_b import StateB + + +class Controller: + def __init__(self): + self.state = StateA() + + def on_event(self, event): + match event: + case "to_a": + self.state = StateA() + case "to_b": + self.state = StateB() + case _: + self.state = self.state diff --git a/python-circular-import/state-machine/state_machine_controller/state_a.py b/python-circular-import/state-machine/state_machine_controller/state_a.py new file mode 100644 index 0000000000..654e72b5aa --- /dev/null +++ b/python-circular-import/state-machine/state_machine_controller/state_a.py @@ -0,0 +1,2 @@ +class StateA: + pass diff --git a/python-circular-import/state-machine/state_machine_controller/state_b.py b/python-circular-import/state-machine/state_machine_controller/state_b.py new file mode 100644 index 0000000000..c575d4609e --- /dev/null +++ b/python-circular-import/state-machine/state_machine_controller/state_b.py @@ -0,0 +1,2 @@ +class StateB: + pass diff --git a/python-circular-import/state-machine/state_machine_module/__init__.py b/python-circular-import/state-machine/state_machine_module/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/state-machine/state_machine_module/__main__.py b/python-circular-import/state-machine/state_machine_module/__main__.py new file mode 100644 index 0000000000..e2af8cbf20 --- /dev/null +++ b/python-circular-import/state-machine/state_machine_module/__main__.py @@ -0,0 +1,5 @@ +from state_machine_module.states import StateA + +if __name__ == "__main__": + current_state = StateA() + current_state = current_state.on_event("to_b") diff --git a/python-circular-import/state-machine/state_machine_module/states.py b/python-circular-import/state-machine/state_machine_module/states.py new file mode 100644 index 0000000000..cbab856e2e --- /dev/null +++ b/python-circular-import/state-machine/state_machine_module/states.py @@ -0,0 +1,14 @@ +class StateA: + def on_event(self, event): + if event == "to_b": + return StateB() + else: + return self + + +class StateB: + def on_event(self, event): + if event == "to_a": + return StateA() + else: + return self diff --git a/python-circular-import/user-code/code_with_me/__init__.py b/python-circular-import/user-code/code_with_me/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-circular-import/user-code/code_with_me/__main__.py b/python-circular-import/user-code/code_with_me/__main__.py new file mode 100644 index 0000000000..2fd684f5b2 --- /dev/null +++ b/python-circular-import/user-code/code_with_me/__main__.py @@ -0,0 +1,15 @@ +from interface import create_instance + + +def main(): + # Create an instance of ImplementationA + implementation = create_instance("A") + implementation.do_something() # Output: Implementation A is doing something. + + # Create an instance of ImplementationB + implementation = create_instance("B") + implementation.do_something() # Output: Implementation B is doing something. + + +if __name__ == "__main__": + main() diff --git a/python-circular-import/user-code/code_with_me/implementation_a.py b/python-circular-import/user-code/code_with_me/implementation_a.py new file mode 100644 index 0000000000..4ff6882e14 --- /dev/null +++ b/python-circular-import/user-code/code_with_me/implementation_a.py @@ -0,0 +1,8 @@ +from interface import BaseInterface + + +class ImplementationA(BaseInterface): + """Concrete implementation A.""" + + def do_something(self): + print("Implementation A is doing something.") diff --git a/python-circular-import/user-code/code_with_me/implementation_b.py b/python-circular-import/user-code/code_with_me/implementation_b.py new file mode 100644 index 0000000000..b93b970c2e --- /dev/null +++ b/python-circular-import/user-code/code_with_me/implementation_b.py @@ -0,0 +1,8 @@ +from interface import BaseInterface + + +class ImplementationB(BaseInterface): + """Concrete implementation B.""" + + def do_something(self): + print("Implementation B is doing something.") diff --git a/python-circular-import/user-code/code_with_me/interface.py b/python-circular-import/user-code/code_with_me/interface.py new file mode 100644 index 0000000000..a198b98e38 --- /dev/null +++ b/python-circular-import/user-code/code_with_me/interface.py @@ -0,0 +1,23 @@ +from abc import ABC, abstractmethod + + +class BaseInterface(ABC): + """Abstract base class defining the interface.""" + + @abstractmethod + def do_something(self): + pass + + +def create_instance(name): + """Factory method to create instances of concrete implementations.""" + if name == "A": + from implementation_a import ImplementationA + + return ImplementationA() + elif name == "B": + from implementation_b import ImplementationB + + return ImplementationB() + else: + raise ValueError(f"Unknown implementation '{name}'") diff --git a/python-circular-import/user-code/pyproject.toml b/python-circular-import/user-code/pyproject.toml new file mode 100644 index 0000000000..e69de29bb2