pytest-async-benchmark 0.2.0__tar.gz → 0.3.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.
Files changed (41) hide show
  1. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/.github/workflows/ci.yml +3 -3
  2. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/CHANGELOG.md +14 -0
  3. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/PKG-INFO +139 -89
  4. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/README.md +137 -87
  5. pytest_async_benchmark-0.3.0/examples/pedantic_examples.py +86 -0
  6. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/pyproject.toml +6 -1
  7. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/scripts/release-check.py +1 -1
  8. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/__init__.py +1 -1
  9. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/display.py +2 -2
  10. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/plugin.py +79 -1
  11. pytest_async_benchmark-0.3.0/src/pytest_async_benchmark/runner.py +142 -0
  12. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/utils.py +2 -1
  13. pytest_async_benchmark-0.3.0/tests/test_pedantic.py +220 -0
  14. pytest_async_benchmark-0.3.0/uv.lock +2871 -0
  15. pytest_async_benchmark-0.2.0/src/pytest_async_benchmark/runner.py +0 -56
  16. pytest_async_benchmark-0.2.0/uv.lock +0 -1279
  17. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/.github/workflows/release.yml +0 -0
  18. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/.github/workflows/test-publish.yml +0 -0
  19. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/.gitignore +0 -0
  20. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/.pre-commit-config.yaml +0 -0
  21. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/LICENSE +0 -0
  22. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/PYPI_SETUP.md +0 -0
  23. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/RELEASE_GUIDE.md +0 -0
  24. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/examples/backwards_compatibility_examples.py +0 -0
  25. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/examples/comparison_examples.py +0 -0
  26. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/examples/marker_syntax_examples.py +0 -0
  27. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/examples/pytest_asyncio_showcase.py +0 -0
  28. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/examples/pytest_examples.py +0 -0
  29. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/examples/quart_api_comparison.py +0 -0
  30. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/screenshot.png +0 -0
  31. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/scripts/quality-check.py +0 -0
  32. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/analytics.py +0 -0
  33. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/comparison.py +0 -0
  34. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/src/pytest_async_benchmark/stats.py +0 -0
  35. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/tests/conftest.py +0 -0
  36. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/tests/test_async_bench.py +0 -0
  37. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/tests/test_async_compatibility.py +0 -0
  38. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/tests/test_backwards_compatibility.py +0 -0
  39. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/tests/test_comparison.py +0 -0
  40. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0}/tests/test_demo.py +0 -0
  41. {pytest_async_benchmark-0.2.0 → pytest_async_benchmark-0.3.0/tests}/test_marker_functionality.py +0 -0
@@ -11,13 +11,13 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- python-version: ["3.9", "3.10", "3.11", "3.12"]
14
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15
15
 
16
16
  steps:
17
17
  - uses: actions/checkout@v4
18
18
 
19
19
  - name: Install uv
20
- uses: astral-sh/setup-uv@v3
20
+ uses: astral-sh/setup-uv@v5
21
21
  with:
22
22
  version: "latest"
23
23
 
@@ -39,7 +39,7 @@ jobs:
39
39
 
40
40
  - name: Test examples
41
41
  run: |
42
- uv run python -m pytest examples/pytest_examples.py -v
42
+ uv run python -m pytest examples/pytest_examples.py examples/pedantic_examples.py -v
43
43
  uv run python examples/comparison_examples.py
44
44
 
45
45
  - name: Test package build
@@ -5,6 +5,20 @@ All notable changes to pytest-async-benchmark will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0] - 2026-11-10
9
+
10
+ ### 🚀 Major Features
11
+ - **Pedantic Mode**: Added `async_benchmark.pedantic()` (and `AsyncBenchmarkRunner.run_pedantic()`) allowing per-iteration setup and teardown while excluding their duration from benchmark measurements (addresses issue #1)
12
+ - **Flexible Hook Support**: Setup and teardown functions can be synchronous or asynchronous coroutines
13
+ - **Argument Passing**: Return values from `setup` (individual values or unpacked tuples) are passed as arguments to both the target function and `teardown`
14
+ - **Dual Interface Pedantic Support**: Works seamlessly with `await async_benchmark.pedantic(...)` or synchronous `async_benchmark.pedantic(...)`
15
+
16
+ ### 🔧 Improvements & Upgrades
17
+ - **Dependency Upgrades**: Upgraded dependencies to latest versions (pytest 9.x, rich 15.x, ruff 0.17.x, aiohttp 3.14.x, quart 0.23.x, pre-commit 4.7.x)
18
+ - **Modern Python Compatibility**: Fixed `asyncio.iscoroutinefunction` deprecations slated for removal in Python 3.16 by switching to `inspect.iscoroutinefunction` across all modules
19
+ - **Pytest-Asyncio v1.x Compatibility**: Added explicit `asyncio_mode = "auto"` and `asyncio_default_fixture_loop_scope = "function"` configuration in `pyproject.toml`
20
+ - **CI Matrix**: Extended GitHub Actions CI matrix to test on Python 3.13
21
+
8
22
  ## [0.2.0] - 2025-05-28
9
23
 
10
24
  ### 🚀 Major Features
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: pytest-async-benchmark
3
- Version: 0.2.0
3
+ Version: 0.3.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
@@ -87,11 +87,12 @@ async def test_my_api():
87
87
  # Your existing async test code
88
88
  pass
89
89
 
90
+
90
91
  # Then just add pytest-async-benchmark and use:
91
92
  @pytest.mark.asyncio
92
93
  async def test_my_api_performance(async_benchmark):
93
94
  result = await async_benchmark(my_async_function)
94
- assert result['mean'] < 0.01
95
+ assert result["mean"] < 0.01
95
96
  ```
96
97
 
97
98
  ## 🚀 Quick Start
@@ -105,18 +106,20 @@ When pytest-asyncio is installed, use the natural async/await syntax:
105
106
  import asyncio
106
107
  import pytest
107
108
 
109
+
108
110
  async def slow_async_operation():
109
111
  await asyncio.sleep(0.01) # 10ms
110
112
  return "result"
111
113
 
114
+
112
115
  @pytest.mark.asyncio
113
116
  @pytest.mark.async_benchmark(rounds=5, iterations=10)
114
117
  async def test_async_performance(async_benchmark):
115
118
  # Use await with pytest-asyncio for best experience
116
119
  result = await async_benchmark(slow_async_operation)
117
-
120
+
118
121
  # Your assertions here
119
- assert result['mean'] < 0.02
122
+ assert result["mean"] < 0.02
120
123
  ```
121
124
 
122
125
  ### Simple Interface (without pytest-asyncio)
@@ -126,17 +129,19 @@ For simpler setups, the sync interface works automatically:
126
129
  import asyncio
127
130
  import pytest
128
131
 
132
+
129
133
  async def slow_async_operation():
130
134
  await asyncio.sleep(0.01)
131
135
  return "result"
132
136
 
137
+
133
138
  @pytest.mark.async_benchmark(rounds=5, iterations=10)
134
139
  def test_sync_performance(async_benchmark):
135
140
  # No await needed - sync interface
136
141
  result = async_benchmark(slow_async_operation)
137
-
142
+
138
143
  # Your assertions here
139
- assert result['mean'] < 0.02 # Should complete in under 20ms
144
+ assert result["mean"] < 0.02 # Should complete in under 20ms
140
145
  ```
141
146
 
142
147
  ### Flexible Configuration Syntax
@@ -148,14 +153,14 @@ pytest-async-benchmark supports **two syntax options** for configuring benchmark
148
153
  @pytest.mark.async_benchmark(rounds=5, iterations=10)
149
154
  async def test_with_marker(async_benchmark):
150
155
  result = await async_benchmark(slow_async_operation)
151
- assert result['rounds'] == 5 # From marker
156
+ assert result["rounds"] == 5 # From marker
152
157
  ```
153
158
 
154
159
  #### Option 2: Function Parameter Syntax
155
160
  ```python
156
161
  async def test_with_parameters(async_benchmark):
157
162
  result = await async_benchmark(slow_async_operation, rounds=5, iterations=10)
158
- assert result['rounds'] == 5 # From function parameters
163
+ assert result["rounds"] == 5 # From function parameters
159
164
  ```
160
165
 
161
166
  ## 🔧 Interface Detection & Flexibility
@@ -170,6 +175,7 @@ When pytest-asyncio is installed, use natural async/await syntax:
170
175
  [tool.pytest.ini_options]
171
176
  asyncio_mode = "auto"
172
177
 
178
+
173
179
  # Then use await syntax
174
180
  @pytest.mark.asyncio
175
181
  async def test_my_benchmark(async_benchmark):
@@ -211,7 +217,7 @@ def test_my_benchmark(async_benchmark):
211
217
  async def test_with_marker(async_benchmark):
212
218
  """Use marker for consistent, visible configuration."""
213
219
  result = await async_benchmark(my_async_function)
214
- assert result['rounds'] == 10 # Configuration is explicit and visible
220
+ assert result["rounds"] == 10 # Configuration is explicit and visible
215
221
  ```
216
222
 
217
223
  #### Simple Interface (without pytest-asyncio)
@@ -220,7 +226,7 @@ async def test_with_marker(async_benchmark):
220
226
  def test_with_marker_sync(async_benchmark):
221
227
  """Use marker for consistent, visible configuration - simple style."""
222
228
  result = async_benchmark(my_async_function) # No await needed
223
- assert result['rounds'] == 10 # Configuration is explicit and visible
229
+ assert result["rounds"] == 10 # Configuration is explicit and visible
224
230
  ```
225
231
 
226
232
  ### Two Configuration Syntaxes
@@ -232,13 +238,14 @@ def test_with_marker_sync(async_benchmark):
232
238
  @pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
233
239
  async def test_with_marker_async(async_benchmark):
234
240
  result = await async_benchmark(my_async_function)
235
- assert result['rounds'] == 10
241
+ assert result["rounds"] == 10
242
+
236
243
 
237
244
  # Without pytest-asyncio
238
245
  @pytest.mark.async_benchmark(rounds=10, iterations=100, warmup_rounds=2)
239
246
  def test_with_marker_sync(async_benchmark):
240
247
  result = async_benchmark(my_async_function)
241
- assert result['rounds'] == 10
248
+ assert result["rounds"] == 10
242
249
  ```
243
250
 
244
251
  #### Function Parameter Syntax (Dynamic)
@@ -247,22 +254,17 @@ def test_with_marker_sync(async_benchmark):
247
254
  @pytest.mark.asyncio
248
255
  async def test_with_parameters_async(async_benchmark):
249
256
  result = await async_benchmark(
250
- my_async_function,
251
- rounds=10,
252
- iterations=100,
253
- warmup_rounds=2
257
+ my_async_function, rounds=10, iterations=100, warmup_rounds=2
254
258
  )
255
- assert result['rounds'] == 10
259
+ assert result["rounds"] == 10
260
+
256
261
 
257
262
  # Without pytest-asyncio
258
263
  def test_with_parameters_simple(async_benchmark):
259
264
  result = async_benchmark(
260
- my_async_function,
261
- rounds=10,
262
- iterations=100,
263
- warmup_rounds=2
265
+ my_async_function, rounds=10, iterations=100, warmup_rounds=2
264
266
  )
265
- assert result['rounds'] == 10
267
+ assert result["rounds"] == 10
266
268
  ```
267
269
 
268
270
  #### Combined Syntax (Override)
@@ -270,15 +272,17 @@ Both interfaces support parameter precedence where function parameters override
270
272
 
271
273
  ```python
272
274
  @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
275
+ async def test_with_override(
276
+ async_benchmark,
277
+ ): # Works with or without @pytest.mark.asyncio
274
278
  """Function parameters override marker settings."""
275
279
  result = await async_benchmark( # Use 'await' only with pytest-asyncio
276
280
  my_async_function,
277
- rounds=20 # This overrides marker's rounds=5
281
+ rounds=20, # This overrides marker's rounds=5
278
282
  # iterations=50 comes from marker
279
283
  )
280
- assert result['rounds'] == 20 # Function parameter wins
281
- assert result['iterations'] == 50 # From marker
284
+ assert result["rounds"] == 20 # Function parameter wins
285
+ assert result["iterations"] == 50 # From marker
282
286
  ```
283
287
 
284
288
  ### Basic Benchmarking
@@ -302,6 +306,42 @@ async def test_my_async_function(async_benchmark):
302
306
 
303
307
  ### Advanced Configuration
304
308
 
309
+ ### 🔬 Pedantic Mode (Setup & Teardown Excluded from Timing)
310
+
311
+ When benchmarking async operations that require per-iteration setup (e.g. creating fresh databases, initializing connection pools, or preparing test fixtures) and teardown, setup/cleanup overhead pollutes the timing results.
312
+
313
+ `async_benchmark.pedantic()` runs setup before and teardown after each iteration, but strictly measures **only the target operation**:
314
+
315
+ ```python
316
+ @pytest.mark.asyncio
317
+ async def test_database_indexing(async_benchmark):
318
+ async def setup():
319
+ db = await create_fresh_database()
320
+ return db
321
+
322
+ async def teardown(db):
323
+ await db.close()
324
+
325
+ async def index_records(db):
326
+ await db.execute("CREATE INDEX idx_user ON users(email)")
327
+
328
+ result = await async_benchmark.pedantic(
329
+ index_records,
330
+ setup=setup,
331
+ teardown=teardown,
332
+ rounds=10,
333
+ iterations=1,
334
+ )
335
+ assert result["mean"] < 0.05
336
+ ```
337
+
338
+ **Key Features:**
339
+ - ⚡ **Zero Setup/Teardown Overhead**: Setup and teardown times are excluded from benchmark measurements.
340
+ - 🔄 **Sync or Async Hooks**: Both `setup` and `teardown` can be synchronous functions or `async def` coroutines.
341
+ - 📦 **Argument Passing**: Return value from `setup` is passed as argument(s) to the target function and `teardown`. Unpacks tuples automatically if returned.
342
+ - 🛡️ **Safe Cleanup**: Teardown runs in a `finally` block even if the target encounters an error.
343
+ - 🌓 **Dual Interface**: Supports `await async_benchmark.pedantic(...)` or synchronous `async_benchmark.pedantic(...)`.
344
+
305
345
  ## 🎯 Two Configuration Syntaxes
306
346
 
307
347
  pytest-async-benchmark offers **two flexible ways** to configure your benchmarks:
@@ -316,10 +356,10 @@ Use pytest markers for **declarative, visible configuration**:
316
356
  async def test_high_precision_benchmark(async_benchmark):
317
357
  """High precision benchmark with marker configuration."""
318
358
  result = await async_benchmark(my_async_function)
319
-
359
+
320
360
  # Configuration is visible and consistent
321
- assert result['rounds'] == 10
322
- assert result['iterations'] == 100
361
+ assert result["rounds"] == 10
362
+ assert result["iterations"] == 100
323
363
  ```
324
364
 
325
365
  **Benefits:**
@@ -338,12 +378,9 @@ async def test_dynamic_benchmark(async_benchmark):
338
378
  """Dynamic benchmark with runtime configuration."""
339
379
  # Configuration can be computed or conditional
340
380
  rounds = 20 if is_production else 5
341
-
381
+
342
382
  result = await async_benchmark(
343
- my_async_function,
344
- rounds=rounds,
345
- iterations=50,
346
- warmup_rounds=1
383
+ my_async_function, rounds=rounds, iterations=50, warmup_rounds=1
347
384
  )
348
385
  ```
349
386
 
@@ -361,19 +398,19 @@ async def test_dynamic_benchmark(async_benchmark):
361
398
  @pytest.mark.async_benchmark(rounds=5, iterations=50, warmup_rounds=1)
362
399
  async def test_with_overrides(async_benchmark):
363
400
  """Use marker defaults with selective overrides."""
364
-
401
+
365
402
  # Quick test with marker defaults
366
403
  quick_result = await async_benchmark(fast_function)
367
-
404
+
368
405
  # Precision test with overridden rounds
369
406
  precise_result = await async_benchmark(
370
407
  slow_function,
371
- rounds=20 # Overrides marker's rounds=5
408
+ rounds=20, # Overrides marker's rounds=5
372
409
  # iterations=50 and warmup_rounds=1 come from marker
373
410
  )
374
-
375
- assert quick_result['rounds'] == 5 # From marker
376
- assert precise_result['rounds'] == 20 # From function override
411
+
412
+ assert quick_result["rounds"] == 5 # From marker
413
+ assert precise_result["rounds"] == 20 # From function override
377
414
  ```
378
415
 
379
416
  ### Traditional Configuration
@@ -383,9 +420,9 @@ async def test_with_overrides(async_benchmark):
383
420
  async def test_with_custom_settings(async_benchmark):
384
421
  result = await async_benchmark(
385
422
  my_async_function,
386
- rounds=10, # Number of rounds to run
387
- iterations=5, # Iterations per round
388
- warmup_rounds=2 # Warmup rounds before measurement
423
+ rounds=10, # Number of rounds to run
424
+ iterations=5, # Iterations per round
425
+ warmup_rounds=2, # Warmup rounds before measurement
389
426
  )
390
427
  ```
391
428
 
@@ -398,12 +435,12 @@ async def test_with_args(async_benchmark):
398
435
  # Process the data
399
436
  await asyncio.sleep(0.01)
400
437
  return len(data) * multiplier
401
-
438
+
402
439
  result = await async_benchmark(
403
440
  process_data,
404
- "test_data", # positional arg
405
- multiplier=2, # keyword arg
406
- rounds=3
441
+ "test_data", # positional arg
442
+ multiplier=2, # keyword arg
443
+ rounds=3,
407
444
  )
408
445
  ```
409
446
 
@@ -414,14 +451,17 @@ async def test_with_args(async_benchmark):
414
451
  ```python
415
452
  from pytest_async_benchmark import quick_compare
416
453
 
454
+
417
455
  async def algorithm_v1():
418
456
  await asyncio.sleep(0.002) # 2ms
419
457
  return "v1_result"
420
458
 
459
+
421
460
  async def algorithm_v2():
422
461
  await asyncio.sleep(0.0015) # 1.5ms - optimized
423
462
  return "v2_result"
424
463
 
464
+
425
465
  # Quick one-liner comparison
426
466
  def test_algorithm_comparison():
427
467
  winner, results = quick_compare(algorithm_v1, algorithm_v2, rounds=5)
@@ -433,12 +473,16 @@ def test_algorithm_comparison():
433
473
  ```python
434
474
  from pytest_async_benchmark import a_vs_b_comparison
435
475
 
476
+
436
477
  def test_detailed_comparison():
437
478
  # Compare with beautiful terminal output
438
479
  a_vs_b_comparison(
439
- "Original Algorithm", algorithm_v1,
440
- "Optimized Algorithm", algorithm_v2,
441
- rounds=8, iterations=20
480
+ "Original Algorithm",
481
+ algorithm_v1,
482
+ "Optimized Algorithm",
483
+ algorithm_v2,
484
+ rounds=8,
485
+ iterations=20,
442
486
  )
443
487
  ```
444
488
 
@@ -447,25 +491,30 @@ def test_detailed_comparison():
447
491
  ```python
448
492
  from pytest_async_benchmark import BenchmarkComparator
449
493
 
494
+
450
495
  def test_multi_scenario():
451
496
  comparator = BenchmarkComparator()
452
-
497
+
453
498
  # Add multiple scenarios
454
499
  comparator.add_scenario(
455
- "Database Query v1", db_query_v1,
456
- rounds=5, iterations=10,
457
- description="Original database implementation"
500
+ "Database Query v1",
501
+ db_query_v1,
502
+ rounds=5,
503
+ iterations=10,
504
+ description="Original database implementation",
458
505
  )
459
-
506
+
460
507
  comparator.add_scenario(
461
- "Database Query v2", db_query_v2,
462
- rounds=5, iterations=10,
463
- description="Optimized with connection pooling"
508
+ "Database Query v2",
509
+ db_query_v2,
510
+ rounds=5,
511
+ iterations=10,
512
+ description="Optimized with connection pooling",
464
513
  )
465
-
514
+
466
515
  # Run comparison and get results
467
516
  results = comparator.run_comparison()
468
-
517
+
469
518
  # Beautiful comparison table automatically displayed
470
519
  # Access programmatic results
471
520
  fastest = results.get_fastest_scenario()
@@ -478,20 +527,20 @@ Each benchmark returns detailed statistics:
478
527
 
479
528
  ```python
480
529
  {
481
- 'min': 0.001234, # Minimum execution time
482
- 'max': 0.005678, # Maximum execution time
483
- 'mean': 0.002456, # Mean execution time
484
- 'median': 0.002123, # Median execution time
485
- 'stddev': 0.000234, # Standard deviation
486
- 'p50': 0.002123, # 50th percentile (median)
487
- 'p90': 0.003456, # 90th percentile
488
- 'p95': 0.004123, # 95th percentile
489
- 'p99': 0.004789, # 99th percentile
490
- 'rounds': 5, # Number of rounds executed
491
- 'iterations': 1, # Number of iterations per round
492
- 'raw_times': [...], # List of raw timing measurements
493
- 'grade': 'A', # Performance grade (A-F)
494
- 'grade_score': 87.5 # Numeric grade score (0-100)
530
+ "min": 0.001234, # Minimum execution time
531
+ "max": 0.005678, # Maximum execution time
532
+ "mean": 0.002456, # Mean execution time
533
+ "median": 0.002123, # Median execution time
534
+ "stddev": 0.000234, # Standard deviation
535
+ "p50": 0.002123, # 50th percentile (median)
536
+ "p90": 0.003456, # 90th percentile
537
+ "p95": 0.004123, # 95th percentile
538
+ "p99": 0.004789, # 99th percentile
539
+ "rounds": 5, # Number of rounds executed
540
+ "iterations": 1, # Number of iterations per round
541
+ "raw_times": [...], # List of raw timing measurements
542
+ "grade": "A", # Performance grade (A-F)
543
+ "grade_score": 87.5, # Numeric grade score (0-100)
495
544
  }
496
545
  ```
497
546
 
@@ -596,22 +645,24 @@ import pytest
596
645
 
597
646
  app = FastAPI()
598
647
 
648
+
599
649
  @app.get("/api/data")
600
650
  async def get_data():
601
651
  # Simulate database query
602
652
  await asyncio.sleep(0.005)
603
653
  return {"data": "example"}
604
654
 
655
+
605
656
  @pytest.mark.asyncio
606
657
  async def test_fastapi_endpoint_performance(async_benchmark):
607
658
  async def make_request():
608
659
  with TestClient(app) as client:
609
660
  response = client.get("/api/data")
610
661
  return response.json()
611
-
662
+
612
663
  result = await async_benchmark(make_request, rounds=10)
613
- assert result['mean'] < 0.1 # Should respond within 100ms
614
- assert result['grade'] in ['A', 'B'] # Should have good performance grade
664
+ assert result["mean"] < 0.1 # Should respond within 100ms
665
+ assert result["grade"] in ["A", "B"] # Should have good performance grade
615
666
  ```
616
667
 
617
668
  ### Quart API Endpoint Comparison
@@ -623,21 +674,22 @@ from pytest_async_benchmark import a_vs_b_comparison
623
674
  import asyncio
624
675
  import aiohttp
625
676
 
677
+
626
678
  async def test_api_v1():
627
679
  async with aiohttp.ClientSession() as session:
628
- async with session.get('http://localhost:5000/api/v1/data') as resp:
680
+ async with session.get("http://localhost:5000/api/v1/data") as resp:
629
681
  return await resp.json()
630
682
 
683
+
631
684
  async def test_api_v2():
632
685
  async with aiohttp.ClientSession() as session:
633
- async with session.get('http://localhost:5000/api/v2/data') as resp:
686
+ async with session.get("http://localhost:5000/api/v2/data") as resp:
634
687
  return await resp.json()
635
688
 
689
+
636
690
  # Compare API versions
637
691
  a_vs_b_comparison(
638
- "API v1", test_api_v1,
639
- "API v2 (Optimized)", test_api_v2,
640
- rounds=10, iterations=5
692
+ "API v1", test_api_v1, "API v2 (Optimized)", test_api_v2, rounds=10, iterations=5
641
693
  )
642
694
  ```
643
695
 
@@ -648,13 +700,11 @@ a_vs_b_comparison(
648
700
  async def test_database_query_performance(async_benchmark):
649
701
  async def fetch_user_data(user_id):
650
702
  async with database.connection() as conn:
651
- return await conn.fetch_one(
652
- "SELECT * FROM users WHERE id = ?", user_id
653
- )
654
-
703
+ return await conn.fetch_one("SELECT * FROM users WHERE id = ?", user_id)
704
+
655
705
  result = await async_benchmark(fetch_user_data, 123, rounds=5)
656
- assert result['mean'] < 0.05 # Should complete within 50ms
657
- assert result['p95'] < 0.1 # 95% of queries under 100ms
706
+ assert result["mean"] < 0.05 # Should complete within 50ms
707
+ assert result["p95"] < 0.1 # 95% of queries under 100ms
658
708
  ```
659
709
 
660
710
  ## 🎯 Using Markers