Coverage for frappe_manager / docker / __init__.py: 73%

48 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-07-02 18:13 +0530

1""" 

2Docker module for Frappe Manager. 

3 

4This module provides unified Docker functionality including: 

5- Docker Compose file editing and building (ComposeFile) 

6- Docker client wrapper (DockerClient) 

7- Docker Compose CLI wrapper (DockerComposeWrapper) 

8- Volume mount utilities (DockerVolumeMount, DockerVolumeType) 

9""" 

10 

11from dataclasses import dataclass 

12from enum import Enum 

13from pathlib import Path 

14from typing import Union 

15 

16from frappe_manager import CLI_SERVICES_DIRECTORY 

17 

18 

19class DockerVolumeType(str, Enum): 

20 volume = "volume" 

21 bind = "bind" 

22 

23 

24class DockerVolumeMount: 

25 def __init__(self, host: str | Path, container: str, type: str, compose_path: Path): 

26 self.host = host 

27 self.type = type 

28 self.container = Path(container) 

29 self.compose_path = compose_path 

30 

31 if type == DockerVolumeType.bind: 

32 self.host = Path(self.host) 

33 # only join ./ paths 

34 if str(host).startswith("./"): 

35 self.host = compose_path.parent.joinpath(host) 

36 

37 def __str__(self): 

38 source = Path(self.host) if not isinstance(self.host, Path) else self.host 

39 

40 if self.type == "bind": 

41 source = str(self.host).replace(str(self.compose_path.parent), ".") 

42 

43 dest = str(self.container) 

44 return f"{source}:{dest}" 

45 

46 

47# For convenient imports: from frappe_manager.docker import ComposeFile, DockerClient, etc. 

48# These use lazy imports to avoid circular dependency issues 

49def __getattr__(name): 

50 """Lazy import to avoid circular dependencies.""" 

51 if name == "ComposeFile": 

52 from frappe_manager.docker.compose_file import ComposeFile 

53 

54 return ComposeFile 

55 if name == "ComposeSecretNotFoundError": 

56 from frappe_manager.docker.compose_exceptions import ComposeSecretNotFoundError 

57 

58 return ComposeSecretNotFoundError 

59 if name == "ComposeServiceNotFound": 

60 from frappe_manager.docker.compose_exceptions import ComposeServiceNotFound 

61 

62 return ComposeServiceNotFound 

63 if name == "DockerClient": 

64 from frappe_manager.docker.docker_client import DockerClient 

65 

66 return DockerClient 

67 if name == "DockerComposeWrapper": 

68 from frappe_manager.docker.docker_compose import DockerComposeWrapper 

69 

70 return DockerComposeWrapper 

71 if name == "DockerException": 

72 from frappe_manager.docker.docker_exceptions import DockerException 

73 

74 return DockerException 

75 if name == "SubprocessOutput": 

76 from frappe_manager.docker.subprocess_output import SubprocessOutput 

77 

78 return SubprocessOutput 

79 raise AttributeError(f"module '{__name__}' has no attribute '{name}'") 

80 

81 

82__all__ = [ 

83 # Compose file management 

84 "ComposeFile", 

85 "ComposeSecretNotFoundError", 

86 "ComposeServiceNotFound", 

87 # Docker wrappers 

88 "DockerClient", 

89 "DockerComposeWrapper", 

90 "DockerException", 

91 "SubprocessOutput", 

92 # Volume utilities 

93 "DockerVolumeMount", 

94 "DockerVolumeType", 

95]