Skip to content

Commit df4f9af

Browse files
committed
test: make async suite pass on python 3.13
1 parent b8af5ad commit df4f9af

11 files changed

Lines changed: 27 additions & 35 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ jobs:
6363
- name: Install dependencies
6464
run: |
6565
python -m pip install --upgrade pip
66-
pip install pytest pytest-asyncio pytest-cov pydantic fastapi jsonschema httpx psutil transformers requests
66+
pip install pytest pytest-cov pydantic fastapi jsonschema httpx psutil transformers requests
6767
6868
- name: Verify async test plugin
69-
run: python -c "import pytest_asyncio"
69+
run: python -c "import anyio"
7070

7171
- name: Run unit tests (no MLX required)
7272
run: |
@@ -118,7 +118,7 @@ jobs:
118118
pip install -e ".[dev,vision]"
119119
120120
- name: Verify async test plugin
121-
run: python -c "import pytest_asyncio"
121+
run: python -c "import anyio"
122122

123123
- name: Verify Apple Silicon
124124
run: |

docs/development/contributing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ The full suite is intentionally split in CI:
5454
- The Ubuntu matrix runs the pure-Python subset only.
5555
- The Apple Silicon job runs MLX-dependent tests that require macOS on ARM.
5656

57-
If you are running tests locally outside the documented `.[dev]` environment, async tests
58-
will fail because `pytest-asyncio` is a dev dependency rather than a runtime dependency.
57+
Async tests run through `anyio` on the asyncio backend. If you are running tests locally,
58+
use the documented dev environment so the test dependencies match CI.
5959

6060
### Code Style
6161

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ dependencies = [
6565
[project.optional-dependencies]
6666
dev = [
6767
"pytest>=7.0.0",
68-
"pytest-asyncio>=0.21.0",
6968
"black>=23.0.0",
7069
"ruff>=0.1.0",
7170
"mypy>=1.0.0",

tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
import pytest
55

66

7+
@pytest.fixture(scope="session")
8+
def anyio_backend():
9+
"""Run anyio tests on asyncio consistently across environments."""
10+
return "asyncio"
11+
12+
713
def pytest_addoption(parser):
814
"""Add custom command line options."""
915
parser.addoption(

tests/test_batching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ def test_multiple_concurrent_requests(self, model_and_tokenizer):
504504
assert len(finished) == len(prompts), f"Only {len(finished)} requests finished"
505505

506506

507-
@pytest.mark.asyncio
507+
@pytest.mark.anyio
508508
class TestEngineAsync:
509509
"""Async tests for the engine."""
510510

tests/test_batching_deterministic.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def sampling_params():
3737
class TestDeterministicSingleRequest:
3838
"""Test single request determinism."""
3939

40-
@pytest.mark.asyncio
40+
@pytest.mark.anyio
4141
async def test_same_prompt_same_output(self, model_and_tokenizer, sampling_params):
4242
"""Same prompt should produce same output with temp=0."""
4343
from vllm_mlx import AsyncEngineCore, EngineConfig, SchedulerConfig
@@ -68,7 +68,7 @@ async def test_same_prompt_same_output(self, model_and_tokenizer, sampling_param
6868
assert len(outputs) == 3
6969
assert outputs[0] == outputs[1] == outputs[2], f"Outputs differ: {outputs}"
7070

71-
@pytest.mark.asyncio
71+
@pytest.mark.anyio
7272
async def test_token_streaming_order(self, model_and_tokenizer, sampling_params):
7373
"""Tokens should stream in order."""
7474
from vllm_mlx import AsyncEngineCore
@@ -94,7 +94,7 @@ async def test_token_streaming_order(self, model_and_tokenizer, sampling_params)
9494
class TestDeterministicConcurrentRequests:
9595
"""Test concurrent request handling with determinism."""
9696

97-
@pytest.mark.asyncio
97+
@pytest.mark.anyio
9898
async def test_concurrent_same_prompt(self, model_and_tokenizer):
9999
"""Multiple concurrent requests with same prompt should get same output."""
100100
from vllm_mlx import (
@@ -137,7 +137,7 @@ async def get_output(rid):
137137
# All should be the same
138138
assert all(r == results[0] for r in results), f"Outputs differ: {results}"
139139

140-
@pytest.mark.asyncio
140+
@pytest.mark.anyio
141141
async def test_concurrent_different_prompts(self, model_and_tokenizer):
142142
"""Different prompts should get different (but deterministic) outputs."""
143143
from vllm_mlx import (
@@ -191,7 +191,7 @@ async def get_output(rid):
191191
class TestBatchingPerformance:
192192
"""Test that batching improves throughput."""
193193

194-
@pytest.mark.asyncio
194+
@pytest.mark.anyio
195195
async def test_batched_faster_than_sequential(self, model_and_tokenizer):
196196
"""Batched requests should be faster than sequential."""
197197
from vllm_mlx import (
@@ -274,7 +274,7 @@ async def get_output(rid):
274274
class TestRequestManagement:
275275
"""Test request lifecycle management."""
276276

277-
@pytest.mark.asyncio
277+
@pytest.mark.anyio
278278
async def test_abort_request(self, model_and_tokenizer):
279279
"""Test aborting a request mid-generation."""
280280
from vllm_mlx import AsyncEngineCore, SamplingParams
@@ -304,7 +304,7 @@ async def test_abort_request(self, model_and_tokenizer):
304304
stats = engine.get_stats()
305305
assert stats["active_requests"] == 0
306306

307-
@pytest.mark.asyncio
307+
@pytest.mark.anyio
308308
async def test_engine_stats(self, model_and_tokenizer):
309309
"""Test engine statistics tracking."""
310310
from vllm_mlx import (
@@ -343,7 +343,7 @@ async def test_engine_stats(self, model_and_tokenizer):
343343
class TestSchedulerPolicy:
344344
"""Test scheduler policies."""
345345

346-
@pytest.mark.asyncio
346+
@pytest.mark.anyio
347347
async def test_fcfs_ordering(self, model_and_tokenizer):
348348
"""Test that FCFS policy processes requests in order."""
349349
from vllm_mlx import (
@@ -396,7 +396,7 @@ async def track_completion(rid, name):
396396
class TestEdgeCases:
397397
"""Test edge cases and error handling."""
398398

399-
@pytest.mark.asyncio
399+
@pytest.mark.anyio
400400
async def test_empty_prompt(self, model_and_tokenizer):
401401
"""Test handling of empty prompt."""
402402
from vllm_mlx import AsyncEngineCore, SamplingParams
@@ -414,7 +414,7 @@ async def test_empty_prompt(self, model_and_tokenizer):
414414
assert out.finished
415415
break
416416

417-
@pytest.mark.asyncio
417+
@pytest.mark.anyio
418418
async def test_very_short_max_tokens(self, model_and_tokenizer):
419419
"""Test with max_tokens=1."""
420420
from vllm_mlx import AsyncEngineCore, SamplingParams
@@ -436,7 +436,7 @@ async def test_very_short_max_tokens(self, model_and_tokenizer):
436436
# Should generate exactly 1 token
437437
assert token_count == 1
438438

439-
@pytest.mark.asyncio
439+
@pytest.mark.anyio
440440
async def test_multiple_start_stop(self, model_and_tokenizer):
441441
"""Test starting and stopping engine multiple times."""
442442
from vllm_mlx import AsyncEngineCore, SamplingParams

tests/test_chat_template_kwargs.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@
1111
from vllm_mlx.engine.base import GenerationOutput
1212

1313

14-
@pytest.fixture
15-
def anyio_backend():
16-
return "asyncio"
17-
18-
1914
def test_chat_completion_request_preserves_chat_template_kwargs():
2015
request = srv.ChatCompletionRequest(
2116
model="test-model",

tests/test_continuous_batching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def test_scheduler_config_batching_params(self):
5353
assert config.completion_batch_size == 32
5454

5555

56-
@pytest.mark.asyncio
56+
@pytest.mark.anyio
5757
class TestContinuousBatchingIntegration:
5858
"""Integration tests requiring actual model loading."""
5959

tests/test_server.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,7 @@ def test_verify_api_key_rejects_invalid(self):
628628

629629
# Should raise HTTPException with 401
630630
with pytest.raises(HTTPException) as exc_info:
631-
asyncio.get_event_loop().run_until_complete(
632-
server.verify_api_key(credentials)
633-
)
631+
asyncio.run(server.verify_api_key(credentials))
634632

635633
assert exc_info.value.status_code == 401
636634
assert "Invalid API key" in str(exc_info.value.detail)
@@ -656,9 +654,7 @@ def test_verify_api_key_accepts_valid(self):
656654
)
657655

658656
# Should not raise any exception
659-
result = asyncio.get_event_loop().run_until_complete(
660-
server.verify_api_key(credentials)
661-
)
657+
result = asyncio.run(server.verify_api_key(credentials))
662658
# verify_api_key returns True on success (no exception raised)
663659
assert result is True or result is None
664660
finally:

tests/test_simple_engine.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@
1010
class TestSimpleEngineConcurrency:
1111
"""Test SimpleEngine lock behavior with concurrent requests."""
1212

13-
@pytest.fixture
14-
def anyio_backend(self):
15-
return "asyncio"
16-
1713
@pytest.fixture
1814
def mock_model(self):
1915
"""Create a mock model that tracks concurrent calls."""

0 commit comments

Comments
 (0)