| OLD | NEW |
| (Empty) |
| 1 # Copyright 2012 Google Inc. All Rights Reserved. | |
| 2 # | |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 # you may not use this file except in compliance with the License. | |
| 5 # You may obtain a copy of the License at | |
| 6 # | |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 # | |
| 9 # Unless required by applicable law or agreed to in writing, software | |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 # See the License for the specific language governing permissions and | |
| 13 # limitations under the License. | |
| 14 | |
| 15 import copy | |
| 16 import threading | |
| 17 import wildcard_iterator | |
| 18 | |
| 19 from bucket_listing_ref import BucketListingRef | |
| 20 from gslib.exception import CommandException | |
| 21 from gslib.plurality_checkable_iterator import PluralityCheckableIterator | |
| 22 from gslib.storage_uri_builder import StorageUriBuilder | |
| 23 from wildcard_iterator import ContainsWildcard | |
| 24 | |
| 25 """ | |
| 26 Name expansion support for the various ways gsutil lets users refer to | |
| 27 collections of data (via explicit wildcarding as well as directory, | |
| 28 bucket, and bucket subdir implicit wildcarding). This class encapsulates | |
| 29 the various rules for determining how these expansions are done. | |
| 30 """ | |
| 31 | |
| 32 | |
| 33 class NameExpansionResult(object): | |
| 34 """ | |
| 35 Holds one fully expanded result from iterating over NameExpansionIterator. | |
| 36 | |
| 37 The member data in this class need to be pickleable because | |
| 38 NameExpansionResult instances are passed through Multiprocessing.Queue. In | |
| 39 particular, don't include any boto state like StorageUri, since that pulls | |
| 40 in a big tree of objects, some of which aren't pickleable (and even if | |
| 41 they were, pickling/unpickling such a large object tree would result in | |
| 42 significant overhead). | |
| 43 | |
| 44 The state held in this object is needed for handling the various naming cases | |
| 45 (e.g., copying from a single source URI to a directory generates different | |
| 46 dest URI names than copying multiple URIs to a directory, to be consistent | |
| 47 with naming rules used by the Unix cp command). For more details see comments | |
| 48 in _NameExpansionIterator. | |
| 49 """ | |
| 50 | |
| 51 def __init__(self, src_uri_str, is_multi_src_request, | |
| 52 src_uri_expands_to_multi, names_container, expanded_uri_str, | |
| 53 have_existing_dst_container=None, is_latest=False): | |
| 54 """ | |
| 55 Args: | |
| 56 src_uri_str: string representation of StorageUri that was expanded. | |
| 57 is_multi_src_request: bool indicator whether src_uri_str expanded to more | |
| 58 than 1 BucketListingRef. | |
| 59 src_uri_expands_to_multi: bool indicator whether the current src_uri | |
| 60 expanded to more than 1 BucketListingRef. | |
| 61 names_container: Bool indicator whether src_uri names a container. | |
| 62 expanded_uri_str: string representation of StorageUri to which src_uri_str | |
| 63 expands. | |
| 64 have_existing_dst_container: bool indicator whether this is a copy | |
| 65 request to an existing bucket, bucket subdir, or directory. Default | |
| 66 None value should be used in cases where this is not needed (commands | |
| 67 other than cp). | |
| 68 is_latest: Bool indicating that the result represents the object's current | |
| 69 version. | |
| 70 """ | |
| 71 self.src_uri_str = src_uri_str | |
| 72 self.is_multi_src_request = is_multi_src_request | |
| 73 self.src_uri_expands_to_multi = src_uri_expands_to_multi | |
| 74 self.names_container = names_container | |
| 75 self.expanded_uri_str = expanded_uri_str | |
| 76 self.have_existing_dst_container = have_existing_dst_container | |
| 77 self.is_latest = is_latest | |
| 78 | |
| 79 def __repr__(self): | |
| 80 return '%s' % self.expanded_uri_str | |
| 81 | |
| 82 def IsEmpty(self): | |
| 83 """Returns True if name expansion yielded no matches.""" | |
| 84 return self.expanded_blr is None | |
| 85 | |
| 86 def GetSrcUriStr(self): | |
| 87 """Returns the string representation of the StorageUri that was expanded.""" | |
| 88 return self.src_uri_str | |
| 89 | |
| 90 def IsMultiSrcRequest(self): | |
| 91 """ | |
| 92 Returns bool indicator whether name expansion resulted in more than 0 | |
| 93 BucketListingRef. | |
| 94 """ | |
| 95 return self.is_multi_src_request | |
| 96 | |
| 97 def SrcUriExpandsToMulti(self): | |
| 98 """ | |
| 99 Returns bool indicator whether the current src_uri expanded to more than | |
| 100 1 BucketListingRef | |
| 101 """ | |
| 102 return self.src_uri_expands_to_multi | |
| 103 | |
| 104 def NamesContainer(self): | |
| 105 """ | |
| 106 Returns bool indicator of whether src_uri names a directory, bucket, or | |
| 107 bucket subdir. | |
| 108 """ | |
| 109 return self.names_container | |
| 110 | |
| 111 def GetExpandedUriStr(self): | |
| 112 """ | |
| 113 Returns the string representation of StorageUri to which src_uri_str | |
| 114 expands. | |
| 115 """ | |
| 116 return self.expanded_uri_str | |
| 117 | |
| 118 def HaveExistingDstContainer(self): | |
| 119 """Returns bool indicator whether this is a copy request to an | |
| 120 existing bucket, bucket subdir, or directory, or None if not | |
| 121 relevant.""" | |
| 122 return self.have_existing_dst_container | |
| 123 | |
| 124 | |
| 125 class _NameExpansionIterator(object): | |
| 126 """ | |
| 127 Iterates over all src_uris, expanding wildcards, object-less bucket names, | |
| 128 subdir bucket names, and directory names, generating a flat listing of all | |
| 129 the matching objects/files. | |
| 130 | |
| 131 You should instantiate this object using the static factory function | |
| 132 NameExpansionIterator, because consumers of this iterator need the | |
| 133 PluralityCheckableIterator wrapper built by that function. | |
| 134 | |
| 135 Yields: | |
| 136 gslib.name_expansion.NameExpansionResult. | |
| 137 | |
| 138 Raises: | |
| 139 CommandException: if errors encountered. | |
| 140 """ | |
| 141 | |
| 142 def __init__(self, command_name, proj_id_handler, headers, debug, | |
| 143 bucket_storage_uri_class, uri_strs, recursion_requested, | |
| 144 have_existing_dst_container=None, flat=True, | |
| 145 all_versions=False, for_all_version_delete=False): | |
| 146 """ | |
| 147 Args: | |
| 148 command_name: name of command being run. | |
| 149 proj_id_handler: ProjectIdHandler to use for current command. | |
| 150 headers: Dictionary containing optional HTTP headers to pass to boto. | |
| 151 debug: Debug level to pass in to boto connection (range 0..3). | |
| 152 bucket_storage_uri_class: Class to instantiate for cloud StorageUris. | |
| 153 Settable for testing/mocking. | |
| 154 uri_strs: PluralityCheckableIterator of URI strings needing expansion. | |
| 155 recursion_requested: True if -R specified on command-line. | |
| 156 have_existing_dst_container: Bool indicator whether this is a copy | |
| 157 request to an existing bucket, bucket subdir, or directory. Default | |
| 158 None value should be used in cases where this is not needed (commands | |
| 159 other than cp). | |
| 160 flat: Bool indicating whether bucket listings should be flattened, i.e., | |
| 161 so the mapped-to results contain objects spanning subdirectories. | |
| 162 all_versions: Bool indicating whether to iterate over all object versions. | |
| 163 for_all_version_delete: Bool indicating whether this is for an all-version | |
| 164 delete. | |
| 165 | |
| 166 Examples of _NameExpansionIterator with flat=True: | |
| 167 - Calling with one of the uri_strs being 'gs://bucket' will enumerate all | |
| 168 top-level objects, as will 'gs://bucket/' and 'gs://bucket/*'. | |
| 169 - 'gs://bucket/**' will enumerate all objects in the bucket. | |
| 170 - 'gs://bucket/abc' will enumerate all next-level objects under directory | |
| 171 abc (i.e., not including subdirectories of abc) if gs://bucket/abc/* | |
| 172 matches any objects; otherwise it will enumerate the single name | |
| 173 gs://bucket/abc | |
| 174 - 'gs://bucket/abc/**' will enumerate all objects under abc or any of its | |
| 175 subdirectories. | |
| 176 - 'file:///tmp' will enumerate all files under /tmp, as will | |
| 177 'file:///tmp/*' | |
| 178 - 'file:///tmp/**' will enumerate all files under /tmp or any of its | |
| 179 subdirectories. | |
| 180 | |
| 181 Example if flat=False: calling with gs://bucket/abc/* lists matching objects | |
| 182 or subdirs, but not sub-subdirs or objects beneath subdirs. | |
| 183 | |
| 184 Note: In step-by-step comments below we give examples assuming there's a | |
| 185 gs://bucket with object paths: | |
| 186 abcd/o1.txt | |
| 187 abcd/o2.txt | |
| 188 xyz/o1.txt | |
| 189 xyz/o2.txt | |
| 190 and a directory file://dir with file paths: | |
| 191 dir/a.txt | |
| 192 dir/b.txt | |
| 193 dir/c/ | |
| 194 """ | |
| 195 self.command_name = command_name | |
| 196 self.proj_id_handler = proj_id_handler | |
| 197 self.headers = headers | |
| 198 self.debug = debug | |
| 199 self.bucket_storage_uri_class = bucket_storage_uri_class | |
| 200 self.suri_builder = StorageUriBuilder(debug, bucket_storage_uri_class) | |
| 201 self.uri_strs = uri_strs | |
| 202 self.recursion_requested = recursion_requested | |
| 203 self.have_existing_dst_container = have_existing_dst_container | |
| 204 self.flat = flat | |
| 205 self.all_versions = all_versions | |
| 206 | |
| 207 # Map holding wildcard strings to use for flat vs subdir-by-subdir listings. | |
| 208 # (A flat listing means show all objects expanded all the way down.) | |
| 209 self._flatness_wildcard = {True: '**', False: '*'} | |
| 210 | |
| 211 def __iter__(self): | |
| 212 for uri_str in self.uri_strs: | |
| 213 # Step 1: Expand any explicitly specified wildcards. The output from this | |
| 214 # step is an iterator of BucketListingRef. | |
| 215 # Starting with gs://buck*/abc* this step would expand to gs://bucket/abcd | |
| 216 if ContainsWildcard(uri_str): | |
| 217 post_step1_iter = self._WildcardIterator(uri_str) | |
| 218 else: | |
| 219 suri = self.suri_builder.StorageUri(uri_str) | |
| 220 post_step1_iter = iter([BucketListingRef(suri)]) | |
| 221 post_step1_iter = PluralityCheckableIterator(post_step1_iter) | |
| 222 | |
| 223 # Step 2: Expand bucket subdirs and versions. The output from this | |
| 224 # step is an iterator of (names_container, BucketListingRef). | |
| 225 # Starting with gs://bucket/abcd this step would expand to: | |
| 226 # iter([(True, abcd/o1.txt), (True, abcd/o2.txt)]). | |
| 227 if self.flat and self.recursion_requested: | |
| 228 post_step2_iter = _ImplicitBucketSubdirIterator(self, | |
| 229 post_step1_iter, self.flat) | |
| 230 elif self.all_versions: | |
| 231 post_step2_iter = _AllVersionIterator(self, post_step1_iter, | |
| 232 headers=self.headers) | |
| 233 else: | |
| 234 post_step2_iter = _NonContainerTuplifyIterator(post_step1_iter) | |
| 235 post_step2_iter = PluralityCheckableIterator(post_step2_iter) | |
| 236 | |
| 237 # Step 3. Expand directories and buckets. This step yields the iterated | |
| 238 # values. Starting with gs://bucket this step would expand to: | |
| 239 # [abcd/o1.txt, abcd/o2.txt, xyz/o1.txt, xyz/o2.txt] | |
| 240 # Starting with file://dir this step would expand to: | |
| 241 # [dir/a.txt, dir/b.txt, dir/c/] | |
| 242 exp_src_bucket_listing_refs = [] | |
| 243 wc = self._flatness_wildcard[self.flat] | |
| 244 src_uri_expands_to_multi = (post_step1_iter.has_plurality() | |
| 245 or post_step2_iter.has_plurality()) | |
| 246 is_multi_src_request = (self.uri_strs.has_plurality() | |
| 247 or src_uri_expands_to_multi) | |
| 248 | |
| 249 if post_step2_iter.is_empty(): | |
| 250 raise CommandException('No URIs matched: %s' % uri_str) | |
| 251 for (names_container, blr) in post_step2_iter: | |
| 252 if (not blr.GetUri().names_container() | |
| 253 and (self.flat or not blr.HasPrefix())): | |
| 254 yield NameExpansionResult(uri_str, is_multi_src_request, | |
| 255 src_uri_expands_to_multi, names_container, | |
| 256 blr.GetUriString(), | |
| 257 self.have_existing_dst_container, | |
| 258 is_latest=blr.IsLatest()) | |
| 259 continue | |
| 260 if not self.recursion_requested: | |
| 261 if blr.GetUri().is_file_uri(): | |
| 262 desc = 'directory' | |
| 263 else: | |
| 264 desc = 'bucket' | |
| 265 print 'Omitting %s "%s". (Did you mean to do %s -R?)' % ( | |
| 266 desc, blr.GetUri(), self.command_name) | |
| 267 continue | |
| 268 if blr.GetUri().is_file_uri(): | |
| 269 # Convert dir to implicit recursive wildcard. | |
| 270 uri_to_iterate = '%s/%s' % (blr.GetUriString(), wc) | |
| 271 else: | |
| 272 # Convert bucket to implicit recursive wildcard. | |
| 273 uri_to_iterate = blr.GetUri().clone_replace_name(wc) | |
| 274 wc_iter = PluralityCheckableIterator( | |
| 275 self._WildcardIterator(uri_to_iterate)) | |
| 276 src_uri_expands_to_multi = (src_uri_expands_to_multi | |
| 277 or wc_iter.has_plurality()) | |
| 278 is_multi_src_request = (self.uri_strs.has_plurality() | |
| 279 or src_uri_expands_to_multi) | |
| 280 for blr in wc_iter: | |
| 281 yield NameExpansionResult(uri_str, is_multi_src_request, | |
| 282 src_uri_expands_to_multi, True, | |
| 283 blr.GetUriString(), | |
| 284 self.have_existing_dst_container, | |
| 285 is_latest=blr.IsLatest()) | |
| 286 | |
| 287 def _WildcardIterator(self, uri_or_str): | |
| 288 """ | |
| 289 Helper to instantiate gslib.WildcardIterator. Args are same as | |
| 290 gslib.WildcardIterator interface, but this method fills in most of the | |
| 291 values from instance state. | |
| 292 | |
| 293 Args: | |
| 294 uri_or_str: StorageUri or URI string naming wildcard objects to iterate. | |
| 295 """ | |
| 296 return wildcard_iterator.wildcard_iterator( | |
| 297 uri_or_str, self.proj_id_handler, | |
| 298 bucket_storage_uri_class=self.bucket_storage_uri_class, | |
| 299 headers=self.headers, debug=self.debug, | |
| 300 all_versions=self.all_versions) | |
| 301 | |
| 302 | |
| 303 def NameExpansionIterator(command_name, proj_id_handler, headers, debug, | |
| 304 bucket_storage_uri_class, uri_strs, | |
| 305 recursion_requested, | |
| 306 have_existing_dst_container=None, flat=True, | |
| 307 all_versions=False, | |
| 308 for_all_version_delete=False): | |
| 309 """ | |
| 310 Static factory function for instantiating _NameExpansionIterator, which | |
| 311 wraps the resulting iterator in a PluralityCheckableIterator and checks | |
| 312 that it is non-empty. Also, allows uri_strs can be either an array or an | |
| 313 iterator. | |
| 314 | |
| 315 Args: | |
| 316 command_name: name of command being run. | |
| 317 proj_id_handler: ProjectIdHandler to use for current command. | |
| 318 headers: Dictionary containing optional HTTP headers to pass to boto. | |
| 319 debug: Debug level to pass in to boto connection (range 0..3). | |
| 320 bucket_storage_uri_class: Class to instantiate for cloud StorageUris. | |
| 321 Settable for testing/mocking. | |
| 322 uri_strs: PluralityCheckableIterator of URI strings needing expansion. | |
| 323 recursion_requested: True if -R specified on command-line. | |
| 324 have_existing_dst_container: Bool indicator whether this is a copy | |
| 325 request to an existing bucket, bucket subdir, or directory. Default | |
| 326 None value should be used in cases where this is not needed (commands | |
| 327 other than cp). | |
| 328 flat: Bool indicating whether bucket listings should be flattened, i.e., | |
| 329 so the mapped-to results contain objects spanning subdirectories. | |
| 330 all_versions: Bool indicating whether to iterate over all object versions. | |
| 331 for_all_version_delete: Bool indicating whether this is for an all-version | |
| 332 delete. | |
| 333 | |
| 334 Examples of ExpandWildcardsAndContainers with flat=True: | |
| 335 - Calling with one of the uri_strs being 'gs://bucket' will enumerate all | |
| 336 top-level objects, as will 'gs://bucket/' and 'gs://bucket/*'. | |
| 337 - 'gs://bucket/**' will enumerate all objects in the bucket. | |
| 338 - 'gs://bucket/abc' will enumerate all next-level objects under directory | |
| 339 abc (i.e., not including subdirectories of abc) if gs://bucket/abc/* | |
| 340 matches any objects; otherwise it will enumerate the single name | |
| 341 gs://bucket/abc | |
| 342 - 'gs://bucket/abc/**' will enumerate all objects under abc or any of its | |
| 343 subdirectories. | |
| 344 - 'file:///tmp' will enumerate all files under /tmp, as will | |
| 345 'file:///tmp/*' | |
| 346 - 'file:///tmp/**' will enumerate all files under /tmp or any of its | |
| 347 subdirectories. | |
| 348 | |
| 349 Example if flat=False: calling with gs://bucket/abc/* lists matching objects | |
| 350 or subdirs, but not sub-subdirs or objects beneath subdirs. | |
| 351 | |
| 352 Note: In step-by-step comments below we give examples assuming there's a | |
| 353 gs://bucket with object paths: | |
| 354 abcd/o1.txt | |
| 355 abcd/o2.txt | |
| 356 xyz/o1.txt | |
| 357 xyz/o2.txt | |
| 358 and a directory file://dir with file paths: | |
| 359 dir/a.txt | |
| 360 dir/b.txt | |
| 361 dir/c/ | |
| 362 """ | |
| 363 uri_strs = PluralityCheckableIterator(uri_strs) | |
| 364 name_expansion_iterator = _NameExpansionIterator( | |
| 365 command_name, proj_id_handler, headers, debug, bucket_storage_uri_class, | |
| 366 uri_strs, recursion_requested, have_existing_dst_container, flat, | |
| 367 all_versions=all_versions, for_all_version_delete=for_all_version_delete) | |
| 368 name_expansion_iterator = PluralityCheckableIterator(name_expansion_iterator) | |
| 369 if name_expansion_iterator.is_empty(): | |
| 370 raise CommandException('No URIs matched') | |
| 371 return name_expansion_iterator | |
| 372 | |
| 373 | |
| 374 class NameExpansionIteratorQueue(object): | |
| 375 """ | |
| 376 Wrapper around NameExpansionIterator that provides a Multiprocessing.Queue | |
| 377 facade. | |
| 378 | |
| 379 Only a blocking get() function can be called, and the block and timeout | |
| 380 params on that function are ignored. All other class functions raise | |
| 381 NotImplementedError. | |
| 382 | |
| 383 This class is thread safe. | |
| 384 """ | |
| 385 | |
| 386 def __init__(self, name_expansion_iterator, final_value): | |
| 387 self.name_expansion_iterator = name_expansion_iterator | |
| 388 self.final_value = final_value | |
| 389 self.lock = threading.Lock() | |
| 390 | |
| 391 def qsize(self): | |
| 392 raise NotImplementedError( | |
| 393 "NameExpansionIteratorQueue.qsize() not implemented") | |
| 394 | |
| 395 def empty(self): | |
| 396 raise NotImplementedError( | |
| 397 "NameExpansionIteratorQueue.empty() not implemented") | |
| 398 | |
| 399 def full(self): | |
| 400 raise NotImplementedError( | |
| 401 "NameExpansionIteratorQueue.full() not implemented") | |
| 402 | |
| 403 def put(self, obj=None, block=None, timeout=None): | |
| 404 raise NotImplementedError( | |
| 405 "NameExpansionIteratorQueue.put() not implemented") | |
| 406 | |
| 407 def put_nowait(self, obj): | |
| 408 raise NotImplementedError( | |
| 409 "NameExpansionIteratorQueue.put_nowait() not implemented") | |
| 410 | |
| 411 def get(self, block=None, timeout=None): | |
| 412 self.lock.acquire() | |
| 413 try: | |
| 414 if self.name_expansion_iterator.is_empty(): | |
| 415 return self.final_value | |
| 416 return self.name_expansion_iterator.next() | |
| 417 finally: | |
| 418 self.lock.release() | |
| 419 | |
| 420 def get_nowait(self): | |
| 421 raise NotImplementedError( | |
| 422 "NameExpansionIteratorQueue.get_nowait() not implemented") | |
| 423 | |
| 424 def get_no_wait(self): | |
| 425 raise NotImplementedError( | |
| 426 "NameExpansionIteratorQueue.get_no_wait() not implemented") | |
| 427 | |
| 428 def close(self): | |
| 429 raise NotImplementedError( | |
| 430 "NameExpansionIteratorQueue.close() not implemented") | |
| 431 | |
| 432 def join_thread(self): | |
| 433 raise NotImplementedError( | |
| 434 "NameExpansionIteratorQueue.join_thread() not implemented") | |
| 435 | |
| 436 def cancel_join_thread(self): | |
| 437 raise NotImplementedError( | |
| 438 "NameExpansionIteratorQueue.cancel_join_thread() not implemented") | |
| 439 | |
| 440 | |
| 441 class _NonContainerTuplifyIterator(object): | |
| 442 """ | |
| 443 Iterator that produces the tuple (False, blr) for each iteration | |
| 444 of blr_iter. Used for cases where blr_iter iterates over a set of | |
| 445 BucketListingRefs known not to name containers. | |
| 446 """ | |
| 447 | |
| 448 def __init__(self, blr_iter): | |
| 449 """ | |
| 450 Args: | |
| 451 blr_iter: iterator of BucketListingRef. | |
| 452 """ | |
| 453 self.blr_iter = blr_iter | |
| 454 | |
| 455 def __iter__(self): | |
| 456 for blr in self.blr_iter: | |
| 457 yield (False, blr) | |
| 458 | |
| 459 | |
| 460 class _ImplicitBucketSubdirIterator(object): | |
| 461 | |
| 462 """ | |
| 463 Iterator wrapper that iterates over blr_iter, performing implicit bucket | |
| 464 subdir expansion. | |
| 465 | |
| 466 Each iteration yields tuple (names_container, expanded BucketListingRefs) | |
| 467 where names_container is true if URI names a directory, bucket, | |
| 468 or bucket subdir (vs how StorageUri.names_container() doesn't | |
| 469 handle latter case). | |
| 470 | |
| 471 For example, iterating over [BucketListingRef("gs://abc")] would expand to: | |
| 472 [BucketListingRef("gs://abc/o1"), BucketListingRef("gs://abc/o2")] | |
| 473 if those subdir objects exist, and [BucketListingRef("gs://abc") otherwise. | |
| 474 """ | |
| 475 | |
| 476 def __init__(self, name_expansion_instance, blr_iter, flat): | |
| 477 """ | |
| 478 Args: | |
| 479 name_expansion_instance: calling instance of NameExpansion class. | |
| 480 blr_iter: iterator of BucketListingRef. | |
| 481 flat: bool indicating whether bucket listings should be flattened, i.e., | |
| 482 so the mapped-to results contain objects spanning subdirectories. | |
| 483 """ | |
| 484 self.blr_iter = blr_iter | |
| 485 self.name_expansion_instance = name_expansion_instance | |
| 486 self.flat = flat | |
| 487 | |
| 488 def __iter__(self): | |
| 489 for blr in self.blr_iter: | |
| 490 uri = blr.GetUri() | |
| 491 if uri.names_object(): | |
| 492 # URI could be a bucket subdir. | |
| 493 implicit_subdir_iterator = PluralityCheckableIterator( | |
| 494 self.name_expansion_instance._WildcardIterator( | |
| 495 self.name_expansion_instance.suri_builder.StorageUri( | |
| 496 '%s/%s' % (uri.uri.rstrip('/'), | |
| 497 self.name_expansion_instance._flatness_wildcard[ | |
| 498 self.flat])))) | |
| 499 if not implicit_subdir_iterator.is_empty(): | |
| 500 for exp_blr in implicit_subdir_iterator: | |
| 501 yield (True, exp_blr) | |
| 502 else: | |
| 503 yield (False, blr) | |
| 504 else: | |
| 505 yield (False, blr) | |
| 506 | |
| 507 class _AllVersionIterator(object): | |
| 508 """ | |
| 509 Iterator wrapper that iterates over blr_iter, performing implicit version | |
| 510 expansion. | |
| 511 | |
| 512 Output behavior is identical to that in _ImplicitBucketSubdirIterator above. | |
| 513 | |
| 514 For example, iterating over [BucketListingRef("gs://abc/o1")] would expand to: | |
| 515 [BucketListingRef("gs://abc/o1#1234"), BucketListingRef("gs://abc/o1#1235")] | |
| 516 """ | |
| 517 | |
| 518 def __init__(self, name_expansion_instance, blr_iter, headers=None): | |
| 519 """ | |
| 520 Args: | |
| 521 name_expansion_instance: calling instance of NameExpansion class. | |
| 522 blr_iter: iterator of BucketListingRef. | |
| 523 flat: bool indicating whether bucket listings should be flattened, i.e., | |
| 524 so the mapped-to results contain objects spanning subdirectories. | |
| 525 """ | |
| 526 self.blr_iter = blr_iter | |
| 527 self.name_expansion_instance = name_expansion_instance | |
| 528 self.headers = headers | |
| 529 | |
| 530 def __iter__(self): | |
| 531 empty = True | |
| 532 for blr in self.blr_iter: | |
| 533 uri = blr.GetUri() | |
| 534 if not uri.names_object(): | |
| 535 empty = False | |
| 536 yield (True, blr) | |
| 537 break | |
| 538 for key in uri.list_bucket( | |
| 539 prefix=uri.object_name, headers=self.headers, all_versions=True): | |
| 540 if key.name != uri.object_name: | |
| 541 # The desired entries will be alphabetically first in this listing. | |
| 542 break | |
| 543 version_blr = BucketListingRef(uri.clone_replace_key(key), key=key) | |
| 544 empty = False | |
| 545 yield (False, version_blr) | |
| 546 # If no version exists, yield the unversioned blr, and let the consuming | |
| 547 # operation fail. This mirrors behavior in _ImplicitBucketSubdirIterator. | |
| 548 if empty: | |
| 549 yield (False, blr) | |
| 550 | |
| OLD | NEW |