Companion code for the post "Playwright Python: Sync or Async API?"

Versions used: Python 3.12.3, playwright 1.64.0, pytest 9.1.1, pytest-playwright 0.10.0,
pytest-playwright-asyncio 0.10.0, pytest-asyncio 1.4.0 (pulled in by pytest-playwright-asyncio).

0. Serve the local site (terminal 1). Report pages answer after about one second on purpose.
     python3 serve.py                 # http://127.0.0.1:8027

1. Scripts (any venv with playwright 1.64 and Chromium installed):
     python sync_script.py            # 3 titles, "done in 3.4 s" (measured 3.4-4.0 s over 3 runs)
     python async_script.py           # 3 titles, "done in 3.4-3.6 s" (same work, awaited one by one)
     python async_concurrent.py       # 3 titles, "done in 1.3-1.4 s" (asyncio.gather, one context per task)
     python conversions_sync.py       # http://127.0.0.1:8027/, Status board, 200
     python conversions_async.py      # same output
     python pitfalls/sync_inside_loop.py   # Error: It looks like you are using Playwright Sync API inside the asyncio loop.
     python pitfalls/sync_in_thread.py     # Report: Sales (sync API in a worker thread via asyncio.to_thread)
     printf '...' | python -m asyncio      # the asyncio REPL with top-level await works (see post)

2. Sync suite (venv A: pip install -r sync-suite/requirements.txt; playwright install chromium):
     cd sync-suite && pytest -v       # 2 passed

3. Async suite (venv B, a separate one: pip install -r async-suite/requirements.txt):
     cd async-suite && pytest -v      # 3 passed
     pytest --numprocesses 2          # 3 passed (after pip install pytest-xdist==3.8.0)

4. Pitfalls, each run with the venv named:
     cd pitfalls/async-no-loop-scope && pytest            # venv B: HANGS (killed by timeout after 300 s and again after 60 s;
                                                          #   -o faulthandler_timeout=15 shows it stuck in the function loop's select)
     cd pitfalls/async-no-loop-scope && pytest -o asyncio_default_test_loop_scope=session   # venv B: 1 passed
     cd pitfalls/async-no-loop-scope && pytest --timeout 10                         # venv B + pytest-timeout 2.4.0: test marked F,
                                                          #   then the run still hangs (killed after 60 s)
     cd pitfalls/async-no-loop-scope && pytest --timeout 10 --timeout-method thread # venv B + pytest-timeout: stack dump, exit 1 after ~10 s
     cd pitfalls/async-fixture-no-loop-scope && pytest    # venv B: HANGS (killed after 60 s)
     cd pitfalls/async-missing-await && mypy tests/test_reports.py   # mypy 2.4.0: error ... [unused-coroutine]
                                                          #   note: Are you missing an await?
     cd pitfalls/async-missing-await && pytest            # venv B: 1 passed, 1 warning
                                                          #   RuntimeWarning: coroutine 'PageAssertions.to_have_title' was never awaited
     cd pitfalls/async-missing-await-strict && pytest     # venv B: 1 failed (PytestUnraisableExceptionWarning)
     cd pitfalls/async-test-sync-page && pytest           # venv C (pytest-playwright + pytest-asyncio 1.4.0):
                                                          #   1 failed, RuntimeError: Runner.run() cannot be called from a running event loop
     both plugins in one venv (pytest-playwright + pytest-playwright-asyncio), any suite:
                                                          #   RuntimeError: pytest-playwright and pytest-playwright-asyncio are not compatible.
                                                          #   Please use only one of them.
     same venv: cd sync-suite && pytest -p no:playwright-asyncio   # 2 passed
     same venv: cd async-suite && pytest -p no:playwright          # 3 passed
