diff --git a/gcloud/storage/__init__.py b/gcloud/storage/__init__.py index 1cb6af842a4ea..1412188501408 100644 --- a/gcloud/storage/__init__.py +++ b/gcloud/storage/__init__.py @@ -45,6 +45,7 @@ from gcloud.storage._implicit_environ import get_default_bucket from gcloud.storage._implicit_environ import get_default_connection from gcloud.storage._implicit_environ import get_default_project +from gcloud.storage._implicit_environ import set_default_project from gcloud.storage.api import create_bucket from gcloud.storage.api import get_all_buckets from gcloud.storage.api import get_bucket @@ -59,7 +60,6 @@ 'https://www.googleapis.com/auth/devstorage.read_write') _BUCKET_ENV_VAR_NAME = 'GCLOUD_BUCKET_NAME' -_PROJECT_ENV_VAR_NAME = 'GCLOUD_PROJECT' def set_default_bucket(bucket=None): @@ -88,25 +88,6 @@ def set_default_bucket(bucket=None): _implicit_environ._DEFAULTS.bucket = bucket -def set_default_project(project=None): - """Set default bucket name either explicitly or implicitly as fall-back. - - In implicit case, currently only supports enviroment variable but will - support App Engine, Compute Engine and other environments in the future. - - Local environment variable used is: - - GCLOUD_PROJECT - - :type project: string - :param project: Optional. The project name to use as default. - """ - if project is None: - project = os.getenv(_PROJECT_ENV_VAR_NAME) - - if project is not None: - _implicit_environ._DEFAULTS.project = project - - def set_default_connection(connection=None): """Set default connection either explicitly or implicitly as fall-back. diff --git a/gcloud/storage/_implicit_environ.py b/gcloud/storage/_implicit_environ.py index 2dbbf139d2be7..4bfbc46a214a4 100644 --- a/gcloud/storage/_implicit_environ.py +++ b/gcloud/storage/_implicit_environ.py @@ -19,6 +19,55 @@ """ +import os + +from gcloud._helpers import _lazy_property_deco + + +_PROJECT_ENV_VAR_NAME = 'GCLOUD_PROJECT' + + +def _get_production_project(): + """Gets the production project if it can be inferred.""" + return os.getenv(_PROJECT_ENV_VAR_NAME) + + +def _determine_default_project(project=None): + """Determine default project ID explicitly or implicitly as fall-back. + + In implicit case, currently only supports enviroment variable but will + support App Engine, Compute Engine and other environments in the future. + + Local environment variable used is: + - GCLOUD_PROJECT + + :type project: string + :param project: Optional. The project name to use as default. + + :rtype: string or ``NoneType`` + :returns: Default project if it can be determined. + """ + if project is None: + project = _get_production_project() + + return project + + +def set_default_project(project=None): + """Set default project either explicitly or implicitly as fall-back. + + :type project: string + :param project: Optional. The project name to use as default. + + :raises: :class:`EnvironmentError` if no project was found. + """ + project = _determine_default_project(project=project) + if project is not None: + _DEFAULTS.project = project + else: + raise EnvironmentError('No project could be inferred.') + + class _DefaultsContainer(object): """Container for defaults. @@ -32,8 +81,16 @@ class _DefaultsContainer(object): :param connection: Persistent implied connection from environment. """ - def __init__(self, project=None, bucket=None, connection=None): - self.project = project + @_lazy_property_deco + @staticmethod + def project(): + """Return the implicit default project.""" + return _determine_default_project() + + def __init__(self, project=None, bucket=None, connection=None, + implicit=False): + if project is not None or not implicit: + self.project = project self.bucket = bucket self.connection = connection @@ -65,4 +122,4 @@ def get_default_connection(): return _DEFAULTS.connection -_DEFAULTS = _DefaultsContainer() +_DEFAULTS = _DefaultsContainer(implicit=True) diff --git a/gcloud/storage/test___init__.py b/gcloud/storage/test___init__.py index 083291b0dd9df..d4a1e3dfca8ea 100644 --- a/gcloud/storage/test___init__.py +++ b/gcloud/storage/test___init__.py @@ -135,73 +135,6 @@ def test_set_explicit_None_w_env_var_set(self): self.assertEqual(default_bucket.connection, CONNECTION) -class Test_set_default_project(unittest2.TestCase): - - def setUp(self): - from gcloud.storage._testing import _setup_defaults - _setup_defaults(self) - - def tearDown(self): - from gcloud.storage._testing import _tear_down_defaults - _tear_down_defaults(self) - - def _callFUT(self, project=None): - from gcloud.storage import set_default_project - return set_default_project(project=project) - - def _monkey(self, implicit_project): - import os - from gcloud.storage import _PROJECT_ENV_VAR_NAME - from gcloud._testing import _Monkey - environ = {_PROJECT_ENV_VAR_NAME: implicit_project} - return _Monkey(os, getenv=environ.get) - - def test_no_env_var_set(self): - from gcloud.storage import _implicit_environ - with self._monkey(None): - self._callFUT() - self.assertEqual(_implicit_environ.get_default_project(), None) - - def test_set_from_env_var(self): - from gcloud.storage import _implicit_environ - IMPLICIT_PROJECT = 'IMPLICIT' - with self._monkey(IMPLICIT_PROJECT): - self._callFUT() - self.assertEqual(_implicit_environ.get_default_project(), - IMPLICIT_PROJECT) - - def test_set_explicit_w_env_var_set(self): - from gcloud.storage import _implicit_environ - EXPLICIT_PROJECT = 'EXPLICIT' - with self._monkey(None): - self._callFUT(EXPLICIT_PROJECT) - self.assertEqual(_implicit_environ.get_default_project(), - EXPLICIT_PROJECT) - - def test_set_explicit_no_env_var_set(self): - from gcloud.storage import _implicit_environ - IMPLICIT_PROJECT = 'IMPLICIT' - EXPLICIT_PROJECT = 'EXPLICIT' - with self._monkey(IMPLICIT_PROJECT): - self._callFUT(EXPLICIT_PROJECT) - self.assertEqual(_implicit_environ.get_default_project(), - EXPLICIT_PROJECT) - - def test_set_explicit_None_wo_env_var_set(self): - from gcloud.storage import _implicit_environ - with self._monkey(None): - self._callFUT(None) - self.assertEqual(_implicit_environ.get_default_project(), None) - - def test_set_explicit_None_w_env_var_set(self): - from gcloud.storage import _implicit_environ - IMPLICIT_PROJECT = 'IMPLICIT' - with self._monkey(IMPLICIT_PROJECT): - self._callFUT(None) - self.assertEqual(_implicit_environ.get_default_project(), - IMPLICIT_PROJECT) - - class Test_set_default_connection(unittest2.TestCase): def setUp(self): diff --git a/gcloud/storage/test__implicit_environ.py b/gcloud/storage/test__implicit_environ.py new file mode 100644 index 0000000000000..b4529ce8596f3 --- /dev/null +++ b/gcloud/storage/test__implicit_environ.py @@ -0,0 +1,162 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# 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 unittest2 + + +class Test__get_production_project(unittest2.TestCase): + + def _callFUT(self): + from gcloud.storage import _implicit_environ + return _implicit_environ._get_production_project() + + def test_no_value(self): + import os + from gcloud._testing import _Monkey + + environ = {} + with _Monkey(os, getenv=environ.get): + project = self._callFUT() + self.assertEqual(project, None) + + def test_value_set(self): + import os + from gcloud._testing import _Monkey + from gcloud.storage._implicit_environ import _PROJECT_ENV_VAR_NAME + + MOCK_PROJECT = object() + environ = {_PROJECT_ENV_VAR_NAME: MOCK_PROJECT} + with _Monkey(os, getenv=environ.get): + project = self._callFUT() + self.assertEqual(project, MOCK_PROJECT) + + +class Test__determine_default_project(unittest2.TestCase): + + def _callFUT(self, project=None): + from gcloud.storage._implicit_environ import _determine_default_project + return _determine_default_project(project=project) + + def _determine_default_helper(self, prod=None, project=None): + from gcloud._testing import _Monkey + from gcloud.storage import _implicit_environ + + _callers = [] + + def prod_mock(): + _callers.append('prod_mock') + return prod + + patched_methods = { + '_get_production_project': prod_mock, + } + + with _Monkey(_implicit_environ, **patched_methods): + returned_project = self._callFUT(project) + + return returned_project, _callers + + def test_no_value(self): + project, callers = self._determine_default_helper() + self.assertEqual(project, None) + self.assertEqual(callers, ['prod_mock']) + + def test_explicit(self): + PROJECT = object() + project, callers = self._determine_default_helper(project=PROJECT) + self.assertEqual(project, PROJECT) + self.assertEqual(callers, []) + + def test_prod(self): + PROJECT = object() + project, callers = self._determine_default_helper(prod=PROJECT) + self.assertEqual(project, PROJECT) + self.assertEqual(callers, ['prod_mock']) + + +class Test_set_default_project(unittest2.TestCase): + + def setUp(self): + from gcloud.storage._testing import _setup_defaults + _setup_defaults(self) + + def tearDown(self): + from gcloud.storage._testing import _tear_down_defaults + _tear_down_defaults(self) + + def _callFUT(self, project=None): + from gcloud.storage._implicit_environ import set_default_project + return set_default_project(project=project) + + def test_raises(self): + from gcloud._testing import _Monkey + from gcloud.storage import _implicit_environ + + _called_project = [] + + def mock_determine(project): + _called_project.append(project) + return None + + with _Monkey(_implicit_environ, + _determine_default_project=mock_determine): + self.assertRaises(EnvironmentError, self._callFUT) + + self.assertEqual(_called_project, [None]) + + def test_set_correctly(self): + from gcloud._testing import _Monkey + from gcloud.storage import _implicit_environ + + self.assertEqual(_implicit_environ._DEFAULTS.project, None) + + PROJECT = object() + _called_project = [] + + def mock_determine(project): + _called_project.append(project) + return PROJECT + + with _Monkey(_implicit_environ, + _determine_default_project=mock_determine): + self._callFUT() + + self.assertEqual(_implicit_environ._DEFAULTS.project, PROJECT) + self.assertEqual(_called_project, [None]) + + +class Test_lazy_loading(unittest2.TestCase): + + def setUp(self): + from gcloud.storage._testing import _setup_defaults + _setup_defaults(self, implicit=True) + + def tearDown(self): + from gcloud.storage._testing import _tear_down_defaults + _tear_down_defaults(self) + + def test_descriptor_for_project(self): + from gcloud._testing import _Monkey + from gcloud.storage import _implicit_environ + + self.assertFalse('project' in _implicit_environ._DEFAULTS.__dict__) + + DEFAULT = object() + + with _Monkey(_implicit_environ, + _determine_default_project=lambda: DEFAULT): + lazy_loaded = _implicit_environ._DEFAULTS.project + + self.assertEqual(lazy_loaded, DEFAULT) + self.assertTrue('project' in _implicit_environ._DEFAULTS.__dict__) diff --git a/regression/storage.py b/regression/storage.py index 02a35058806cc..46e7927158ff7 100644 --- a/regression/storage.py +++ b/regression/storage.py @@ -20,13 +20,14 @@ from gcloud import exceptions from gcloud import storage from gcloud.storage._helpers import _base64_md5hash +from gcloud.storage import _implicit_environ from gcloud.storage.batch import Batch HTTP = httplib2.Http() SHARED_BUCKETS = {} -storage._PROJECT_ENV_VAR_NAME = 'GCLOUD_TESTS_PROJECT_ID' +_implicit_environ._PROJECT_ENV_VAR_NAME = 'GCLOUD_TESTS_PROJECT_ID' storage.set_defaults()