55import tempfile
66import time
77from http import HTTPStatus
8+ from typing import Any , Callable , TypeVar , cast
89
910import filelock
1011import flask
12+ from flask .typing import ResponseReturnValue
1113
1214import processor
1315
14-
1516# The file will be flock()'ed if a report is being processed.
1617LOCK_FILE = '/tmp/results-processor.lock'
1718# If the file above is locked, this timestamp file contains the UNIX timestamp
3233app = flask .Flask (__name__ )
3334
3435
35- def _atomic_write (path , content ) :
36+ def _atomic_write (path : str , content : str ) -> None :
3637 # Do not auto-delete the file because we will move it after closing it.
3738 temp = tempfile .NamedTemporaryFile (mode = 'wt' , delete = False )
3839 temp .write (content )
@@ -41,12 +42,15 @@ def _atomic_write(path, content):
4142 os .replace (temp .name , path )
4243
4344
44- def _serial_task (func ):
45+ F = TypeVar ('F' , bound = Callable [..., Any ])
46+
47+
48+ def _serial_task (func : F ) -> F :
4549 lock = filelock .FileLock (LOCK_FILE )
4650
4751 # It is important to use wraps() to preserve the original name & docstring.
4852 @functools .wraps (func )
49- def decorated_func (* args , ** kwargs ) :
53+ def decorated_func (* args : object , ** kwargs : object ) -> object :
5054 try :
5155 with lock .acquire (timeout = 1 ):
5256 return func (* args , ** kwargs )
@@ -55,24 +59,24 @@ def decorated_func(*args, **kwargs):
5559 return ('A result is currently being processed.' ,
5660 HTTPStatus .SERVICE_UNAVAILABLE )
5761
58- return decorated_func
62+ return cast ( F , decorated_func )
5963
6064
61- def _internal_only (func ) :
65+ def _internal_only (func : F ) -> F :
6266 @functools .wraps (func )
63- def decorated_func (* args , ** kwargs ) :
67+ def decorated_func (* args : object , ** kwargs : object ) -> object :
6468 if (not app .debug and
6569 # This header cannot be set by external requests.
6670 # https://cloud.google.com/tasks/docs/creating-appengine-handlers?hl=en#reading_app_engine_task_request_headers
6771 not flask .request .headers .get ('X-AppEngine-QueueName' )):
6872 return ('External requests not allowed' , HTTPStatus .FORBIDDEN )
6973 return func (* args , ** kwargs )
7074
71- return decorated_func
75+ return cast ( F , decorated_func )
7276
7377
7478@app .route ('/_ah/liveness_check' )
75- def liveness_check ():
79+ def liveness_check () -> ResponseReturnValue :
7680 lock = filelock .FileLock (LOCK_FILE )
7781 try :
7882 lock .acquire (timeout = 0.1 )
@@ -91,7 +95,7 @@ def liveness_check():
9195
9296
9397@app .route ('/_ah/readiness_check' )
94- def readiness_check ():
98+ def readiness_check () -> ResponseReturnValue :
9599 lock = filelock .FileLock (LOCK_FILE )
96100 try :
97101 lock .acquire (timeout = 0.1 )
@@ -106,7 +110,7 @@ def readiness_check():
106110@app .route ('/api/results/process' , methods = ['POST' ])
107111@_internal_only
108112@_serial_task
109- def task_handler ():
113+ def task_handler () -> ResponseReturnValue :
110114 _atomic_write (TIMESTAMP_FILE , str (time .time ()))
111115
112116 task_id = flask .request .headers .get ('X-AppEngine-TaskName' )
0 commit comments