11
11
from astacus .node .snapshot import Snapshot
12
12
from astacus .node .snapshotter import Snapshotter
13
13
from contextlib import closing
14
- from fnmatch import fnmatch
15
14
from pathlib import Path
16
15
from typing import Iterable , Sequence
17
16
from typing_extensions import override
18
17
18
+ import logging
19
19
import os
20
20
import sqlite3
21
21
22
+ logger = logging .getLogger (__name__ )
23
+
22
24
23
25
class SQLiteSnapshot (Snapshot ):
24
26
def __init__ (self , dst : Path , db : Path ) -> None :
@@ -107,15 +109,17 @@ def __init__(
107
109
108
110
def perform_snapshot (self , * , progress : Progress ) -> None :
109
111
files = self ._list_files_and_create_directories ()
110
- new_or_existing = self ._compare_current_snapshot (files )
111
- for_upsert = self ._compare_with_src (new_or_existing )
112
- with_digests = self ._compute_digests (for_upsert )
113
- self ._upsert_files (with_digests )
114
- self ._con .execute ("drop table if exists new_files;" )
115
- self ._con .commit ()
112
+ with self ._con :
113
+ self ._con .execute ("begin" )
114
+ new_or_existing = self ._compare_current_snapshot (files )
115
+ for_upsert = self ._compare_with_src (new_or_existing )
116
+ with_digests = self ._compute_digests (for_upsert )
117
+ self ._upsert_files (with_digests )
118
+ self ._con .execute ("drop table if exists new_files;" )
116
119
117
120
def _list_files_and_create_directories (self ) -> Iterable [Path ]:
118
121
"""List all files, and create directories in src."""
122
+ logger .info ("Listing files in %s" , self ._src )
119
123
for dir_ , _ , files in os .walk (self ._src ):
120
124
dir_path = Path (dir_ )
121
125
if any (parent .name == magic .ASTACUS_TMPDIR for parent in dir_path .parents ):
@@ -124,15 +128,8 @@ def _list_files_and_create_directories(self) -> Iterable[Path]:
124
128
(self ._dst / rel_dir ).mkdir (parents = True , exist_ok = True )
125
129
for f in files :
126
130
rel_path = rel_dir / f
127
- full_path = dir_path / f
128
- if full_path .is_symlink ():
129
- continue
130
- for group in self ._groups :
131
- # fnmatch works strangely with paths until 3.13 so convert to string
132
- # https://github.com/python/cpython/issues/73435
133
- if fnmatch (str (rel_path ), group .root_glob ) and f not in group .excluded_names :
134
- yield rel_path
135
- break
131
+ if not (dir_path / f ).is_symlink () and self ._groups .any_match (rel_path ):
132
+ yield rel_path
136
133
137
134
def _compare_current_snapshot (self , files : Iterable [Path ]) -> Iterable [tuple [Path , SnapshotFile | None ]]:
138
135
with closing (self ._con .cursor ()) as cur :
@@ -164,9 +161,10 @@ def _compare_current_snapshot(self, files: Iterable[Path]) -> Iterable[tuple[Pat
164
161
"""
165
162
)
166
163
if not self ._same_root_mode ():
164
+ logger .info ("Deleting files in %s that are not in current snapshot" , self ._dst )
167
165
for (relative_path ,) in cur :
168
- os . unlink ( self . _dst / relative_path )
169
- self ._con . commit ( )
166
+ assert isinstance ( relative_path , str )
167
+ ( self ._dst / relative_path ). unlink ( missing_ok = True )
170
168
cur .execute (
171
169
"""
172
170
insert into new_files
@@ -201,13 +199,18 @@ def _compare_current_snapshot(self, files: Iterable[Path]) -> Iterable[tuple[Pat
201
199
yield Path (row [0 ]), row_to_snapshotfile (row )
202
200
203
201
def _compare_with_src (self , files : Iterable [tuple [Path , SnapshotFile | None ]]) -> Iterable [SnapshotFile ]:
202
+ logger .info ("Checking metadata for files in %s" , self ._dst )
204
203
for relpath , existing in files :
205
- new = self ._file_in_src (relpath )
206
- if existing is None or not existing .underlying_file_is_the_same (new ):
207
- self ._maybe_link (relpath )
208
- yield new
204
+ try :
205
+ new = self ._file_in_src (relpath )
206
+ if existing is None or not existing .underlying_file_is_the_same (new ):
207
+ self ._maybe_link (relpath )
208
+ yield new
209
+ except FileNotFoundError :
210
+ logger .warning ("File %s disappeared while snapshotting" , relpath )
209
211
210
212
def _upsert_files (self , files : Iterable [SnapshotFile ]) -> None :
213
+ logger .info ("Upserting files in snapshot db" )
211
214
self ._con .executemany (
212
215
"""
213
216
insert or replace
@@ -219,28 +222,31 @@ def _upsert_files(self, files: Iterable[SnapshotFile]) -> None:
219
222
)
220
223
221
224
def release (self , hexdigests : Iterable [str ], * , progress : Progress ) -> None :
222
- with closing (self ._con .cursor ()) as cur :
223
- cur .execute (
224
- """
225
- create temporary table hexdigests (
226
- hexdigest text not null
227
- );
228
- """
229
- )
230
- cur .executemany (
231
- "insert into hexdigests (hexdigest) values (?);" ,
232
- ((h ,) for h in hexdigests if h != "" ),
233
- )
234
- cur .execute (
235
- """
236
- select relative_path
237
- from snapshot_files
238
- where hexdigest in (select hexdigest from hexdigests);
239
- """
240
- )
241
- for (relative_path ,) in cur :
242
- (self ._dst / relative_path ).unlink (missing_ok = True )
243
- cur .execute ("drop table hexdigests;" )
225
+ with self ._con :
226
+ self ._con .execute ("begin" )
227
+ with closing (self ._con .cursor ()) as cur :
228
+ cur .execute (
229
+ """
230
+ create temporary table hexdigests (
231
+ hexdigest text not null
232
+ );
233
+ """
234
+ )
235
+ cur .executemany (
236
+ "insert into hexdigests (hexdigest) values (?);" ,
237
+ ((h ,) for h in hexdigests if h != "" ),
238
+ )
239
+ cur .execute (
240
+ """
241
+ select relative_path
242
+ from snapshot_files
243
+ where hexdigest in (select hexdigest from hexdigests);
244
+ """
245
+ )
246
+ for (relative_path ,) in cur :
247
+ assert isinstance (relative_path , str )
248
+ (self ._dst / relative_path ).unlink (missing_ok = True )
249
+ cur .execute ("drop table hexdigests;" )
244
250
245
251
246
252
def row_to_path_and_snapshotfile (row : tuple ) -> tuple [Path , SnapshotFile | None ]:
0 commit comments