22import itertools
33from collections import Mapping
44from .settings import config
5- from .errors import DataJointError
5+ from .errors import DataJointError , MissingExternalFile
66from .hash import uuid_from_buffer , uuid_from_file
77from .table import Table
88from .declare import EXTERNAL_TABLE_ROOT
99from . import s3
1010from .utils import safe_write , safe_copy
1111
1212CACHE_SUBFOLDING = (2 , 2 ) # (2, 2) means "0123456789abcd" will be saved as "01/23/0123456789abcd"
13+ SUPPORT_MIGRATED_BLOBS = True # support blobs migrated from datajoint 0.11.*
1314
1415
1516def subfold (name , folds ):
@@ -124,11 +125,21 @@ def fput(self, local_filepath):
124125 def peek (self , blob_hash , bytes_to_peek = 120 ):
125126 return self .get (blob_hash , size = bytes_to_peek )
126127
127- def get (self , blob_hash , size = - 1 ):
128+ def get (self , blob_hash , * , size = - 1 ):
128129 """
129130 get an object from external store.
130131 :param size: max number of bytes to retrieve. If size<0, retrieve entire blob
132+ :param explicit_path: if given, then use it as relative path rather than the path derived from
131133 """
134+
135+ def read_file (filepath , size ):
136+ try :
137+ with open (filepath , 'rb' ) as f :
138+ blob = f .read (size )
139+ except FileNotFoundError :
140+ raise MissingExternalFile ('Lost access to external blob %s.' % full_path ) from None
141+ return blob
142+
132143 if blob_hash is None :
133144 return None
134145
@@ -154,18 +165,33 @@ def get(self, blob_hash, size=-1):
154165 subfolders = os .path .join (* subfold (blob_hash .hex , self .spec ['subfolding' ]))
155166 full_path = os .path .join (self .spec ['location' ], self .database , subfolders , blob_hash .hex )
156167 try :
157- with open (full_path , 'rb' ) as f :
158- blob = f .read (size )
159- except FileNotFoundError :
160- raise DataJointError ('Lost access to external blob %s.' % full_path ) from None
168+ blob = read_file (full_path , size )
169+ except MissingExternalFile :
170+ if not SUPPORT_MIGRATED_BLOBS :
171+ raise
172+ # migrated blobs from 0.11
173+ relative_filepath , contents_hash = (self & {'hash' : blob_hash }).fetch1 (
174+ 'filepath' , 'contents_hash' )
175+ if relative_filepath is None :
176+ raise
177+ blob = read_file (os .path .join (self .spec ['location' ], relative_filepath ))
161178 else :
162179 if size > 0 :
163180 blob_size = os .path .getsize (full_path )
164181 elif self .spec ['protocol' ] == 's3' :
165182 full_path = '/' .join (
166183 (self .database ,) + subfold (blob_hash .hex , self .spec ['subfolding' ]) + (blob_hash .hex ,))
167184 if size < 0 :
168- blob = self .s3 .get (full_path )
185+ try :
186+ blob = self .s3 .get (full_path )
187+ except MissingExternalFile :
188+ if not SUPPORT_MIGRATED_BLOBS :
189+ raise
190+ relative_filepath , contents_hash = (self & {'hash' : blob_hash }).fetch1 (
191+ 'filepath' , 'contents_hash' )
192+ if relative_filepath is None :
193+ raise
194+ blob = self .s3 .get (relative_filepath )
169195 else :
170196 blob = self .s3 .partial_get (full_path , 0 , size )
171197 blob_size = self .s3 .get_size (full_path )
0 commit comments