Coverage for src/prosemark/freewriting/ports/file_system.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-09-24 18:08 +0000

1"""File system port interface for freewriting feature. 

2 

3This module defines the port interface for file system operations 

4required by the freewriting functionality. 

5""" 

6 

7from __future__ import annotations 

8 

9from abc import ABC, abstractmethod 

10from pathlib import Path 

11 

12 

13class FileSystemPort(ABC): 

14 """Port interface for file system operations. 

15 

16 This port defines the contract for low-level file operations 

17 that the freewriting feature needs, including reading, writing, 

18 and directory management. 

19 """ 

20 

21 @abstractmethod 

22 def write_file(self, file_path: str, content: str, append: bool = False) -> None: # noqa: FBT001, FBT002 

23 """Write content to file. 

24 

25 Args: 

26 file_path: Target file path (should be absolute). 

27 content: Content to write. 

28 append: Whether to append (True) or overwrite (False). 

29 

30 Raises: 

31 FileSystemError: If write operation fails. 

32 

33 """ 

34 

35 @abstractmethod 

36 def read_file(self, file_path: str) -> str: 

37 """Read content from file. 

38 

39 Args: 

40 file_path: Path to file to read. 

41 

42 Returns: 

43 File content as string. 

44 

45 Raises: 

46 FileSystemError: If read operation fails. 

47 

48 """ 

49 

50 @abstractmethod 

51 def file_exists(self, file_path: str) -> bool: 

52 """Check if file exists. 

53 

54 Args: 

55 file_path: Path to check. 

56 

57 Returns: 

58 True if file exists, False otherwise. 

59 

60 """ 

61 

62 @abstractmethod 

63 def create_directory(self, directory_path: str, parents: bool = True) -> None: # noqa: FBT001, FBT002 

64 """Create directory if it doesn't exist. 

65 

66 Args: 

67 directory_path: Path to directory to create. 

68 parents: Whether to create parent directories if they don't exist. 

69 

70 Raises: 

71 FileSystemError: If directory creation fails. 

72 

73 """ 

74 

75 @abstractmethod 

76 def get_current_directory(self) -> str: 

77 """Get current working directory. 

78 

79 Returns: 

80 Absolute path to current directory. 

81 

82 """ 

83 

84 @abstractmethod 

85 def is_writable(self, directory_path: str) -> bool: 

86 """Check if directory is writable. 

87 

88 Args: 

89 directory_path: Directory to check. 

90 

91 Returns: 

92 True if writable, False otherwise. 

93 

94 """ 

95 

96 @abstractmethod 

97 def get_absolute_path(self, path: str) -> str: 

98 """Convert path to absolute path. 

99 

100 Args: 

101 path: Path to convert (can be relative or absolute). 

102 

103 Returns: 

104 Absolute path string. 

105 

106 """ 

107 

108 @abstractmethod 

109 def join_paths(self, *paths: str) -> str: 

110 """Join multiple path components into a single path. 

111 

112 Args: 

113 *paths: Path components to join. 

114 

115 Returns: 

116 Joined path string. 

117 

118 """ 

119 

120 @abstractmethod 

121 def get_file_size(self, file_path: str) -> int: 

122 """Get size of file in bytes. 

123 

124 Args: 

125 file_path: Path to file. 

126 

127 Returns: 

128 File size in bytes. 

129 

130 Raises: 

131 FileSystemError: If file doesn't exist or size cannot be determined. 

132 

133 """ 

134 

135 @abstractmethod 

136 def backup_file(self, file_path: str, backup_suffix: str = '.bak') -> str: 

137 """Create backup copy of file. 

138 

139 Args: 

140 file_path: Path to file to backup. 

141 backup_suffix: Suffix to add to backup file name. 

142 

143 Returns: 

144 Path to backup file. 

145 

146 Raises: 

147 FileSystemError: If backup creation fails. 

148 

149 """ 

150 

151 @abstractmethod 

152 def ensure_parent_directory(self, file_path: str) -> None: 

153 """Ensure parent directory of file exists. 

154 

155 Args: 

156 file_path: Path to file whose parent directory should exist. 

157 

158 Raises: 

159 FileSystemError: If parent directory cannot be created. 

160 

161 """ 

162 

163 @staticmethod 

164 def resolve_path(path: str) -> Path: # pragma: no cover 

165 """Resolve path to pathlib.Path object. 

166 

167 Args: 

168 path: Path string to resolve. 

169 

170 Returns: 

171 Resolved Path object. 

172 

173 """ 

174 return Path(path).resolve()