1717import json
1818import os
1919import threading
20+ import typing
2021
2122from google .auth .credentials import Credentials as GoogleAuthCredentials
2223from google .auth .exceptions import DefaultCredentialsError
2324from firebase_admin import credentials
2425from firebase_admin .__about__ import __version__
26+ from firebase_admin import _typing
2527
2628
27- _apps = {}
29+ _T = typing .TypeVar ("_T" )
30+
31+ _apps : typing .Dict [str , "App" ] = {}
2832_apps_lock = threading .RLock ()
29- _clock = datetime .datetime .utcnow
33+ _clock = lambda : datetime .datetime .now ( datetime . timezone . utc )
3034
3135_DEFAULT_APP_NAME = '[DEFAULT]'
3236_FIREBASE_CONFIG_ENV_VAR = 'FIREBASE_CONFIG'
3337_CONFIG_VALID_KEYS = ['databaseAuthVariableOverride' , 'databaseURL' , 'httpTimeout' , 'projectId' ,
3438 'storageBucket' ]
3539
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" :
3745 """Initializes and returns a new App instance.
3846
3947 Creates a new App instance using the specified options
@@ -86,7 +94,7 @@ def initialize_app(credential=None, options=None, name=_DEFAULT_APP_NAME):
8694 'you call initialize_app().' ).format (name ))
8795
8896
89- def delete_app (app ) :
97+ def delete_app (app : "App" ) -> None :
9098 """Gracefully deletes an App instance.
9199
92100 Args:
@@ -114,7 +122,7 @@ def delete_app(app):
114122 'second argument.' ).format (app .name ))
115123
116124
117- def get_app (name = _DEFAULT_APP_NAME ):
125+ def get_app (name : str = _DEFAULT_APP_NAME ) -> "App" :
118126 """Retrieves an App instance by name.
119127
120128 Args:
@@ -148,7 +156,7 @@ def get_app(name=_DEFAULT_APP_NAME):
148156class _AppOptions :
149157 """A collection of configuration options for an App."""
150158
151- def __init__ (self , options ) :
159+ def __init__ (self , options : typing . Optional [ typing . Dict [ str , typing . Any ]]) -> None :
152160 if options is None :
153161 options = self ._load_from_environment ()
154162
@@ -157,11 +165,16 @@ def __init__(self, options):
157165 'must be a dictionary.' .format (type (options )))
158166 self ._options = options
159167
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 ]:
161174 """Returns the option identified by the provided key."""
162175 return self ._options .get (key , default )
163176
164- def _load_from_environment (self ):
177+ def _load_from_environment (self ) -> typing . Dict [ str , typing . Any ] :
165178 """Invoked when no options are passed to __init__, loads options from FIREBASE_CONFIG.
166179
167180 If the value of the FIREBASE_CONFIG environment variable starts with "{" an attempt is made
@@ -193,7 +206,12 @@ class App:
193206 common to all Firebase APIs.
194207 """
195208
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 :
197215 """Constructs a new App using the provided name and options.
198216
199217 Args:
@@ -218,37 +236,37 @@ def __init__(self, name, credential, options):
218236 'with a valid credential instance.' )
219237 self ._options = _AppOptions (options )
220238 self ._lock = threading .RLock ()
221- self ._services = {}
239+ self ._services : typing . Optional [ typing . Dict [ str , typing . Any ]] = {}
222240
223241 App ._validate_project_id (self ._options .get ('projectId' ))
224242 self ._project_id_initialized = False
225243
226244 @classmethod
227- def _validate_project_id (cls , project_id ) :
245+ def _validate_project_id (cls , project_id : typing . Optional [ str ]) -> None :
228246 if project_id is not None and not isinstance (project_id , str ):
229247 raise ValueError (
230248 'Invalid project ID: "{0}". project ID must be a string.' .format (project_id ))
231249
232250 @property
233- def name (self ):
251+ def name (self ) -> str :
234252 return self ._name
235253
236254 @property
237- def credential (self ):
255+ def credential (self ) -> credentials . Base :
238256 return self ._credential
239257
240258 @property
241- def options (self ):
259+ def options (self ) -> _AppOptions :
242260 return self ._options
243261
244262 @property
245- def project_id (self ):
263+ def project_id (self ) -> typing . Optional [ str ] :
246264 if not self ._project_id_initialized :
247265 self ._project_id = self ._lookup_project_id ()
248266 self ._project_id_initialized = True
249267 return self ._project_id
250268
251- def _lookup_project_id (self ):
269+ def _lookup_project_id (self ) -> typing . Optional [ str ] :
252270 """Looks up the Firebase project ID associated with an App.
253271
254272 If a ``projectId`` is specified in app options, it is returned. Then tries to
@@ -259,10 +277,10 @@ def _lookup_project_id(self):
259277 Returns:
260278 str: A project ID string or None.
261279 """
262- project_id = self ._options .get ('projectId' )
280+ project_id : typing . Optional [ str ] = self ._options .get ('projectId' )
263281 if not project_id :
264282 try :
265- project_id = self ._credential . project_id
283+ project_id = getattr ( self ._credential , " project_id" )
266284 except (AttributeError , DefaultCredentialsError ):
267285 pass
268286 if not project_id :
@@ -271,7 +289,7 @@ def _lookup_project_id(self):
271289 App ._validate_project_id (self ._options .get ('projectId' ))
272290 return project_id
273291
274- def _get_service (self , name , initializer ) :
292+ def _get_service (self , name : str , initializer : _typing . ServiceInitializer [ _T ]) -> _T :
275293 """Returns the service instance identified by the given name.
276294
277295 Services are functional entities exposed by the Admin SDK (e.g. auth, database). Each
@@ -301,15 +319,16 @@ def _get_service(self, name, initializer):
301319 self ._services [name ] = initializer (self )
302320 return self ._services [name ]
303321
304- def _cleanup (self ):
322+ def _cleanup (self ) -> None :
305323 """Cleans up any services associated with this App.
306324
307325 Checks whether each service contains a close() method, and calls it if available.
308326 This is to be called when an App is being deleted, thus ensuring graceful termination of
309327 any services started by the App.
310328 """
311329 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 ()
315334 self ._services = None
0 commit comments