Skip to content

Commit 2801483

Browse files
authored
Implementing a httpTimeout option (#72)
* Implementing a httpTimeout option * Upgraded to latest version of google.auth library and fixed a failing test * Updated documentation
1 parent 65ab9ec commit 2801483

File tree

8 files changed

+47
-8
lines changed

8 files changed

+47
-8
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
.cache/
55
.tox/
66
*.egg-info/
7+
build/
8+
dist/
79
*~
810
scripts/cert.json
911
scripts/apikey.txt

firebase_admin/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ def initialize_app(credential=None, options=None, name=_DEFAULT_APP_NAME):
4545
Args:
4646
credential: A credential object used to initialize the SDK (optional). If none is provided,
4747
Google Application Default Credentials are used.
48-
options: A dictionary of configuration options (optional).
48+
options: A dictionary of configuration options (optional). Supported options include
49+
``databaseURL``, ``storageBucket`` and ``httpTimeout``. If ``httpTimeout`` is not set,
50+
HTTP connections initiated by client modules such as ``db`` will not timeout.
4951
name: Name of the app (optional).
5052
5153
Returns:

firebase_admin/db.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ class _Client(_http_client.JsonHttpClient):
673673

674674
_DEFAULT_AUTH_OVERRIDE = '_admin_'
675675

676-
def __init__(self, credential, base_url, auth_override=_DEFAULT_AUTH_OVERRIDE):
676+
def __init__(self, credential, base_url, auth_override=_DEFAULT_AUTH_OVERRIDE, timeout=None):
677677
"""Creates a new _Client from the given parameters.
678678
679679
This exists primarily to enable testing. For regular use, obtain _Client instances by
@@ -686,6 +686,8 @@ def __init__(self, credential, base_url, auth_override=_DEFAULT_AUTH_OVERRIDE):
686686
auth_override: A dictionary representing auth variable overrides or None (optional).
687687
Default value provides admin privileges. A None value here provides un-authenticated
688688
guest privileges.
689+
timeout: HTTP request timeout in seconds (optional). If not set connections will never
690+
timeout, which is the default behavior of the underlying requests library.
689691
"""
690692
_http_client.JsonHttpClient.__init__(
691693
self, credential=credential, base_url=base_url, headers={'User-Agent': _USER_AGENT})
@@ -694,14 +696,16 @@ def __init__(self, credential, base_url, auth_override=_DEFAULT_AUTH_OVERRIDE):
694696
self._auth_override = 'auth_variable_override={0}'.format(encoded)
695697
else:
696698
self._auth_override = None
699+
self._timeout = timeout
697700

698701
@classmethod
699702
def from_app(cls, app):
700703
"""Creates a new _Client for a given App"""
704+
credential = app.credential.get_credential()
701705
db_url = cls._get_db_url(app)
702706
auth_override = cls._get_auth_override(app)
703-
credential = app.credential.get_credential()
704-
return _Client(credential, db_url, auth_override)
707+
timeout = app.options.get('httpTimeout')
708+
return _Client(credential, db_url, auth_override, timeout)
705709

706710
@classmethod
707711
def _get_db_url(cls, app):
@@ -737,6 +741,10 @@ def _get_auth_override(cls, app):
737741
def auth_override(self):
738742
return self._auth_override
739743

744+
@property
745+
def timeout(self):
746+
return self._timeout
747+
740748
def request(self, method, url, **kwargs):
741749
"""Makes an HTTP call using the Python requests library.
742750
@@ -762,6 +770,8 @@ def request(self, method, url, **kwargs):
762770
else:
763771
params = self._auth_override
764772
kwargs['params'] = params
773+
if self._timeout:
774+
kwargs['timeout'] = self._timeout
765775
try:
766776
return super(_Client, self).request(method, url, **kwargs)
767777
except requests.exceptions.RequestException as error:

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pytest >= 3.0.6
33
pytest-cov >= 2.4.0
44
tox >= 2.6.0
55

6-
google-auth >= 1.0.0
6+
google-auth >= 1.1.0
77
google-cloud-storage >= 1.2.0
88
requests >= 2.13.0
99
six >= 1.6.1

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
long_description = ('The Firebase Admin Python SDK enables server-side (backend) Python developers '
3333
'to integrate Firebase into their services and applications.')
3434
install_requires = [
35-
'google-auth>=1.0.0',
35+
'google-auth>=1.1.0',
3636
'google-cloud-storage>=1.2.0',
3737
'requests>=2.13.0',
3838
'six>=1.6.1'

tests/test_credentials.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import google.auth
2121
from google.auth import crypt
22+
from google.auth import exceptions
2223
from google.oauth2 import credentials as gcredentials
2324
from google.oauth2 import service_account
2425
from firebase_admin import credentials
@@ -114,7 +115,7 @@ def test_init(self, app_default):
114115
indirect=True)
115116
def test_nonexisting_path(self, app_default):
116117
del app_default
117-
with pytest.raises(IOError):
118+
with pytest.raises(exceptions.DefaultCredentialsError):
118119
credentials.ApplicationDefault()
119120

120121

tests/test_db.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,15 @@ def test_no_db_url(self):
534534
def test_valid_db_url(self, url):
535535
firebase_admin.initialize_app(testutils.MockCredential(), {'databaseURL' : url})
536536
ref = db.reference()
537+
recorder = []
538+
adapter = MockAdapter('{}', 200, recorder)
539+
ref._client.session.mount(url, adapter)
537540
assert ref._client.base_url == 'https://test.firebaseio.com'
538541
assert ref._client.auth_override is None
542+
assert ref._client.timeout is None
543+
assert ref.get() == {}
544+
assert len(recorder) == 1
545+
assert recorder[0]._extra_kwargs.get('timeout') is None
539546

540547
@pytest.mark.parametrize('url', [
541548
None, '', 'foo', 'http://test.firebaseio.com', 'https://google.com',
@@ -554,6 +561,7 @@ def test_valid_auth_override(self, override):
554561
})
555562
ref = db.reference()
556563
assert ref._client.base_url == 'https://test.firebaseio.com'
564+
assert ref._client.timeout is None
557565
if override == {}:
558566
assert ref._client.auth_override is None
559567
else:
@@ -570,6 +578,22 @@ def test_invalid_auth_override(self, override):
570578
with pytest.raises(ValueError):
571579
db.reference()
572580

581+
def test_http_timeout(self):
582+
test_url = 'https://test.firebaseio.com'
583+
firebase_admin.initialize_app(testutils.MockCredential(), {
584+
'databaseURL' : test_url,
585+
'httpTimeout': 60
586+
})
587+
ref = db.reference()
588+
recorder = []
589+
adapter = MockAdapter('{}', 200, recorder)
590+
ref._client.session.mount(test_url, adapter)
591+
assert ref._client.base_url == test_url
592+
assert ref._client.timeout == 60
593+
assert ref.get() == {}
594+
assert len(recorder) == 1
595+
assert recorder[0]._extra_kwargs['timeout'] == 60
596+
573597
def test_app_delete(self):
574598
app = firebase_admin.initialize_app(
575599
testutils.MockCredential(), {'databaseURL' : 'https://test.firebaseio.com'})

tests/testutils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def __init__(self, data, status, recorder):
100100
self._recorder = recorder
101101

102102
def send(self, request, **kwargs):
103-
del kwargs
103+
request._extra_kwargs = kwargs
104104
self._recorder.append(request)
105105
resp = models.Response()
106106
resp.url = request.url

0 commit comments

Comments
 (0)