Coverage for src/lexigram/web/routing/controllers.py: 29%

21 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 04:37 +0800

1"""Controller base class with DI support""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7 

8class Controller: 

9 """Base controller class with dependency injection support. 

10 

11 Controllers group related route handlers and can specify dependencies 

12 to be injected by the DI container. 

13 

14 Example: 

15 class UserController(Controller): 

16 @get("/users") 

17 async def list_users(self): 

18 return [] 

19 """ 

20 

21 def __init__(self) -> None: 

22 # Dependencies will be injected by DI container 

23 pass 

24 

25 @classmethod 

26 def collect_routes(cls) -> list[dict[str, Any]]: 

27 """Collect routes from controller methods. 

28 

29 It scans the class and its base classes for methods with 

30 _route_config attribute and returns a list of route definitions. 

31 

32 Returns: 

33 List of route dictionaries with keys: method, path, handler_name, etc. 

34 """ 

35 routes = [] 

36 seen_handlers = set() 

37 

38 # Collect from MRO (Method Resolution Order) - base classes first 

39 for klass in cls.__mro__: 

40 if klass is Controller or klass is object: 

41 continue 

42 

43 for attr_name in dir(klass): 

44 if attr_name.startswith("_") or attr_name in seen_handlers: 

45 continue 

46 

47 attr_value = getattr(klass, attr_name, None) 

48 if attr_value is not None and hasattr(attr_value, "_route_config"): 

49 route_config = attr_value._route_config 

50 routes.append( 

51 { 

52 "method": route_config["method"], 

53 "path": route_config["path"], 

54 "handler_name": attr_name, 

55 "response_model": route_config.get("response_model"), 

56 "request_model": route_config.get("request_model"), 

57 "status_code": route_config.get("status_code", 200), 

58 "summary": route_config.get("summary"), 

59 "description": route_config.get("description"), 

60 "tags": route_config.get("tags"), 

61 "operation_id": route_config.get("operation_id"), 

62 "responses": route_config.get("responses"), 

63 "deprecated": route_config.get("deprecated", False), 

64 }, 

65 ) 

66 seen_handlers.add(attr_name) 

67 

68 return routes