Coverage for mcpgateway / tools / builder / factory.py: 100%

31 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-02-11 07:10 +0000

1# -*- coding: utf-8 -*- 

2"""Location: ./mcpgateway/tools/builder/factory.py 

3Copyright 2025 

4SPDX-License-Identifier: Apache-2.0 

5Authors: Teryl Taylor 

6 

7Factory for creating MCP Stack deployment implementations. 

8 

9This module provides a factory pattern for creating the appropriate deployment 

10implementation (Dagger or Plain Python) based on availability and user preference. 

11 

12The factory handles graceful fallback from Dagger to Python if dependencies are 

13unavailable, ensuring the deployment system works in various environments. 

14 

15Example: 

16 >>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False) 

17 ⚠ Dagger not installed. Using plain python. 

18 >>> # Validate configuration (output varies by config) 

19 >>> # deployer.validate("mcp-stack.yaml") 

20""" 

21 

22# Standard 

23from enum import Enum 

24 

25# Third-Party 

26from rich.console import Console 

27 

28# First-Party 

29from mcpgateway.tools.builder.pipeline import CICDModule 

30 

31 

32class CICDTypes(str, Enum): 

33 """Deployment implementation types. 

34 

35 Attributes: 

36 DAGGER: Dagger-based implementation (optimal performance) 

37 PYTHON: Plain Python implementation (fallback, no dependencies) 

38 

39 Examples: 

40 >>> # Test enum values 

41 >>> CICDTypes.DAGGER.value 

42 'dagger' 

43 >>> CICDTypes.PYTHON.value 

44 'python' 

45 

46 >>> # Test enum comparison 

47 >>> CICDTypes.DAGGER == "dagger" 

48 True 

49 >>> CICDTypes.PYTHON == "python" 

50 True 

51 

52 >>> # Test enum membership 

53 >>> "dagger" in [t.value for t in CICDTypes] 

54 True 

55 >>> "python" in [t.value for t in CICDTypes] 

56 True 

57 

58 >>> # Test enum iteration 

59 >>> types = list(CICDTypes) 

60 >>> len(types) 

61 2 

62 >>> CICDTypes.DAGGER in types 

63 True 

64 """ 

65 

66 DAGGER = "dagger" 

67 PYTHON = "python" 

68 

69 

70console = Console() 

71 

72 

73class DeployFactory: 

74 """Factory for creating MCP Stack deployment implementations. 

75 

76 This factory implements the Strategy pattern, allowing dynamic selection 

77 between Dagger and Python implementations based on availability. 

78 """ 

79 

80 @staticmethod 

81 def create_deployer(deployer: str, verbose: bool = False) -> tuple[CICDModule, CICDTypes]: 

82 """Create a deployment implementation instance. 

83 

84 Attempts to load the requested deployer type with automatic fallback 

85 to Python implementation if dependencies are missing. 

86 

87 Args: 

88 deployer: Deployment type to create ("dagger" or "python") 

89 verbose: Enable verbose logging during creation 

90 

91 Returns: 

92 tuple: (deployer_instance, actual_type) 

93 - deployer_instance: Instance of MCPStackDagger or MCPStackPython 

94 - actual_type: CICDTypes enum indicating which implementation was loaded 

95 

96 Raises: 

97 RuntimeError: If no implementation can be loaded (critical failure) 

98 

99 Example: 

100 >>> # Try to load Dagger, fall back to Python if unavailable 

101 >>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False) 

102 ⚠ Dagger not installed. Using plain python. 

103 >>> if mode == CICDTypes.DAGGER: 

104 ... print("Using optimized Dagger implementation") 

105 ... else: 

106 ... print("Using fallback Python implementation") 

107 Using fallback Python implementation 

108 """ 

109 # Attempt to load Dagger implementation first if requested 

110 if deployer == "dagger": 

111 try: 

112 # First-Party 

113 from mcpgateway.tools.builder.dagger_deploy import DAGGER_AVAILABLE, MCPStackDagger 

114 

115 # Check if dagger is actually available (not just the module) 

116 if not DAGGER_AVAILABLE: 

117 raise ImportError("Dagger SDK not installed") 

118 

119 if verbose: 

120 console.print("[green]✓ Dagger module loaded[/green]") 

121 

122 return (MCPStackDagger(verbose), CICDTypes.DAGGER) 

123 

124 except ImportError: 

125 # Dagger dependencies not available, fall back to Python 

126 console.print("[yellow]⚠ Dagger not installed. Using plain python.[/yellow]") 

127 

128 # Load plain Python implementation (fallback or explicitly requested) 

129 try: 

130 # First-Party 

131 from mcpgateway.tools.builder.python_deploy import MCPStackPython 

132 

133 if verbose and deployer != "dagger": 

134 console.print("[blue]Using plain Python implementation[/blue]") 

135 

136 return (MCPStackPython(verbose), CICDTypes.PYTHON) 

137 

138 except ImportError as e: 

139 # Critical failure - neither implementation can be loaded 

140 console.print("[red]✗ ERROR: Cannot import deployment modules[/red]") 

141 console.print(f"[red] Details: {e}[/red]") 

142 console.print("[yellow] Make sure you're running from the project root[/yellow]") 

143 console.print("[yellow] and PYTHONPATH is set correctly[/yellow]") 

144 

145 # This should never be reached if PYTHONPATH is set correctly 

146 raise RuntimeError(f"Unable to load deployer of type '{deployer}'. ")