pytest-async-benchmark 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/.gitignore +1 -0
- pytest_async_benchmark-0.2.0/CHANGELOG.md +67 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/PKG-INFO +307 -18
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/README.md +304 -17
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/RELEASE_GUIDE.md +1 -1
- pytest_async_benchmark-0.2.0/examples/backwards_compatibility_examples.py +174 -0
- pytest_async_benchmark-0.2.0/examples/marker_syntax_examples.py +124 -0
- pytest_async_benchmark-0.2.0/examples/pytest_asyncio_showcase.py +163 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/examples/pytest_examples.py +33 -20
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/examples/quart_api_comparison.py +1 -1
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/pyproject.toml +6 -6
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/scripts/release-check.py +12 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/__init__.py +1 -1
- pytest_async_benchmark-0.2.0/src/pytest_async_benchmark/plugin.py +185 -0
- pytest_async_benchmark-0.2.0/test_marker_functionality.py +53 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/tests/test_async_bench.py +12 -8
- pytest_async_benchmark-0.2.0/tests/test_async_compatibility.py +80 -0
- pytest_async_benchmark-0.2.0/tests/test_backwards_compatibility.py +84 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/tests/test_demo.py +12 -8
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/uv.lock +8 -3
- pytest_async_benchmark-0.1.1/src/pytest_async_benchmark/plugin.py +0 -91
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/.github/workflows/ci.yml +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/.github/workflows/release.yml +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/.github/workflows/test-publish.yml +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/.pre-commit-config.yaml +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/LICENSE +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/PYPI_SETUP.md +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/examples/comparison_examples.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/screenshot.png +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/scripts/quality-check.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/analytics.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/comparison.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/display.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/runner.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/stats.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/src/pytest_async_benchmark/utils.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/tests/conftest.py +0 -0
- {pytest_async_benchmark-0.1.1 → pytest_async_benchmark-0.2.0}/tests/test_comparison.py +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to pytest-async-benchmark will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2025-05-28
|
|
9
|
+
|
|
10
|
+
### 🚀 Major Features
|
|
11
|
+
- **Backwards Compatibility**: Automatic interface detection supporting both sync and async usage patterns
|
|
12
|
+
- **pytest-asyncio Integration**: Full compatibility with pytest-asyncio plugin when available
|
|
13
|
+
- **Dual Interface Support**: Works with `await async_benchmark(...)` (with pytest-asyncio) or `result = async_benchmark(...)` (without)
|
|
14
|
+
- **Smart Detection**: Automatically detects pytest-asyncio availability and provides appropriate interface
|
|
15
|
+
|
|
16
|
+
### ✨ Added
|
|
17
|
+
- Automatic pytest-asyncio detection and interface switching
|
|
18
|
+
- Legacy sync interface support for backwards compatibility with v0.1.x
|
|
19
|
+
- Optional dependency configuration for pytest-asyncio
|
|
20
|
+
- Comprehensive backwards compatibility test suite
|
|
21
|
+
- `_SyncResultWrapper` class for seamless sync interface
|
|
22
|
+
- Enhanced documentation covering both usage patterns
|
|
23
|
+
|
|
24
|
+
### 🔧 Changed
|
|
25
|
+
- **NON-BREAKING**: pytest-asyncio moved from required to optional dependency
|
|
26
|
+
- `AsyncBenchmarkFixture.__call__` now returns different types based on environment
|
|
27
|
+
- Enhanced fixture to support both sync and async interfaces automatically
|
|
28
|
+
- Updated examples to show both usage patterns
|
|
29
|
+
- README updated with backwards compatibility documentation
|
|
30
|
+
|
|
31
|
+
### 🛠️ Fixed
|
|
32
|
+
- RuntimeError issues when pytest-asyncio not available
|
|
33
|
+
- Event loop management conflicts in different environments
|
|
34
|
+
- Compatibility issues with projects not using pytest-asyncio
|
|
35
|
+
|
|
36
|
+
### 📚 Documentation
|
|
37
|
+
- Added pytest-asyncio compatibility section to README
|
|
38
|
+
- Updated all code examples to demonstrate async/await usage
|
|
39
|
+
- Created dedicated pytest-asyncio showcase example
|
|
40
|
+
- Enhanced Quick Start guide with modern async patterns
|
|
41
|
+
|
|
42
|
+
### 🧪 Testing
|
|
43
|
+
- All 40 tests passing with pytest-asyncio integration
|
|
44
|
+
- Examples thoroughly tested and validated
|
|
45
|
+
- Comprehensive test coverage maintained
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## [0.1.1] - 2025-05-27
|
|
50
|
+
|
|
51
|
+
### Initial Features
|
|
52
|
+
- Core async benchmarking functionality
|
|
53
|
+
- Rich console output with beautiful formatting
|
|
54
|
+
- Statistical analysis and comparison tools
|
|
55
|
+
- Multiple examples and comprehensive testing
|
|
56
|
+
|
|
57
|
+
### Core Components
|
|
58
|
+
- `AsyncBenchmarkFixture` for pytest integration
|
|
59
|
+
- `AsyncBenchmarkRunner` for timing execution
|
|
60
|
+
- `BenchmarkComparator` for performance analysis
|
|
61
|
+
- Rich-based display formatting
|
|
62
|
+
|
|
63
|
+
### Examples
|
|
64
|
+
- Basic pytest examples
|
|
65
|
+
- Quart API comparison
|
|
66
|
+
- Comparison analysis tools
|
|
67
|
+
- Statistical benchmarking patterns
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-async-benchmark
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: pytest-async-benchmark: Modern pytest benchmarking for async code. 🚀
|
|
5
5
|
Author-email: Mihai Farcas <contact@mihai.ltd>
|
|
6
6
|
License: MIT License
|
|
@@ -28,6 +28,8 @@ License-File: LICENSE
|
|
|
28
28
|
Requires-Python: >=3.9
|
|
29
29
|
Requires-Dist: pytest>=8.3.5
|
|
30
30
|
Requires-Dist: rich>=14.0.0
|
|
31
|
+
Provides-Extra: asyncio
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'asyncio'
|
|
31
33
|
Description-Content-Type: text/markdown
|
|
32
34
|
|
|
33
35
|
# pytest-async-benchmark 🚀
|
|
@@ -37,8 +39,8 @@ Modern pytest benchmarking for async code with beautiful terminal output and adv
|
|
|
37
39
|
## ✨ Features
|
|
38
40
|
|
|
39
41
|
- 🎯 **Async-First**: Designed specifically for benchmarking `async def` functions
|
|
40
|
-
- 🔌 **Pytest Integration**: Seamless integration as a pytest plugin
|
|
41
|
-
- 🎨 **Rich Output**: Beautiful terminal reporting!
|
|
42
|
+
- 🔌 **Pytest Integration**: Seamless integration as a pytest plugin with full **pytest-asyncio** support
|
|
43
|
+
- 🎨 **Rich Output**: Beautiful terminal reporting powered by Rich!
|
|
42
44
|
- 📊 **Comprehensive Stats**: Min, max, mean, median, std dev, percentiles, and more
|
|
43
45
|
- ⚖️ **A vs B Comparisons**: Compare different implementations side-by-side
|
|
44
46
|
- 📈 **Multi-Scenario Analysis**: Benchmark multiple scenarios with detailed comparison tables
|
|
@@ -46,18 +48,59 @@ Modern pytest benchmarking for async code with beautiful terminal output and adv
|
|
|
46
48
|
- ⚡ **Auto Calibration**: Intelligent round and iteration detection
|
|
47
49
|
- 🔄 **Quick Compare**: One-line comparison utilities
|
|
48
50
|
- 🏆 **Winner Detection**: Automatic identification of best-performing implementation
|
|
49
|
-
- 🚀 **Easy to Use**: Simple fixture-based API
|
|
51
|
+
- 🚀 **Easy to Use**: Simple fixture-based API with native `async`/`await` support
|
|
52
|
+
- 🔧 **pytest-asyncio Compatible**: Works perfectly with pytest-asyncio's event loop management
|
|
50
53
|
|
|
51
54
|
## 📦 Installation
|
|
52
55
|
|
|
56
|
+
### For Existing pytest-asyncio Users 🚀
|
|
57
|
+
**Already testing async APIs (FastAPI, Quart, aiohttp)?** You're all set with the basic installation:
|
|
58
|
+
|
|
53
59
|
```bash
|
|
54
|
-
|
|
60
|
+
pip install pytest-async-benchmark
|
|
55
61
|
# or
|
|
62
|
+
uv add pytest-async-benchmark
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
You'll get the full async/await experience immediately since you already have pytest-asyncio!
|
|
66
|
+
|
|
67
|
+
### For New Users
|
|
68
|
+
Choose your installation based on your needs:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Full installation with async/await support (recommended)
|
|
72
|
+
pip install pytest-async-benchmark[asyncio]
|
|
73
|
+
uv add pytest-async-benchmark --optional asyncio
|
|
74
|
+
|
|
75
|
+
# Basic installation (simple interface)
|
|
56
76
|
pip install pytest-async-benchmark
|
|
77
|
+
uv add pytest-async-benchmark
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Quick Check ✅
|
|
81
|
+
Already using `@pytest.mark.asyncio` in your tests? Then the basic installation is all you need:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# If you already have tests like this:
|
|
85
|
+
@pytest.mark.asyncio
|
|
86
|
+
async def test_my_api():
|
|
87
|
+
# Your existing async test code
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
# Then just add pytest-async-benchmark and use:
|
|
91
|
+
@pytest.mark.asyncio
|
|
92
|
+
async def test_my_api_performance(async_benchmark):
|
|
93
|
+
result = await async_benchmark(my_async_function)
|
|
94
|
+
assert result['mean'] < 0.01
|
|
57
95
|
```
|
|
58
96
|
|
|
59
97
|
## 🚀 Quick Start
|
|
60
98
|
|
|
99
|
+
pytest-async-benchmark automatically adapts to your environment, providing **two convenient interfaces**:
|
|
100
|
+
|
|
101
|
+
### Async Interface (with pytest-asyncio)
|
|
102
|
+
When pytest-asyncio is installed, use the natural async/await syntax:
|
|
103
|
+
|
|
61
104
|
```python
|
|
62
105
|
import asyncio
|
|
63
106
|
import pytest
|
|
@@ -66,26 +109,190 @@ async def slow_async_operation():
|
|
|
66
109
|
await asyncio.sleep(0.01) # 10ms
|
|
67
110
|
return "result"
|
|
68
111
|
|
|
69
|
-
|
|
70
|
-
|
|
112
|
+
@pytest.mark.asyncio
|
|
113
|
+
@pytest.mark.async_benchmark(rounds=5, iterations=10)
|
|
114
|
+
async def test_async_performance(async_benchmark):
|
|
115
|
+
# Use await with pytest-asyncio for best experience
|
|
116
|
+
result = await async_benchmark(slow_async_operation)
|
|
117
|
+
|
|
118
|
+
# Your assertions here
|
|
119
|
+
assert result['mean'] < 0.02
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Simple Interface (without pytest-asyncio)
|
|
123
|
+
For simpler setups, the sync interface works automatically:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import asyncio
|
|
127
|
+
import pytest
|
|
128
|
+
|
|
129
|
+
async def slow_async_operation():
|
|
130
|
+
await asyncio.sleep(0.01)
|
|
131
|
+
return "result"
|
|
132
|
+
|
|
133
|
+
@pytest.mark.async_benchmark(rounds=5, iterations=10)
|
|
134
|
+
def test_sync_performance(async_benchmark):
|
|
135
|
+
# No await needed - sync interface
|
|
136
|
+
result = async_benchmark(slow_async_operation)
|
|
71
137
|
|
|
72
138
|
# Your assertions here
|
|
73
139
|
assert result['mean'] < 0.02 # Should complete in under 20ms
|
|
74
140
|
```
|
|
75
141
|
|
|
142
|
+
### Flexible Configuration Syntax
|
|
143
|
+
|
|
144
|
+
pytest-async-benchmark supports **two syntax options** for configuring benchmarks:
|
|
145
|
+
|
|
146
|
+
#### Option 1: Marker Syntax (Recommended)
|
|
147
|
+
```python
|
|
148
|
+
@pytest.mark.async_benchmark(rounds=5, iterations=10)
|
|
149
|
+
async def test_with_marker(async_benchmark):
|
|
150
|
+
result = await async_benchmark(slow_async_operation)
|
|
151
|
+
assert result['rounds'] == 5 # From marker
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Option 2: Function Parameter Syntax
|
|
155
|
+
```python
|
|
156
|
+
async def test_with_parameters(async_benchmark):
|
|
157
|
+
result = await async_benchmark(slow_async_operation, rounds=5, iterations=10)
|
|
158
|
+
assert result['rounds'] == 5 # From function parameters
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 🔧 Interface Detection & Flexibility
|
|
162
|
+
|
|
163
|
+
pytest-async-benchmark **automatically detects** your environment and provides the best interface:
|
|
164
|
+
|
|
165
|
+
### With pytest-asyncio (Recommended)
|
|
166
|
+
When pytest-asyncio is installed, use natural async/await syntax:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# Set in your pyproject.toml for automatic async test detection
|
|
170
|
+
[tool.pytest.ini_options]
|
|
171
|
+
asyncio_mode = "auto"
|
|
172
|
+
|
|
173
|
+
# Then use await syntax
|
|
174
|
+
@pytest.mark.asyncio
|
|
175
|
+
async def test_my_benchmark(async_benchmark):
|
|
176
|
+
result = await async_benchmark(my_async_function)
|
|
177
|
+
# Your assertions here
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Benefits of pytest-asyncio integration:**
|
|
181
|
+
- ✅ Native `async`/`await` syntax support
|
|
182
|
+
- ✅ Automatic event loop management
|
|
183
|
+
- ✅ No `RuntimeError: cannot be called from a running event loop`
|
|
184
|
+
- ✅ Better compatibility with async frameworks like FastAPI, Quart, aiohttp
|
|
185
|
+
- ✅ Cleaner test code with standard async patterns
|
|
186
|
+
|
|
187
|
+
### Without pytest-asyncio (Simple Setup)
|
|
188
|
+
When pytest-asyncio is not available, the simple interface works automatically:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# No pytest-asyncio required
|
|
192
|
+
def test_my_benchmark(async_benchmark):
|
|
193
|
+
result = async_benchmark(my_async_function) # No await needed
|
|
194
|
+
# Your assertions here
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Benefits of simple interface:**
|
|
198
|
+
- ✅ No additional dependencies required
|
|
199
|
+
- ✅ Simpler setup for basic use cases
|
|
200
|
+
- ✅ Perfect for getting started quickly
|
|
201
|
+
- ✅ Automatic event loop management internally
|
|
202
|
+
|
|
76
203
|
## 🎯 Core Usage Examples
|
|
77
204
|
|
|
205
|
+
### Interface Flexibility
|
|
206
|
+
|
|
207
|
+
#### Async Interface (with pytest-asyncio)
|
|
208
|
+
```python
|
|
209
|
+
@pytest.mark.asyncio
|
|
210
|
+
@pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
|
|
211
|
+
async def test_with_marker(async_benchmark):
|
|
212
|
+
"""Use marker for consistent, visible configuration."""
|
|
213
|
+
result = await async_benchmark(my_async_function)
|
|
214
|
+
assert result['rounds'] == 10 # Configuration is explicit and visible
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Simple Interface (without pytest-asyncio)
|
|
218
|
+
```python
|
|
219
|
+
@pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
|
|
220
|
+
def test_with_marker_sync(async_benchmark):
|
|
221
|
+
"""Use marker for consistent, visible configuration - simple style."""
|
|
222
|
+
result = async_benchmark(my_async_function) # No await needed
|
|
223
|
+
assert result['rounds'] == 10 # Configuration is explicit and visible
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Two Configuration Syntaxes
|
|
227
|
+
|
|
228
|
+
#### Marker Syntax (Declarative)
|
|
229
|
+
```python
|
|
230
|
+
# With pytest-asyncio
|
|
231
|
+
@pytest.mark.asyncio
|
|
232
|
+
@pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
|
|
233
|
+
async def test_with_marker_async(async_benchmark):
|
|
234
|
+
result = await async_benchmark(my_async_function)
|
|
235
|
+
assert result['rounds'] == 10
|
|
236
|
+
|
|
237
|
+
# Without pytest-asyncio
|
|
238
|
+
@pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
|
|
239
|
+
def test_with_marker_sync(async_benchmark):
|
|
240
|
+
result = async_benchmark(my_async_function)
|
|
241
|
+
assert result['rounds'] == 10
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Function Parameter Syntax (Dynamic)
|
|
245
|
+
```python
|
|
246
|
+
# With pytest-asyncio
|
|
247
|
+
@pytest.mark.asyncio
|
|
248
|
+
async def test_with_parameters_async(async_benchmark):
|
|
249
|
+
result = await async_benchmark(
|
|
250
|
+
my_async_function,
|
|
251
|
+
rounds=10,
|
|
252
|
+
iterations=100,
|
|
253
|
+
warmup_rounds=2
|
|
254
|
+
)
|
|
255
|
+
assert result['rounds'] == 10
|
|
256
|
+
|
|
257
|
+
# Without pytest-asyncio
|
|
258
|
+
def test_with_parameters_simple(async_benchmark):
|
|
259
|
+
result = async_benchmark(
|
|
260
|
+
my_async_function,
|
|
261
|
+
rounds=10,
|
|
262
|
+
iterations=100,
|
|
263
|
+
warmup_rounds=2
|
|
264
|
+
)
|
|
265
|
+
assert result['rounds'] == 10
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Combined Syntax (Override)
|
|
269
|
+
Both interfaces support parameter precedence where function parameters override marker settings:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
@pytest.mark.async_benchmark(rounds=5, iterations=50) # Default config
|
|
273
|
+
async def test_with_override(async_benchmark): # Works with or without @pytest.mark.asyncio
|
|
274
|
+
"""Function parameters override marker settings."""
|
|
275
|
+
result = await async_benchmark( # Use 'await' only with pytest-asyncio
|
|
276
|
+
my_async_function,
|
|
277
|
+
rounds=20 # This overrides marker's rounds=5
|
|
278
|
+
# iterations=50 comes from marker
|
|
279
|
+
)
|
|
280
|
+
assert result['rounds'] == 20 # Function parameter wins
|
|
281
|
+
assert result['iterations'] == 50 # From marker
|
|
282
|
+
```
|
|
283
|
+
|
|
78
284
|
### Basic Benchmarking
|
|
79
285
|
|
|
80
286
|
```python
|
|
81
|
-
|
|
287
|
+
@pytest.mark.asyncio
|
|
288
|
+
async def test_my_async_function(async_benchmark):
|
|
82
289
|
async def my_function():
|
|
83
290
|
# Your async code here
|
|
84
291
|
await some_async_operation()
|
|
85
292
|
return result
|
|
86
293
|
|
|
87
294
|
# Benchmark with default settings (5 rounds, 1 iteration each)
|
|
88
|
-
stats = async_benchmark(my_function)
|
|
295
|
+
stats = await async_benchmark(my_function)
|
|
89
296
|
|
|
90
297
|
# Access comprehensive timing statistics
|
|
91
298
|
print(f"Mean execution time: {stats['mean']:.3f}s")
|
|
@@ -95,9 +302,86 @@ def test_my_async_function(async_benchmark):
|
|
|
95
302
|
|
|
96
303
|
### Advanced Configuration
|
|
97
304
|
|
|
305
|
+
## 🎯 Two Configuration Syntaxes
|
|
306
|
+
|
|
307
|
+
pytest-async-benchmark offers **two flexible ways** to configure your benchmarks:
|
|
308
|
+
|
|
309
|
+
### 🏷️ Marker Syntax (Recommended)
|
|
310
|
+
|
|
311
|
+
Use pytest markers for **declarative, visible configuration**:
|
|
312
|
+
|
|
98
313
|
```python
|
|
99
|
-
|
|
100
|
-
|
|
314
|
+
@pytest.mark.asyncio
|
|
315
|
+
@pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
|
|
316
|
+
async def test_high_precision_benchmark(async_benchmark):
|
|
317
|
+
"""High precision benchmark with marker configuration."""
|
|
318
|
+
result = await async_benchmark(my_async_function)
|
|
319
|
+
|
|
320
|
+
# Configuration is visible and consistent
|
|
321
|
+
assert result['rounds'] == 10
|
|
322
|
+
assert result['iterations'] == 100
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Benefits:**
|
|
326
|
+
- ✅ **Visible configuration** - Parameters are clear at test level
|
|
327
|
+
- ✅ **IDE support** - Better tooling and autocomplete
|
|
328
|
+
- ✅ **Test discovery** - Easy to find all benchmark tests
|
|
329
|
+
- ✅ **Consistent configs** - Same settings across related tests
|
|
330
|
+
|
|
331
|
+
### ⚙️ Function Parameter Syntax
|
|
332
|
+
|
|
333
|
+
Use function parameters for **dynamic, flexible configuration**:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
@pytest.mark.asyncio
|
|
337
|
+
async def test_dynamic_benchmark(async_benchmark):
|
|
338
|
+
"""Dynamic benchmark with runtime configuration."""
|
|
339
|
+
# Configuration can be computed or conditional
|
|
340
|
+
rounds = 20 if is_production else 5
|
|
341
|
+
|
|
342
|
+
result = await async_benchmark(
|
|
343
|
+
my_async_function,
|
|
344
|
+
rounds=rounds,
|
|
345
|
+
iterations=50,
|
|
346
|
+
warmup_rounds=1
|
|
347
|
+
)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Benefits:**
|
|
351
|
+
- ✅ **Dynamic configuration** - Runtime parameter calculation
|
|
352
|
+
- ✅ **Conditional logic** - Different configs based on environment
|
|
353
|
+
- ✅ **Per-call customization** - Each benchmark call can differ
|
|
354
|
+
|
|
355
|
+
### 🔄 Combined Syntax (Best of Both)
|
|
356
|
+
|
|
357
|
+
**Function parameters override marker parameters**:
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
@pytest.mark.asyncio
|
|
361
|
+
@pytest.mark.async_benchmark(rounds=5, iterations=50, warmup_rounds=1)
|
|
362
|
+
async def test_with_overrides(async_benchmark):
|
|
363
|
+
"""Use marker defaults with selective overrides."""
|
|
364
|
+
|
|
365
|
+
# Quick test with marker defaults
|
|
366
|
+
quick_result = await async_benchmark(fast_function)
|
|
367
|
+
|
|
368
|
+
# Precision test with overridden rounds
|
|
369
|
+
precise_result = await async_benchmark(
|
|
370
|
+
slow_function,
|
|
371
|
+
rounds=20 # Overrides marker's rounds=5
|
|
372
|
+
# iterations=50 and warmup_rounds=1 come from marker
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
assert quick_result['rounds'] == 5 # From marker
|
|
376
|
+
assert precise_result['rounds'] == 20 # From function override
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Traditional Configuration
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
@pytest.mark.asyncio
|
|
383
|
+
async def test_with_custom_settings(async_benchmark):
|
|
384
|
+
result = await async_benchmark(
|
|
101
385
|
my_async_function,
|
|
102
386
|
rounds=10, # Number of rounds to run
|
|
103
387
|
iterations=5, # Iterations per round
|
|
@@ -108,13 +392,14 @@ def test_with_custom_settings(async_benchmark):
|
|
|
108
392
|
### With Function Arguments
|
|
109
393
|
|
|
110
394
|
```python
|
|
111
|
-
|
|
395
|
+
@pytest.mark.asyncio
|
|
396
|
+
async def test_with_args(async_benchmark):
|
|
112
397
|
async def process_data(data, multiplier=1):
|
|
113
398
|
# Process the data
|
|
114
399
|
await asyncio.sleep(0.01)
|
|
115
400
|
return len(data) * multiplier
|
|
116
401
|
|
|
117
|
-
result = async_benchmark(
|
|
402
|
+
result = await async_benchmark(
|
|
118
403
|
process_data,
|
|
119
404
|
"test_data", # positional arg
|
|
120
405
|
multiplier=2, # keyword arg
|
|
@@ -317,13 +602,14 @@ async def get_data():
|
|
|
317
602
|
await asyncio.sleep(0.005)
|
|
318
603
|
return {"data": "example"}
|
|
319
604
|
|
|
320
|
-
|
|
605
|
+
@pytest.mark.asyncio
|
|
606
|
+
async def test_fastapi_endpoint_performance(async_benchmark):
|
|
321
607
|
async def make_request():
|
|
322
608
|
with TestClient(app) as client:
|
|
323
609
|
response = client.get("/api/data")
|
|
324
610
|
return response.json()
|
|
325
611
|
|
|
326
|
-
result = async_benchmark(make_request, rounds=10)
|
|
612
|
+
result = await async_benchmark(make_request, rounds=10)
|
|
327
613
|
assert result['mean'] < 0.1 # Should respond within 100ms
|
|
328
614
|
assert result['grade'] in ['A', 'B'] # Should have good performance grade
|
|
329
615
|
```
|
|
@@ -358,6 +644,7 @@ a_vs_b_comparison(
|
|
|
358
644
|
### Database Query Benchmarking
|
|
359
645
|
|
|
360
646
|
```python
|
|
647
|
+
@pytest.mark.asyncio
|
|
361
648
|
async def test_database_query_performance(async_benchmark):
|
|
362
649
|
async def fetch_user_data(user_id):
|
|
363
650
|
async with database.connection() as conn:
|
|
@@ -365,7 +652,7 @@ async def test_database_query_performance(async_benchmark):
|
|
|
365
652
|
"SELECT * FROM users WHERE id = ?", user_id
|
|
366
653
|
)
|
|
367
654
|
|
|
368
|
-
result = async_benchmark(fetch_user_data, 123, rounds=5)
|
|
655
|
+
result = await async_benchmark(fetch_user_data, 123, rounds=5)
|
|
369
656
|
assert result['mean'] < 0.05 # Should complete within 50ms
|
|
370
657
|
assert result['p95'] < 0.1 # 95% of queries under 100ms
|
|
371
658
|
```
|
|
@@ -374,9 +661,10 @@ async def test_database_query_performance(async_benchmark):
|
|
|
374
661
|
|
|
375
662
|
```python
|
|
376
663
|
@pytest.mark.async_benchmark
|
|
377
|
-
|
|
664
|
+
@pytest.mark.asyncio
|
|
665
|
+
async def test_performance(async_benchmark):
|
|
378
666
|
# Your benchmark test
|
|
379
|
-
result = async_benchmark(my_async_function)
|
|
667
|
+
result = await async_benchmark(my_async_function)
|
|
380
668
|
assert result is not None
|
|
381
669
|
```
|
|
382
670
|
|
|
@@ -405,6 +693,7 @@ A dictionary with comprehensive statistics including min, max, mean, median, std
|
|
|
405
693
|
|
|
406
694
|
- Python ≥ 3.9
|
|
407
695
|
- pytest ≥ 8.3.5
|
|
696
|
+
- pytest-asyncio ≥ 0.23.0 (automatically installed)
|
|
408
697
|
|
|
409
698
|
Note: Rich (for beautiful terminal output) is automatically installed as a dependency.
|
|
410
699
|
|