Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: "actions/checkout@v4"
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ filterwarnings = [
"error",
"ignore: You seem to already have a custom sys.excepthook handler installed. I'll skip installing Trio's custom handler, but this means MultiErrors will not show full tracebacks.:RuntimeWarning",
# See: https://github.com/agronholm/anyio/issues/508
"ignore: trio.MultiError is deprecated since Trio 0.22.0:trio.TrioDeprecationWarning"
"ignore: trio.MultiError is deprecated since Trio 0.22.0:trio.TrioDeprecationWarning",
# Python 3.14 deprecates asyncio.iscoroutinefunction, but uvicorn 0.35.0 still uses it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://pypi.org/project/uvicorn is currently 0.38.0

"ignore: 'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning"
]
markers = [
"copied_from(source, changes=None): mark test as copied from somewhere else, along with a description of changes made to accodomate e.g. our test setup",
Expand Down
17 changes: 12 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,9 @@ def install_signal_handlers(self) -> None:
async def serve(self, sockets=None):
self.restart_requested = asyncio.Event()

loop = asyncio.get_event_loop()
tasks = {
loop.create_task(super().serve(sockets=sockets)),
loop.create_task(self.watch_restarts()),
asyncio.create_task(super().serve(sockets=sockets)),
asyncio.create_task(self.watch_restarts()),
}
await asyncio.wait(tasks)

Expand Down Expand Up @@ -269,7 +268,15 @@ async def watch_restarts(self) -> None: # pragma: no cover


def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]:
thread = threading.Thread(target=server.run)
def run_server():
# Manually set up event loop for Python 3.14 compatibility
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
server.config.setup_event_loop()
loop.run_until_complete(server.serve())
loop.close()

thread = threading.Thread(target=run_server)
thread.start()
try:
while not server.started:
Expand All @@ -282,6 +289,6 @@ def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]:

@pytest.fixture(scope="session")
def server() -> typing.Iterator[TestServer]:
config = Config(app=app, lifespan="off", loop="asyncio")
config = Config(app=app, lifespan="off")
server = TestServer(config=config)
yield from serve_in_thread(server)