1515 pass
1616
1717from errno import EAGAIN , ECONNRESET , ETIMEDOUT
18+ from traceback import print_exception
1819
1920from .authentication import Basic , Bearer , require_authentication
2021from .exceptions import (
@@ -37,12 +38,15 @@ class Server:
3738 host : str = None
3839 port : int = None
3940
40- def __init__ (self , socket_source : Protocol , root_path : str = None ) -> None :
41+ def __init__ (
42+ self , socket_source : Protocol , root_path : str = None , * , debug : bool = False
43+ ) -> None :
4144 """Create a server, and get it ready to run.
4245
4346 :param socket: An object that is a source of sockets. This could be a `socketpool`
4447 in CircuitPython or the `socket` module in CPython.
4548 :param str root_path: Root directory to serve files from
49+ :param bool debug: Enables debug messages useful during development
4650 """
4751 self ._auths = []
4852 self ._buffer = bytearray (1024 )
@@ -53,6 +57,8 @@ def __init__(self, socket_source: Protocol, root_path: str = None) -> None:
5357 self .root_path = root_path
5458 self .stopped = False
5559
60+ self .debug = debug
61+
5662 def route (self , path : str , methods : Union [str , List [str ]] = GET ) -> Callable :
5763 """
5864 Decorator used to add a route.
@@ -119,8 +125,9 @@ def serve_forever(self, host: str, port: int = 80) -> None:
119125 self .poll ()
120126 except KeyboardInterrupt : # Exit on Ctrl-C e.g. during development
121127 return
122- except : # pylint: disable=bare-except
123- continue
128+ except Exception as error : # pylint: disable=broad-except
129+ if self .debug :
130+ _debug_exception_in_handler (error )
124131
125132 def start (self , host : str , port : int = 80 ) -> None :
126133 """
@@ -142,6 +149,9 @@ def start(self, host: str, port: int = 80) -> None:
142149 self ._sock .listen (10 )
143150 self ._sock .setblocking (False ) # Non-blocking socket
144151
152+ if self .debug :
153+ _debug_started_server (self )
154+
145155 def stop (self ) -> None :
146156 """
147157 Stops the server from listening for new connections and closes the socket.
@@ -245,7 +255,7 @@ def _handle_request(self, request: Request, handler: Union[Callable, None]):
245255 # ...request.method is GET or HEAD, try to serve a file from the filesystem.
246256 elif request .method in [GET , HEAD ]:
247257 self ._serve_file_from_filesystem (request )
248- # ...
258+
249259 else :
250260 Response (request , status = BAD_REQUEST_400 ).send ()
251261
@@ -276,6 +286,9 @@ def poll(self):
276286 if (request := self ._receive_request (conn , client_address )) is None :
277287 return
278288
289+ if self .debug :
290+ _debug_incoming_request (request )
291+
279292 # Find a handler for the route
280293 handler = self .routes .find_handler (_Route (request .path , request .method ))
281294
@@ -349,3 +362,24 @@ def socket_timeout(self, value: int) -> None:
349362 self ._timeout = value
350363 else :
351364 raise ValueError ("Server.socket_timeout must be a positive numeric value." )
365+
366+
367+ def _debug_started_server (server : "Server" ):
368+ """Prints a message when the server starts."""
369+ host , port = server .host , server .port
370+
371+ print (f"Started development server on http://{ host } :{ port } " )
372+
373+
374+ def _debug_incoming_request (request : "Request" ):
375+ """Prints a message when a request is received."""
376+ client_ip = request .client_address [0 ]
377+ method = request .method
378+ size = len (request .raw_request )
379+
380+ print (f"{ client_ip } -- { method } { request .path } { size } " )
381+
382+
383+ def _debug_exception_in_handler (error : Exception ):
384+ """Prints a message when an exception is raised in a handler."""
385+ print_exception (error )
0 commit comments