OLD | NEW |
| (Empty) |
1 # Copyright 2012 Google Inc. All Rights Reserved. | |
2 # | |
3 # Permission is hereby granted, free of charge, to any person obtaining a | |
4 # copy of this software and associated documentation files (the | |
5 # "Software"), to deal in the Software without restriction, including | |
6 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
7 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
8 # persons to whom the Software is furnished to do so, subject to the fol- | |
9 # lowing conditions: | |
10 # | |
11 # The above copyright notice and this permission notice shall be included | |
12 # in all copies or substantial portions of the Software. | |
13 # | |
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
20 # IN THE SOFTWARE. | |
21 | |
22 import time | |
23 | |
24 | |
25 class BucketListingRef(object): | |
26 """ | |
27 Container that holds a reference to one result from a bucket listing, allowing | |
28 polymorphic iteration over wildcard-iterated URIs, Keys, or Prefixes. At a | |
29 minimum, every reference contains a StorageUri. If the reference came from a | |
30 bucket listing (as opposed to a manually instantiated ref that might populate | |
31 only the StorageUri), it will additionally contain either a Key or a Prefix, | |
32 depending on whether it was a reference to an object or was just a prefix of a | |
33 path (i.e., bucket subdirectory). The latter happens when the bucket was | |
34 listed using delimiter='/'. | |
35 | |
36 Note that Keys are shallow-populated, based on the contents extracted from | |
37 parsing a bucket listing. This includes name, length, and other fields | |
38 (basically, the info listed by gsutil ls -l), but does not include information | |
39 like ACL and location (which require separate server requests, which is why | |
40 there's a separate gsutil ls -L option to get this more detailed info). | |
41 """ | |
42 | |
43 def __init__(self, uri, key=None, prefix=None, headers=None): | |
44 """Instantiate BucketListingRef from uri and (if available) key or prefix. | |
45 | |
46 Args: | |
47 uri: StorageUri for the object (required). | |
48 key: Key for the object, or None if not available. | |
49 prefix: Prefix for the subdir, or None if not available. | |
50 headers: Dictionary containing optional HTTP headers to pass to boto | |
51 (which happens when GetKey() is called on an BucketListingRef which | |
52 has no constructor-populated Key), or None if not available. | |
53 | |
54 At most one of key and prefix can be populated. | |
55 """ | |
56 assert key is None or prefix is None | |
57 self.uri = uri | |
58 self.key = key | |
59 self.prefix = prefix | |
60 self.headers = headers or {} | |
61 | |
62 def GetUri(self): | |
63 """Get URI form of listed URI. | |
64 | |
65 Returns: | |
66 StorageUri. | |
67 """ | |
68 return self.uri | |
69 | |
70 def GetUriString(self): | |
71 """Get string URI form of listed URI. | |
72 | |
73 Returns: | |
74 String. | |
75 """ | |
76 return self.uri.uri | |
77 | |
78 def NamesBucket(self): | |
79 """Determines if this BucketListingRef names a bucket. | |
80 | |
81 Returns: | |
82 bool indicator. | |
83 """ | |
84 return self.key is None and self.prefix is None and self.uri.names_bucket() | |
85 | |
86 def IsLatest(self): | |
87 """Determines if this BucketListingRef names the latest version of an | |
88 object. | |
89 | |
90 Returns: | |
91 bool indicator. | |
92 """ | |
93 return hasattr(self.uri, 'is_latest') and self.uri.is_latest | |
94 | |
95 def GetRStrippedUriString(self): | |
96 """Get string URI form of listed URI, stripped of any right trailing | |
97 delims, and without version string. | |
98 | |
99 Returns: | |
100 String. | |
101 """ | |
102 return self.uri.versionless_uri.rstrip('/') | |
103 | |
104 def HasKey(self): | |
105 """Return bool indicator of whether this BucketListingRef has a Key.""" | |
106 return bool(self.key) | |
107 | |
108 def HasPrefix(self): | |
109 """Return bool indicator of whether this BucketListingRef has a Prefix.""" | |
110 return bool(self.prefix) | |
111 | |
112 def GetKey(self): | |
113 """Get Key form of listed URI. | |
114 | |
115 Returns: | |
116 Subclass of boto.s3.key.Key. | |
117 | |
118 Raises: | |
119 BucketListingRefException: for bucket-only uri. | |
120 """ | |
121 # For gsutil ls -l gs://bucket self.key will be populated from (boto) | |
122 # parsing the bucket listing. But as noted and handled below there are | |
123 # cases where self.key isn't populated. | |
124 if not self.key: | |
125 if not self.uri.names_object(): | |
126 raise BucketListingRefException( | |
127 'Attempt to call GetKey() on Key-less BucketListingRef (uri=%s) ' % | |
128 self.uri) | |
129 # This case happens when we do gsutil ls -l on a object name-ful | |
130 # StorageUri with no object-name wildcard. Since the ls command | |
131 # implementation only reads bucket info we need to read the object | |
132 # for this case. | |
133 self.key = self.uri.get_key(validate=False, headers=self.headers) | |
134 # When we retrieve the object this way its last_modified timestamp | |
135 # is formatted in RFC 1123 format, which is different from when we | |
136 # retrieve from the bucket listing (which uses ISO 8601 format), so | |
137 # convert so we consistently return ISO 8601 format. | |
138 tuple_time = (time.strptime(self.key.last_modified, | |
139 '%a, %d %b %Y %H:%M:%S %Z')) | |
140 self.key.last_modified = time.strftime('%Y-%m-%dT%H:%M:%S', tuple_time) | |
141 return self.key | |
142 | |
143 def GetPrefix(self): | |
144 """Get Prefix form of listed URI. | |
145 | |
146 Returns: | |
147 boto.s3.prefix.Prefix. | |
148 | |
149 Raises: | |
150 BucketListingRefException: if this object has no Prefix. | |
151 """ | |
152 if not self.prefix: | |
153 raise BucketListingRefException( | |
154 'Attempt to call GetPrefix() on Prefix-less BucketListingRef ' | |
155 '(uri=%s)' % self.uri) | |
156 return self.prefix | |
157 | |
158 def __repr__(self): | |
159 """Returns string representation of BucketListingRef.""" | |
160 return 'BucketListingRef(%s, HasKey=%s, HasPrefix=%s)' % ( | |
161 self.uri, self.HasKey(), self.HasPrefix()) | |
162 | |
163 | |
164 class BucketListingRefException(StandardError): | |
165 """Exception thrown for invalid BucketListingRef requests.""" | |
166 | |
167 def __init__(self, reason): | |
168 StandardError.__init__(self) | |
169 self.reason = reason | |
170 | |
171 def __repr__(self): | |
172 return 'BucketListingRefException: %s' % self.reason | |
173 | |
174 def __str__(self): | |
175 return 'BucketListingRefException: %s' % self.reason | |
OLD | NEW |