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

77 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-09 00:47 -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): # type: ignore[override] 

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 async def items(self) -> t.Any: 

33 try: 

34 routes_adapter = depends.get("routes") 

35 if hasattr(routes_adapter, "routes"): 

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

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

38 return route_paths 

39 return [] 

40 except Exception as e: 

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

42 return [] 

43 

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

45 return item 

46 

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

48 return t.cast(str, self.config.change_freq) 

49 

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

51 if item == "/": 

52 return 1.0 

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

54 if segments == 1: 

55 return 0.8 

56 if segments == 2: 

57 return 0.6 

58 return 0.4 

59 

60 async def _background_refresh(self) -> None: 

61 strategy_options = self.config.strategy_options 

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

63 if not background_refresh: 

64 return 

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

66 while True: 

67 try: 

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

69 debug("CachedSitemap: Refreshing sitemap cache") 

70 except asyncio.CancelledError: 

71 debug("CachedSitemap: Background refresh cancelled") 

72 break 

73 except Exception as e: 

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

75 await asyncio.sleep(300) 

76 

77 async def init(self) -> None: 

78 if not self.config.domain: 

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

80 raise ValueError(msg) 

81 extended_ttl = self.config.cache_ttl * 2 

82 self.sitemap = SitemapApp( 

83 self, 

84 domain=self.config.domain, 

85 cache_ttl=extended_ttl, 

86 ) 

87 strategy_options = self.config.strategy_options 

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

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

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

91 

92 async def cleanup(self) -> None: 

93 if self._background_task: 

94 self._background_task.cancel() 

95 try: 

96 await self._background_task 

97 except asyncio.CancelledError: 

98 pass 

99 

100 

101Sitemap = CachedSitemap 

102 

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

104MODULE_STATUS = AdapterStatus.STABLE 

105 

106with suppress(Exception): 

107 depends.set(Sitemap)