diff --git a/gcloud/storage/_helpers.py b/gcloud/storage/_helpers.py index ad0fd43d7f2a..f0f28867231c 100644 --- a/gcloud/storage/_helpers.py +++ b/gcloud/storage/_helpers.py @@ -20,6 +20,9 @@ from Crypto.Hash import MD5 import base64 +from gcloud.storage._implicit_environ import get_default_connection +from gcloud.storage.batch import Batch + class _PropertyMixin(object): """Abstract mixin for cloud storage classes with associated propertties. @@ -101,6 +104,30 @@ def patch(self): self._set_properties(api_response) +def _require_connection(connection=None): + """Infer a connection from the environment, if not passed explicitly. + + :type connection: :class:`gcloud.storage.connection.Connection` + :param connection: Optional. + + :rtype: :class:`gcloud.storage.connection.Connection` + :returns: A connection based on the current environment. + :raises: :class:`EnvironmentError` if ``connection`` is ``None``, and + cannot be inferred from the environment. + """ + # NOTE: We use current Batch directly since it inherits from Connection. + if connection is None: + connection = Batch.current() + + if connection is None: + connection = get_default_connection() + + if connection is None: + raise EnvironmentError('Connection could not be inferred.') + + return connection + + def _scalar_property(fieldname): """Create a property descriptor around the :class:`_PropertyMixin` helpers. """ diff --git a/gcloud/storage/api.py b/gcloud/storage/api.py index 47956add611e..1d5a047e073d 100644 --- a/gcloud/storage/api.py +++ b/gcloud/storage/api.py @@ -20,8 +20,7 @@ from gcloud.exceptions import NotFound from gcloud._helpers import get_default_project -from gcloud.storage._implicit_environ import get_default_connection -from gcloud.storage.batch import Batch +from gcloud.storage._helpers import _require_connection from gcloud.storage.bucket import Bucket from gcloud.storage.iterator import Iterator @@ -227,27 +226,3 @@ def get_items_from_response(self, response): bucket = Bucket(name, connection=self.connection) bucket._set_properties(item) yield bucket - - -def _require_connection(connection=None): - """Infer a connection from the environment, if not passed explicitly. - - :type connection: :class:`gcloud.storage.connection.Connection` - :param connection: Optional. - - :rtype: :class:`gcloud.storage.connection.Connection` - :returns: A connection based on the current environment. - :raises: :class:`EnvironmentError` if ``connection`` is ``None``, and - cannot be inferred from the environment. - """ - # NOTE: We use current Batch directly since it inherits from Connection. - if connection is None: - connection = Batch.current() - - if connection is None: - connection = get_default_connection() - - if connection is None: - raise EnvironmentError('Connection could not be inferred.') - - return connection diff --git a/gcloud/storage/blob.py b/gcloud/storage/blob.py index dc30d21d5f7e..74e934813ec7 100644 --- a/gcloud/storage/blob.py +++ b/gcloud/storage/blob.py @@ -31,6 +31,7 @@ from gcloud.credentials import generate_signed_url from gcloud.exceptions import NotFound from gcloud.storage._helpers import _PropertyMixin +from gcloud.storage._helpers import _require_connection from gcloud.storage._helpers import _scalar_property from gcloud.storage import _implicit_environ from gcloud.storage.acl import ObjectACL @@ -164,7 +165,8 @@ def public_url(self): bucket_name=self.bucket.name, quoted_name=quote(self.name, safe='')) - def generate_signed_url(self, expiration, method='GET'): + def generate_signed_url(self, expiration, method='GET', + connection=None, credentials=None): """Generates a signed URL for this blob. If you have a blob that you want to allow access to for a set @@ -181,6 +183,15 @@ def generate_signed_url(self, expiration, method='GET'): :type method: string :param method: The HTTP verb that will be used when requesting the URL. + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :param credentials: The OAuth2 credentials to use to sign the URL. + :rtype: string :returns: A signed URL you can use to access the resource until expiration. @@ -189,23 +200,33 @@ def generate_signed_url(self, expiration, method='GET'): bucket_name=self.bucket.name, quoted_name=quote(self.name, safe='')) + if credentials is None: + connection = _require_connection(connection) + credentials = connection.credentials + return generate_signed_url( - self.connection.credentials, resource=resource, + credentials, resource=resource, api_access_endpoint=_API_ACCESS_ENDPOINT, expiration=expiration, method=method) - def exists(self): + def exists(self, connection=None): """Determines whether or not this blob exists. + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. + :rtype: boolean :returns: True if the blob exists in Cloud Storage. """ + connection = _require_connection(connection) try: # We only need the status code (200 or not) so we seek to # minimize the returned payload. query_params = {'fields': 'name'} - self.connection.api_request(method='GET', path=self.path, - query_params=query_params) + connection.api_request(method='GET', path=self.path, + query_params=query_params) return True except NotFound: return False @@ -242,15 +263,20 @@ def delete(self): """ return self.bucket.delete_blob(self.name) - def download_to_file(self, file_obj): + def download_to_file(self, file_obj, connection=None): """Download the contents of this blob into a file-like object. :type file_obj: file :param file_obj: A file handle to which to write the blob's data. + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. + :raises: :class:`gcloud.exceptions.NotFound` """ - + connection = _require_connection(connection) download_url = self.media_link # Use apitools 'Download' facility. @@ -261,7 +287,7 @@ def download_to_file(self, file_obj): headers['Range'] = 'bytes=0-%d' % (self.chunk_size - 1,) request = http_wrapper.Request(download_url, 'GET', headers) - download.InitializeDownload(request, self.connection.http) + download.InitializeDownload(request, connection.http) # Should we be passing callbacks through from caller? We can't # pass them as None, because apitools wants to print to the console @@ -269,33 +295,43 @@ def download_to_file(self, file_obj): download.StreamInChunks(callback=lambda *args: None, finish_callback=lambda *args: None) - def download_to_filename(self, filename): + def download_to_filename(self, filename, connection=None): """Download the contents of this blob into a named file. :type filename: string :param filename: A filename to be passed to ``open``. + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. + :raises: :class:`gcloud.exceptions.NotFound` """ with open(filename, 'wb') as file_obj: - self.download_to_file(file_obj) + self.download_to_file(file_obj, connection=connection) mtime = time.mktime(self.updated.timetuple()) os.utime(file_obj.name, (mtime, mtime)) - def download_as_string(self): + def download_as_string(self, connection=None): """Download the contents of this blob as a string. + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. + :rtype: bytes :returns: The data stored in this blob. :raises: :class:`gcloud.exceptions.NotFound` """ string_buffer = BytesIO() - self.download_to_file(string_buffer) + self.download_to_file(string_buffer, connection=connection) return string_buffer.getvalue() def upload_from_file(self, file_obj, rewind=False, size=None, - content_type=None, num_retries=6): + content_type=None, num_retries=6, connection=None): """Upload the contents of this blob from a file-like object. The content type of the upload will either be @@ -331,7 +367,13 @@ def upload_from_file(self, file_obj, rewind=False, size=None, :type num_retries: integer :param num_retries: Number of upload retries. Defaults to 6. + + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. """ + connection = _require_connection(connection) content_type = (content_type or self._properties.get('contentType') or 'application/octet-stream') @@ -341,11 +383,10 @@ def upload_from_file(self, file_obj, rewind=False, size=None, # Get the basic stats about the file. total_bytes = size or os.fstat(file_obj.fileno()).st_size - conn = self.connection headers = { 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', - 'User-Agent': conn.USER_AGENT, + 'User-Agent': connection.USER_AGENT, } upload = transfer.Upload(file_obj, content_type, total_bytes, @@ -357,20 +398,20 @@ def upload_from_file(self, file_obj, rewind=False, size=None, upload_config = _UploadConfig() # Temporary URL, until we know simple vs. resumable. - base_url = conn.API_BASE_URL + '/upload' - upload_url = conn.build_api_url(api_base_url=base_url, - path=self.bucket.path + '/o') + base_url = connection.API_BASE_URL + '/upload' + upload_url = connection.build_api_url(api_base_url=base_url, + path=self.bucket.path + '/o') # Use apitools 'Upload' facility. request = http_wrapper.Request(upload_url, 'POST', headers) upload.ConfigureRequest(upload_config, request, url_builder) query_params = url_builder.query_params - base_url = conn.API_BASE_URL + '/upload' - request.url = conn.build_api_url(api_base_url=base_url, - path=self.bucket.path + '/o', - query_params=query_params) - upload.InitializeUpload(request, conn.http) + base_url = connection.API_BASE_URL + '/upload' + request.url = connection.build_api_url(api_base_url=base_url, + path=self.bucket.path + '/o', + query_params=query_params) + upload.InitializeUpload(request, connection.http) # Should we be passing callbacks through from caller? We can't # pass them as None, because apitools wants to print to the console @@ -380,7 +421,7 @@ def upload_from_file(self, file_obj, rewind=False, size=None, callback=lambda *args: None, finish_callback=lambda *args: None) else: - http_response = http_wrapper.MakeRequest(conn.http, request, + http_response = http_wrapper.MakeRequest(connection.http, request, retries=num_retries) response_content = http_response.content if not isinstance(response_content, @@ -388,7 +429,8 @@ def upload_from_file(self, file_obj, rewind=False, size=None, response_content = response_content.decode('utf-8') self._set_properties(json.loads(response_content)) - def upload_from_filename(self, filename, content_type=None): + def upload_from_filename(self, filename, content_type=None, + connection=None): """Upload this blob's contents from the content of a named file. The content type of the upload will either be @@ -412,15 +454,22 @@ def upload_from_filename(self, filename, content_type=None): :type content_type: string or ``NoneType`` :param content_type: Optional type of content being uploaded. + + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. """ content_type = content_type or self._properties.get('contentType') if content_type is None: content_type, _ = mimetypes.guess_type(filename) with open(filename, 'rb') as file_obj: - self.upload_from_file(file_obj, content_type=content_type) + self.upload_from_file(file_obj, content_type=content_type, + connection=connection) - def upload_from_string(self, data, content_type='text/plain'): + def upload_from_string(self, data, content_type='text/plain', + connection=None): """Upload contents of this blob from the provided string. .. note:: @@ -437,14 +486,19 @@ def upload_from_string(self, data, content_type='text/plain'): :type data: bytes or text :param data: The data to store in this blob. If the value is text, it will be encoded as UTF-8. + + :type connection: :class:`gcloud.storage.connection.Connection` or + ``NoneType`` + :param connection: Optional. The connection to use when sending + requests. If not provided, falls back to default. """ if isinstance(data, six.text_type): data = data.encode('utf-8') string_buffer = BytesIO() string_buffer.write(data) self.upload_from_file(file_obj=string_buffer, rewind=True, - size=len(data), - content_type=content_type) + size=len(data), content_type=content_type, + connection=connection) def make_public(self): """Make this blob public giving all users read access.""" diff --git a/gcloud/storage/test__helpers.py b/gcloud/storage/test__helpers.py index 431fd9bc9b1e..d8f608ab71d1 100644 --- a/gcloud/storage/test__helpers.py +++ b/gcloud/storage/test__helpers.py @@ -115,6 +115,44 @@ def _patch_property(self, name, value): self.assertEqual(test._patched, ('solfege', 'Latido')) +class Test__require_connection(unittest2.TestCase): + + def _callFUT(self, connection=None): + from gcloud.storage._helpers import _require_connection + return _require_connection(connection=connection) + + def _monkey(self, connection): + from gcloud.storage._testing import _monkey_defaults + return _monkey_defaults(connection=connection) + + def test_implicit_unset(self): + with self._monkey(None): + with self.assertRaises(EnvironmentError): + self._callFUT() + + def test_implicit_unset_w_existing_batch(self): + CONNECTION = object() + with self._monkey(None): + with _NoCommitBatch(connection=CONNECTION): + self.assertEqual(self._callFUT(), CONNECTION) + + def test_implicit_unset_passed_explicitly(self): + CONNECTION = object() + with self._monkey(None): + self.assertTrue(self._callFUT(CONNECTION) is CONNECTION) + + def test_implicit_set(self): + IMPLICIT_CONNECTION = object() + with self._monkey(IMPLICIT_CONNECTION): + self.assertTrue(self._callFUT() is IMPLICIT_CONNECTION) + + def test_implicit_set_passed_explicitly(self): + IMPLICIT_CONNECTION = object() + CONNECTION = object() + with self._monkey(IMPLICIT_CONNECTION): + self.assertTrue(self._callFUT(CONNECTION) is CONNECTION) + + class Test__base64_md5hash(unittest2.TestCase): def _callFUT(self, bytes_to_sign): @@ -208,3 +246,18 @@ def __init__(self): def b64encode(self, value): self._called_b64encode.append(value) return value + + +class _NoCommitBatch(object): + + def __init__(self, connection): + self._connection = connection + + def __enter__(self): + from gcloud.storage.batch import _BATCHES + _BATCHES.push(self._connection) + return self._connection + + def __exit__(self, *args): + from gcloud.storage.batch import _BATCHES + _BATCHES.pop() diff --git a/gcloud/storage/test_api.py b/gcloud/storage/test_api.py index f9449fbd5eef..3874a518a4e6 100644 --- a/gcloud/storage/test_api.py +++ b/gcloud/storage/test_api.py @@ -349,44 +349,6 @@ def test_get_items_from_response_non_empty(self): self.assertEqual(bucket.name, BLOB_NAME) -class Test__require_connection(unittest2.TestCase): - - def _callFUT(self, connection=None): - from gcloud.storage.api import _require_connection - return _require_connection(connection=connection) - - def _monkey(self, connection): - from gcloud.storage._testing import _monkey_defaults - return _monkey_defaults(connection=connection) - - def test_implicit_unset(self): - with self._monkey(None): - with self.assertRaises(EnvironmentError): - self._callFUT() - - def test_implicit_unset_w_existing_batch(self): - CONNECTION = object() - with self._monkey(None): - with _NoCommitBatch(connection=CONNECTION): - self.assertEqual(self._callFUT(), CONNECTION) - - def test_implicit_unset_passed_explicitly(self): - CONNECTION = object() - with self._monkey(None): - self.assertTrue(self._callFUT(CONNECTION) is CONNECTION) - - def test_implicit_set(self): - IMPLICIT_CONNECTION = object() - with self._monkey(IMPLICIT_CONNECTION): - self.assertTrue(self._callFUT() is IMPLICIT_CONNECTION) - - def test_implicit_set_passed_explicitly(self): - IMPLICIT_CONNECTION = object() - CONNECTION = object() - with self._monkey(IMPLICIT_CONNECTION): - self.assertTrue(self._callFUT(CONNECTION) is CONNECTION) - - class Http(object): _called_with = None @@ -399,18 +361,3 @@ def __init__(self, headers, content): def request(self, **kw): self._called_with = kw return self._response, self._content - - -class _NoCommitBatch(object): - - def __init__(self, connection): - self._connection = connection - - def __enter__(self): - from gcloud.storage.batch import _BATCHES - _BATCHES.push(self._connection) - return self._connection - - def __exit__(self, *args): - from gcloud.storage.batch import _BATCHES - _BATCHES.pop() diff --git a/gcloud/storage/test_blob.py b/gcloud/storage/test_blob.py index 4c3d38dcbaef..3df74e7efe4e 100644 --- a/gcloud/storage/test_blob.py +++ b/gcloud/storage/test_blob.py @@ -155,24 +155,42 @@ def test_public_url_w_slash_in_name(self): blob.public_url, 'http://commondatastorage.googleapis.com/name/parent%2Fchild') - def test_generate_signed_url_w_default_method(self): + def _basic_generate_signed_url_helper(self, credentials=None): from gcloud._testing import _Monkey from gcloud.storage import blob as MUT BLOB_NAME = 'blob-name' EXPIRATION = '2014-10-16T20:34:37.000Z' connection = _Connection() - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) URI = ('http://example.com/abucket/a-blob-name?Signature=DEADBEEF' '&Expiration=2014-10-16T20:34:37.000Z') + _called_require = [] + + def mock_require(connection): + _called_require.append(connection) + return connection + SIGNER = _Signer() - with _Monkey(MUT, generate_signed_url=SIGNER): - self.assertEqual(blob.generate_signed_url(EXPIRATION), URI) + with _Monkey(MUT, generate_signed_url=SIGNER, + _require_connection=mock_require): + signed_uri = blob.generate_signed_url(EXPIRATION, + connection=connection, + credentials=credentials) + self.assertEqual(signed_uri, URI) + + if credentials is None: + self.assertEqual(_called_require, [connection]) + else: + self.assertEqual(_called_require, []) PATH = '/name/%s' % (BLOB_NAME,) - EXPECTED_ARGS = (_Connection.credentials,) + if credentials is None: + EXPECTED_ARGS = (_Connection.credentials,) + else: + EXPECTED_ARGS = (credentials,) EXPECTED_KWARGS = { 'api_access_endpoint': 'https://storage.googleapis.com', 'expiration': EXPIRATION, @@ -181,6 +199,13 @@ def test_generate_signed_url_w_default_method(self): } self.assertEqual(SIGNER._signed, [(EXPECTED_ARGS, EXPECTED_KWARGS)]) + def test_generate_signed_url_w_default_method(self): + self._basic_generate_signed_url_helper() + + def test_generate_signed_url_w_credentials(self): + credentials = object() + self._basic_generate_signed_url_helper(credentials=credentials) + def test_generate_signed_url_w_slash_in_name(self): from gcloud._testing import _Monkey from gcloud.storage import blob as MUT @@ -188,14 +213,16 @@ def test_generate_signed_url_w_slash_in_name(self): BLOB_NAME = 'parent/child' EXPIRATION = '2014-10-16T20:34:37.000Z' connection = _Connection() - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) URI = ('http://example.com/abucket/a-blob-name?Signature=DEADBEEF' '&Expiration=2014-10-16T20:34:37.000Z') SIGNER = _Signer() with _Monkey(MUT, generate_signed_url=SIGNER): - self.assertEqual(blob.generate_signed_url(EXPIRATION), URI) + signed_url = blob.generate_signed_url(EXPIRATION, + connection=connection) + self.assertEqual(signed_url, URI) EXPECTED_ARGS = (_Connection.credentials,) EXPECTED_KWARGS = { @@ -213,15 +240,16 @@ def test_generate_signed_url_w_explicit_method(self): BLOB_NAME = 'blob-name' EXPIRATION = '2014-10-16T20:34:37.000Z' connection = _Connection() - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) URI = ('http://example.com/abucket/a-blob-name?Signature=DEADBEEF' '&Expiration=2014-10-16T20:34:37.000Z') SIGNER = _Signer() with _Monkey(MUT, generate_signed_url=SIGNER): - self.assertEqual( - blob.generate_signed_url(EXPIRATION, method='POST'), URI) + signed_uri = blob.generate_signed_url(EXPIRATION, method='POST', + connection=connection) + self.assertEqual(signed_uri, URI) PATH = '/name/%s' % (BLOB_NAME,) EXPECTED_ARGS = (_Connection.credentials,) @@ -238,19 +266,19 @@ def test_exists_miss(self): NONESUCH = 'nonesuch' not_found_response = {'status': NOT_FOUND} connection = _Connection(not_found_response) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(NONESUCH, bucket=bucket) - self.assertFalse(blob.exists()) + self.assertFalse(blob.exists(connection=connection)) def test_exists_hit(self): from six.moves.http_client import OK BLOB_NAME = 'blob-name' found_response = {'status': OK} connection = _Connection(found_response) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) bucket._blobs[BLOB_NAME] = 1 - self.assertTrue(blob.exists()) + self.assertTrue(blob.exists(connection=connection)) def test_rename(self): BLOB_NAME = 'blob-name' @@ -271,11 +299,11 @@ def test_delete(self): BLOB_NAME = 'blob-name' not_found_response = {'status': NOT_FOUND} connection = _Connection(not_found_response) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) bucket._blobs[BLOB_NAME] = 1 blob.delete() - self.assertFalse(blob.exists()) + self.assertFalse(blob.exists(connection=connection)) def _download_to_file_helper(self, chunk_size=None): from six.moves.http_client import OK @@ -290,7 +318,7 @@ def _download_to_file_helper(self, chunk_size=None): (chunk1_response, b'abc'), (chunk2_response, b'def'), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) MEDIA_LINK = 'http://example.com/media/' properties = {'mediaLink': MEDIA_LINK} blob = self._makeOne(BLOB_NAME, bucket=bucket, properties=properties) @@ -298,7 +326,7 @@ def _download_to_file_helper(self, chunk_size=None): blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = chunk_size fh = BytesIO() - blob.download_to_file(fh) + blob.download_to_file(fh, connection=connection) self.assertEqual(fh.getvalue(), b'abcdef') def test_download_to_file_default(self): @@ -322,7 +350,7 @@ def test_download_to_filename(self): (chunk1_response, b'abc'), (chunk2_response, b'def'), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) MEDIA_LINK = 'http://example.com/media/' properties = {'mediaLink': MEDIA_LINK, 'updated': '2014-12-06T13:13:50.690Z'} @@ -330,7 +358,7 @@ def test_download_to_filename(self): blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 3 with NamedTemporaryFile() as f: - blob.download_to_filename(f.name) + blob.download_to_filename(f.name, connection=connection) f.flush() with open(f.name, 'rb') as g: wrote = g.read() @@ -351,13 +379,13 @@ def test_download_as_string(self): (chunk1_response, b'abc'), (chunk2_response, b'def'), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) MEDIA_LINK = 'http://example.com/media/' properties = {'mediaLink': MEDIA_LINK} blob = self._makeOne(BLOB_NAME, bucket=bucket, properties=properties) blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 3 - fetched = blob.download_as_string() + fetched = blob.download_as_string(connection=connection) self.assertEqual(fetched, b'abcdef') def _upload_from_file_simple_test_helper(self, properties=None, @@ -373,7 +401,7 @@ def _upload_from_file_simple_test_helper(self, properties=None, connection = _Connection( (response, b'{}'), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket, properties=properties) blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 5 @@ -381,7 +409,8 @@ def _upload_from_file_simple_test_helper(self, properties=None, fh.write(DATA) fh.flush() blob.upload_from_file(fh, rewind=True, - content_type=content_type_arg) + content_type=content_type_arg, + connection=connection) rq = connection.http._requested self.assertEqual(len(rq), 1) self.assertEqual(rq[0]['method'], 'POST') @@ -442,7 +471,7 @@ def test_upload_from_file_resumable(self): (chunk1_response, b''), (chunk2_response, b'{}'), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 5 @@ -451,7 +480,7 @@ def test_upload_from_file_resumable(self): with NamedTemporaryFile() as fh: fh.write(DATA) fh.flush() - blob.upload_from_file(fh, rewind=True) + blob.upload_from_file(fh, rewind=True, connection=connection) rq = connection.http._requested self.assertEqual(len(rq), 3) self.assertEqual(rq[0]['method'], 'POST') @@ -500,14 +529,14 @@ def test_upload_from_file_w_slash_in_name(self): (chunk1_response, ''), (chunk2_response, ''), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 5 with NamedTemporaryFile() as fh: fh.write(DATA) fh.flush() - blob.upload_from_file(fh, rewind=True) + blob.upload_from_file(fh, rewind=True, connection=connection) self.assertEqual(fh.tell(), len(DATA)) rq = connection.http._requested self.assertEqual(len(rq), 1) @@ -547,7 +576,7 @@ def _upload_from_filename_test_helper(self, properties=None, (chunk1_response, ''), (chunk2_response, ''), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket, properties=properties) blob._CHUNK_SIZE_MULTIPLE = 1 @@ -555,7 +584,8 @@ def _upload_from_filename_test_helper(self, properties=None, with NamedTemporaryFile(suffix='.jpeg') as fh: fh.write(DATA) fh.flush() - blob.upload_from_filename(fh.name, content_type=content_type_arg) + blob.upload_from_filename(fh.name, content_type=content_type_arg, + connection=connection) rq = connection.http._requested self.assertEqual(len(rq), 1) self.assertEqual(rq[0]['method'], 'POST') @@ -612,11 +642,11 @@ def test_upload_from_string_w_bytes(self): (chunk1_response, ''), (chunk2_response, ''), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 5 - blob.upload_from_string(DATA) + blob.upload_from_string(DATA, connection=connection) rq = connection.http._requested self.assertEqual(len(rq), 1) self.assertEqual(rq[0]['method'], 'POST') @@ -651,11 +681,11 @@ def test_upload_from_string_w_text(self): (chunk1_response, ''), (chunk2_response, ''), ) - bucket = _Bucket(connection) + bucket = _Bucket(None) blob = self._makeOne(BLOB_NAME, bucket=bucket) blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 5 - blob.upload_from_string(DATA) + blob.upload_from_string(DATA, connection=connection) rq = connection.http._requested self.assertEqual(len(rq), 1) self.assertEqual(rq[0]['method'], 'POST') diff --git a/gcloud/storage/test_bucket.py b/gcloud/storage/test_bucket.py index 1de52a5e55b2..c56c12745d0e 100644 --- a/gcloud/storage/test_bucket.py +++ b/gcloud/storage/test_bucket.py @@ -118,21 +118,25 @@ def test___iter___non_empty(self): self.assertEqual(kw['query_params'], {'projection': 'noAcl'}) def test___contains___miss(self): + from gcloud.storage._testing import _monkey_defaults NAME = 'name' NONESUCH = 'nonesuch' connection = _Connection() - bucket = self._makeOne(NAME, connection) - self.assertFalse(NONESUCH in bucket) + bucket = self._makeOne(NAME, None) + with _monkey_defaults(connection=connection): + self.assertFalse(NONESUCH in bucket) kw, = connection._requested self.assertEqual(kw['method'], 'GET') self.assertEqual(kw['path'], '/b/%s/o/%s' % (NAME, NONESUCH)) def test___contains___hit(self): + from gcloud.storage._testing import _monkey_defaults NAME = 'name' BLOB_NAME = 'blob-name' connection = _Connection({'name': BLOB_NAME}) - bucket = self._makeOne(NAME, connection) - self.assertTrue(BLOB_NAME in bucket) + bucket = self._makeOne(NAME, None) + with _monkey_defaults(connection=connection): + self.assertTrue(BLOB_NAME in bucket) kw, = connection._requested self.assertEqual(kw['method'], 'GET') self.assertEqual(kw['path'], '/b/%s/o/%s' % (NAME, BLOB_NAME))