Coverage for src/lexigram/features/module.py: 100%

18 statements  

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

1"""Feature flags module for dependency injection.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from lexigram.contracts.feature_flags import FlagProviderProtocol 

8from lexigram.di.module import DynamicModule, Module, module 

9from lexigram.features.di.provider import FeatureFlagsProvider 

10 

11 

12@module() 

13class FeatureFlagsModule(Module): 

14 """In-memory and extensible feature flag evaluation. 

15 

16 Call :meth:`configure` to seed initial flag state from a 

17 :class:`~lexigram.features.config.FeatureFlagsConfig`. 

18 

19 Usage:: 

20 

21 from lexigram.features.config import FeatureFlagsConfig 

22 

23 @module( 

24 imports=[ 

25 FeatureFlagsModule.configure( 

26 FeatureFlagsConfig(initial_flags={"beta_feature": True}) 

27 ) 

28 ] 

29 ) 

30 class AppModule(Module): 

31 pass 

32 """ 

33 

34 @classmethod 

35 def configure(cls, config: Any | None = None) -> DynamicModule: 

36 """Create a FeatureFlagsModule with explicit configuration. 

37 

38 Args: 

39 config: :class:`~lexigram.features.config.FeatureFlagsConfig` 

40 or ``None`` for framework defaults (all flags disabled). 

41 

42 Returns: 

43 A :class:`~lexigram.di.module.DynamicModule` descriptor. 

44 """ 

45 from lexigram.features.config import FeatureFlagsConfig 

46 

47 if config is not None and not isinstance(config, FeatureFlagsConfig): 

48 raise TypeError( 

49 f"config must be FeatureFlagsConfig, got {type(config).__name__}" 

50 ) 

51 

52 return DynamicModule( 

53 module=cls, 

54 providers=[FeatureFlagsProvider(config=config)], 

55 exports=[FlagProviderProtocol], 

56 ) 

57 

58 @classmethod 

59 def stub(cls, config: Any = None) -> DynamicModule: 

60 """Return an in-memory FeatureFlagsModule for testing. 

61 

62 Registers the feature flag provider with no flags enabled by 

63 default. Suitable for unit and integration tests. 

64 

65 Returns: 

66 A DynamicModule with all flags disabled. 

67 """ 

68 from lexigram.features.config import FeatureFlagsConfig 

69 

70 return DynamicModule( 

71 module=cls, 

72 providers=[FeatureFlagsProvider(config=FeatureFlagsConfig())], 

73 exports=[FlagProviderProtocol], 

74 ) 

75 

76 

77__all__ = ["FeatureFlagsModule"]