Coverage for src / local_deep_research / exporters / registry.py: 95%

34 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-25 01:07 +0000

1"""Exporter registry for document format discovery. 

2 

3This module provides the ExporterRegistry class that manages exporter 

4registration and lookup. Exporters can be registered using the @register 

5decorator for automatic discovery. 

6""" 

7 

8from typing import Dict, List, Optional, Type 

9 

10from loguru import logger 

11 

12from .base import BaseExporter 

13 

14 

15class ExporterRegistry: 

16 """Registry for document exporters. 

17 

18 This class manages the registration and lookup of exporter classes. 

19 Exporters can be registered using the @register class method as a decorator. 

20 

21 Example: 

22 @ExporterRegistry.register 

23 class PDFExporter(BaseExporter): 

24 ... 

25 

26 # Later, to get the exporter: 

27 exporter = ExporterRegistry.get_exporter("pdf") 

28 """ 

29 

30 _exporters: Dict[str, Type[BaseExporter]] = {} 

31 _instances: Dict[str, BaseExporter] = {} 

32 

33 @classmethod 

34 def register(cls, exporter_class: Type[BaseExporter]) -> Type[BaseExporter]: 

35 """Register an exporter class. 

36 

37 Can be used as a decorator: 

38 @ExporterRegistry.register 

39 class MyExporter(BaseExporter): 

40 ... 

41 

42 Args: 

43 exporter_class: The exporter class to register 

44 

45 Returns: 

46 The exporter class (unchanged), allowing use as decorator 

47 """ 

48 # Instantiate temporarily to get format_name 

49 instance = exporter_class() 

50 format_name = instance.format_name.lower() 

51 cls._exporters[format_name] = exporter_class 

52 logger.debug(f"Registered exporter for format: {format_name}") 

53 return exporter_class 

54 

55 @classmethod 

56 def get_exporter(cls, format_name: str) -> Optional[BaseExporter]: 

57 """Get an exporter instance for the given format. 

58 

59 Uses singleton pattern - returns cached instance if available. 

60 

61 Args: 

62 format_name: The format identifier (e.g., 'pdf', 'odt') 

63 

64 Returns: 

65 An exporter instance, or None if format not supported 

66 """ 

67 format_name = format_name.lower() 

68 

69 # Return cached instance if available 

70 if format_name in cls._instances: 

71 return cls._instances[format_name] 

72 

73 # Create new instance 

74 exporter_class = cls._exporters.get(format_name) 

75 if exporter_class: 

76 instance = exporter_class() 

77 cls._instances[format_name] = instance 

78 return instance 

79 

80 return None 

81 

82 @classmethod 

83 def get_available_formats(cls) -> List[str]: 

84 """Get list of available export formats. 

85 

86 Returns: 

87 List of format identifiers (e.g., ['pdf', 'odt', 'latex']) 

88 """ 

89 return list(cls._exporters.keys()) 

90 

91 @classmethod 

92 def is_format_supported(cls, format_name: str) -> bool: 

93 """Check if a format is supported. 

94 

95 Args: 

96 format_name: The format identifier to check 

97 

98 Returns: 

99 True if the format is supported, False otherwise 

100 """ 

101 return format_name.lower() in cls._exporters 

102 

103 @classmethod 

104 def clear(cls) -> None: 

105 """Clear all registered exporters. 

106 

107 Primarily useful for testing. 

108 """ 

109 cls._exporters.clear() 

110 cls._instances.clear()