Coverage for fastblocks/adapters/sitemap/cached.py: 0%

78 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-21 04:50 -0700

1"""FastBlocks Cached Sitemap Adapter. 

2 

3Heavy caching wrapper for sitemap generation with background refresh 

4and persistent storage for high-performance sitemap serving. 

5""" 

6 

7import asyncio 

8import typing as t 

9from contextlib import suppress 

10from uuid import UUID 

11 

12from acb.adapters import AdapterStatus 

13from acb.debug import debug 

14from acb.depends import depends 

15 

16from ._base import SitemapBase, SitemapBaseSettings 

17from .core import BaseSitemap, SitemapApp 

18 

19 

20class CachedSitemapSettings(SitemapBaseSettings): 

21 pass 

22 

23 

24class CachedSitemap(BaseSitemap[str], SitemapBase): 

25 sitemap: SitemapApp | None = None 

26 _background_task: asyncio.Task[t.Any] | None = None 

27 

28 def __init__(self) -> None: 

29 super().__init__() 

30 self._underlying_adapter = None 

31 

32 @depends.inject 

33 def items(self) -> list[str]: 

34 try: 

35 routes_adapter = depends.get("routes") 

36 if hasattr(routes_adapter, "routes"): 

37 route_paths = [r.path for r in routes_adapter.routes] 

38 debug(f"CachedSitemap: Cached {len(route_paths)} routes") 

39 return route_paths 

40 return [] 

41 except Exception as e: 

42 debug(f"CachedSitemap: Error getting routes: {e}") 

43 return [] 

44 

45 def location(self, item: str) -> str: 

46 return item 

47 

48 def changefreq(self, item: str) -> str: 

49 return self.config.change_freq 

50 

51 def priority(self, item: str) -> float: 

52 if item == "/": 

53 return 1.0 

54 segments = len([s for s in item.split("/") if s]) 

55 if segments == 1: 

56 return 0.8 

57 elif segments == 2: 

58 return 0.6 

59 else: 

60 return 0.4 

61 

62 async def _background_refresh(self) -> None: 

63 strategy_options = self.config.strategy_options 

64 background_refresh = strategy_options.get("background_refresh", True) 

65 if not background_refresh: 

66 return 

67 debug("CachedSitemap: Starting background refresh task") 

68 while True: 

69 try: 

70 await asyncio.sleep(self.config.cache_ttl) 

71 debug("CachedSitemap: Refreshing sitemap cache") 

72 except asyncio.CancelledError: 

73 debug("CachedSitemap: Background refresh cancelled") 

74 break 

75 except Exception as e: 

76 debug(f"CachedSitemap: Background refresh error: {e}") 

77 await asyncio.sleep(300) 

78 

79 async def init(self) -> None: 

80 if not self.config.domain: 

81 msg = "domain must be set in sitemap settings" 

82 raise ValueError(msg) 

83 extended_ttl = self.config.cache_ttl * 2 

84 self.sitemap = SitemapApp( 

85 self, 

86 domain=self.config.domain, 

87 cache_ttl=extended_ttl, 

88 ) 

89 strategy_options = self.config.strategy_options 

90 if strategy_options.get("background_refresh", True): 

91 self._background_task = asyncio.create_task(self._background_refresh()) 

92 debug(f"CachedSitemap: Initialized with domain={self.config.domain}") 

93 

94 async def cleanup(self) -> None: 

95 if self._background_task: 

96 self._background_task.cancel() 

97 try: 

98 await self._background_task 

99 except asyncio.CancelledError: 

100 pass 

101 

102 

103Sitemap = CachedSitemap 

104 

105MODULE_ID = UUID("01937d86-cfa2-73b4-0675-901234567823") 

106MODULE_STATUS = AdapterStatus.STABLE 

107 

108with suppress(Exception): 

109 depends.set(Sitemap)