Skip to content

Commit bf93d46

Browse files
committed
Merge pull request #576 from tseaver/508-unify_http_exceptions
Fix #508: unify http exceptions
2 parents 2c75763 + 6f2796c commit bf93d46

File tree

14 files changed

+142
-183
lines changed

14 files changed

+142
-183
lines changed
Lines changed: 71 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
Getting started with Cloud Storage
22
==================================
33

4-
This tutorial focuses on using ``gcloud`` to access
5-
Google Cloud Storage.
6-
We'll go through the basic concepts,
7-
how to operate on buckets and blobs,
8-
and how to handle access control,
9-
among other things.
4+
This tutorial focuses on using ``gcloud`` to access Google Cloud Storage.
5+
We'll go through the basic concepts, how to operate on buckets and blobs,
6+
and how to handle access control, among other things.
107

11-
We're going to assume that you've already downloaded
12-
and installed the library.
8+
We're going to assume that you've already downloaded and installed the
9+
library.
1310

1411
Creating a project
1512
------------------
@@ -19,19 +16,15 @@ Creating a project
1916
Enabling the API
2017
----------------
2118

22-
Now that you created a project,
23-
you need to **turn on** the Google Cloud Storage API.
24-
This is sort of like telling Google
25-
which services you intend to use for this project.
19+
Now that you created a project, you need to **turn on** the Google Cloud
20+
Storage API. This is sort of like telling Google which services you intend
21+
to use for this project.
2622

27-
* **Click on APIs & Auth**
28-
on the left hand side,
29-
and scroll down to where it says
30-
"Google Cloud Storage JSON API".
23+
* **Click on APIs & Auth** on the left hand side, and scroll down to where
24+
it says "Google Cloud Storage JSON API".
3125

32-
* **Click the "Off" button**
33-
on the right side
34-
to turn it into an "On" button.
26+
* **Click the "Off" button** on the right side to turn it into an "On"
27+
button.
3528

3629
Enabling a service account
3730
--------------------------
@@ -41,31 +34,25 @@ Enabling a service account
4134
Creating a connection
4235
---------------------
4336

44-
The first step in accessing Cloud Storage
45-
is to create a connection to the service::
37+
The first step in accessing Cloud Storage is to create a connection to the
38+
service::
4639

4740
>>> from gcloud import storage
4841
>>> connection = storage.get_connection(project_name)
4942

50-
We're going to use this
51-
:class:`connection <gcloud.storage.connection.Connection>` object
52-
for the rest of this guide.
43+
We're going to use this :class:`connection
44+
<gcloud.storage.connection.Connection>` object for the rest of this guide.
5345

5446
Creating a bucket
5547
-----------------
5648

57-
Once you've established a connection to Cloud Storage,
58-
the first thing you typically want to do is
59-
create a new bucket.
60-
A bucket is a container used to store
61-
objects in Cloud Storage
62-
(if you're familiar with S3,
63-
buckets on Cloud Storage mean the same thing).
64-
Think of each bucket as a single "disk drive",
65-
where you can store lots of files on each.
66-
How you organize your data is up to you,
67-
but it's typical to group common data
68-
in a single bucket.
49+
Once you've established a connection to Cloud Storage, the first thing you
50+
typically want to do is create a new bucket. A bucket is a container used
51+
to store objects in Cloud Storage (if you're familiar with S3, buckets on
52+
Cloud Storage mean the same thing). Think of each bucket as a single "disk
53+
drive", where you can store lots of files on each. How you organize your
54+
data is up to you, but it's typical to group common data in a single
55+
bucket.
6956

7057
Let's create a bucket:
7158

@@ -74,59 +61,41 @@ Let's create a bucket:
7461
File "<stdin>", line 1, in <module>
7562
File "gcloud/storage/connection.py", line 340, in create_bucket
7663
data={'name': bucket.name})
77-
File "gcloud/storage/connection.py", line 224, in api_request
78-
raise exceptions.ConnectionError(response, content)
79-
gcloud.storage.exceptions.ConnectionError: {'status': '409', 'alternate-protocol': '443:quic', 'content-length': '271', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'expires': 'Sat, 15 Mar 2014 19:19:47 GMT', 'server': 'GSE', '-content-encoding': 'gzip', 'cache-control': 'private, max-age=0', 'date': 'Sat, 15 Mar 2014 19:19:47 GMT', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'application/json; charset=UTF-8'}{
80-
"error": {
81-
"errors": [
82-
{
83-
"domain": "global",
84-
"reason": "conflict",
85-
"message": "Sorry, that name is not available. Please try a different one."
86-
}
87-
],
88-
"code": 409,
89-
"message": "Sorry, that name is not available. Please try a different one."
90-
}
91-
}
64+
File "gcloud/storage/connection.py", line 324, in api_request
65+
raise make_exception(response, content)
66+
...
9267

9368
**Whoops!**
94-
It might be important to mention
95-
that bucket names are like domain names:
96-
it's one big namespace that we all share,
97-
so you have to pick a bucket name that isn't already taken.
9869

99-
It's up to you to decide what a good name is,
100-
let's assume that you found a unique name
101-
and are ready to move on with your newly created bucket.
70+
It might be important to mention that bucket names are like domain names:
71+
it's one big namespace that we all share, so you have to pick a bucket name
72+
that isn't already taken.
73+
74+
It's up to you to decide what a good name is, let's assume that you found a
75+
unique name and are ready to move on with your newly created bucket.
10276

10377
Storing data
10478
------------
10579

106-
OK, so you have a bucket. Now what?
107-
Cloud Storage is just an arbitrary data container,
108-
so you can put whatever format of data you want.
109-
The naming of your files is also arbitrary,
110-
however the Cloud Storage online file browser
111-
tries to make it feel a bit like a file system
112-
by recognizing forward-slashes (``/``)
113-
so if you want to group data into "directories",
80+
OK, so you have a bucket. Now what? Cloud Storage is just an arbitrary
81+
data container, so you can put whatever format of data you want. The
82+
naming of your files is also arbitrary, however the Cloud Storage online
83+
file browser tries to make it feel a bit like a file system by recognizing
84+
forward-slashes (``/``) so if you want to group data into "directories",
11485
you can do that.
11586

116-
The fundamental container for a file in Cloud Storage
117-
is called an Object, however ``gcloud`` uses the term ``Blob``
118-
to avoid confusion with the Python built-in ``object``.
87+
The fundamental container for a file in Cloud Storage is called an Object,
88+
however ``gcloud`` uses the term ``Blob`` to avoid confusion with the
89+
Python built-in ``object``.
11990

120-
If you want to set some data,
121-
you just create a ``Blob`` inside your bucket
91+
If you want to set some data, you just create a ``Blob`` inside your bucket
12292
and store your data inside the blob::
12393

12494
>>> blob = bucket.new_blob('greeting.txt')
12595
>>> blob.upload_from_string('Hello world!')
12696

127-
:func:`new_blob <gcloud.storage.bucket.Bucket.new_blob>`
128-
creates a :class:`Blob <gcloud.storage.blob.Blob>` object locally
129-
and
97+
:func:`new_blob <gcloud.storage.bucket.Bucket.new_blob>` creates a
98+
:class:`Blob <gcloud.storage.blob.Blob>` object locally and
13099
:func:`upload_from_string <gcloud.storage.blob.Blob.upload_from_string>`
131100
allows you to put a string into the blob.
132101

@@ -160,21 +129,19 @@ and check if they are the same in a terminal::
160129

161130
$ diff kitten.jpg kitten2.jpg
162131

163-
Notice that we're using
164-
:func:`get_blob <gcloud.storage.bucket.Bucket.get_blob>`
165-
to retrieve a blob we know exists remotely.
166-
If the blob doesn't exist, it will return ``None``.
132+
Notice that we're using :func:`get_blob
133+
<gcloud.storage.bucket.Bucket.get_blob>` to retrieve a blob we know exists
134+
remotely. If the blob doesn't exist, it will return ``None``.
167135

168136
.. note:: ``get_blob`` is **not** retrieving the entire object's data.
169137

170-
If you want to "get-or-create" the blob
171-
(that is, overwrite it if it already exists),
172-
you can use :func:`new_blob <gcloud.storage.bucket.Bucket.new_blob>`.
173-
However, keep in mind, the blob is not created
174-
until you store some data inside of it.
138+
If you want to "get-or-create" the blob (that is, overwrite it if it
139+
already exists), you can use :func:`new_blob
140+
<gcloud.storage.bucket.Bucket.new_blob>`. However, keep in mind, the blob
141+
is not created until you store some data inside of it.
175142

176-
If you want to check whether a blob exists,
177-
you can use the ``in`` operator in Python::
143+
If you want to check whether a blob exists, you can use the ``in`` operator
144+
in Python::
178145

179146
>>> print 'kitten.jpg' in bucket
180147
True
@@ -184,35 +151,33 @@ you can use the ``in`` operator in Python::
184151
Accessing a bucket
185152
------------------
186153

187-
If you already have a bucket,
188-
use :func:`get_bucket <gcloud.storage.connection.Connection.get_bucket>`
189-
to retrieve the bucket object::
154+
If you already have a bucket, use :func:`get_bucket
155+
<gcloud.storage.connection.Connection.get_bucket>` to retrieve the bucket
156+
object::
190157

191158
>>> bucket = connection.get_bucket('my-bucket')
192159

193-
If you want to get all the blobs in the bucket,
194-
you can use
160+
If you want to get all the blobs in the bucket, you can use
195161
:func:`get_all_blobs <gcloud.storage.bucket.Bucket.get_all_blobs>`::
196162

197163
>>> blobs = bucket.get_all_blobs()
198164

199-
However, if you're looking to iterate through the blobs,
200-
you can use the bucket itself as an iterator::
165+
However, if you're looking to iterate through the blobs, you can use the
166+
bucket itself as an iterator::
201167

202168
>>> for blob in bucket:
203169
... print blob
204170

205171
Deleting a bucket
206172
-----------------
207173

208-
You can delete a bucket using the
209-
:func:`delete_bucket <gcloud.storage.connection.Connection.delete_bucket>`
210-
method::
174+
You can delete a bucket using the :func:`delete_bucket
175+
<gcloud.storage.connection.Connection.delete_bucket>` method::
211176

212177
>>> connection.delete_bucket('my-bucket')
213178

214-
Remember, the bucket you're deleting needs to be empty,
215-
otherwise you'll get an error.
179+
Remember, the bucket you're deleting needs to be empty, otherwise you'll
180+
get an error.
216181

217182
If you have a full bucket, you can delete it this way::
218183

@@ -221,9 +186,9 @@ If you have a full bucket, you can delete it this way::
221186
Listing available buckets
222187
-------------------------
223188

224-
The :class:`Connection <gcloud.storage.connection.Connection>`
225-
object itself is iterable,
226-
so you can loop over it, or call ``list`` on it to get a list object::
189+
The :class:`Connection <gcloud.storage.connection.Connection>` object
190+
itself is iterable, so you can loop over it, or call ``list`` on it to get
191+
a list object::
227192

228193
>>> for bucket in connection:
229194
... print bucket.name
@@ -232,19 +197,14 @@ so you can loop over it, or call ``list`` on it to get a list object::
232197
Managing access control
233198
-----------------------
234199

235-
Cloud storage provides fine-grained access control
236-
for both buckets and blobs.
237-
`gcloud` tries to simplify access control
238-
by working with entities and "grants".
239-
On any ACL,
240-
you get a reference to an entity,
241-
and then either grant or revoke a specific access level.
242-
Additionally, we provide two default entities:
243-
all users, and all authenticated users.
200+
Cloud storage provides fine-grained access control for both buckets and
201+
blobs. `gcloud` tries to simplify access control by working with entities
202+
and "grants". On any ACL, you get a reference to an entity, and then
203+
either grant or revoke a specific access level. Additionally, we provide
204+
two default entities: all users, and all authenticated users.
244205

245206
For example, if you want to grant read access to all users on your bucket::
246207

247208
>>> bucket.get_acl().all().grant_read()
248209

249-
For more detail on access control,
250-
see :mod:`gcloud.storage.acl`.
210+
For more detail on access control, see :mod:`gcloud.storage.acl`.

docs/gcloud-api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ Credentials
2828
:members:
2929
:undoc-members:
3030
:show-inheritance:
31+
32+
Exceptions
33+
~~~~~~~~~~
34+
35+
.. automodule:: gcloud.exceptions
36+
:members:
37+
:undoc-members:
38+
:show-inheritance:

docs/storage-api.rst

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,3 @@ Iterators
3030
:members:
3131
:undoc-members:
3232
:show-inheritance:
33-
34-
Exceptions
35-
~~~~~~~~~~
36-
37-
.. automodule:: gcloud.storage.exceptions
38-
:members:
39-
:undoc-members:
40-
:show-inheritance:

gcloud/datastore/connection.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414

1515
"""Connections to gcloud datastore API servers."""
1616

17-
import six
18-
1917
from gcloud import connection
18+
from gcloud.exceptions import make_exception
2019
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
2120
from gcloud.datastore import helpers
2221

@@ -54,7 +53,7 @@ def _request(self, dataset_id, method, data):
5453
5554
:rtype: string
5655
:returns: The string response content from the API call.
57-
:raises: :class:`six.moves.http_client.HTTPException` if the response
56+
:raises: :class:`gcloud.exceptions.GCloudError` if the response
5857
code is not 200 OK.
5958
"""
6059
headers = {
@@ -68,9 +67,7 @@ def _request(self, dataset_id, method, data):
6867

6968
status = headers['status']
7069
if status != '200':
71-
message = ('Request failed with status code %s. '
72-
'Error was: %s' % (status, content))
73-
raise six.moves.http_client.HTTPException(message)
70+
raise make_exception(headers, content)
7471

7572
return content
7673

gcloud/datastore/test_connection.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,16 @@ def test__request_w_200(self):
100100
self.assertEqual(http._called_with['body'], DATA)
101101

102102
def test__request_not_200(self):
103-
import six
103+
from gcloud.exceptions import BadRequest
104104

105105
DATASET_ID = 'DATASET'
106106
METHOD = 'METHOD'
107107
DATA = 'DATA'
108108
conn = self._makeOne()
109-
conn._http = Http({'status': '400'}, 'Bad Request')
110-
with self.assertRaises(six.moves.http_client.HTTPException) as e:
109+
conn._http = Http({'status': '400'}, '{"message": "Bad Request"}')
110+
with self.assertRaises(BadRequest) as e:
111111
conn._request(DATASET_ID, METHOD, DATA)
112-
expected_message = ('Request failed with status code 400. '
113-
'Error was: Bad Request')
112+
expected_message = ('400 Bad Request')
114113
self.assertEqual(str(e.exception), expected_message)
115114

116115
def test__rpc(self):
@@ -845,12 +844,13 @@ class Http(object):
845844
_called_with = None
846845

847846
def __init__(self, headers, content):
848-
self._headers = headers
847+
from httplib2 import Response
848+
self._response = Response(headers)
849849
self._content = content
850850

851851
def request(self, **kw):
852852
self._called_with = kw
853-
return self._headers, self._content
853+
return self._response, self._content
854854

855855

856856
class HttpMultiple(object):

0 commit comments

Comments
 (0)