2525from __future__ import absolute_import , print_function
2626
2727from collections import OrderedDict
28+ from functools import partial
2829import os
2930import sys
3031
@@ -74,16 +75,10 @@ class ScanCache(object):
7475 This is NOT thread-safe, but is multi-process safe.
7576 """
7677 def __init__ (self , cache_dir ):
77- fileutils .create_dir (cache_dir )
78-
79- # create a unique temp directory in cache_dir
80- self .cache_base_dir = fileutils .get_temp_dir (cache_dir , prefix = timeutils .time2tstamp () + '-' )
81-
82- # and subdirs for infos and scans caches
78+ self .cache_base_dir = cache_dir
79+ # subdirs for infos and scans caches
8380 self .cache_infos_dir = os .path .join (self .cache_base_dir , 'infos' )
84- fileutils .create_dir (self .cache_infos_dir )
8581 self .cache_scans_dir = os .path .join (self .cache_base_dir , 'scans' )
86- fileutils .create_dir (self .cache_scans_dir )
8782
8883 # workaround for https://github.com/grantjenks/python-diskcache/issues/32
8984 from diskcache import Disk
@@ -93,7 +88,7 @@ def __init__(self, directory, size_threshold, pickle_protocol):
9388 super (DiskWithNoHighPickleProtocol , self ).__init__ (directory , size_threshold , pickle_protocol )
9489 self ._protocol = 0
9590
96- # and finially cache instances
91+ # and finally cache instances
9792 from diskcache import Cache
9893 self .infos = Cache (self .cache_infos_dir , disk = DiskWithNoHighPickleProtocol )
9994 self .scans = Cache (self .cache_scans_dir , disk = DiskWithNoHighPickleProtocol )
@@ -112,29 +107,27 @@ def put_infos(self, path, file_infos):
112107 in file_infos has already been scanned or False otherwise.
113108 """
114109 self .infos .set (path , file_infos )
115- has_cached_details = self .scan_key (path , file_infos ) in self .scans
110+ is_scan_cached = self .scan_key (path , file_infos ) in self .scans
116111 if TRACE :
117- logger_debug ('put_infos:' , 'path:' , path , 'has_cached_details :' , has_cached_details , 'file_infos:' , file_infos , '\n ' )
112+ logger_debug ('put_infos:' , 'path:' , path , 'is_scan_cached :' , is_scan_cached , 'file_infos:' , file_infos , '\n ' )
118113 logger_debug ('put_infos:' , 'cached_infos:' , self .infos [path ], '\n ' )
119-
120- return has_cached_details
114+ return is_scan_cached
121115
122116 def put_scan (self , path , file_infos , scan_result ):
123117 """
124- Put scan_result in the cache. Also put file_infos in the cache if needed .
118+ Put scan_result in the cache if not already cached .
125119 """
126- is_cached = self .put_infos (path , file_infos )
127- if not is_cached :
128- scan_key = self .scan_key (path , file_infos )
129- self .scans .add (scan_key , scan_result )
130- if TRACE :
131- logger_debug ('put_scan:' , 'scan_key:' , scan_key , 'file_infos:' , file_infos , 'scan_result:' , scan_result , '\n ' )
132- logger_debug ('put_scan:' , 'cached_infos:' , self .infos [path ], '\n ' )
133- logger_debug ('put_scan:' , 'scan_key:' , scan_key , 'cached_scan:' , self .scans [scan_key ], '\n ' )
120+ scan_key = self .scan_key (path , file_infos )
121+ self .scans .add (scan_key , scan_result )
122+ if TRACE :
123+ logger_debug ('put_scan:' , 'scan_key:' , scan_key , 'file_infos:' , file_infos , 'scan_result:' , scan_result , '\n ' )
124+ logger_debug ('put_scan:' , 'cached_infos:' , self .infos [path ], '\n ' )
125+ logger_debug ('put_scan:' , 'scan_key:' , scan_key , 'cached_scan:' , self .scans [scan_key ], '\n ' )
134126
135127 def iterate (self , with_infos = True ):
136128 """
137- Return an iterator of scan data for all cached scans e.g. the whole cache.
129+ Yield scan data for all cached scans e.g. the whole cache.
130+ If a scan is missing for a given info, an error is appended to scan_errors.
138131 """
139132 for path in self .infos :
140133 file_infos = self .infos [path ]
@@ -144,28 +137,55 @@ def iterate(self, with_infos=True):
144137 # we flatten these as direct attributes of a file object
145138 scan_result .update (file_infos .items ())
146139 else :
147- # always report errors
140+ # always include errors even if empty
148141 scan_result ['scan_errors' ] = file_infos .get ('scan_errors' , [])
149142
143+ no_scan_details = dict (scan_errors = [
144+ ('ERROR: Requested scan details unavailable in cache.' ,
145+ 'This is either a bug or processing was aborted with CTRL-C.' )]
146+ )
147+
150148 scan_key = self .scan_key (path , file_infos )
151- scan_details = self .scans [scan_key ].items ()
152- for scan_name , scan_data in scan_details :
153- if scan_name == 'scan_errors' :
154- scan_result ['scan_errors' ].extend (scan_data )
155- else :
156- scan_result [scan_name ] = scan_data
149+ scan_details = self .scans .get (scan_key , no_scan_details )
157150 if TRACE :
158151 logger_debug ('iterate:' , 'scan_details:' , scan_details , 'for path:' , path , 'scan_key:' , scan_key , '\n ' )
152+
153+ # append errors to other top level errors if any
154+ scan_errors = scan_details .pop ('scan_errors' , [])
155+ scan_result ['scan_errors' ].extend (scan_errors )
156+
157+ scan_result .update (scan_details )
159158 yield scan_result
160159
160+ def close (self ):
161+ """
162+ Close the underlying caches.
163+ """
164+ if self .infos :
165+ self .infos .close ()
166+ if self .scans :
167+ self .scans .close ()
168+
161169 def clear (self , * args ):
162170 """
163171 Purge the cache by deleting the corresponding cached data files.
164172 """
165- self .infos .close ()
166- self .scans .close ()
173+ self .close ()
167174 fileutils .delete (self .cache_base_dir )
168175
169176
170- def get_scans_cache ():
171- return ScanCache (cache_dir = scans_cache_dir )
177+ def get_scans_cache (cache_dir = scans_cache_dir ):
178+ """
179+ Return a new unique persistent cache instance.
180+ """
181+ return ScanCache (cache_dir )
182+
183+
184+ def get_scans_cache_class (cache_dir = scans_cache_dir ):
185+ """
186+ Return a new unique persistent cache instance.
187+ """
188+ fileutils .create_dir (cache_dir )
189+ # create a unique temp directory in cache_dir
190+ cache_dir = fileutils .get_temp_dir (cache_dir , prefix = timeutils .time2tstamp () + '-' )
191+ return partial (ScanCache , cache_dir )
0 commit comments