22import threading
33from datetime import datetime , timedelta
44from json import dumps
5- from typing import Any , Iterable , List , Mapping
5+ from typing import Any , Dict , Generator , Iterable , List , Literal , Mapping , NoReturn , Optional , Sequence , Tuple
66
77import pkg_resources
88import pyramid .httpexceptions as exc
1313from lxml import etree
1414from pyramid .config import Configurator
1515from pyramid .events import NewRequest
16+ from pyramid .request import Request
1617from pyramid .response import Response
1718from six import b
1819from six .moves .urllib_parse import quote_plus
3031
3132
3233class NoCache (object ):
33- def __init__ (self ):
34+ """ Dummy implementation for when caching isn't enabled """
35+
36+ def __init__ (self ) -> None :
3437 pass
3538
36- def __getitem__ (self , item ) :
39+ def __getitem__ (self , item : Any ) -> None :
3740 return None
3841
39- def __setitem__ (self , instance , value ) :
42+ def __setitem__ (self , instance : Any , value : Any ) -> Any :
4043 return value
4144
4245
43- def robots_handler (request ) :
46+ def robots_handler (request : Request ) -> Response :
4447 """
45- Impelements robots.txt
48+ Implements robots.txt
4649
4750 :param request: the HTTP request
4851 :return: robots.txt
@@ -55,7 +58,7 @@ def robots_handler(request):
5558 )
5659
5760
58- def status_handler (request ) :
61+ def status_handler (request : Request ) -> Response :
5962 """
6063 Implements the /api/status endpoint
6164
@@ -80,34 +83,38 @@ def status_handler(request):
8083
8184
8285class MediaAccept (object ):
83- def __init__ (self , accept ):
86+ def __init__ (self , accept : str ):
8487 self ._type = AcceptableType (accept )
8588
86- def has_key (self , key ) :
89+ def has_key (self , key : Any ) -> Literal [ True ] :
8790 return True
8891
89- def get (self , item ) :
92+ def get (self , item : Any ) -> Any :
9093 return self ._type .matches (item )
9194
92- def __contains__ (self , item ) :
95+ def __contains__ (self , item : Any ) -> Any :
9396 return self ._type .matches (item )
9497
95- def __str__ (self ):
98+ def __str__ (self ) -> str :
9699 return str (self ._type )
97100
98101
99102xml_types = ('text/xml' , 'application/xml' , 'application/samlmetadata+xml' )
100103
101104
102- def _is_xml_type (accepter ) :
105+ def _is_xml_type (accepter : MediaAccept ) -> bool :
103106 return any ([x in accepter for x in xml_types ])
104107
105108
106- def _is_xml (data ) :
109+ def _is_xml (data : Any ) -> bool :
107110 return isinstance (data , (etree ._Element , etree ._ElementTree ))
108111
109112
110- def _fmt (data , accepter ):
113+ def _fmt (data : Any , accepter : MediaAccept ) -> Tuple [str , str ]:
114+ """
115+ Format data according to the accepted content type of the requester.
116+ Return data as string (either XML or json) and a content-type.
117+ """
111118 if data is None or len (data ) == 0 :
112119 return "" , 'text/plain'
113120 if _is_xml (data ) and _is_xml_type (accepter ):
@@ -127,7 +134,7 @@ def call(entry: str) -> None:
127134 return None
128135
129136
130- def request_handler (request ) :
137+ def request_handler (request : Request ) -> Response :
131138 """
132139 The main GET request handler for pyFF. Implements caching and forwards the request to process_handler
133140
@@ -146,7 +153,7 @@ def request_handler(request):
146153 return r
147154
148155
149- def process_handler (request ) :
156+ def process_handler (request : Request ) -> Response :
150157 """
151158 The main request handler for pyFF. Implements API call hooks and content negotiation.
152159
@@ -155,7 +162,8 @@ def process_handler(request):
155162 """
156163 _ctypes = {'xml' : 'application/samlmetadata+xml;application/xml;text/xml' , 'json' : 'application/json' }
157164
158- def _d (x , do_split = True ):
165+ def _d (x : Optional [str ], do_split : bool = True ) -> Tuple [Optional [str ], Optional [str ]]:
166+ """ Split a path into a base component and an extension. """
159167 if x is not None :
160168 x = x .strip ()
161169
@@ -293,7 +301,7 @@ def _d(x, do_split=True):
293301 raise exc .exception_response (404 )
294302
295303
296- def webfinger_handler (request ) :
304+ def webfinger_handler (request : Request ) -> Response :
297305 """An implementation the webfinger protocol
298306 (http://tools.ietf.org/html/draft-ietf-appsawg-webfinger-12)
299307 in order to provide information about up and downstream metadata available at
@@ -335,11 +343,11 @@ def webfinger_handler(request):
335343 if resource is None :
336344 resource = request .host_url
337345
338- jrd = dict ()
346+ jrd : Dict [ str , Any ] = dict ()
339347 dt = datetime .now () + duration2timedelta ("PT1H" )
340348 jrd ['expires' ] = dt .isoformat ()
341349 jrd ['subject' ] = request .host_url
342- links = list ()
350+ links : List [ Dict [ str , Any ]] = list ()
343351 jrd ['links' ] = links
344352
345353 _dflt_rels = {
@@ -352,7 +360,7 @@ def webfinger_handler(request):
352360 else :
353361 rel = [rel ]
354362
355- def _links (url , title = None ):
363+ def _links (url : str , title : Any = None ) -> None :
356364 if url .startswith ('/' ):
357365 url = url .lstrip ('/' )
358366 for r in rel :
@@ -381,7 +389,7 @@ def _links(url, title=None):
381389 return response
382390
383391
384- def resources_handler (request ) :
392+ def resources_handler (request : Request ) -> Response :
385393 """
386394 Implements the /api/resources endpoint
387395
@@ -409,7 +417,7 @@ def _info(r: Resource) -> Mapping[str, Any]:
409417 return response
410418
411419
412- def pipeline_handler (request ) :
420+ def pipeline_handler (request : Request ) -> Response :
413421 """
414422 Implements the /api/pipeline endpoint
415423
@@ -422,7 +430,7 @@ def pipeline_handler(request):
422430 return response
423431
424432
425- def search_handler (request ) :
433+ def search_handler (request : Request ) -> Response :
426434 """
427435 Implements the /api/search endpoint
428436
@@ -438,7 +446,7 @@ def search_handler(request):
438446 log .debug ("match={}" .format (match ))
439447 store = request .registry .md .store
440448
441- def _response ():
449+ def _response () -> Generator [ bytes , bytes , None ] :
442450 yield b ('[' )
443451 in_loop = False
444452 entities = store .search (query = match .lower (), entity_filter = entity_filter )
@@ -454,8 +462,8 @@ def _response():
454462 return response
455463
456464
457- def add_cors_headers_response_callback (event ) :
458- def cors_headers (request , response ) :
465+ def add_cors_headers_response_callback (event : NewRequest ) -> None :
466+ def cors_headers (request : Request , response : Response ) -> None :
459467 response .headers .update (
460468 {
461469 'Access-Control-Allow-Origin' : '*' ,
@@ -469,7 +477,7 @@ def cors_headers(request, response):
469477 event .request .add_response_callback (cors_headers )
470478
471479
472- def launch_memory_usage_server (port = 9002 ):
480+ def launch_memory_usage_server (port : int = 9002 ) -> None :
473481 import cherrypy
474482 import dowser
475483
@@ -479,7 +487,7 @@ def launch_memory_usage_server(port=9002):
479487 cherrypy .engine .start ()
480488
481489
482- def mkapp (* args , ** kwargs ) :
490+ def mkapp (* args : Any , ** kwargs : Any ) -> Any :
483491 md = kwargs .pop ('md' , None )
484492 if md is None :
485493 md = MDRepository ()
0 commit comments