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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright 2012 Google Inc.
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 gslib
16 import itertools
17 import wildcard_iterator
18
19 from gslib.storage_uri_builder import StorageUriBuilder
20 from wildcard_iterator import ContainsWildcard
21 from bucket_listing_ref import BucketListingRef
22
23 """
24 Name expansion support for the various ways gsutil lets users refer to
25 collections of data (via explicit wildcarding as well as directory,
26 bucket, and bucket subdir implicit wildcarding). This class encapsulates
27 the various rules for determining how these expansions are done.
28 """
29
30
31 class NameExpansionResult(object):
32 """
33 Holds results of calls to NameExpansionHandler.ExpandWildcardsAndContainers().
34 """
35 # Currently we build a dict (self._expansion_map) to hold the expansion
36 # results instead of using a generator to iterate incrementally
37 # because the caller needs to know the count before iterating and
38 # performing copy operations (in order to determine if this is a
39 # multi-source copy request). That limits the scalability of wildcard
40 # iteration, since the entire expansion needs to fit in memory (see
41 # http://code.google.com/p/gsutil/issues/detail?id=80).
42 # TODO: Rework NameExpansionResult to save
43 # {StorageUri: generator of BucketListingRefs to which it expands}
44 # and change the accessor functions to determine if expansion has
45 # length > 1 without first
46 # NameExpansionResult.IterExpandedBucketListingRefsFor() and
47 # NameExpansionResult.SrcUriExpandsToMultipleSources() to work without ever
48 # materializing the list.
49
50 def __init__(self):
51 # dict {StorageUri: [BucketListingRefs to which it expands]}
52 # Note: in the future we'll change RHS to be an iterator from the
53 # underlying generator supported by WildcardIterator, for scalabilty.
54 self._expansion_map = {}
55 # dict {StorageUri: bool indicator of whether src_uri names a container}
56 # where names_container is true if URI names a directory, bucket, or
57 # bucket subdir (vs how StorageUri.names_container() doesn't handle
58 # latter case).
59 self._names_container_map = {}
60
61 def __repr__(self):
62 return self._expansion_map.__repr__()
63
64 def _AddExpansion(self, src_uri, names_container,
65 expanded_bucket_listing_refs):
66 """
67 Args:
68 src_uri: StorageUri.
69 names_container: bool indicator whether src_uri names a container.
70 expanded_bucket_listing_refs: [BucketListingRef] to which src_uri expands.
71 """
72 self._expansion_map[src_uri] = expanded_bucket_listing_refs
73 self._names_container_map[src_uri] = names_container
74
75 def IsEmpty(self):
76 """Returns True if name expansion yielded no matches."""
77 for v in self._expansion_map.values():
78 if v:
79 return False
80 return True
81
82 def NamesContainer(self, src_uri):
83 """Returns bool indicator of whether src_uri names a directory, bucket, or
84 bucket subdir.
85 """
86 return self._names_container_map[src_uri]
87
88 def GetSrcUris(self):
89 """Returns the list of src_uri's for which name expansion was requested."""
90 return self._expansion_map.keys()
91
92 # Note: We return iterators from the following functinos
93 # instead of the underlying lists so we can later replace
94 # this representation with a generator implementation to fix
95 # http://code.google.com/p/gsutil/issues/detail?id=80.
96
97 def IterExpandedBucketListingRefsFor(self, src_uri):
98 """
99 Returns an iterator of BucketListingRefs to which the given src_uri
100 expanded.
101 """
102 return iter(self._expansion_map[src_uri])
103
104 def IterExpandedBucketListingRefs(self):
105 """
106 Returns an iterator of all BucketListingRefs (across all src_uris that were
107 expanded) from this NameExpansionResult.
108 """
109 #result = []
110 #for exp_list in self._expansion_map.values():
111 # result.extend(exp_list)
112 # return iter(result)
113 list_of_iters = []
114 for src_uri in self._expansion_map:
115 list_of_iters.extend(self._expansion_map[src_uri])
116 return itertools.chain(list_of_iters)
117
118 def __iter__(self):
119 return self.IterExpandedBucketListingRefs()
120
121 def IterExpandedUris(self):
122 """
123 Returns an iterator of all StorageUris (across all src_uris that were
124 expanded) from this NameExpansionResult.
125 """
126 result = []
127 for bucket_listing_ref in self.IterExpandedBucketListingRefs():
128 result.append(bucket_listing_ref.GetUri())
129 return iter(result)
130
131 def IterExpandedUriStrings(self):
132 """
133 Returns an iterator of all URI strings (across all src_uris that were
134 expanded) from this NameExpansionResult.
135 """
136 result = []
137 for bucket_listing_ref in self.IterExpandedBucketListingRefs():
138 result.append(bucket_listing_ref.GetUriString())
139 return iter(result)
140
141 def IterExpandedKeys(self):
142 """
143 Returns an iterator of all Keys (across all src_uris that were expanded)
144 from this NameExpansionResult.
145 """
146 result = []
147 for bucket_listing_ref in self.IterExpandedBucketListingRefs():
148 result.append(bucket_listing_ref.GetKey())
149 return iter(result)
150
151 def IsMultiSrcRequest(self):
152 """Returns True if this name expansion resulted in more than 1 URI."""
153 if len(self._expansion_map) == 0:
154 return False
155 return (len(self._expansion_map) > 1
156 or len(self._expansion_map.values()[0]) > 1)
157
158 def SrcUriExpandsToMultipleSources(self, src_uri):
159 """
160 Checks that src_uri names a singleton (file or object) after
161 dir/wildcard expansion. The decision is more nuanced than simply
162 src_uri.names_singleton()) because of the possibility that an object path
163 might name a bucket "sub-directory", which in turn depends on whether
164 src_uri expanded to multiple URIs. For example, when running the command:
165 gsutil cp -R gs://bucket/abc ./dir
166 gs://bucket/abc would be an object if nothing matches gs://bucket/abc/*;
167 but would be a bucket subdir otherwise.
168
169 Args:
170 src_uri: StorageUri to check.
171
172 Returns:
173 bool indicator.
174 """
175 return len(self._expansion_map[src_uri]) > 1
176
177
178 class NameExpansionHandler(object):
179
180 def __init__(self, command_name, proj_id_handler, headers, debug,
181 bucket_storage_uri_class):
182 """
183 Args:
184 command_name: name of command being run.
185 proj_id_handler: ProjectIdHandler to use for current command.
186 headers: Dictionary containing optional HTTP headers to pass to boto.
187 debug: Debug level to pass in to boto connection (range 0..3).
188 bucket_storage_uri_class: Class to instantiate for cloud StorageUris.
189 Settable for testing/mocking.
190 """
191 self.command_name = command_name
192 self.proj_id_handler = proj_id_handler
193 self.headers = headers
194 self.debug = debug
195 self.bucket_storage_uri_class = bucket_storage_uri_class
196 self.suri_builder = StorageUriBuilder(debug, bucket_storage_uri_class)
197
198 # Map holding wildcard strings to use for flat vs subdir-by-subdir listings.
199 # (A flat listing means show all objects expanded all the way down.)
200 self._flatness_wildcard = {True: '**', False: '*'}
201
202 def WildcardIterator(self, uri_or_str):
203 """
204 Helper to instantiate gslib.WildcardIterator. Args are same as
205 gslib.WildcardIterator interface, but this method fills in most of the
206 values from class state.
207
208 Args:
209 uri_or_str: StorageUri or URI string naming wildcard objects to iterate.
210 """
211 return wildcard_iterator.wildcard_iterator(
212 uri_or_str, self.proj_id_handler,
213 bucket_storage_uri_class=self.bucket_storage_uri_class,
214 headers=self.headers, debug=self.debug)
215
216 def ExpandWildcardsAndContainers(self, uri_strs, recursion_requested,
217 flat=True):
218 """
219 Expands wildcards, object-less bucket names, subdir bucket names, and
220 directory names, producing a flat listing of all the matching objects/files.
221
222 Args:
223 uri_strs: List of URI strings needing expansion.
224 recursion_requested: True if -R specified on command-line.
225 flat: Bool indicating whether bucket listings should be flattened, i.e.,
226 so the mapped-to results contain objects spanning subdirectories.
227
228 Returns:
229 gslib.name_expansion.NameExpansionResult.
230
231 Raises:
232 CommandException: if errors encountered.
233
234 Examples with flat=True:
235 - Calling with one of the uri_strs being 'gs://bucket' will enumerate all
236 top-level objects, as will 'gs://bucket/' and 'gs://bucket/*'.
237 - 'gs://bucket/**' will enumerate all objects in the bucket.
238 - 'gs://bucket/abc' will enumerate all next-level objects under directory
239 abc (i.e., not including subdirectories of abc) if gs://bucket/abc/*
240 matches any objects; otherwise it will enumerate the single name
241 gs://bucket/abc
242 - 'gs://bucket/abc/**' will enumerate all objects under abc or any of its
243 subdirectories.
244 - 'file:///tmp' will enumerate all files under /tmp, as will
245 'file:///tmp/*'
246 - 'file:///tmp/**' will enumerate all files under /tmp or any of its
247 subdirectories.
248
249 Example if flat=False: calling with gs://bucket/abc/* lists matching objects
250 or subdirs, but not sub-subdirs or objects beneath subdirs.
251
252 Note: In step-by-step comments below we give examples assuming there's a
253 gs://bucket with object paths:
254 abcd/o1.txt
255 abcd/o2.txt
256 xyz/o1.txt
257 xyz/o2.txt
258 and a directory file://dir with file paths:
259 dir/a.txt
260 dir/b.txt
261 dir/c/
262 """
263 result = NameExpansionResult()
264 for uri_str in uri_strs:
265
266 # Step 1: Expand any explicitly specified wildcards.
267 # Starting with gs://buck*/abc* this step would expand to gs://bucket/abcd
268 if ContainsWildcard(uri_str):
269 post_step1_bucket_listing_refs = list(self.WildcardIterator(uri_str))
270 else:
271 post_step1_bucket_listing_refs = [
272 BucketListingRef(self.suri_builder.StorageUri(uri_str))]
273
274 # Step 2: Expand subdirs.
275 # Starting with gs://bucket/abcd this step would expand to:
276 # [abcd/o1.txt, abcd/o2.txt].
277 uri_names_container = False
278 if flat:
279 if recursion_requested:
280 post_step2_bucket_listing_refs = []
281 for bucket_listing_ref in post_step1_bucket_listing_refs:
282 (uri_names_container, bucket_listing_refs) = (
283 self._DoImplicitBucketSubdirExpansionIfApplicable(
284 bucket_listing_ref.GetUri(), flat))
285 post_step2_bucket_listing_refs.extend(bucket_listing_refs)
286 else:
287 uri_names_container = False
288 post_step2_bucket_listing_refs = post_step1_bucket_listing_refs
289 else:
290 uri_names_container = False
291 post_step2_bucket_listing_refs = post_step1_bucket_listing_refs
292
293 # Step 3. Expand directories and buckets.
294 # Starting with gs://bucket this step would expand to:
295 # [abcd/o1.txt, abcd/o2.txt, xyz/o1.txt, xyz/o2.txt]
296 # Starting with file://dir this step would expand to:
297 # [dir/a.txt, dir/b.txt, dir/c/]
298 exp_src_bucket_listing_refs = []
299 wc = self._flatness_wildcard[flat]
300 for bucket_listing_ref in post_step2_bucket_listing_refs:
301 if (not bucket_listing_ref.GetUri().names_container()
302 and (flat or not bucket_listing_ref.HasPrefix())):
303 exp_src_bucket_listing_refs.append(bucket_listing_ref)
304 continue
305 if not recursion_requested:
306 if bucket_listing_ref.GetUri().is_file_uri():
307 desc = 'directory'
308 else:
309 desc = 'bucket'
310 print 'Omitting %s "%s". (Did you mean to do %s -R?)' % (
311 desc, bucket_listing_ref.GetUri(), self.command_name)
312 continue
313 uri_names_container = True
314 if bucket_listing_ref.GetUri().is_file_uri():
315 # Convert dir to implicit recursive wildcard.
316 uri_to_iter = '%s/%s' % (bucket_listing_ref.GetUriString(), wc)
317 else:
318 # Convert bucket to implicit recursive wildcard.
319 uri_to_iter = bucket_listing_ref.GetUri().clone_replace_name(wc)
320 wildcard_result = list(self.WildcardIterator(uri_to_iter))
321 if len(wildcard_result) > 0:
322 exp_src_bucket_listing_refs.extend(wildcard_result)
323
324 result._AddExpansion(self.suri_builder.StorageUri(uri_str),
325 uri_names_container,
326 exp_src_bucket_listing_refs)
327
328 return result
329
330 def _DoImplicitBucketSubdirExpansionIfApplicable(self, uri, flat):
331 """
332 Checks whether uri could be an implicit bucket subdir, and expands if so;
333 else returns list containing uri. For example gs://abc would be an implicit
334 bucket subdir if the -R option was specified and gs://abc/* matches
335 anything.
336 Can only be called for -R (recursion requested).
337
338 Args:
339 uri: StorageUri.
340 flat: bool indicating whether bucket listings should be flattened, i.e.,
341 so the mapped-to results contain objects spanning subdirectories.
342
343 Returns:
344 tuple (names_container, [BucketListingRefs to which uri expanded])
345 where names_container is true if URI names a directory, bucket,
346 or bucket subdir (vs how StorageUri.names_container() doesn't
347 handle latter case).
348 """
349 names_container = False
350 result_list = []
351 if uri.names_object():
352 # URI could be a bucket subdir.
353 implicit_subdir_matches = list(self.WildcardIterator(
354 self.suri_builder.StorageUri('%s/%s' % (uri.uri.rstrip('/'),
355 self._flatness_wildcard[flat]))))
356 if len(implicit_subdir_matches) > 0:
357 names_container = True
358 result_list.extend(implicit_subdir_matches)
359 else:
360 result_list.append(BucketListingRef(uri))
361 else:
362 result_list.append(BucketListingRef(uri))
363 return (names_container, result_list)
364
365 def StorageUri(self, uri_str):
366 """
367 Helper to instantiate boto.StorageUri with gsutil default flag values.
368 Uses self.bucket_storage_uri_class to support mocking/testing.
369 (Identical to the same-named function in command.py; that and this
370 copy make it convenient to call StorageUri() with a single argument,
371 from the respective classes.)
372
373 Args:
374 uri_str: StorageUri naming bucket + optional object.
375
376 Returns:
377 boto.StorageUri for given uri_str.
378
379 Raises:
380 InvalidUriError: if uri_str not valid.
381 """
382 return gslib.util.StorageUri(uri_str, self.bucket_storage_uri_class,
383 self.debug)
OLDNEW
« 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