17
17
import json
18
18
import os
19
19
import threading
20
+ import typing
20
21
21
22
from google .auth .credentials import Credentials as GoogleAuthCredentials
22
23
from google .auth .exceptions import DefaultCredentialsError
23
24
from firebase_admin import credentials
24
25
from firebase_admin .__about__ import __version__
26
+ from firebase_admin import _typing
25
27
26
28
27
- _apps = {}
29
+ _T = typing .TypeVar ("_T" )
30
+
31
+ _apps : typing .Dict [str , "App" ] = {}
28
32
_apps_lock = threading .RLock ()
29
- _clock = datetime .datetime .utcnow
33
+ _clock = lambda : datetime .datetime .now ( datetime . timezone . utc )
30
34
31
35
_DEFAULT_APP_NAME = '[DEFAULT]'
32
36
_FIREBASE_CONFIG_ENV_VAR = 'FIREBASE_CONFIG'
33
37
_CONFIG_VALID_KEYS = ['databaseAuthVariableOverride' , 'databaseURL' , 'httpTimeout' , 'projectId' ,
34
38
'storageBucket' ]
35
39
36
- def initialize_app (credential = None , options = None , name = _DEFAULT_APP_NAME ):
40
+ def initialize_app (
41
+ credential : typing .Optional [_typing .CredentialLike ] = None ,
42
+ options : typing .Optional [typing .Dict [str , typing .Any ]] = None ,
43
+ name : str = _DEFAULT_APP_NAME
44
+ ) -> "App" :
37
45
"""Initializes and returns a new App instance.
38
46
39
47
Creates a new App instance using the specified options
@@ -86,7 +94,7 @@ def initialize_app(credential=None, options=None, name=_DEFAULT_APP_NAME):
86
94
'you call initialize_app().' ).format (name ))
87
95
88
96
89
- def delete_app (app ) :
97
+ def delete_app (app : "App" ) -> None :
90
98
"""Gracefully deletes an App instance.
91
99
92
100
Args:
@@ -114,7 +122,7 @@ def delete_app(app):
114
122
'second argument.' ).format (app .name ))
115
123
116
124
117
- def get_app (name = _DEFAULT_APP_NAME ):
125
+ def get_app (name : str = _DEFAULT_APP_NAME ) -> "App" :
118
126
"""Retrieves an App instance by name.
119
127
120
128
Args:
@@ -148,7 +156,7 @@ def get_app(name=_DEFAULT_APP_NAME):
148
156
class _AppOptions :
149
157
"""A collection of configuration options for an App."""
150
158
151
- def __init__ (self , options ) :
159
+ def __init__ (self , options : typing . Optional [ typing . Dict [ str , typing . Any ]]) -> None :
152
160
if options is None :
153
161
options = self ._load_from_environment ()
154
162
@@ -157,11 +165,16 @@ def __init__(self, options):
157
165
'must be a dictionary.' .format (type (options )))
158
166
self ._options = options
159
167
160
- def get (self , key , default = None ):
168
+ @typing .overload
169
+ def get (self , key : str , default : None = None ) -> typing .Optional [typing .Any ]: ...
170
+ # possible issue: needs return Any | _T ?
171
+ @typing .overload
172
+ def get (self , key : str , default : _T ) -> _T : ...
173
+ def get (self , key : str , default : typing .Any = None ) -> typing .Optional [typing .Any ]:
161
174
"""Returns the option identified by the provided key."""
162
175
return self ._options .get (key , default )
163
176
164
- def _load_from_environment (self ):
177
+ def _load_from_environment (self ) -> typing . Dict [ str , typing . Any ] :
165
178
"""Invoked when no options are passed to __init__, loads options from FIREBASE_CONFIG.
166
179
167
180
If the value of the FIREBASE_CONFIG environment variable starts with "{" an attempt is made
@@ -193,7 +206,12 @@ class App:
193
206
common to all Firebase APIs.
194
207
"""
195
208
196
- def __init__ (self , name , credential , options ):
209
+ def __init__ (
210
+ self ,
211
+ name : str ,
212
+ credential : _typing .CredentialLike ,
213
+ options : typing .Optional [typing .Dict [str , typing .Any ]]
214
+ ) -> None :
197
215
"""Constructs a new App using the provided name and options.
198
216
199
217
Args:
@@ -218,37 +236,37 @@ def __init__(self, name, credential, options):
218
236
'with a valid credential instance.' )
219
237
self ._options = _AppOptions (options )
220
238
self ._lock = threading .RLock ()
221
- self ._services = {}
239
+ self ._services : typing . Optional [ typing . Dict [ str , typing . Any ]] = {}
222
240
223
241
App ._validate_project_id (self ._options .get ('projectId' ))
224
242
self ._project_id_initialized = False
225
243
226
244
@classmethod
227
- def _validate_project_id (cls , project_id ) :
245
+ def _validate_project_id (cls , project_id : typing . Optional [ str ]) -> None :
228
246
if project_id is not None and not isinstance (project_id , str ):
229
247
raise ValueError (
230
248
'Invalid project ID: "{0}". project ID must be a string.' .format (project_id ))
231
249
232
250
@property
233
- def name (self ):
251
+ def name (self ) -> str :
234
252
return self ._name
235
253
236
254
@property
237
- def credential (self ):
255
+ def credential (self ) -> credentials . Base :
238
256
return self ._credential
239
257
240
258
@property
241
- def options (self ):
259
+ def options (self ) -> _AppOptions :
242
260
return self ._options
243
261
244
262
@property
245
- def project_id (self ):
263
+ def project_id (self ) -> typing . Optional [ str ] :
246
264
if not self ._project_id_initialized :
247
265
self ._project_id = self ._lookup_project_id ()
248
266
self ._project_id_initialized = True
249
267
return self ._project_id
250
268
251
- def _lookup_project_id (self ):
269
+ def _lookup_project_id (self ) -> typing . Optional [ str ] :
252
270
"""Looks up the Firebase project ID associated with an App.
253
271
254
272
If a ``projectId`` is specified in app options, it is returned. Then tries to
@@ -259,10 +277,10 @@ def _lookup_project_id(self):
259
277
Returns:
260
278
str: A project ID string or None.
261
279
"""
262
- project_id = self ._options .get ('projectId' )
280
+ project_id : typing . Optional [ str ] = self ._options .get ('projectId' )
263
281
if not project_id :
264
282
try :
265
- project_id = self ._credential . project_id
283
+ project_id = getattr ( self ._credential , " project_id" )
266
284
except (AttributeError , DefaultCredentialsError ):
267
285
pass
268
286
if not project_id :
@@ -271,7 +289,7 @@ def _lookup_project_id(self):
271
289
App ._validate_project_id (self ._options .get ('projectId' ))
272
290
return project_id
273
291
274
- def _get_service (self , name , initializer ) :
292
+ def _get_service (self , name : str , initializer : _typing . ServiceInitializer [ _T ]) -> _T :
275
293
"""Returns the service instance identified by the given name.
276
294
277
295
Services are functional entities exposed by the Admin SDK (e.g. auth, database). Each
@@ -301,15 +319,16 @@ def _get_service(self, name, initializer):
301
319
self ._services [name ] = initializer (self )
302
320
return self ._services [name ]
303
321
304
- def _cleanup (self ):
322
+ def _cleanup (self ) -> None :
305
323
"""Cleans up any services associated with this App.
306
324
307
325
Checks whether each service contains a close() method, and calls it if available.
308
326
This is to be called when an App is being deleted, thus ensuring graceful termination of
309
327
any services started by the App.
310
328
"""
311
329
with self ._lock :
312
- for service in self ._services .values ():
313
- if hasattr (service , 'close' ) and hasattr (service .close , '__call__' ):
314
- service .close ()
330
+ if self ._services :
331
+ for service in self ._services .values ():
332
+ if hasattr (service , 'close' ) and hasattr (service .close , '__call__' ):
333
+ service .close ()
315
334
self ._services = None
0 commit comments