Skip to content

Commit

Permalink
Adding lazy loading support for storage default project.
Browse files Browse the repository at this point in the history
  • Loading branch information
dhermes committed Mar 14, 2015
1 parent f47f62c commit 6f98e15
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 91 deletions.
21 changes: 1 addition & 20 deletions gcloud/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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.
Expand Down
63 changes: 60 additions & 3 deletions gcloud/storage/_implicit_environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -65,4 +122,4 @@ def get_default_connection():
return _DEFAULTS.connection


_DEFAULTS = _DefaultsContainer()
_DEFAULTS = _DefaultsContainer(implicit=True)
67 changes: 0 additions & 67 deletions gcloud/storage/test___init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
162 changes: 162 additions & 0 deletions gcloud/storage/test__implicit_environ.py
Original file line number Diff line number Diff line change
@@ -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__)
3 changes: 2 additions & 1 deletion regression/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()


Expand Down

0 comments on commit 6f98e15

Please sign in to comment.