Skip to content

Conversation

@tony
Copy link
Member

@tony tony commented Dec 29, 2025

Summary

Enable clean async doctests without asyncio.run() boilerplate:

# BEFORE (ugly boilerplate)
>>> async def example():
...     result = await fetch_data()
...     return result
>>> asyncio.run(example())
42

# AFTER (clean top-level await)
>>> result = await fetch_data()
>>> result
42

Implementation

  • Uses Python 3.8+ PyCF_ALLOW_TOP_LEVEL_AWAIT compile flag
  • Detects CO_COROUTINE flag to transparently handle sync vs async code
  • One asyncio.Runner per DocTest block (allows create_task() in one line, await in the next)
  • _Runner310 shim provides Python 3.10 compatibility (asyncio.Runner is 3.11+)
  • Renamed testdocutilsrun_doctest_docutils to avoid pytest auto-collection

Test plan

  • Basic top-level await: >>> await asyncio.sleep(0)
  • Async functions with return values
  • Mixed sync/async examples in same block
  • State persistence across examples
  • Async context managers (async with)
  • Async iteration (async for)
  • Async comprehensions
  • Expected exceptions in async code
  • Markdown file support
  • All 95 existing tests pass

@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

❌ Patch coverage is 67.65799% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.68%. Comparing base (b08957a) to head (8781eaa).

Files with missing lines Patch % Lines
src/doctest_docutils.py 45.56% 86 Missing ⚠️
src/pytest_doctest_docutils.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #59      +/-   ##
==========================================
- Coverage   71.11%   70.68%   -0.43%     
==========================================
  Files          14       15       +1     
  Lines         914     1177     +263     
==========================================
+ Hits          650      832     +182     
- Misses        264      345      +81     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

tony added a commit that referenced this pull request Dec 29, 2025
why: Document new features and breaking changes for upcoming release
what:
- Add AsyncDocTestRunner feature with before/after code examples
- Note pytest plugin integration
- Document testdocutils -> run_doctest_docutils rename as breaking change
@tony
Copy link
Member Author

tony commented Dec 29, 2025

Code Review Summary

Overall Assessment: ✅ LGTM with one minor suggestion

This PR implements async doctest support with top-level await functionality - a well-designed feature that follows stdlib patterns and includes comprehensive tests (all 108 tests pass).

Issues Found

After thorough analysis by 5 specialized review agents (AGENTS.md compliance, bug scanning, git history, previous PR patterns, and code comments), only 1 issue met the confidence threshold for reporting:


Import Style Violation (Confidence: 88/100)

Location: src/doctest_docutils.py line 19

Issue: from contextlib import AbstractContextManager violates the AGENTS.md stdlib namespace import rule.

AGENTS.md states: "Use namespace imports for stdlib: import enum instead of from enum import Enum"

Note: The file already has import contextlib on line 8. The class definition on line 46 could use contextlib.AbstractContextManager["_Runner310"] instead.

Suggested fix:

- from contextlib import AbstractContextManager
...
- class _Runner310(AbstractContextManager["_Runner310"]):
+ class _Runner310(contextlib.AbstractContextManager["_Runner310"]):

What Looked Good

  • No critical bugs detected - Event loop management, exception handling, and resource cleanup all properly implemented
  • Clean git history - Atomic commits with clear purposes
  • All tests pass (108 tests including 24 new async-specific tests)
  • Type checking passes (mypy)
  • Linting passes (ruff)
  • Code comments follow guidance - Appropriate explanations for type ignores and implementation choices
  • Breaking change documented - Function rename properly noted in CHANGES

Filtered Issues (Below 80 confidence threshold)

The following were considered but filtered out as style preferences rather than requirements:

  • Import inside function body (pattern already used elsewhere in file)
  • NumPy docstrings for private test helpers
  • NumPy docstrings for dunder methods
  • Coverage metrics (no threshold specified in AGENTS.md)
  • Deprecation warning for renamed function (internal utility, change documented)

@tony
Copy link
Member Author

tony commented Dec 29, 2025

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

tony added a commit that referenced this pull request Dec 29, 2025
why: Document new features and breaking changes for upcoming release
what:
- Add AsyncDocTestRunner feature with before/after code examples
- Note pytest plugin integration
- Document testdocutils -> run_doctest_docutils rename as breaking change
@tony tony force-pushed the asyncio branch 2 times, most recently from 3bfe654 to 780890a Compare December 29, 2025 20:59
tony added a commit that referenced this pull request Dec 29, 2025
why: Document new features and breaking changes for upcoming release
what:
- Add AsyncDocTestRunner feature with before/after code examples
- Note pytest plugin integration
- Document testdocutils -> run_doctest_docutils rename as breaking change
tony added a commit that referenced this pull request Dec 29, 2025
why: Document new features and breaking changes for upcoming release
what:
- Add AsyncDocTestRunner feature with before/after code examples
- Note pytest plugin integration
- Document testdocutils -> run_doctest_docutils rename as breaking change
tony added 10 commits December 30, 2025 10:26
why: Prepare for asyncio development with consistent patterns
what:
- Add critical doctest rules (executable tests, no SKIP workarounds)
- Add async doctest pattern with asyncio.run()
- Add Asyncio Development section with subprocess patterns
- Add async API conventions, testing patterns, and anti-patterns
…wait

why: Enable clean async doctests without asyncio.run() boilerplate
what:
- Add _Runner310 shim for Python 3.10 compatibility (asyncio.Runner is 3.11+)
- Add _run_doctest_example() with PyCF_ALLOW_TOP_LEVEL_AWAIT compile flag
- Add AsyncDocTestRunner extending DocTestRunner with async support
- Add AsyncDebugRunner for raise-on-error behavior
- Rename testdocutils -> run_doctest_docutils to avoid pytest collection
- Detect CO_COROUTINE flag to transparently handle sync vs async code
why: Provide async doctest support in pytest plugin
what:
- Import AsyncDocTestRunner from doctest_docutils
- Change PytestDoctestRunner to extend AsyncDocTestRunner
- Async support is now transparent for all doctest files
why: Make asyncio module available in doctests without explicit import
what:
- Add autouse doctest_namespace fixture
- Inject asyncio module for use in async doctests
why: Verify async doctest functionality works correctly
what:
- Use functional pytest style with NamedTuple parametrization
- Add RST and Markdown dual coverage for all test cases
- Test basic top-level await with asyncio.sleep
- Test async functions returning values
- Test mixed sync/async examples in same block
- Test state persistence across examples
- Test async context managers (async with)
- Test async iteration (async for)
- Test async comprehensions
- Test expected exceptions in async code
- Add pytester-based plugin integration tests
- Test conftest fixture integration with async
- Test failure reporting for async doctests
why: Document new features and breaking changes for upcoming release
what:
- Add AsyncDocTestRunner feature with before/after code examples
- Note pytest plugin integration
- Document testdocutils -> run_doctest_docutils rename as breaking change
why: Comply with project coding standards requiring NumPy docstrings
what:
- Add Parameters/Returns sections to _rst_content, _md_content helpers
- Add docstrings to _Runner310.__init__, __enter__, __exit__ methods
why: Comply with project docstring standards
what:
- Add Parameters section documenting debug and loop_factory
- Add Returns section documenting return type
why: Comply with project requirement for working doctests
what:
- Add Examples section demonstrating basic async doctest usage
- Shows top-level await working transparently
why: Document the new capability enabled by AsyncDocTestRunner
what:
- Replace asyncio.run() boilerplate with top-level await syntax
- Show that await works directly in doctests now
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants