A test HTTP server implementation based on Uvicorn Server.
| 476 | |
| 477 | |
| 478 | class TestServer(Server): |
| 479 | """A test HTTP server implementation based on Uvicorn Server.""" |
| 480 | |
| 481 | @property |
| 482 | def url(self) -> URL: |
| 483 | """Get the base URL of the server. |
| 484 | |
| 485 | Returns: |
| 486 | A URL instance with the server's base URL. |
| 487 | """ |
| 488 | protocol = 'https' if self.config.is_ssl else 'http' |
| 489 | return URL(f'{protocol}://{self.config.host}:{self.config.port}/') |
| 490 | |
| 491 | async def serve(self, sockets: list[socket] | None = None) -> None: |
| 492 | """Run the server and set up restart capability. |
| 493 | |
| 494 | Args: |
| 495 | sockets: Optional list of sockets to bind to. |
| 496 | """ |
| 497 | self.restart_requested = asyncio.Event() |
| 498 | |
| 499 | loop = asyncio.get_event_loop() |
| 500 | tasks = { |
| 501 | loop.create_task(super().serve(sockets=sockets)), |
| 502 | loop.create_task(self.watch_restarts()), |
| 503 | } |
| 504 | await asyncio.wait(tasks) |
| 505 | |
| 506 | async def restart(self) -> None: |
| 507 | """Request server restart and wait for it to complete. |
| 508 | |
| 509 | This method can be called from a different thread than the one the server is running on, |
| 510 | and from a different async environment. |
| 511 | """ |
| 512 | self.started = False |
| 513 | self.restart_requested.set() |
| 514 | while not self.started: # noqa: ASYNC110 |
| 515 | await asyncio.sleep(0.2) |
| 516 | |
| 517 | async def watch_restarts(self) -> None: |
| 518 | """Watch for and handle restart requests.""" |
| 519 | while True: |
| 520 | if self.should_exit: |
| 521 | return |
| 522 | |
| 523 | try: |
| 524 | await asyncio.wait_for(self.restart_requested.wait(), timeout=0.1) |
| 525 | except asyncio.TimeoutError: |
| 526 | continue |
| 527 | |
| 528 | self.restart_requested.clear() |
| 529 | await self.shutdown() |
| 530 | await self.startup() |
| 531 | |
| 532 | def run(self, sockets: list[socket] | None = None) -> None: |
| 533 | """Run the server.""" |
| 534 | # Set the event loop policy in thread with server for Windows and Python 3.12+. |
| 535 | # This is necessary because there are problems with closing connections when using `ProactorEventLoop`. |
no outgoing calls