Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(791)

Unified Diff: third_party/gsutil/gslib/name_expansion.py

Issue 10199002: Upgrade gsutil to 3.4 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed comments Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/gsutil/gslib/help_provider.py ('k') | third_party/gsutil/gslib/no_op_auth_plugin.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/gsutil/gslib/name_expansion.py
diff --git a/third_party/gsutil/gslib/name_expansion.py b/third_party/gsutil/gslib/name_expansion.py
new file mode 100644
index 0000000000000000000000000000000000000000..82d9d031b24e8c8f211ca6ccffec50a101496de8
--- /dev/null
+++ b/third_party/gsutil/gslib/name_expansion.py
@@ -0,0 +1,383 @@
+# Copyright 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import gslib
+import itertools
+import wildcard_iterator
+
+from gslib.storage_uri_builder import StorageUriBuilder
+from wildcard_iterator import ContainsWildcard
+from bucket_listing_ref import BucketListingRef
+
+"""
+Name expansion support for the various ways gsutil lets users refer to
+collections of data (via explicit wildcarding as well as directory,
+bucket, and bucket subdir implicit wildcarding). This class encapsulates
+the various rules for determining how these expansions are done.
+"""
+
+
+class NameExpansionResult(object):
+ """
+ Holds results of calls to NameExpansionHandler.ExpandWildcardsAndContainers().
+ """
+ # Currently we build a dict (self._expansion_map) to hold the expansion
+ # results instead of using a generator to iterate incrementally
+ # because the caller needs to know the count before iterating and
+ # performing copy operations (in order to determine if this is a
+ # multi-source copy request). That limits the scalability of wildcard
+ # iteration, since the entire expansion needs to fit in memory (see
+ # http://code.google.com/p/gsutil/issues/detail?id=80).
+ # TODO: Rework NameExpansionResult to save
+ # {StorageUri: generator of BucketListingRefs to which it expands}
+ # and change the accessor functions to determine if expansion has
+ # length > 1 without first
+ # NameExpansionResult.IterExpandedBucketListingRefsFor() and
+ # NameExpansionResult.SrcUriExpandsToMultipleSources() to work without ever
+ # materializing the list.
+
+ def __init__(self):
+ # dict {StorageUri: [BucketListingRefs to which it expands]}
+ # Note: in the future we'll change RHS to be an iterator from the
+ # underlying generator supported by WildcardIterator, for scalabilty.
+ self._expansion_map = {}
+ # dict {StorageUri: bool indicator of whether src_uri names a container}
+ # where names_container is true if URI names a directory, bucket, or
+ # bucket subdir (vs how StorageUri.names_container() doesn't handle
+ # latter case).
+ self._names_container_map = {}
+
+ def __repr__(self):
+ return self._expansion_map.__repr__()
+
+ def _AddExpansion(self, src_uri, names_container,
+ expanded_bucket_listing_refs):
+ """
+ Args:
+ src_uri: StorageUri.
+ names_container: bool indicator whether src_uri names a container.
+ expanded_bucket_listing_refs: [BucketListingRef] to which src_uri expands.
+ """
+ self._expansion_map[src_uri] = expanded_bucket_listing_refs
+ self._names_container_map[src_uri] = names_container
+
+ def IsEmpty(self):
+ """Returns True if name expansion yielded no matches."""
+ for v in self._expansion_map.values():
+ if v:
+ return False
+ return True
+
+ def NamesContainer(self, src_uri):
+ """Returns bool indicator of whether src_uri names a directory, bucket, or
+ bucket subdir.
+ """
+ return self._names_container_map[src_uri]
+
+ def GetSrcUris(self):
+ """Returns the list of src_uri's for which name expansion was requested."""
+ return self._expansion_map.keys()
+
+ # Note: We return iterators from the following functinos
+ # instead of the underlying lists so we can later replace
+ # this representation with a generator implementation to fix
+ # http://code.google.com/p/gsutil/issues/detail?id=80.
+
+ def IterExpandedBucketListingRefsFor(self, src_uri):
+ """
+ Returns an iterator of BucketListingRefs to which the given src_uri
+ expanded.
+ """
+ return iter(self._expansion_map[src_uri])
+
+ def IterExpandedBucketListingRefs(self):
+ """
+ Returns an iterator of all BucketListingRefs (across all src_uris that were
+ expanded) from this NameExpansionResult.
+ """
+ #result = []
+ #for exp_list in self._expansion_map.values():
+ # result.extend(exp_list)
+ # return iter(result)
+ list_of_iters = []
+ for src_uri in self._expansion_map:
+ list_of_iters.extend(self._expansion_map[src_uri])
+ return itertools.chain(list_of_iters)
+
+ def __iter__(self):
+ return self.IterExpandedBucketListingRefs()
+
+ def IterExpandedUris(self):
+ """
+ Returns an iterator of all StorageUris (across all src_uris that were
+ expanded) from this NameExpansionResult.
+ """
+ result = []
+ for bucket_listing_ref in self.IterExpandedBucketListingRefs():
+ result.append(bucket_listing_ref.GetUri())
+ return iter(result)
+
+ def IterExpandedUriStrings(self):
+ """
+ Returns an iterator of all URI strings (across all src_uris that were
+ expanded) from this NameExpansionResult.
+ """
+ result = []
+ for bucket_listing_ref in self.IterExpandedBucketListingRefs():
+ result.append(bucket_listing_ref.GetUriString())
+ return iter(result)
+
+ def IterExpandedKeys(self):
+ """
+ Returns an iterator of all Keys (across all src_uris that were expanded)
+ from this NameExpansionResult.
+ """
+ result = []
+ for bucket_listing_ref in self.IterExpandedBucketListingRefs():
+ result.append(bucket_listing_ref.GetKey())
+ return iter(result)
+
+ def IsMultiSrcRequest(self):
+ """Returns True if this name expansion resulted in more than 1 URI."""
+ if len(self._expansion_map) == 0:
+ return False
+ return (len(self._expansion_map) > 1
+ or len(self._expansion_map.values()[0]) > 1)
+
+ def SrcUriExpandsToMultipleSources(self, src_uri):
+ """
+ Checks that src_uri names a singleton (file or object) after
+ dir/wildcard expansion. The decision is more nuanced than simply
+ src_uri.names_singleton()) because of the possibility that an object path
+ might name a bucket "sub-directory", which in turn depends on whether
+ src_uri expanded to multiple URIs. For example, when running the command:
+ gsutil cp -R gs://bucket/abc ./dir
+ gs://bucket/abc would be an object if nothing matches gs://bucket/abc/*;
+ but would be a bucket subdir otherwise.
+
+ Args:
+ src_uri: StorageUri to check.
+
+ Returns:
+ bool indicator.
+ """
+ return len(self._expansion_map[src_uri]) > 1
+
+
+class NameExpansionHandler(object):
+
+ def __init__(self, command_name, proj_id_handler, headers, debug,
+ bucket_storage_uri_class):
+ """
+ Args:
+ command_name: name of command being run.
+ proj_id_handler: ProjectIdHandler to use for current command.
+ headers: Dictionary containing optional HTTP headers to pass to boto.
+ debug: Debug level to pass in to boto connection (range 0..3).
+ bucket_storage_uri_class: Class to instantiate for cloud StorageUris.
+ Settable for testing/mocking.
+ """
+ self.command_name = command_name
+ self.proj_id_handler = proj_id_handler
+ self.headers = headers
+ self.debug = debug
+ self.bucket_storage_uri_class = bucket_storage_uri_class
+ self.suri_builder = StorageUriBuilder(debug, bucket_storage_uri_class)
+
+ # Map holding wildcard strings to use for flat vs subdir-by-subdir listings.
+ # (A flat listing means show all objects expanded all the way down.)
+ self._flatness_wildcard = {True: '**', False: '*'}
+
+ def WildcardIterator(self, uri_or_str):
+ """
+ Helper to instantiate gslib.WildcardIterator. Args are same as
+ gslib.WildcardIterator interface, but this method fills in most of the
+ values from class state.
+
+ Args:
+ uri_or_str: StorageUri or URI string naming wildcard objects to iterate.
+ """
+ return wildcard_iterator.wildcard_iterator(
+ uri_or_str, self.proj_id_handler,
+ bucket_storage_uri_class=self.bucket_storage_uri_class,
+ headers=self.headers, debug=self.debug)
+
+ def ExpandWildcardsAndContainers(self, uri_strs, recursion_requested,
+ flat=True):
+ """
+ Expands wildcards, object-less bucket names, subdir bucket names, and
+ directory names, producing a flat listing of all the matching objects/files.
+
+ Args:
+ uri_strs: List of URI strings needing expansion.
+ recursion_requested: True if -R specified on command-line.
+ flat: Bool indicating whether bucket listings should be flattened, i.e.,
+ so the mapped-to results contain objects spanning subdirectories.
+
+ Returns:
+ gslib.name_expansion.NameExpansionResult.
+
+ Raises:
+ CommandException: if errors encountered.
+
+ Examples with flat=True:
+ - Calling with one of the uri_strs being 'gs://bucket' will enumerate all
+ top-level objects, as will 'gs://bucket/' and 'gs://bucket/*'.
+ - 'gs://bucket/**' will enumerate all objects in the bucket.
+ - 'gs://bucket/abc' will enumerate all next-level objects under directory
+ abc (i.e., not including subdirectories of abc) if gs://bucket/abc/*
+ matches any objects; otherwise it will enumerate the single name
+ gs://bucket/abc
+ - 'gs://bucket/abc/**' will enumerate all objects under abc or any of its
+ subdirectories.
+ - 'file:///tmp' will enumerate all files under /tmp, as will
+ 'file:///tmp/*'
+ - 'file:///tmp/**' will enumerate all files under /tmp or any of its
+ subdirectories.
+
+ Example if flat=False: calling with gs://bucket/abc/* lists matching objects
+ or subdirs, but not sub-subdirs or objects beneath subdirs.
+
+ Note: In step-by-step comments below we give examples assuming there's a
+ gs://bucket with object paths:
+ abcd/o1.txt
+ abcd/o2.txt
+ xyz/o1.txt
+ xyz/o2.txt
+ and a directory file://dir with file paths:
+ dir/a.txt
+ dir/b.txt
+ dir/c/
+ """
+ result = NameExpansionResult()
+ for uri_str in uri_strs:
+
+ # Step 1: Expand any explicitly specified wildcards.
+ # Starting with gs://buck*/abc* this step would expand to gs://bucket/abcd
+ if ContainsWildcard(uri_str):
+ post_step1_bucket_listing_refs = list(self.WildcardIterator(uri_str))
+ else:
+ post_step1_bucket_listing_refs = [
+ BucketListingRef(self.suri_builder.StorageUri(uri_str))]
+
+ # Step 2: Expand subdirs.
+ # Starting with gs://bucket/abcd this step would expand to:
+ # [abcd/o1.txt, abcd/o2.txt].
+ uri_names_container = False
+ if flat:
+ if recursion_requested:
+ post_step2_bucket_listing_refs = []
+ for bucket_listing_ref in post_step1_bucket_listing_refs:
+ (uri_names_container, bucket_listing_refs) = (
+ self._DoImplicitBucketSubdirExpansionIfApplicable(
+ bucket_listing_ref.GetUri(), flat))
+ post_step2_bucket_listing_refs.extend(bucket_listing_refs)
+ else:
+ uri_names_container = False
+ post_step2_bucket_listing_refs = post_step1_bucket_listing_refs
+ else:
+ uri_names_container = False
+ post_step2_bucket_listing_refs = post_step1_bucket_listing_refs
+
+ # Step 3. Expand directories and buckets.
+ # Starting with gs://bucket this step would expand to:
+ # [abcd/o1.txt, abcd/o2.txt, xyz/o1.txt, xyz/o2.txt]
+ # Starting with file://dir this step would expand to:
+ # [dir/a.txt, dir/b.txt, dir/c/]
+ exp_src_bucket_listing_refs = []
+ wc = self._flatness_wildcard[flat]
+ for bucket_listing_ref in post_step2_bucket_listing_refs:
+ if (not bucket_listing_ref.GetUri().names_container()
+ and (flat or not bucket_listing_ref.HasPrefix())):
+ exp_src_bucket_listing_refs.append(bucket_listing_ref)
+ continue
+ if not recursion_requested:
+ if bucket_listing_ref.GetUri().is_file_uri():
+ desc = 'directory'
+ else:
+ desc = 'bucket'
+ print 'Omitting %s "%s". (Did you mean to do %s -R?)' % (
+ desc, bucket_listing_ref.GetUri(), self.command_name)
+ continue
+ uri_names_container = True
+ if bucket_listing_ref.GetUri().is_file_uri():
+ # Convert dir to implicit recursive wildcard.
+ uri_to_iter = '%s/%s' % (bucket_listing_ref.GetUriString(), wc)
+ else:
+ # Convert bucket to implicit recursive wildcard.
+ uri_to_iter = bucket_listing_ref.GetUri().clone_replace_name(wc)
+ wildcard_result = list(self.WildcardIterator(uri_to_iter))
+ if len(wildcard_result) > 0:
+ exp_src_bucket_listing_refs.extend(wildcard_result)
+
+ result._AddExpansion(self.suri_builder.StorageUri(uri_str),
+ uri_names_container,
+ exp_src_bucket_listing_refs)
+
+ return result
+
+ def _DoImplicitBucketSubdirExpansionIfApplicable(self, uri, flat):
+ """
+ Checks whether uri could be an implicit bucket subdir, and expands if so;
+ else returns list containing uri. For example gs://abc would be an implicit
+ bucket subdir if the -R option was specified and gs://abc/* matches
+ anything.
+ Can only be called for -R (recursion requested).
+
+ Args:
+ uri: StorageUri.
+ flat: bool indicating whether bucket listings should be flattened, i.e.,
+ so the mapped-to results contain objects spanning subdirectories.
+
+ Returns:
+ tuple (names_container, [BucketListingRefs to which uri expanded])
+ where names_container is true if URI names a directory, bucket,
+ or bucket subdir (vs how StorageUri.names_container() doesn't
+ handle latter case).
+ """
+ names_container = False
+ result_list = []
+ if uri.names_object():
+ # URI could be a bucket subdir.
+ implicit_subdir_matches = list(self.WildcardIterator(
+ self.suri_builder.StorageUri('%s/%s' % (uri.uri.rstrip('/'),
+ self._flatness_wildcard[flat]))))
+ if len(implicit_subdir_matches) > 0:
+ names_container = True
+ result_list.extend(implicit_subdir_matches)
+ else:
+ result_list.append(BucketListingRef(uri))
+ else:
+ result_list.append(BucketListingRef(uri))
+ return (names_container, result_list)
+
+ def StorageUri(self, uri_str):
+ """
+ Helper to instantiate boto.StorageUri with gsutil default flag values.
+ Uses self.bucket_storage_uri_class to support mocking/testing.
+ (Identical to the same-named function in command.py; that and this
+ copy make it convenient to call StorageUri() with a single argument,
+ from the respective classes.)
+
+ Args:
+ uri_str: StorageUri naming bucket + optional object.
+
+ Returns:
+ boto.StorageUri for given uri_str.
+
+ Raises:
+ InvalidUriError: if uri_str not valid.
+ """
+ return gslib.util.StorageUri(uri_str, self.bucket_storage_uri_class,
+ self.debug)
« no previous file with comments | « third_party/gsutil/gslib/help_provider.py ('k') | third_party/gsutil/gslib/no_op_auth_plugin.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698