mcp-code-indexer 3.1.4__py3-none-any.whl → 3.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,6 @@ and manual cleanup methods.
9
9
  import logging
10
10
  import time
11
11
  from typing import List, Optional
12
- from pathlib import Path
13
12
 
14
13
  logger = logging.getLogger(__name__)
15
14
 
@@ -17,200 +16,213 @@ logger = logging.getLogger(__name__)
17
16
  class CleanupManager:
18
17
  """
19
18
  Manages cleanup operations for file descriptions with retention policies.
20
-
19
+
21
20
  Handles soft deletion by updating to_be_cleaned timestamps and provides
22
21
  periodic cleanup to permanently remove old records after the retention period.
23
22
  """
24
-
23
+
25
24
  def __init__(self, db_manager, retention_months: int = 6):
26
25
  """
27
26
  Initialize cleanup manager.
28
-
27
+
29
28
  Args:
30
29
  db_manager: DatabaseManager instance
31
- retention_months: Number of months to retain records before permanent deletion
30
+ retention_months: Number of months to retain records before
31
+ permanent deletion
32
32
  """
33
33
  self.db_manager = db_manager
34
34
  self.retention_months = retention_months
35
-
35
+
36
36
  async def mark_file_for_cleanup(self, project_id: str, file_path: str) -> bool:
37
37
  """
38
38
  Mark a specific file for cleanup by setting to_be_cleaned timestamp.
39
-
39
+
40
40
  Args:
41
41
  project_id: Project identifier
42
42
  file_path: Path to file to mark for cleanup
43
-
43
+
44
44
  Returns:
45
45
  True if file was marked, False if file not found
46
46
  """
47
47
  cleanup_timestamp = int(time.time())
48
-
49
- async with self.db_manager.get_write_connection_with_retry("mark_file_for_cleanup") as db:
48
+
49
+ async with self.db_manager.get_write_connection_with_retry(
50
+ "mark_file_for_cleanup"
51
+ ) as db:
50
52
  cursor = await db.execute(
51
53
  """
52
- UPDATE file_descriptions
53
- SET to_be_cleaned = ?
54
+ UPDATE file_descriptions
55
+ SET to_be_cleaned = ?
54
56
  WHERE project_id = ? AND file_path = ? AND to_be_cleaned IS NULL
55
57
  """,
56
- (cleanup_timestamp, project_id, file_path)
58
+ (cleanup_timestamp, project_id, file_path),
57
59
  )
58
60
  await db.commit()
59
-
61
+
60
62
  # Check if any rows were affected
61
63
  return cursor.rowcount > 0
62
-
63
- async def mark_files_for_cleanup(self, project_id: str, file_paths: List[str]) -> int:
64
+
65
+ async def mark_files_for_cleanup(
66
+ self, project_id: str, file_paths: List[str]
67
+ ) -> int:
64
68
  """
65
69
  Mark multiple files for cleanup in a batch operation.
66
-
70
+
67
71
  Args:
68
72
  project_id: Project identifier
69
73
  file_paths: List of file paths to mark for cleanup
70
-
74
+
71
75
  Returns:
72
76
  Number of files marked for cleanup
73
77
  """
74
78
  if not file_paths:
75
79
  return 0
76
-
80
+
77
81
  cleanup_timestamp = int(time.time())
78
-
82
+
79
83
  async def batch_operation(conn):
80
84
  data = [(cleanup_timestamp, project_id, path) for path in file_paths]
81
85
  cursor = await conn.executemany(
82
86
  """
83
- UPDATE file_descriptions
84
- SET to_be_cleaned = ?
87
+ UPDATE file_descriptions
88
+ SET to_be_cleaned = ?
85
89
  WHERE project_id = ? AND file_path = ? AND to_be_cleaned IS NULL
86
90
  """,
87
- data
91
+ data,
88
92
  )
89
93
  return cursor.rowcount
90
-
94
+
91
95
  marked_count = await self.db_manager.execute_transaction_with_retry(
92
96
  batch_operation,
93
97
  f"mark_files_for_cleanup_{len(file_paths)}_files",
94
- timeout_seconds=30.0
98
+ timeout_seconds=30.0,
95
99
  )
96
-
100
+
97
101
  logger.info(f"Marked {marked_count} files for cleanup in project {project_id}")
98
102
  return marked_count
99
-
103
+
100
104
  async def restore_file_from_cleanup(self, project_id: str, file_path: str) -> bool:
101
105
  """
102
106
  Restore a file from cleanup by clearing its to_be_cleaned timestamp.
103
-
107
+
104
108
  Args:
105
109
  project_id: Project identifier
106
110
  file_path: Path to file to restore
107
-
111
+
108
112
  Returns:
109
113
  True if file was restored, False if file not found
110
114
  """
111
- async with self.db_manager.get_write_connection_with_retry("restore_file_from_cleanup") as db:
115
+ async with self.db_manager.get_write_connection_with_retry(
116
+ "restore_file_from_cleanup"
117
+ ) as db:
112
118
  cursor = await db.execute(
113
119
  """
114
- UPDATE file_descriptions
115
- SET to_be_cleaned = NULL
120
+ UPDATE file_descriptions
121
+ SET to_be_cleaned = NULL
116
122
  WHERE project_id = ? AND file_path = ? AND to_be_cleaned IS NOT NULL
117
123
  """,
118
- (project_id, file_path)
124
+ (project_id, file_path),
119
125
  )
120
126
  await db.commit()
121
-
127
+
122
128
  return cursor.rowcount > 0
123
-
129
+
124
130
  async def get_files_to_be_cleaned(self, project_id: str) -> List[dict]:
125
131
  """
126
132
  Get list of files marked for cleanup in a project.
127
-
133
+
128
134
  Args:
129
135
  project_id: Project identifier
130
-
136
+
131
137
  Returns:
132
138
  List of dictionaries with file_path and to_be_cleaned timestamp
133
139
  """
134
140
  async with self.db_manager.get_connection() as db:
135
141
  cursor = await db.execute(
136
142
  """
137
- SELECT file_path, to_be_cleaned
138
- FROM file_descriptions
143
+ SELECT file_path, to_be_cleaned
144
+ FROM file_descriptions
139
145
  WHERE project_id = ? AND to_be_cleaned IS NOT NULL
140
146
  ORDER BY to_be_cleaned DESC, file_path
141
147
  """,
142
- (project_id,)
148
+ (project_id,),
143
149
  )
144
150
  rows = await cursor.fetchall()
145
-
151
+
146
152
  return [
147
153
  {
148
- 'file_path': row['file_path'],
149
- 'marked_for_cleanup': row['to_be_cleaned'],
150
- 'marked_date': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(row['to_be_cleaned']))
154
+ "file_path": row["file_path"],
155
+ "marked_for_cleanup": row["to_be_cleaned"],
156
+ "marked_date": time.strftime(
157
+ "%Y-%m-%d %H:%M:%S", time.localtime(row["to_be_cleaned"])
158
+ ),
151
159
  }
152
160
  for row in rows
153
161
  ]
154
-
162
+
155
163
  async def perform_cleanup(self, project_id: Optional[str] = None) -> int:
156
164
  """
157
165
  Permanently delete records that exceed the retention period.
158
-
166
+
159
167
  Args:
160
- project_id: If specified, only clean up this project. Otherwise clean all projects.
161
-
168
+ project_id: If specified, only clean up this project. Otherwise
169
+ clean all projects.
170
+
162
171
  Returns:
163
172
  Number of records permanently deleted
164
173
  """
165
174
  # Calculate cutoff timestamp (retention_months ago)
166
- cutoff_seconds = self.retention_months * 30 * 24 * 60 * 60 # Approximate months to seconds
175
+ cutoff_seconds = (
176
+ self.retention_months * 30 * 24 * 60 * 60
177
+ ) # Approximate months to seconds
167
178
  cutoff_timestamp = int(time.time()) - cutoff_seconds
168
-
179
+
169
180
  async def cleanup_operation(conn):
170
181
  if project_id:
171
182
  cursor = await conn.execute(
172
183
  """
173
- DELETE FROM file_descriptions
174
- WHERE project_id = ? AND to_be_cleaned IS NOT NULL AND to_be_cleaned < ?
184
+ DELETE FROM file_descriptions
185
+ WHERE project_id = ? AND to_be_cleaned IS NOT NULL
186
+ AND to_be_cleaned < ?
175
187
  """,
176
- (project_id, cutoff_timestamp)
188
+ (project_id, cutoff_timestamp),
177
189
  )
178
190
  else:
179
191
  cursor = await conn.execute(
180
192
  """
181
- DELETE FROM file_descriptions
193
+ DELETE FROM file_descriptions
182
194
  WHERE to_be_cleaned IS NOT NULL AND to_be_cleaned < ?
183
195
  """,
184
- (cutoff_timestamp,)
196
+ (cutoff_timestamp,),
185
197
  )
186
-
198
+
187
199
  return cursor.rowcount
188
-
200
+
189
201
  deleted_count = await self.db_manager.execute_transaction_with_retry(
190
202
  cleanup_operation,
191
203
  f"perform_cleanup_{project_id or 'all_projects'}",
192
- timeout_seconds=60.0
204
+ timeout_seconds=60.0,
193
205
  )
194
-
206
+
195
207
  if deleted_count > 0:
196
208
  scope = f"project {project_id}" if project_id else "all projects"
197
209
  logger.info(f"Permanently deleted {deleted_count} old records from {scope}")
198
-
210
+
199
211
  return deleted_count
200
-
212
+
201
213
  async def get_cleanup_stats(self, project_id: Optional[str] = None) -> dict:
202
214
  """
203
215
  Get statistics about cleanup state.
204
-
216
+
205
217
  Args:
206
218
  project_id: If specified, get stats for this project only
207
-
219
+
208
220
  Returns:
209
221
  Dictionary with cleanup statistics
210
222
  """
211
223
  cutoff_seconds = self.retention_months * 30 * 24 * 60 * 60
212
224
  cutoff_timestamp = int(time.time()) - cutoff_seconds
213
-
225
+
214
226
  async with self.db_manager.get_connection() as db:
215
227
  if project_id:
216
228
  base_where = "WHERE project_id = ?"
@@ -218,38 +230,52 @@ class CleanupManager:
218
230
  else:
219
231
  base_where = ""
220
232
  params = ()
221
-
233
+
222
234
  # Active files
223
235
  cursor = await db.execute(
224
- f"SELECT COUNT(*) FROM file_descriptions {base_where} AND to_be_cleaned IS NULL",
225
- params
236
+ (
237
+ f"SELECT COUNT(*) FROM file_descriptions {base_where} "
238
+ "AND to_be_cleaned IS NULL"
239
+ ),
240
+ params,
226
241
  )
227
242
  active_count = (await cursor.fetchone())[0]
228
-
243
+
229
244
  # Files marked for cleanup
230
245
  cursor = await db.execute(
231
- f"SELECT COUNT(*) FROM file_descriptions {base_where} AND to_be_cleaned IS NOT NULL",
232
- params
246
+ (
247
+ f"SELECT COUNT(*) FROM file_descriptions {base_where} "
248
+ "AND to_be_cleaned IS NOT NULL"
249
+ ),
250
+ params,
233
251
  )
234
252
  marked_count = (await cursor.fetchone())[0]
235
-
253
+
236
254
  # Files eligible for permanent deletion
237
255
  if project_id:
238
256
  cursor = await db.execute(
239
- "SELECT COUNT(*) FROM file_descriptions WHERE project_id = ? AND to_be_cleaned IS NOT NULL AND to_be_cleaned < ?",
240
- (project_id, cutoff_timestamp)
257
+ (
258
+ "SELECT COUNT(*) FROM file_descriptions WHERE project_id = ? "
259
+ "AND to_be_cleaned IS NOT NULL AND to_be_cleaned < ?"
260
+ ),
261
+ (project_id, cutoff_timestamp),
241
262
  )
242
263
  else:
243
264
  cursor = await db.execute(
244
- "SELECT COUNT(*) FROM file_descriptions WHERE to_be_cleaned IS NOT NULL AND to_be_cleaned < ?",
245
- (cutoff_timestamp,)
265
+ (
266
+ "SELECT COUNT(*) FROM file_descriptions WHERE "
267
+ "to_be_cleaned IS NOT NULL AND to_be_cleaned < ?"
268
+ ),
269
+ (cutoff_timestamp,),
246
270
  )
247
271
  eligible_for_deletion = (await cursor.fetchone())[0]
248
-
272
+
249
273
  return {
250
- 'active_files': active_count,
251
- 'marked_for_cleanup': marked_count,
252
- 'eligible_for_deletion': eligible_for_deletion,
253
- 'retention_months': self.retention_months,
254
- 'cutoff_date': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(cutoff_timestamp))
274
+ "active_files": active_count,
275
+ "marked_for_cleanup": marked_count,
276
+ "eligible_for_deletion": eligible_for_deletion,
277
+ "retention_months": self.retention_months,
278
+ "cutoff_date": time.strftime(
279
+ "%Y-%m-%d %H:%M:%S", time.localtime(cutoff_timestamp)
280
+ ),
255
281
  }