[
  {
    "path": ".builds/alpine.yml",
    "content": "image: alpine/latest\npackages:\n  - curl\n  - gcc\n  - libffi-dev\n  - musl-dev\n  - openssl-dev\n  - python3-dev\n  # required to build cryptography\n  - rust\n  - cargo\nsources:\n  - https://github.com/python-trio/trio\ntasks:\n  - test: |\n      python3 -m venv venv\n      source venv/bin/activate\n      cd trio\n      CI_BUILD_ID=$JOB_ID CI_BUILD_URL=$JOB_URL ./ci.sh\nenvironment:\n  CODECOV_TOKEN: 87cefb17-c44b-4f2f-8b30-1fff5769ce46\n  JOB_NAME: Alpine\n"
  },
  {
    "path": ".builds/fedora.yml",
    "content": "image: fedora/rawhide\npackages:\n  - python3-devel\n  - python3-pip\nsources:\n  - https://github.com/python-trio/trio\ntasks:\n  - test: |\n      python3 -m venv venv\n      source venv/bin/activate\n      cd trio\n      CI_BUILD_ID=$JOB_ID CI_BUILD_URL=$JOB_URL ./ci.sh\nenvironment:\n  CODECOV_TOKEN: 87cefb17-c44b-4f2f-8b30-1fff5769ce46\n  JOB_NAME: Fedora\n"
  },
  {
    "path": ".builds/freebsd.yml",
    "content": "image: freebsd/latest\npackages:\n  - curl\n  - python39\n  - py39-sqlite3\n  - rust  # required to build cryptography\nsources:\n  - https://github.com/python-trio/trio\ntasks:\n  - setup: sudo ln -s /usr/local/bin/bash /bin/bash\n  - test: |\n      python3.9 -m venv venv\n      source venv/bin/activate\n      cd trio\n      CI_BUILD_ID=$JOB_ID CI_BUILD_URL=$JOB_URL ./ci.sh\nenvironment:\n  CODECOV_TOKEN: 87cefb17-c44b-4f2f-8b30-1fff5769ce46\n  JOB_NAME: FreeBSD\n"
  },
  {
    "path": ".codecov.yml",
    "content": "# -- repository yaml --\n\n# Explicitly wait for all jobs to finish, as wait_for_ci prematurely triggers.\n# See https://github.com/python-trio/trio/issues/2689\ncodecov:\n  notify:\n    # This number needs to be changed whenever the number of runs in CI is changed.\n    # Another option is codecov-cli: https://github.com/codecov/codecov-cli#send-notifications\n    after_n_builds: 31\n    wait_for_ci: false\n    notify_error: true # if uploads fail, replace cov comment with a comment with errors.\n  require_ci_to_pass: false\n\n  # Publicly exposing the token has some small risks from mistakes or malicious actors.\n  # See https://docs.codecov.com/docs/codecov-tokens for correctly configuring it.\n  token: 87cefb17-c44b-4f2f-8b30-1fff5769ce46\n\n# only post PR comment if coverage changes\ncomment:\n  require_changes: true\n\ncoverage:\n  # required range\n  precision: 5\n  round: down\n  range: 100..100\n  status:\n    project:\n      default:\n        target: 100%\n    patch:\n      default:\n        target: 100%  # require patches to be 100%\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "# sorting all imports with isort\n933f77b96f0092e1baab4474a9208fc2e379aa32\n# enabling ruff's flake8-commas rule\nb25c02a94e2defcb0fad32976b02218be1133bdf\n"
  },
  {
    "path": ".gitattributes",
    "content": "# For files generated by trio/_tools/gen_exports.py or trio/_tools/windows_ffi_build.py\ntrio/_core/_generated*    linguist-generated=true\n# Treat generated files as binary in git diff\ntrio/_core/_generated*    -diff\n"
  },
  {
    "path": ".github/workflows/autodeps.yml",
    "content": "name: Autodeps\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron:  '0 0 1 * *'\n\njobs:\n  Autodeps:\n    if: github.repository_owner == 'python-trio'\n    name: Autodeps\n    timeout-minutes: 10\n    runs-on: 'ubuntu-latest'\n    # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#changing-github_token-permissions\n    permissions:\n      pull-requests: write\n      issues: write\n      repository-projects: write\n      contents: write\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: true  # credentials are needed to push commits\n      - name: Setup python\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.10\"\n\n      - name: Bump dependencies\n        run: |\n          python -m pip install -U pip pre-commit\n          python -m pip install -r test-requirements.txt\n          uv pip compile --universal --python-version=3.10 --upgrade test-requirements.in -o test-requirements.txt\n          uv pip compile --universal --python-version=3.11 --upgrade docs-requirements.in -o docs-requirements.txt\n          pre-commit autoupdate --jobs 0\n\n      - name: Install new requirements\n        run: python -m pip install -r test-requirements.txt\n\n      - name: Pre-commit fixes\n        run: pre-commit run -a || true\n\n      - name: Commit changes and create automerge PR\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          # setup git repo\n          git switch --force-create autodeps/bump_from_${GITHUB_SHA:0:6}\n          git config user.name 'github-actions[bot]'\n          git config user.email '41898282+github-actions[bot]@users.noreply.github.com'\n\n          if ! git commit -am \"Dependency updates\"; then\n            echo \"No changes to commit!\"\n            exit 0\n          fi\n\n          git push --force --set-upstream origin autodeps/bump_from_${GITHUB_SHA:0:6}\n\n          # git push returns before github is ready for a pr, so we poll until success\n          for BACKOFF in 1 2 4 8 0; do\n            sleep $BACKOFF\n            if gh pr create \\\n              --label dependencies --body \"\" \\\n              --title \"Bump dependencies from commit ${GITHUB_SHA:0:6}\" \\\n              ; then\n              break\n            fi\n          done\n\n          if [ $BACKOFF -eq 0 ]; then\n            echo \"Could not create the PR\"\n            exit 1\n          fi\n\n          # gh pr create returns before the pr is ready, so we again poll until success\n          # https://github.com/cli/cli/issues/2619#issuecomment-1240543096\n          for BACKOFF in 1 2 4 8 0; do\n            sleep $BACKOFF\n            if gh pr merge --auto --squash; then\n              break\n            fi\n          done\n\n          if [ $BACKOFF -eq 0 ]; then\n            echo \"Could not set automerge\"\n            exit 1\n          fi\n"
  },
  {
    "path": ".github/workflows/check-newsfragment.yml",
    "content": "name: Check newsfragment\n\npermissions: {}\n\non:\n  pull_request:\n    types: [labeled, unlabeled, opened, synchronize]\n    branches:\n    - main\n\njobs:\n  check-newsfragment:\n    if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip newsfragment') }}\n    runs-on: 'ubuntu-latest'\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          persist-credentials: false\n\n      - name: Check newsfragments\n        run: |\n          if git diff --name-only origin/main | grep -v '/_tests/' | grep 'src/trio/'; then\n            git diff --name-only origin/main | grep 'newsfragments/' || exit 1\n          fi\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\npermissions: {}\n\non:\n  push:\n    branches-ignore:\n      # these branches always have another event associated\n      - gh-readonly-queue/**        # GitHub's merge queue uses `merge_group`\n      - autodeps/**                 # autodeps always makes a PR\n      - pre-commit-ci-update-config # pre-commit.ci's updates always have a PR\n  pull_request:\n  merge_group:\n\nconcurrency:\n  group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && format('-{0}', github.sha) || '' }}\n  cancel-in-progress: true\n\nenv:\n  dists-artifact-name: python-package-distributions\n  dist-name: trio\n\njobs:\n  build:\n    name: 👷 dists\n\n    runs-on: ubuntu-latest\n\n    outputs:\n      dist-version: ${{ steps.dist-version.outputs.version }}\n      sdist-artifact-name: ${{ steps.artifact-name.outputs.sdist }}\n      wheel-artifact-name: ${{ steps.artifact-name.outputs.wheel }}\n\n    steps:\n      - name: Switch to using Python 3.11\n        uses: actions/setup-python@v6\n        with:\n          python-version: 3.11\n\n      - name: Grab the source from Git\n        uses: actions/checkout@v5\n        with:\n          persist-credentials: false\n\n      - name: Get the dist version\n        id: dist-version\n        run: >-\n          echo \"version=$(\n          grep ^__version__ src/trio/_version.py\n          | sed 's#__version__ = \"\\([^\"]\\+\\)\"#\\1#'\n          )\"\n          >> \"${GITHUB_OUTPUT}\"\n\n      - name: Set the expected dist artifact names\n        id: artifact-name\n        run: |\n          echo \"sdist=${DIST_NAME}-${VERSION}.tar.gz\" >> \"${GITHUB_OUTPUT}\"\n          echo \"wheel=${DIST_NAME}-${VERSION}-py3-none-any.whl\" >> \"${GITHUB_OUTPUT}\"\n        env:\n          DIST_NAME: ${{ env.dist-name }}\n          VERSION: ${{ steps.dist-version.outputs.version }}\n\n      - name: Install build\n        run: python -Im pip install build\n\n      - name: Build dists\n        run: python -Im build\n      - name: Verify that the artifacts with expected names got created\n        run: >-\n          ls -1\n          dist/${SDIST}\n          dist/${WHEEL}\n        env:\n          SDIST: ${{ steps.artifact-name.outputs.sdist }}\n          WHEEL: ${{ steps.artifact-name.outputs.wheel }}\n      - name: Store the distribution packages\n        uses: actions/upload-artifact@v5\n        with:\n          name: ${{ env.dists-artifact-name }}\n          # NOTE: Exact expected file names are specified here\n          # NOTE: as a safety measure — if anything weird ends\n          # NOTE: up being in this dir or not all dists will be\n          # NOTE: produced, this will fail the workflow.\n          path: |\n            dist/${{ steps.artifact-name.outputs.sdist }}\n            dist/${{ steps.artifact-name.outputs.wheel }}\n          retention-days: 5\n\n  smoke-tests:\n    name: Smoke tests\n    needs:\n      - build\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Switch to using Python 3.11\n        uses: actions/setup-python@v6\n        with:\n          python-version: 3.11\n\n      - name: >-\n          Smoke-test:\n          retrieve the project source from an sdist inside the GHA artifact\n        uses: re-actors/checkout-python-sdist@release/v2\n        with:\n          source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}\n          workflow-artifact-name: ${{ env.dists-artifact-name }}\n\n      - name: >-\n          Smoke-test: move the sdist-retrieved dir into sdist-src\n        run: |\n          mv -v '${{ github.workspace }}' '${{ runner.temp }}/sdist-src'\n          mkdir -pv '${{ github.workspace }}'\n          mv -v '${{ runner.temp }}/sdist-src' '${{ github.workspace }}/sdist-src'\n        shell: bash -eEuo pipefail {0}\n\n      - name: >-\n          Smoke-test: grab the source from Git into git-src\n        uses: actions/checkout@v5\n        with:\n          path: git-src\n          persist-credentials: false\n\n      - name: >-\n          Smoke-test: install test requirements from the Git repo\n        run: >-\n          python -Im\n          pip install -c test-requirements.txt -r test-requirements.txt\n        shell: bash -eEuo pipefail {0}\n        working-directory: git-src\n\n      - name: >-\n          Smoke-test: collect tests from the Git repo\n        env:\n          PYTHONPATH: src/\n        run: >-\n          pytest --collect-only -qq .\n          | sort\n          | tee collected-tests\n        shell: bash -eEuo pipefail {0}\n        working-directory: git-src\n\n      - name: >-\n          Smoke-test: collect tests from the sdist tarball\n        env:\n          PYTHONPATH: src/\n        run: >-\n          pytest --collect-only -qq .\n          | sort\n          | tee collected-tests\n        shell: bash -eEuo pipefail {0}\n        working-directory: sdist-src\n\n      - name: >-\n          Smoke-test:\n          verify that all the tests from Git are included in the sdist\n        run: diff --unified sdist-src/collected-tests git-src/collected-tests\n        shell: bash -eEuo pipefail {0}\n\n  Windows:\n    name: 'Windows (${{ matrix.python }}, ${{ matrix.arch }}${{ matrix.extra_name }})'\n    needs:\n      - build\n\n    timeout-minutes: 20\n    runs-on: 'windows-latest'\n    strategy:\n      fail-fast: false\n      matrix:\n        python: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.14t']\n        arch: ['x86', 'x64']\n        lsp: ['']\n        lsp_extract_file: ['']\n        extra_name: ['']\n        include:\n          - python: '3.10'\n            arch: 'x64'\n            lsp: 'https://raw.githubusercontent.com/python-trio/trio-ci-assets/master/komodia-based-vpn-setup.zip'\n            lsp_extract_file: 'komodia-based-vpn-setup.exe'\n            extra_name: ', with Komodia LSP'\n          - python: '3.10'\n            arch: 'x64'\n            lsp: 'https://www.proxifier.com/download/legacy/ProxifierSetup342.exe'\n            lsp_extract_file: ''\n            extra_name: ', with IFS LSP'\n          - python: 'pypy-3.11'\n            arch: 'x64'\n            lsp: ''\n            lsp_extract_file: ''\n            extra_name: ''\n          #- python: '3.10'\n          #  arch: 'x64'\n          #  lsp: 'http://download.pctools.com/mirror/updates/9.0.0.2308-SDavfree-lite_en.exe'\n          #  lsp_extract_file: ''\n          #  extra_name: ', with non-IFS LSP'\n\n    continue-on-error: >-\n      ${{\n        (\n          endsWith(matrix.python, '-dev')\n          || endsWith(matrix.python, '-nightly')\n        )\n        && true\n        || false\n      }}\n    steps:\n      - name: Retrieve the project source from an sdist inside the GHA artifact\n        uses: re-actors/checkout-python-sdist@release/v2\n        with:\n          source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}\n          workflow-artifact-name: ${{ env.dists-artifact-name }}\n      - name: Setup Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '${{ matrix.python }}'\n          architecture: '${{ matrix.arch }}'\n          allow-prereleases: true\n      - name: Run tests\n        run: ./ci.sh\n        shell: bash\n        env:\n          LSP: '${{ matrix.lsp }}'\n          LSP_EXTRACT_FILE: '${{ matrix.lsp_extract_file }}'\n      - if: always()\n        uses: codecov/codecov-action@v5\n        with:\n          name: Windows (${{ matrix.python }}, ${{ matrix.arch }}${{ matrix.extra_name }})\n          # multiple flags is marked as an error in codecov UI, but is actually fine\n          # https://github.com/codecov/feedback/issues/567\n          flags: Windows,${{ matrix.python }}\n          fail_ci_if_error: true\n\n  Ubuntu:\n    name: 'Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})'\n    needs:\n      - build\n\n    timeout-minutes: 10\n    runs-on: 'ubuntu-latest'\n    strategy:\n      fail-fast: false\n      matrix:\n        python: ['pypy-3.11', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t']\n        check_formatting: ['0']\n        no_test_requirements: ['0']\n        extra_name: ['']\n        include:\n          - python: '3.13'\n            check_formatting: '1'\n            extra_name: ', check formatting'\n          # separate test run that doesn't install test-requirements.txt\n          - python: '3.10'\n            no_test_requirements: '1'\n            extra_name: ', no test-requirements'\n    continue-on-error: >-\n      ${{\n        (\n          endsWith(matrix.python, '-dev')\n          || endsWith(matrix.python, '-nightly')\n        )\n        && true\n        || false\n      }}\n    steps:\n      - name: Retrieve the project source from an sdist inside the GHA artifact\n        if: matrix.check_formatting != '1'\n        uses: re-actors/checkout-python-sdist@release/v2\n        with:\n          source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}\n          workflow-artifact-name: ${{ env.dists-artifact-name }}\n      - name: Grab the source from Git\n        if: matrix.check_formatting == '1'\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '${{ matrix.python }}'\n          cache: pip\n          cache-dependency-path: test-requirements.txt\n          allow-prereleases: true\n      - name: Setup minimum Python version\n        if: matrix.check_formatting == '1'\n        uses: actions/setup-python@v6\n        with:\n          python-version: '3.10'\n          cache: pip\n          cache-dependency-path: test-requirements.txt\n          allow-prereleases: true\n      - name: Check Formatting\n        if: matrix.check_formatting == '1'\n        run:\n          python -m pip install tox &&\n          tox -m check\n      - name: Install python3-apport\n        if: matrix.check_formatting == '0'\n        run: |\n          sudo apt update\n          sudo apt install -q python3-apport\n      - name: Run tests\n        if: matrix.check_formatting == '0'\n        run: ./ci.sh\n        env:\n          NO_TEST_REQUIREMENTS: '${{ matrix.no_test_requirements }}'\n      - if: >-\n          always()\n          && matrix.check_formatting != '1'\n        uses: codecov/codecov-action@v5\n        with:\n          name: Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})\n          flags: Ubuntu,${{ matrix.python }}\n          fail_ci_if_error: true\n\n  macOS:\n    name: 'macOS (${{ matrix.python }})'\n    needs:\n      - build\n\n    timeout-minutes: 15\n    runs-on: 'macos-latest'\n    strategy:\n      fail-fast: false\n      matrix:\n        python: ['pypy-3.11', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t']\n    continue-on-error: >-\n      ${{\n        (\n          endsWith(matrix.python, '-dev')\n          || endsWith(matrix.python, '-nightly')\n        )\n        && true\n        || false\n      }}\n    steps:\n      - name: Retrieve the project source from an sdist inside the GHA artifact\n        uses: re-actors/checkout-python-sdist@release/v2\n        with:\n          source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}\n          workflow-artifact-name: ${{ env.dists-artifact-name }}\n      - name: Setup Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '${{ matrix.python }}'\n          cache: pip\n          cache-dependency-path: test-requirements.txt\n          allow-prereleases: true\n      - name: Run tests\n        run: ./ci.sh\n      - if: always()\n        uses: codecov/codecov-action@v5\n        with:\n          name: macOS (${{ matrix.python }})\n          flags: macOS,${{ matrix.python }}\n          fail_ci_if_error: true\n\n  # run CI on a musl linux\n  Alpine:\n    name: \"Alpine\"\n    needs:\n      - build\n\n    runs-on: ubuntu-latest\n    container: alpine\n    steps:\n      - name: Install necessary packages\n        # can't use setup-python because that python doesn't seem to work;\n        # `python3-dev` (rather than `python:alpine`) for some ctypes reason,\n        # `curl`, `gpg`, `git` for codecov-action v4/v5 to work (https://github.com/codecov/codecov-action/issues/1320).\n        # `nodejs` for pyright (`node-env` pulls in nodejs but that takes a while and can time out the test).\n        # `perl` for a platform independent `sed -i` alternative\n        run: apk update && apk add python3-dev bash curl gpg git nodejs perl\n\n      - name: Retrieve the project source from an sdist inside the GHA artifact\n        # must be after `apk add` because it relies on `bash` existing\n        uses: re-actors/checkout-python-sdist@release/v2\n        with:\n          source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}\n          workflow-artifact-name: ${{ env.dists-artifact-name }}\n\n      - name: Enter virtual environment\n        run: python -m venv .venv\n\n      - name: Run tests\n        run: source .venv/bin/activate && ./ci.sh\n\n      - name: Get Python version for codecov flag\n        id: get-version\n        shell: python\n        run: |\n          import sys, os\n          with open(os.environ[\"GITHUB_OUTPUT\"], \"a\") as f:\n            f.write(\"version=\" + \".\".join(map(str, sys.version_info[:2])))\n            f.write(\"\\n\")\n\n      - if: always()\n        uses: codecov/codecov-action@v5\n        with:\n          name: Alpine\n          flags: Alpine,${{ steps.get-version.outputs.version }}\n          fail_ci_if_error: true\n\n  Cython:\n    name: \"Cython\"\n    needs:\n      - build\n\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # Cython 2 supports 3.10 and 3.11 and Cython 3 supports all versions we do,\n          # so test both the lowest and higher version for both\n          - python: '3.10'\n            cython: '<3'\n          - python: '3.11'\n            cython: '<3'\n          # TODO: technically we should pin cython versions\n          - python: '3.10'\n            cython: '>=3'\n          - python: '3.14'\n            cython: '>=3'\n    steps:\n      - name: Retrieve the project source from an sdist inside the GHA artifact\n        uses: re-actors/checkout-python-sdist@release/v2\n        with:\n          source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }}\n          workflow-artifact-name: ${{ env.dists-artifact-name }}\n      - name: Setup Python\n        uses: actions/setup-python@v6\n        with:\n            python-version: '${{ matrix.python }}'\n            cache: pip\n        # setuptools is needed to get distutils on 3.12, which cythonize requires\n      - name: install trio and setuptools\n        run: python -m pip install --upgrade pip . setuptools 'coverage[toml]'\n\n      - name: add cython plugin to the coveragepy config\n        run: >-\n          sed -i 's#plugins\\s=\\s\\[\\]#plugins = [\"Cython.Coverage\"]#'\n          pyproject.toml\n\n      - name: install cython & compile pyx file\n        env:\n          CFLAGS: ${{ env.CFLAGS }} -DCYTHON_TRACE_NOGIL=1\n        run: |\n          python -m pip install \"cython${{ matrix.cython }}\"\n          cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx\n\n      - name: import & run module\n        run: coverage run -m tests.cython.run_test_cython\n\n      - name: Get Python version for codecov flag\n        id: get-version\n        shell: python\n        run: |\n          import sys, os\n          with open(os.environ[\"GITHUB_OUTPUT\"], \"a\") as f:\n            f.write(\"version=\" + \".\".join(map(str, sys.version_info[:2])))\n            f.write(\"\\n\")\n\n      - run: |\n          coverage combine\n          coverage report\n\n      - if: always()\n        uses: codecov/codecov-action@v5\n        with:\n          name: Cython\n          flags: Cython,${{ steps.get-version.outputs.version }}\n          fail_ci_if_error: true\n\n  # https://github.com/marketplace/actions/alls-green#why\n  check:  # This job does nothing and is only used for the branch protection\n\n    if: always()\n\n    needs:\n      - smoke-tests\n      - Windows\n      - Ubuntu\n      - macOS\n      - Alpine\n      - Cython\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Decide whether the needed jobs succeeded or failed\n        uses: re-actors/alls-green@release/v1\n        with:\n          jobs: ${{ toJSON(needs) }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "on:\n  push:\n    tags:\n      - v*\n\npermissions: {}\n\n# a lot of code taken from https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - uses: actions/setup-python@v5\n        with:\n          python-version: \"3.10\"\n      - run: python -m pip install build\n      - run: python -m build\n\n      - uses: actions/upload-artifact@v4\n        with:\n          name: trio-dist\n          path: |\n            dist/*.tar.gz\n            dist/*.whl\n\n  pypi-publish:\n    needs: [build]\n    name: upload release to PyPI\n    runs-on: ubuntu-latest\n    environment:\n      name: release\n      url: https://pypi.org/project/trio\n    permissions:\n      id-token: write\n\n    steps:\n      - uses: actions/download-artifact@v4\n        with:\n          pattern: trio-*\n          path: dist\n          merge-multiple: true\n\n      - name: Publish package distributions to PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n"
  },
  {
    "path": ".gitignore",
    "content": "# generated by cythonize\ntests/cython/test_cython.c\n\n# In case somebody wants to restore the directory for local testing\nnotes-to-self/\n\n# Project-specific generated files\ndocs/build/\n\nbench/results/\nbench/env/\nbench/trio/\n\n# Byte-compiled / optimized / DLL files / editor temp files\n__pycache__/\n*.py[cod]\n*~\n\\#*\n.#*\n*.swp\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\n/build/\n/develop-eggs/\n/dist/\n/eggs/\n/lib/\n/lib64/\n/parts/\n/sdist/\n/var/\n*.egg-info/\n.installed.cfg\n*.egg\n/.pybuild\npip-wheel-metadata/\n\n# Installer logs\npip-log.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.venv/\npyvenv.cfg\n.coverage\n.coverage.*\n.cache\n.pytest_cache/\n.mypy_cache/\nnosetests.xml\ncoverage.xml\n\n# Temp file during Mypy processing\nmypy_annotate.dat\n\n# Translations\n*.mo\n\n# Mr Developer\n.mr.developer.cfg\n.project\n.pydevproject\n\n# Rope\n.ropeproject\n\n# Django stuff:\n*.log\n*.pot\n\n# Sphinx documentation\ndoc/_build/\n\n# PyCharm\n.idea/\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "ci:\n  autofix_prs: true\n  autoupdate_schedule: weekly\n  submodules: false\n  # pip-compile requires internet, regenerate-files may get cache\n  # issues in CI, so they're run in check.sh\n  skip: [pip-compile, regenerate-files]\n\nrepos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: trailing-whitespace\n      - id: end-of-file-fixer\n      - id: check-yaml\n      - id: check-toml\n      - id: check-merge-conflict\n      - id: mixed-line-ending\n      - id: check-case-conflict\n      - id: sort-simple-yaml\n        files: .pre-commit-config.yaml\n  - repo: https://github.com/psf/black-pre-commit-mirror\n    rev: 26.3.1\n    hooks:\n      - id: black\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    rev: v0.15.6\n    hooks:\n      - id: ruff-check\n        types: [file]\n        types_or: [python, pyi, toml]\n        args: [\"--show-fixes\"]\n  - repo: https://github.com/codespell-project/codespell\n    rev: v2.4.2\n    hooks:\n      - id: codespell\n        additional_dependencies:\n          # tomli needed on 3.10. tomllib is available in stdlib on 3.11+\n          - tomli\n  - repo: https://github.com/adhtruong/mirrors-typos\n    rev: v1.44.0\n    hooks:\n      - id: typos\n  - repo: https://github.com/sphinx-contrib/sphinx-lint\n    rev: v1.0.2\n    hooks:\n      - id: sphinx-lint\n  - repo: https://github.com/woodruffw/zizmor-pre-commit\n    rev: v1.23.1\n    hooks:\n      - id: zizmor\n  - repo: local\n    hooks:\n      - id: regenerate-files\n        name: regenerate generated files\n        language: python\n        entry: python src/trio/_tools/gen_exports.py\n        pass_filenames: false\n        additional_dependencies: [\"astor\", \"attrs\", \"black\", \"ruff\"]\n        files: ^src\\/trio\\/_core\\/(_run|(_i(o_(common|epoll|kqueue|windows)|nstrumentation)))\\.py$\n      - id: regenerate-windows-cffi\n        name: regenerate windows CFFI\n        language: python\n        entry: python src/trio/_tools/windows_ffi_build.py\n        pass_filenames: false\n        additional_dependencies: [\"cffi\"]\n        files: ^src\\/trio\\/_tools\\/windows_ffi_build\\.py$\n      - id: sync-test-requirements\n        name: synchronize test requirements\n        language: python\n        entry: python src/trio/_tools/sync_requirements.py\n        pass_filenames: false\n        additional_dependencies: [\"pyyaml\"]\n        files: ^(test-requirements\\.txt)|(\\.pre-commit-config\\.yaml)$\n  - repo: https://github.com/astral-sh/uv-pre-commit\n    rev: 0.10.11\n    hooks:\n      # Compile requirements\n      - id: pip-compile\n        name: uv pip-compile test-requirements.in\n        args: [\n          \"--universal\",\n          \"--python-version=3.10\",\n          \"test-requirements.in\",\n          \"-o\",\n          \"test-requirements.txt\"]\n        files: ^test-requirements\\.(in|txt)$\n      - id: pip-compile\n        name: uv pip-compile docs-requirements.in\n        args: [\n          \"--universal\",\n          \"--python-version=3.11\",\n          \"docs-requirements.in\",\n          \"-o\",\n          \"docs-requirements.txt\"]\n        files: ^docs-requirements\\.(in|txt)$\n"
  },
  {
    "path": ".readthedocs.yml",
    "content": "# https://docs.readthedocs.io/en/latest/config-file/index.html\nversion: 2\n\nformats:\n  - htmlzip\n  - epub\n\nbuild:\n  os: \"ubuntu-22.04\"\n  tools:\n    python: \"3.11\"\n\npython:\n  install:\n    - requirements: docs-requirements.txt\n    - path: .\n\nsphinx:\n  fail_on_warning: true\n  configuration: docs/source/conf.py\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "For the Trio code of conduct, see:\n    https://trio.readthedocs.io/en/latest/code-of-conduct.html\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "For the Trio contributing guide, see:\n    https://trio.readthedocs.io/en/latest/contributing.html\n"
  },
  {
    "path": "LICENSE",
    "content": "This software is made available under the terms of *either* of the\nlicenses found in LICENSE.APACHE2 or LICENSE.MIT. Contributions to\nTrio are made under the terms of *both* these licenses.\n"
  },
  {
    "path": "LICENSE.APACHE2",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "LICENSE.MIT",
    "content": "Copyright Contributors to the Trio project.\n\nThe MIT License (MIT)\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include .codecov.yml\ninclude check.sh\ninclude ci.sh\ninclude LICENSE LICENSE.MIT LICENSE.APACHE2\ninclude README.rst\ninclude CODE_OF_CONDUCT.md CONTRIBUTING.md\ninclude *-requirements.in\ninclude *-requirements.txt\ninclude src/trio/py.typed\ninclude src/trio/_tests/astrill-codesigning-cert.cer\nrecursive-include src/trio/_tests/test_ssl_certs *.pem\nrecursive-include docs *\nrecursive-include tests *\nprune docs/build\n"
  },
  {
    "path": "README.rst",
    "content": ".. image:: https://img.shields.io/badge/chat-join%20now-blue.svg\n   :target: https://gitter.im/python-trio/general\n   :alt: Join chatroom\n\n.. image:: https://img.shields.io/badge/forum-join%20now-blue.svg\n   :target: https://trio.discourse.group\n   :alt: Join forum\n\n.. image:: https://img.shields.io/badge/docs-read%20now-blue.svg\n   :target: https://trio.readthedocs.io\n   :alt: Documentation\n\n.. image:: https://img.shields.io/pypi/v/trio.svg\n   :target: https://pypi.org/project/trio\n   :alt: Latest PyPi version\n\n.. image:: https://img.shields.io/conda/vn/conda-forge/trio.svg\n   :target: https://anaconda.org/conda-forge/trio\n   :alt: Latest conda-forge version\n\n.. image:: https://codecov.io/gh/python-trio/trio/branch/main/graph/badge.svg\n   :target: https://codecov.io/gh/python-trio/trio\n   :alt: Test coverage\n\nTrio – a friendly Python library for async concurrency and I/O\n==============================================================\n\n.. image:: https://raw.githubusercontent.com/python-trio/trio/9b0bec646a31e0d0f67b8b6ecc6939726faf3e17/logo/logo-with-background.svg\n   :width: 200px\n   :align: right\n\nThe Trio project aims to produce a production-quality,\n`permissively licensed\n<https://github.com/python-trio/trio/blob/main/LICENSE>`__,\nasync/await-native I/O library for Python. Like all async libraries,\nits main purpose is to help you write programs that do **multiple\nthings at the same time** with **parallelized I/O**. A web spider that\nwants to fetch lots of pages in parallel, a web server that needs to\njuggle lots of downloads and websocket connections simultaneously, a\nprocess supervisor monitoring multiple subprocesses... that sort of\nthing. Compared to other libraries, Trio attempts to distinguish\nitself with an obsessive focus on **usability** and\n**correctness**. Concurrency is complicated; we try to make it *easy*\nto get things *right*.\n\nTrio was built from the ground up to take advantage of the `latest\nPython features <https://www.python.org/dev/peps/pep-0492/>`__, and\ndraws inspiration from `many sources\n<https://github.com/python-trio/trio/wiki/Reading-list>`__, in\nparticular Dave Beazley's `Curio <https://curio.readthedocs.io/>`__.\nThe resulting design is radically simpler than older competitors like\n`asyncio <https://docs.python.org/3/library/asyncio.html>`__ and\n`Twisted <https://twistedmatrix.com/>`__, yet just as capable. Trio is\nthe Python I/O library I always wanted; I find it makes building\nI/O-oriented programs easier, less error-prone, and just plain more\nfun. `Perhaps you'll find the same\n<https://github.com/python-trio/trio/wiki/Testimonials>`__.\n\nTrio is a mature and well-tested project: the overall design is solid,\nand the existing features are fully documented and widely used in\nproduction. While we occasionally make minor interface adjustments,\nbreaking changes are rare. We encourage you to use Trio with confidence,\nbut if you rely on long-term API stability, consider `subscribing to\nissue #1 <https://github.com/python-trio/trio/issues/1>`__ for advance\nnotice of any compatibility updates.\n\n\nWhere to next?\n--------------\n\n**I want to try it out!** Awesome! We have a `friendly tutorial\n<https://trio.readthedocs.io/en/stable/tutorial.html>`__ to get you\nstarted; no prior experience with async coding is required.\n\n**Ugh, I don't want to read all that – show me some code!** If you're\nimpatient, then here's a `simple concurrency example\n<https://trio.readthedocs.io/en/stable/tutorial.html#tutorial-example-tasks-intro>`__,\nan `echo client\n<https://trio.readthedocs.io/en/stable/tutorial.html#tutorial-echo-client-example>`__,\nand an `echo server\n<https://trio.readthedocs.io/en/stable/tutorial.html#tutorial-echo-server-example>`__.\n\n**How does Trio make programs easier to read and reason about than\ncompeting approaches?** Trio is based on a new way of thinking that we\ncall \"structured concurrency\". The best theoretical introduction is\nthe article `Notes on structured concurrency, or: Go statement\nconsidered harmful\n<https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/>`__.\nOr, `check out this talk at PyCon 2018\n<https://www.youtube.com/watch?v=oLkfnc_UMcE>`__ to see a\ndemonstration of implementing the \"Happy Eyeballs\" algorithm in an\nolder library versus Trio.\n\n**Cool, but will it work on my system?** Probably! As long as you have\nsome kind of Python 3.10-or-better (CPython or `currently maintained versions of\nPyPy3 <https://doc.pypy.org/en/latest/faq.html#which-python-versions-does-pypy-implement>`__\nare both fine), and are using Linux, macOS, Windows, or FreeBSD, then Trio\nwill work. Other environments might work too, but those\nare the ones we test on. And all of our dependencies are pure Python,\nexcept for CFFI on Windows, which has wheels available, so\ninstallation should be easy (no C compiler needed).\n\n**I tried it, but it's not working.** Sorry to hear that! You can try\nasking for help in our `chat room\n<https://gitter.im/python-trio/general>`__ or `forum\n<https://trio.discourse.group>`__, `filing a bug\n<https://github.com/python-trio/trio/issues/new>`__, or `posting a\nquestion on StackOverflow\n<https://stackoverflow.com/questions/ask?tags=python+python-trio>`__,\nand we'll do our best to help you out.\n\n**Trio is awesome, and I want to help make it more awesome!** You're\nthe best! There's tons of work to do – filling in missing\nfunctionality, building up an ecosystem of Trio-using libraries,\nusability testing (e.g., maybe try teaching yourself or a friend to\nuse Trio and make a list of every error message you hit and place\nwhere you got confused?), improving the docs, ... check out our `guide\nfor contributors\n<https://trio.readthedocs.io/en/stable/contributing.html>`__!\n\n**I don't have any immediate plans to use it, but I love geeking out\nabout I/O library design!** That's a little weird? But let's be\nhonest, you'll fit in great around here. We have a `whole sub-forum\nfor discussing structured concurrency\n<https://trio.discourse.group/c/structured-concurrency>`__ (developers\nof other systems welcome!). Or check out our `discussion of design\nchoices\n<https://trio.readthedocs.io/en/stable/design.html#user-level-api-principles>`__,\n`reading list\n<https://github.com/python-trio/trio/wiki/Reading-list>`__, and\n`issues tagged design-discussion\n<https://github.com/python-trio/trio/labels/design%20discussion>`__.\n\n**I want to make sure my company's lawyers won't get angry at me!** No\nworries, Trio is permissively licensed under your choice of MIT or\nApache 2. See `LICENSE\n<https://github.com/python-trio/trio/blob/main/LICENSE>`__ for details.\n\n\nCode of conduct\n---------------\n\nContributors are requested to follow our `code of conduct\n<https://trio.readthedocs.io/en/stable/code-of-conduct.html>`__ in all\nproject spaces.\n"
  },
  {
    "path": "ci.sh",
    "content": "#!/bin/bash\n\nset -ex -o pipefail\n\n# disable warnings about pyright being out of date\n# used in test_exports and in check.sh\nexport PYRIGHT_PYTHON_IGNORE_WARNINGS=1\n\n# Log some general info about the environment\necho \"::group::Environment\"\nuname -a\nenv | sort\necho \"::endgroup::\"\n\n# Curl's built-in retry system is not very robust; it gives up on lots of\n# network errors that we want to retry on. Wget might work better, but it's\n# not installed on azure pipelines's windows boxes. So... let's try some good\n# old-fashioned brute force. (This is also a convenient place to put options\n# we always want, like -f to tell curl to give an error if the server sends an\n# error response, and -L to follow redirects.)\nfunction curl-harder() {\n    for BACKOFF in 0 1 2 4 8 15 15 15 15; do\n        sleep $BACKOFF\n        if curl -fL --connect-timeout 5 \"$@\"; then\n            return 0\n        fi\n    done\n    return 1\n}\n\n################################################################\n# We have a Python environment!\n################################################################\n\necho \"::group::Versions\"\npython -c \"import sys, struct, ssl; print('python:', sys.version); print('version_info:', sys.version_info); print('bits:', struct.calcsize('P') * 8); print('openssl:', ssl.OPENSSL_VERSION, ssl.OPENSSL_VERSION_INFO)\"\necho \"::endgroup::\"\n\necho \"::group::Install dependencies\"\npython -m pip install -U pip uv -c test-requirements.txt\npython -m pip --version\npython -m uv --version\n\npython -m uv pip install build\n\npython -m build\nwheel_package=$(ls dist/*.whl)\npython -m uv pip install \"trio @ $wheel_package\" -c test-requirements.txt\n\n# Actual tests\n# expands to 0 != 1 if NO_TEST_REQUIREMENTS is not set, if set the `-0` has no effect\n# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02\nif [ \"${NO_TEST_REQUIREMENTS-0}\" == 1 ]; then\n    python -m uv pip install pytest coverage -c test-requirements.txt\n    flags=\"--skip-optional-imports\"\nelse\n    python -m uv pip install -r test-requirements.txt --no-deps\n    flags=\"\"\nfi\n\n# If we're testing with a LSP installed, then it might break network\n# stuff, so wait until after we've finished setting everything else\n# up.\nif [ \"$LSP\" != \"\" ]; then\n    echo \"Installing LSP from ${LSP}\"\n    # We use --insecure because one of the LSP's has been observed to give\n    # cert verification errors:\n    #\n    #   https://github.com/python-trio/trio/issues/1478\n    #\n    # *Normally*, you should never ever use --insecure, especially when\n    # fetching an executable! But *in this case*, we're intentionally\n    # installing some untrustworthy quasi-malware onto into a sandboxed\n    # machine for testing. So MITM attacks are really the least of our\n    # worries.\n    if [ \"$LSP_EXTRACT_FILE\" != \"\" ]; then\n        # We host the Astrill VPN installer ourselves, and encrypt it\n        # so as to decrease the chances of becoming an inadvertent\n        # public redistributor.\n        curl-harder -o lsp-installer.zip \"$LSP\"\n        unzip -P \"not very secret trio ci key\" lsp-installer.zip \"$LSP_EXTRACT_FILE\"\n        mv \"$LSP_EXTRACT_FILE\" lsp-installer.exe\n    else\n        curl-harder --insecure -o lsp-installer.exe \"$LSP\"\n    fi\n    # This is only needed for the Astrill LSP, but there's no harm in\n    # doing it all the time. The cert was manually extracted by installing\n    # the package in a VM, clicking \"Always trust from this publisher\"\n    # when installing, and then running 'certmgr.msc' and exporting the\n    # certificate. See:\n    #    http://www.migee.com/2010/09/24/solution-for-unattendedsilent-installs-and-would-you-like-to-install-this-device-software/\n    certutil -addstore \"TrustedPublisher\" src/trio/_tests/astrill-codesigning-cert.cer\n    # Double-slashes are how you tell windows-bash that you want a single\n    # slash, and don't treat this as a unix-style filename that needs to\n    # be replaced by a windows-style filename.\n    # http://www.mingw.org/wiki/Posix_path_conversion\n    ./lsp-installer.exe //silent //norestart\n    echo \"Waiting for LSP to appear in Winsock catalog\"\n    while ! netsh winsock show catalog | grep \"Layered Chain Entry\"; do\n        sleep 1\n    done\n    netsh winsock show catalog\nfi\necho \"::endgroup::\"\n\necho \"::group::Setup for tests\"\n\n# We run the tests from inside an empty directory, to make sure Python\n# doesn't pick up any .py files from our working dir. Might have already\n# been created by a previous run.\nmkdir empty || true\ncd empty\n\nINSTALLDIR=$(python -c \"import os, trio; print(os.path.dirname(trio.__file__))\")\ncp ../pyproject.toml \"$INSTALLDIR\"  # TODO: remove this\n\n# get mypy tests a nice cache\nMYPYPATH=\"..\" mypy --config-file= --cache-dir=./.mypy_cache -c \"import trio\" >/dev/null 2>/dev/null || true\n\n# support subprocess spawning with coverage.py\necho \"import coverage; coverage.process_startup()\" | tee -a \"$INSTALLDIR/../sitecustomize.py\"\n\nperl -i -pe 's/-p trio\\._tests\\.pytest_plugin//' \"$INSTALLDIR/pyproject.toml\"\n\necho \"::endgroup::\"\necho \"::group:: Run Tests\"\nif PYTHONPATH=../tests COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml \\\n        coverage run --rcfile=../pyproject.toml -m \\\n        pytest -ra --junitxml=../test-results.xml \\\n        -p _trio_check_attrs_aliases --verbose --durations=10 \\\n        -p trio._tests.pytest_plugin --run-slow $flags \"${INSTALLDIR}\"; then\n    PASSED=true\nelse\n    PASSED=false\nfi\necho \"::endgroup::\"\necho \"::group::Coverage\"\n\ncoverage combine --rcfile ../pyproject.toml\ncd ..  # coverage needs to be in the folder containing src/trio\ncp empty/.coverage .\ncoverage report -m --rcfile ./pyproject.toml\ncoverage xml --rcfile ./pyproject.toml\n\n# Remove the LSP again; again we want to do this ASAP to avoid\n# accidentally breaking other stuff.\nif [ \"$LSP\" != \"\" ]; then\n    netsh winsock reset\nfi\n\necho \"::endgroup::\"\n$PASSED\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSPHINXPROJ    = Trio\nSOURCEDIR     = source\nBUILDDIR      = build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=source\r\nset BUILDDIR=build\r\nset SPHINXPROJ=Trio\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "docs/notes.txt",
    "content": "it's possible from extension/configuration modules to get sphinx to\nknow about new roles and even new autodoc types.\n\nSee curio docs, which cribbed from the python docs and have a link to\nthem\n\nand also:\n\nhttps://github.com/aio-libs/sphinxcontrib-asyncio/pull/1/files\n\nwhich added autodoc hooks to sphinxcontrib-asyncio\n\n\nit looks like there's a table of cross-reference roles in\nsphinx/domains/python.py (look for PyXRefRole), which inherits from\nsphinx.roles.XRefRole, which has some notes on how to subclass and\nchange rendering (see 'result_nodes' method)\n\nso..... it might even be possible to give async functions/methods their\nown color :-)\n"
  },
  {
    "path": "docs/source/_static/.gitkeep",
    "content": ""
  },
  {
    "path": "docs/source/_static/styles.css",
    "content": "/* Make .. deprecation:: blocks visible\n * (by default they're entirely unstyled)\n */\n.deprecated {\n    background-color: #ffe13b;\n}\n\n/* Make typevar/paramspec names distinguishable from classes. */\n.typevarref {\n    text-decoration: dashed underline;\n}\n\n/* Add a snakey triskelion ornament to <hr>\n * https://stackoverflow.com/questions/8862344/css-hr-with-ornament/18541258#18541258\n * but only do it to <hr>s in the content box, b/c the RTD popup control panel\n * thingummy also has an <hr> in it, and putting the ornament on that looks\n * *really weird*. (In particular, the background color is wrong.)\n */\n.rst-content hr {\n    overflow: visible;\n}\n\n.rst-content hr:after {\n    /* This .svg gets displayed on top of the middle of the hrule. It has a box\n     * behind the logo that's colored to match the RTD theme body background\n     * color (#fcfcfc), which hides the middle part of the hrule to make it\n     * look like there's a gap in it. The size of the box determines the size\n     * of the gap.\n     */\n    background: url('ornament.svg') no-repeat top center;\n    background-size: contain;\n    content: \"\";\n    display: block;\n    height: 30px;\n    position: relative;\n    top: -15px;\n}\n\n/* Hacks to make the upper-left logo area look nicer */\n\n.wy-side-nav-search > a {\n    color: #306998 !important;\n}\n\n/* vertically center version text */\n.wy-side-nav-search > a {\n    display: flex;\n    align-items: center;\n    margin: auto;\n    width: max-content;\n}\n\n.wy-side-nav-search > a img.logo {\n    margin-left: 0;\n    margin-right: 5px;\n}\n\n/* Get rid of the weird super dark \"Contents\" label that wastes vertical space\n */\n.wy-menu-vertical > p.caption {\n    display: none !important;\n}\n\n/* I do not like RTD's use of Roboto Slab for headlines. So force it back to\n * Lato (or whatever fallback it's using if Lato isn't available for some\n * reason). I also experimented with using Montserrat to be extra obnoxiously\n * on brand, but honestly you couldn't really tell so there wasn't much point\n * in adding page weight for that, and this is going to match the body text\n * better. (Montserrat for body text *definitely* didn't look good, alas.)\n */\nh1, h2, h3, h4, h5, h6, legend, .rst-content .toctree-wrapper p.caption {\n    font-family: inherit !important;\n}\n\n/* Get rid of the horrible red for literal content */\n.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal {\n    color: #222 !important;\n}\n\n/* Style the \"Need help?\" text just underneath the search box */\n.trio-help-hint {\n    line-height: normal;\n    margin-bottom: 0;\n    /* font-size: 12px; */\n    font-size: 80%;  /* matches the \"Search docs\" box */\n    padding-top: 6px;\n    color: #306998;\n    text-align: center;\n}\n\na.trio-help-hint, .trio-help-hint a:link, .trio-help-hint a:visited {\n    color: inherit;\n    /* Like text-decoration: underline, but with a thinner line */\n    text-decoration: none;\n    border-bottom: 1px solid;\n}\n"
  },
  {
    "path": "docs/source/_templates/.gitkeep",
    "content": ""
  },
  {
    "path": "docs/source/_templates/genindex.html",
    "content": "{% extends \"!genindex.html\" %}\n\n{# check sphinx/themes/basic/genindex if this snippet has become outdated #}\n\n{% block body %}\n\n<h1 id=\"index\">{{ _('Index') }}</h1>\n\n<div class=\"genindex-jumpbox\">\n {% for key, dummy in genindexentries -%}\n <a href=\"#{{ key }}\"><strong>{{ key }}</strong></a>\n {% if not loop.last %}| {% endif %}\n {%- endfor %}\n</div>\n\n{%- for key, entries in genindexentries %}\n<h2 id=\"{{ key }}\">{{ key }}</h2>\n<table style=\"width: 100%\" class=\"indextable genindextable\"><tr>\n  {%- for column in entries|slice_index(2) if column %}\n  <td style=\"width: 33%; vertical-align: top;\"><ul>\n    {%- for entryname, (links, subitems, _) in column %}\n      {% set name = links[0][1].rsplit('#', 1)[1] if links else '' %}\n      <li>{{ indexentries(name if name and '-' not in name else entryname, links) }}\n      {%- if subitems %}\n      <ul>\n      {%- for subentryname, subentrylinks in subitems %}\n        {% set sname = subentrylinks[0][1].rsplit('#', 1)[1] if subentrylinks else '' %}\n        <li>{{ indexentries(sname if sname and '-' not in sname else subentryname, subentrylinks) }}</li>\n      {%- endfor %}\n      </ul>\n      {%- endif -%}</li>\n    {%- endfor %}\n  </ul></td>\n  {%- endfor %}\n</tr></table>\n{% endfor %}\n\n{% endblock %}\n"
  },
  {
    "path": "docs/source/_templates/layout.html",
    "content": "{#\nhttps://stackoverflow.com/questions/25243482/how-to-add-sphinx-generated-index-to-the-sidebar-when-using-read-the-docs-theme\n#}\n{% extends \"!layout.html\" %}\n\n{% block sidebartitle %}\n\n{# the logo helper function was removed in Sphinx 6 and deprecated since Sphinx 4 #}\n{# the master_doc variable was renamed to root_doc in Sphinx 4 (master_doc still exists in later Sphinx versions) #}\n{# check sphinx_rtd_theme/layout.html:sidebartitle if this snippet has become outdated #}\n\n<a class=\"logo\" href=\"{{ pathto(root_doc) }}\">\n  <img class=\"logo\" src=\"{{ logo_url }}\" />\n  {%- set nav_version = version %}\n  {% if current_version %}\n    {%- set nav_version = current_version %}\n  {% endif %}\n  {# don't show the version on RTD if it's the default #}\n  {% if nav_version != 'latest' %}\n    <div class=\"trio-version\">{{ nav_version }}</div>\n  {% endif %}\n</a>\n\n{% include \"searchbox.html\" %}\n\n<p class=\"trio-help-hint\">Need help? <a\nhref=\"https://gitter.im/python-trio/general\">Live chat</a>, <a\nhref=\"https://trio.discourse.group\">forum</a>, <a\nhref=\"https://stackoverflow.com/questions/ask?tags=python+python-trio\">StackOverflow</a>.</p>\n{% endblock %}\n"
  },
  {
    "path": "docs/source/awesome-trio-libraries.rst",
    "content": "Awesome Trio Libraries\n======================\n\n.. List of Trio Libraries\n\n   A list of libraries that support Trio, similar to the awesome-python\n   list here: https://github.com/vinta/awesome-python/\n\n\n.. currentmodule:: trio\n\nYou have completed the tutorial, and are enthusiastic about building\ngreat new applications and libraries with async functionality.\nHowever, to get much useful work done you will want to use some of\nthe great libraries that support Trio-flavoured concurrency. This list\nis not complete, but gives a starting point. Another great way to find\nTrio-compatible libraries is to search on PyPI for the ``Framework :: Trio``\ntag -> `PyPI Search <https://pypi.org/search/?c=Framework+%3A%3A+Trio>`__\n\n\nGetting Started\n---------------\n* `cookiecutter-trio <https://github.com/python-trio/cookiecutter-trio>`__ - This is a cookiecutter template for Python projects that use Trio. It makes it easy to start a new project, by providing a bunch of preconfigured boilerplate.\n* `pytest-trio <https://github.com/python-trio/pytest-trio>`__ - Pytest plugin to test async-enabled Trio functions.\n* `sphinxcontrib-trio <https://github.com/python-trio/sphinxcontrib-trio>`__ - Make Sphinx better at documenting Python functions and methods. In particular, it makes it easy to document async functions.\n\n\nWeb and HTML\n------------\n* `httpx <https://www.python-httpx.org/>`__ - HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.\n* `trio-websocket <https://github.com/HyperionGray/trio-websocket>`__ - A WebSocket client and server implementation striving for safety, correctness, and ergonomics.\n* `quart-trio <https://github.com/pgjones/quart-trio>`__ - Like Flask, but for Trio. A simple and powerful framework for building async web applications and REST APIs. Tip: this is an ASGI-based framework, so you'll also need an HTTP server with ASGI support.\n* `hypercorn <https://github.com/pgjones/hypercorn>`__ - An HTTP server for hosting your ASGI apps. Supports HTTP/1.1, HTTP/2, HTTP/3, and Websockets. Can be run as a standalone server, or embedded in a larger Trio app. Use it with ``quart-trio``, or any other Trio-compatible ASGI framework.\n* `DeFramed <https://github.com/smurfix/deframed>`__ - DeFramed is a Web non-framework that supports a 99%-server-centric approach to Web coding, including support for the `Remi <https://github.com/dddomodossola/remi>`__ GUI library.\n* `pura <https://github.com/groove-x/pura>`__ - A simple web framework for embedding realtime graphical visualization into Trio apps, enabling inspection and manipulation of program state during development.\n* `pyscalpel <https://scalpel.readthedocs.io/en/latest/>`__ - A fast and powerful webscraping library.\n* `muffin <https://github.com/klen/muffin>`_ - Muffin is a fast, simple ASGI web-framework\n* `asgi-tools <https://github.com/klen/asgi-tools>`_ - Tools to quickly build lightest ASGI apps (also contains a test client with lifespan, websocket support)\n* `starlette <https://github.com/encode/starlette>`_ - The little ASGI framework that shines.\n\n\nDatabase\n--------\n\n* `triopg <https://github.com/python-trio/triopg>`__ - PostgreSQL client for Trio based on asyncpg.\n* `trio-mysql <https://github.com/python-trio/trio-mysql>`__ - Pure Python MySQL Client.\n* `sqlalchemy_aio <https://github.com/RazerM/sqlalchemy_aio>`__ - Add asyncio and Trio support to SQLAlchemy core, derived from alchimia.\n* `redio <https://github.com/Tronic/redio>`__ - Redis client, pure Python and Trio.\n* `trio_redis <https://github.com/omnidots/trio_redis>`__ - A Redis client for Trio. Depends on hiredis-py.\n* `asyncakumuli <https://github.com/M-o-a-T/asyncakumuli>`__ - Client for the `Akumuli <https://akumuli.org/>`__ time series database.\n* `aio-databases <https://github.com/klen/aio-databases>`_ - Async Support for various databases (triopg, trio-mysql)\n* `peewee-aio <https://github.com/klen/peewee-aio>`_ - Peewee Async ORM with trio support (triopg, trio-mysql).\n* `coredis <https://github.com/alisaifee/coredis>`_ - Fast, async, fully-typed Redis client with support for cluster and sentinel\n\n\nIOT\n---\n* `DistMQTT <https://github.com/M-o-a-T/distmqtt>`__ - DistMQTT is an open source MQTT client and broker implementation. It is a fork of hbmqtt with support for anyio and DistKV.\n* `asyncgpio <https://github.com/python-trio/trio-gpio>`__ - Allows easy access to the GPIO pins on your Raspberry Pi or similar embedded computer.\n* `asyncowfs <https://github.com/M-o-a-T/asyncowfs>`__ - High-level, object-oriented access to 1wire sensors and actors.\n* `DistKV <https://github.com/M-o-a-T/distkv>`__ - a persistent, distributed, master-less key/value storage with async notification and some IoT-related plug-ins.\n\n\nBuilding Command Line Apps\n--------------------------\n* `trio-click <https://github.com/python-trio/trio-click>`__ - Python composable command line utility, trio-compatible version.\n* `urwid <https://github.com/urwid/urwid>`__ - Urwid is a console user interface library for Python.\n\n\nBuilding GUI Apps\n-----------------\n* `QTrio <https://qtrio.readthedocs.io/en/stable/>`__ - Integration between Trio and either the PyQt or PySide Qt wrapper libraries.  Uses Trio's :ref:`guest mode <guest-mode>`.\n\n\nMulti-Core/Multiprocessing\n--------------------------\n* `tractor <https://github.com/goodboy/tractor>`__ - An experimental, trionic (aka structured concurrent) \"actor model\" for distributed multi-core Python.\n* `Trio run_in_process <https://github.com/ethereum/trio-run-in-process>`__ - Trio based API for running code in a separate process.\n* `trio-parallel <https://trio-parallel.readthedocs.io/>`__ - CPU parallelism for Trio\n\n\nStream Processing\n-----------------\n* `Slurry <https://github.com/andersea/slurry>`__ - Slurry is a microframework for building reactive, data processing applications with Trio.\n\n\nDistributed Task Queue\n----------------------\n* `streaQ <https://github.com/tastyware/streaq>`_ - Fast, async, fully-typed distributed task queue via Redis streams\n\n\nRPC\n---\n* `purepc <https://github.com/python-trio/purerpc>`__ - Native, async Python gRPC client and server implementation using anyio.\n\n\nTesting\n-------\n* `pytest-trio <https://github.com/python-trio/pytest-trio>`__ - Pytest plugin for trio.\n* `hypothesis-trio <https://github.com/python-trio/hypothesis-trio>`__ - Hypothesis supports Trio out of the box for\n  ``@given(...)`` tests; this extension provides Trio-compatible stateful testing.\n* `trustme <https://github.com/python-trio/trustme>`__ - #1 quality TLS certs while you wait, for the discerning tester.\n* `pytest-aio <https://github.com/klen/pytest-aio>`_ - Pytest plugin with support for trio, curio, asyncio\n* `logot <https://github.com/etianen/logot>`_ - Test whether your async code is logging correctly.\n\n\nTools and Utilities\n-------------------\n* `trio-util <https://github.com/groove-x/trio-util>`__ - An assortment of utilities for the Trio async/await framework.\n* `flake8-async <https://github.com/python-trio/flake8-async>`__ - Highly opinionated linter for various sorts of problems in Trio, AnyIO and/or asyncio. Can run as a flake8 plugin, or standalone with support for autofixing some errors.\n* `tricycle <https://github.com/oremanj/tricycle>`__ - This is a library of interesting-but-maybe-not-yet-fully-proven extensions to Trio.\n* `tenacity <https://github.com/jd/tenacity>`__ - Retrying library for Python with async/await support.\n* `perf-timer <https://github.com/belm0/perf-timer>`__ - A code timer with Trio async support (see ``TrioPerfTimer``).  Collects execution time of a block of code excluding time when the coroutine isn't scheduled, such as during blocking I/O and sleep.  Also offers ``trio_perf_counter()`` for low-level timing.\n* `aiometer <https://github.com/florimondmanca/aiometer>`__ - Execute lots of tasks concurrently while controlling concurrency limits\n* `triotp <https://linkdd.github.io/triotp>`__ - OTP framework for Python Trio\n* `aioresult <https://github.com/arthur-tacca/aioresult>`__ - Get the return value of a background async function in Trio or anyio, along with a simple Future class and wait utilities\n* `aiologic <https://github.com/x42005e1f/aiologic>`__ - Thread-safe synchronization and communication primitives: locks, capacity limiters, queues, etc.\n* `culsans <https://github.com/x42005e1f/culsans>`__ - Janus-like sync-async queue with Trio support. Unlike aiologic queues, provides API compatible interfaces.\n\n\nTrio/Asyncio Interoperability\n-----------------------------\n* `anyio <https://github.com/agronholm/anyio>`__ - AnyIO is a asynchronous compatibility API that allows applications and libraries written against it to run unmodified on asyncio or trio.\n* `sniffio <https://github.com/python-trio/sniffio>`__ - This is a tiny package whose only purpose is to let you detect which async library your code is running under.\n* `trio-asyncio <https://github.com/python-trio/trio-asyncio>`__ - Trio-Asyncio lets you use many asyncio libraries from your Trio app.\n"
  },
  {
    "path": "docs/source/code-of-conduct.rst",
    "content": ".. _code-of-conduct:\n\nCode of Conduct\n===============\n\nThis code of conduct applies to the Trio project, and all associated\nprojects in the `python-trio <https://github.com/python-trio>`__\norganization.\n\n\n.. _coc-when-something-happens:\n\nWhen Something Happens\n----------------------\n\nIf you see a Code of Conduct violation, follow these steps:\n\n1. Let the person know that what they did is not appropriate and ask\n   them to stop and/or edit their message(s) or commits.\n2. That person should immediately stop the behavior and correct the\n   issue.\n3. If this doesn't happen, or if you're uncomfortable speaking up,\n   :ref:`contact the maintainers <coc-contacting-maintainers>`.\n4. As soon as possible, a maintainer will look into the issue, and take\n   :ref:`further action (see below) <coc-further-enforcement>`, starting with\n   a warning, then temporary block, then long-term repo or organization\n   ban.\n\nWhen reporting, please include any relevant details, links, screenshots,\ncontext, or other information that may be used to better understand and\nresolve the situation.\n\n**The maintainer team will prioritize the well-being and comfort of the\nrecipients of the violation over the comfort of the violator.** See\n:ref:`some examples below <coc-enforcement-examples>`.\n\nOur Pledge\n----------\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers of this project pledge to making\nparticipation in our community a harassment-free experience for\neveryone, regardless of age, body size, disability, ethnicity, gender\nidentity and expression, level of experience, technical preferences,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\nOur Standards\n-------------\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n-  Using welcoming and inclusive language.\n-  Being respectful of differing viewpoints and experiences.\n-  Gracefully accepting constructive feedback.\n-  Focusing on what is best for the community.\n-  Showing empathy and kindness towards other community members.\n-  Encouraging and raising up your peers in the project so you can all\n   bask in hacks and glory.\n\nExamples of unacceptable behavior by participants include:\n\n-  The use of sexualized language or imagery and unwelcome sexual\n   attention or advances, including when simulated online. The only\n   exception to sexual topics is channels/spaces specifically for topics\n   of sexual identity.\n-  Casual mention of slavery or indentured servitude and/or false\n   comparisons of one's occupation or situation to slavery. Please\n   consider using or asking about alternate terminology when referring\n   to such metaphors in technology.\n-  Making light of/making mocking comments about trigger warnings and\n   content warnings.\n-  Trolling, insulting/derogatory comments, and personal or political\n   attacks.\n-  Public or private harassment, deliberate intimidation, or threats.\n-  Publishing others' private information, such as a physical or\n   electronic address, without explicit permission. This includes any\n   sort of \"outing\" of any aspect of someone's identity without their\n   consent.\n-  Publishing screenshots or quotes of private interactions in the\n   context of this project without all quoted users' *explicit* consent.\n-  Publishing of private communication that doesn't have to do with\n   reporting harassment.\n-  Any of the above even when `presented as \"ironic\" or\n   \"joking\" <https://en.wikipedia.org/wiki/Hipster_racism>`__.\n-  Any attempt to present \"reverse-ism\" versions of the above as\n   violations. Examples of reverse-isms are \"reverse racism\", \"reverse\n   sexism\", \"heterophobia\", and \"cisphobia\".\n-  Unsolicited explanations under the assumption that someone doesn't\n   already know it. Ask before you teach! Don't assume what people's\n   knowledge gaps are.\n-  `Feigning or exaggerating\n   surprise <https://www.recurse.com/manual#no-feigned-surprise>`__ when\n   someone admits to not knowing something.\n-  \"`Well-actuallies <https://www.recurse.com/manual#no-well-actuallys>`__\"\n-  Other conduct which could reasonably be considered inappropriate in a\n   professional or community setting.\n\nScope\n-----\n\nThis Code of Conduct applies both within spaces involving this project\nand in other spaces involving community members. This includes the\nrepository, its Pull Requests and Issue tracker, its Twitter community,\nprivate email communications in the context of the project, and any\nevents where members of the project are participating, as well as\nadjacent communities and venues affecting the project's members.\n\nDepending on the violation, the maintainers may decide that violations\nof this code of conduct that have happened outside of the scope of the\ncommunity may deem an individual unwelcome, and take appropriate action\nto maintain the comfort and safety of its members.\n\n.. _coc-other-community-standards:\n\nOther Community Standards\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAs a project on GitHub, this project is additionally covered by the\n`GitHub Community\nGuidelines <https://help.github.com/articles/github-community-guidelines/>`__.\n\nEnforcement of those guidelines after violations overlapping with the\nabove are the responsibility of the entities, and enforcement may happen\nin any or all of the services/communities.\n\nMaintainer Enforcement Process\n------------------------------\n\nOnce the maintainers get involved, they will follow a documented series\nof steps and do their best to preserve the well-being of project\nmembers. This section covers actual concrete steps.\n\n\n.. _coc-contacting-maintainers:\n\nContacting Maintainers\n~~~~~~~~~~~~~~~~~~~~~~\n\nAs a small project, we don't have a Code of Conduct enforcement team.\nHopefully that will be addressed as we grow, but for now, any issues\nshould be addressed to `Nathaniel J. Smith\n<https://github.com/njsmith>`__, via `email <mailto:njs@pobox.com>`__\nor any other medium that you feel comfortable with. Using words like\n\"Trio code of conduct\" in your subject will help make sure your\nmessage is noticed quickly.\n\n\n.. _coc-further-enforcement:\n\nFurther Enforcement\n~~~~~~~~~~~~~~~~~~~\n\nIf you've already followed the :ref:`initial enforcement steps\n<coc-when-something-happens>`, these are the steps maintainers will\ntake for further enforcement, as needed:\n\n1. Repeat the request to stop.\n2. If the person doubles down, they will have offending messages removed\n   or edited by a maintainers given an official warning. The PR or Issue\n   may be locked.\n3. If the behavior continues or is repeated later, the person will be\n   blocked from participating for 24 hours.\n4. If the behavior continues or is repeated after the temporary block, a\n   long-term (6-12mo) ban will be used.\n5. If after this the behavior still continues, a permanent ban may be\n   enforced.\n\nOn top of this, maintainers may remove any offending messages, images,\ncontributions, etc, as they deem necessary.\n\nMaintainers reserve full rights to skip any of these steps, at their\ndiscretion, if the violation is considered to be a serious and/or\nimmediate threat to the health and well-being of members of the\ncommunity. These include any threats, serious physical or verbal\nattacks, and other such behavior that would be completely unacceptable\nin any social setting that puts our members at risk.\n\nMembers expelled from events or venues with any sort of paid attendance\nwill not be refunded.\n\nWho Watches the Watchers?\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMaintainers and other leaders who do not follow or enforce the Code of\nConduct in good faith may face temporary or permanent repercussions as\ndetermined by other members of the project's leadership. These may\ninclude anything from removal from the maintainer team to a permanent\nban from the community.\n\nAdditionally, as a project hosted on GitHub, :ref:`their Code of\nConduct may be applied against maintainers of this project\n<coc-other-community-standards>`, externally of this project's\nprocedures.\n\n\n.. _coc-enforcement-examples:\n\nEnforcement Examples\n--------------------\n\nThe Best Case\n~~~~~~~~~~~~~\n\nThe vast majority of situations work out like this. This interaction is\ncommon, and generally positive.\n\n    Alex: \"Yeah I used X and it was really crazy!\"\n\n    Patt (not a maintainer): \"Hey, could you not use that word? What\n    about 'ridiculous' instead?\"\n\n    Alex: \"oh sorry, sure.\" -> edits old comment to say \"it was really\n    confusing!\"\n\nThe Maintainer Case\n~~~~~~~~~~~~~~~~~~~\n\nSometimes, though, you need to get maintainers involved. Maintainers\nwill do their best to resolve conflicts, but people who were harmed by\nsomething **will take priority**.\n\n    Patt: \"Honestly, sometimes I just really hate using $library and\n    anyone who uses it probably sucks at their job.\"\n\n    Alex: \"Whoa there, could you dial it back a bit? There's a CoC thing\n    about attacking folks' tech use like that.\"\n\n    Patt: \"I'm not attacking anyone, what's your problem?\"\n\n    Alex: \"@maintainers hey uh. Can someone look at this issue? Patt is\n    getting a bit aggro. I tried to nudge them about it, but nope.\"\n\n    KeeperOfCommitBits: (on issue) \"Hey Patt, maintainer here. Could you\n    tone it down? This sort of attack is really not okay in this space.\"\n\n    Patt: \"Leave me alone I haven't said anything bad wtf is wrong with\n    you.\"\n\n    KeeperOfCommitBits: (deletes user's comment), \"@patt I mean it.\n    Please refer to the CoC over at (URL to this CoC) if you have\n    questions, but you can consider this an actual warning. I'd\n    appreciate it if you reworded your messages in this thread, since\n    they made folks there uncomfortable. Let's try and be kind, yeah?\"\n\n    Patt: \"@keeperofbits Okay sorry. I'm just frustrated and I'm kinda\n    burnt out and I guess I got carried away. I'll DM Alex a note\n    apologizing and edit my messages. Sorry for the trouble.\"\n\n    KeeperOfCommitBits: \"@patt Thanks for that. I hear you on the\n    stress. Burnout sucks :/. Have a good one!\"\n\nThe Nope Case\n~~~~~~~~~~~~~\n\n    PepeTheFrog🐸: \"Hi, I am a literal actual nazi and I think white\n    supremacists are quite fashionable.\"\n\n    Patt: \"NOOOOPE. OH NOPE NOPE.\"\n\n    Alex: \"JFC NO. NOPE. @keeperofbits NOPE NOPE LOOK HERE\"\n\n    KeeperOfCommitBits: \"👀 Nope. NOPE NOPE NOPE. 🔥\"\n\n    PepeTheFrog🐸 has been banned from all organization or user\n    repositories belonging to KeeperOfCommitBits.\n\nAttribution\n-----------\n\nThis Code of Conduct was generated using `WeAllJS Code of Conduct\nGenerator <https://npm.im/weallbehave>`__, which is based on the\n`WeAllJS Code of Conduct <https://wealljs.org/code-of-conduct>`__, which\nis itself based on `Contributor\nCovenant <http://contributor-covenant.org>`__, version 1.4, available at\nhttp://contributor-covenant.org/version/1/4, and the LGBTQ in Technology\nSlack `Code of Conduct <http://lgbtq.technology/coc.html>`__.\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "#!/usr/bin/env python3\n#\n# Trio documentation build configuration file, created by\n# sphinx-quickstart on Sat Jan 21 19:11:14 2017.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nfrom __future__ import annotations\n\nimport collections.abc\nimport glob\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, cast\n\nif sys.version_info >= (3, 11):\n    from sphinx.util.inventory import _InventoryItem\n\nfrom sphinx.util.logging import getLogger\n\nif TYPE_CHECKING:\n    from sphinx.application import Sphinx\n    from sphinx.util.typing import Inventory\n\n# For our local_customization module\nsys.path.insert(0, os.path.abspath(\".\"))\n\n# Enable reloading with `typing.TYPE_CHECKING` being True\nos.environ[\"SPHINX_AUTODOC_RELOAD_MODULES\"] = \"1\"\n\n# Handle writing newsfragments into the history file.\n# We want to keep files unchanged when testing locally.\n# So immediately revert the contents after running towncrier,\n# then substitute when Sphinx wants to read it in.\nhistory_file = Path(\"history.rst\")\n\nhistory_new: str | None\nif glob.glob(\"../../newsfragments/*.*.rst\"):\n    print(\"-- Found newsfragments; running towncrier --\", flush=True)\n    history_orig = history_file.read_bytes()\n    import subprocess\n\n    # In case changes were staged, preserve indexed version.\n    # This grabs the hash of the current staged version.\n    history_staged = subprocess.run(\n        [\"git\", \"rev-parse\", \"--verify\", \":docs/source/history.rst\"],\n        check=True,\n        cwd=\"../..\",\n        stdout=subprocess.PIPE,\n        encoding=\"ascii\",\n    ).stdout.strip()\n    try:\n        subprocess.run(\n            [\"towncrier\", \"--keep\", \"--date\", \"not released yet\"],\n            cwd=\"../..\",\n            check=True,\n        )\n        history_new = history_file.read_text(\"utf8\")\n    finally:\n        # Make sure this reverts even if a failure occurred.\n        # Restore whatever was staged.\n        print(f\"Restoring history.rst = {history_staged}\")\n        subprocess.run(\n            [\n                \"git\",\n                \"update-index\",\n                \"--cacheinfo\",\n                f\"100644,{history_staged},docs/source/history.rst\",\n            ],\n            cwd=\"../..\",\n            check=False,\n        )\n        # And restore the working copy.\n        history_file.write_bytes(history_orig)\n    del history_orig  # We don't need this any more.\nelse:\n    # Leave it as is.\n    history_new = None\n\n\ndef on_read_source(app: Sphinx, docname: str, content: list[str]) -> None:\n    \"\"\"Substitute the modified history file.\"\"\"\n    if docname == \"history\" and history_new is not None:\n        # This is a 1-item list with the file contents.\n        content[0] = history_new\n\n\n# Sphinx is very finicky, and somewhat buggy, so we have several different\n# methods to help it resolve links.\n# 1. The ones that are not possible to fix are added to `nitpick_ignore`\n# 2. some can be resolved with a simple alias in `autodoc_type_aliases`,\n#    even if that is primarily meant for TypeAliases\n# 3. autodoc_process_signature is hooked up to an event, and we use it for\n#    whole-sale replacing types in signatures where internal details are not\n#    relevant or hard to read.\n# 4. add_intersphinx manually modifies the intersphinx mappings after\n#    objects.inv has been parsed, to resolve bugs and version differences\n#    that causes some objects to be looked up incorrectly.\n# 5. docs/source/typevars.py handles redirecting `typing_extensions` objects to `typing`, and linking `TypeVar`s to `typing.TypeVar` instead of sphinx wanting to link them to their individual definitions.\n# It's possible there's better methods for resolving some of the above\n# problems, but this works for now:tm:\n\n# Warn about all references to unknown targets\nnitpicky = True\n# Except for these ones, which we expect to point to unknown targets:\nnitpick_ignore = [\n    (\"py:class\", \"CapacityLimiter-like object\"),\n    (\"py:class\", \"bytes-like\"),\n    # Was removed but still shows up in changelog\n    (\"py:class\", \"trio.lowlevel.RunLocal\"),\n    # trio.abc is documented at random places scattered throughout the docs\n    (\"py:mod\", \"trio.abc\"),\n    (\"py:exc\", \"Anything else\"),\n    (\"py:class\", \"async function\"),\n    (\"py:class\", \"sync function\"),\n    # these do not have documentation on python.org\n    # nor entries in objects.inv\n    (\"py:class\", \"socket.AddressFamily\"),\n    (\"py:class\", \"socket.SocketKind\"),\n]\nautodoc_inherit_docstrings = False\ndefault_role = \"obj\"\n\n\n# A dictionary for users defined type aliases that maps a type name to the full-qualified object name. It is used to keep type aliases not evaluated in the document.\n# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases\n# but it can also be used to help resolve various linking problems\nautodoc_type_aliases = {\n    # SSLListener.accept's return type is seen as trio._ssl.SSLStream\n    \"SSLStream\": \"trio.SSLStream\",\n}\n\n\n# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#event-autodoc-process-signature\ndef autodoc_process_signature(\n    app: Sphinx,\n    what: str,\n    name: str,\n    obj: object,\n    options: object,\n    signature: str,\n    return_annotation: str,\n) -> tuple[str, str]:\n    \"\"\"Modify found signatures to fix various issues.\"\"\"\n    if signature is not None:\n        signature = signature.replace(\"~_contextvars.Context\", \"~contextvars.Context\")\n        if name == \"trio.lowlevel.RunVar\":  # Typevar is not useful here.\n            signature = signature.replace(\": ~trio._core._local.T\", \"\")\n        if \"_NoValue\" in signature:\n            # Strip the type from the union, make it look like = ...\n            signature = signature.replace(\" | type[trio._core._local._NoValue]\", \"\")\n            signature = signature.replace(\"<class 'trio._core._local._NoValue'>\", \"...\")\n        if \"DTLS\" in name:\n            signature = signature.replace(\"SSL.Context\", \"OpenSSL.SSL.Context\")\n        # Don't specify PathLike[str] | PathLike[bytes], this is just for humans.\n        signature = signature.replace(\"StrOrBytesPath\", \"str | bytes | os.PathLike\")\n\n    return signature, return_annotation\n\n\n# currently undocumented things\nlogger = getLogger(\"trio\")\nUNDOCUMENTED = {\n    \"trio.MemorySendChannel\",\n    \"trio.MemoryReceiveChannel\",\n    \"trio.MemoryChannelStatistics\",\n    \"trio.SocketStream.aclose\",\n    \"trio.SocketStream.receive_some\",\n    \"trio.SocketStream.send_all\",\n    \"trio.SocketStream.send_eof\",\n    \"trio.SocketStream.wait_send_all_might_not_block\",\n    \"trio._subprocess.HasFileno.fileno\",\n    \"trio.lowlevel.ParkingLot.broken_by\",\n}\n\n\ndef autodoc_process_docstring(\n    app: Sphinx,\n    what: str,\n    name: str,\n    obj: object,\n    options: object,\n    lines: list[str],\n) -> None:\n    if not lines:\n        # TODO: document these and remove them from here\n        if name in UNDOCUMENTED:\n            return\n\n        logger.warning(f\"{name} has no docstring\")\n    else:\n        if name in UNDOCUMENTED:\n            logger.warning(\n                f\"outdated list of undocumented things in docs/source/conf.py: {name!r} has a docstring\"\n            )\n\n\ndef setup(app: Sphinx) -> None:\n    # Add our custom styling to make our documentation better!\n    app.add_css_file(\"styles.css\")\n    app.connect(\"autodoc-process-signature\", autodoc_process_signature)\n    app.connect(\"autodoc-process-docstring\", autodoc_process_docstring)\n\n    # After Intersphinx runs, add additional mappings.\n    app.connect(\"builder-inited\", add_intersphinx, priority=1000)\n    app.connect(\"source-read\", on_read_source)\n\n\nhtml_context = {\"current_version\": os.environ.get(\"READTHEDOCS_VERSION_NAME\")}\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.intersphinx\",\n    \"sphinx.ext.coverage\",\n    \"sphinx.ext.napoleon\",\n    \"sphinxcontrib_trio\",\n    \"sphinxcontrib.jquery\",\n    \"sphinx_codeautolink\",\n    \"local_customization\",\n    \"typevars\",\n]\n\nintersphinx_mapping = {\n    \"python\": (\"https://docs.python.org/3\", None),\n    \"outcome\": (\"https://outcome.readthedocs.io/en/latest/\", None),\n    \"pyopenssl\": (\"https://www.pyopenssl.org/en/stable/\", None),\n    \"sniffio\": (\"https://sniffio.readthedocs.io/en/latest/\", None),\n    \"trio-util\": (\"https://trio-util.readthedocs.io/en/latest/\", None),\n    \"flake8-async\": (\"https://flake8-async.readthedocs.io/en/latest/\", None),\n}\n\n# See https://sphinx-codeautolink.readthedocs.io/en/latest/reference.html#configuration\ncodeautolink_autodoc_inject = False\ncodeautolink_global_preface = \"\"\"\nimport trio\nfrom trio import *\n\"\"\"\n\n\ndef add_intersphinx(app: Sphinx) -> None:\n    \"\"\"Add some specific intersphinx mappings.\n\n    Hooked up to builder-inited. app.builder.env.interpshinx_inventory is not an official API, so this may break on new sphinx versions.\n    \"\"\"\n\n    def add_mapping(\n        reftype: str,\n        library: str,\n        obj: str,\n        version: str = \"3.12\",\n        target: str | None = None,\n    ) -> None:\n        \"\"\"helper function\"\"\"\n        url_version = \"3\" if version == \"3.12\" else version\n        if target is None:\n            target = f\"{library}.{obj}\"\n\n        # sphinx doing fancy caching stuff makes this attribute invisible\n        # to type checkers\n        inventory = app.builder.env.intersphinx_inventory  # type: ignore[attr-defined]\n        assert isinstance(inventory, dict)\n        inventory = cast(\"Inventory\", inventory)\n\n        if sys.version_info >= (3, 11):\n            inventory[f\"py:{reftype}\"][f\"{target}\"] = _InventoryItem(\n                project_name=\"Python\",\n                project_version=version,\n                uri=f\"https://docs.python.org/{url_version}/library/{library}.html/{obj}\",\n                display_name=\"-\",\n            )\n        else:\n            inventory[f\"py:{reftype}\"][f\"{target}\"] = (\n                \"Python\",\n                version,\n                f\"https://docs.python.org/{url_version}/library/{library}.html/{obj}\",\n                \"-\",\n            )\n\n    # This has been removed in Py3.12, so add a link to the 3.11 version with deprecation warnings.\n    add_mapping(\"method\", \"pathlib\", \"Path.link_to\", \"3.11\")\n\n    # defined in py:data in objects.inv, but sphinx looks for a py:class\n    # see https://github.com/sphinx-doc/sphinx/issues/10974\n    # to dump the objects.inv for the stdlib, you can run\n    # python -m sphinx.ext.intersphinx http://docs.python.org/3/objects.inv\n    add_mapping(\"class\", \"math\", \"inf\")\n    add_mapping(\"class\", \"types\", \"FrameType\")\n\n    # new in py3.12, and need target because sphinx is unable to look up\n    # the module of the object if compiling on <3.12\n    if not hasattr(collections.abc, \"Buffer\"):\n        add_mapping(\"class\", \"collections.abc\", \"Buffer\", target=\"Buffer\")\n\n\nautodoc_member_order = \"bysource\"\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = [\"_templates\"]\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = \".rst\"\n\n# The master toctree document.\nmaster_doc = \"index\"\n\n# General information about the project.\nproject = \"Trio\"\ncopyright = \"2017, Nathaniel J. Smith\"  # noqa: A001 # Name shadows builtin\nauthor = \"Nathaniel J. Smith\"\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nimport importlib.metadata\n\nversion = importlib.metadata.version(\"trio\")\n# The full version, including alpha/beta/rc tags.\nrelease = version\n\nhtml_favicon = \"_static/favicon-32.png\"\nhtml_logo = \"../../logo/wordmark-transparent.svg\"\n# & down below in html_theme_options we set logo_only=True\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = \"en\"\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns: list[str] = []\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = \"default\"\n\nhighlight_language = \"python3\"\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n# This avoids a warning by the epub builder that it can't figure out\n# the MIME type for our favicon.\nsuppress_warnings = [\"epub.unknown_project_files\"]\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\n# html_theme = 'alabaster'\n\n# We have to set this ourselves, not only because it's useful for local\n# testing, but also because if we don't then RTD will throw away our\n# html_theme_options.\nhtml_theme = \"sphinx_rtd_theme\"\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\nhtml_theme_options = {\n    # default is 2\n    # show deeper nesting in the RTD theme's sidebar TOC\n    # https://stackoverflow.com/questions/27669376/\n    # I'm not 100% sure this actually does anything with our current\n    # versions/settings...\n    \"navigation_depth\": 4,\n    \"logo_only\": True,\n    \"prev_next_buttons_location\": \"both\",\n    \"style_nav_header_background\": \"#d2e7fa\",\n}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = [\"_static\"]\n\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = \"Triodoc\"\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements: dict[str, object] = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, \"Trio.tex\", \"Trio Documentation\", \"Nathaniel J. Smith\", \"manual\"),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [(master_doc, \"trio\", \"Trio Documentation\", [author], 1)]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (\n        master_doc,\n        \"Trio\",\n        \"Trio Documentation\",\n        author,\n        \"Trio\",\n        \"One line description of project.\",\n        \"Miscellaneous\",\n    ),\n]\n"
  },
  {
    "path": "docs/source/contributing.rst",
    "content": ".. _contributing:\n\nContributing to Trio and related projects\n=========================================\n\nSo you're interested in contributing to Trio or `one of our associated\nprojects <https://github.com/python-trio>`__? That's awesome! Trio is\nan open-source project maintained by an informal group of\nvolunteers. Our goal is to make async I/O in Python more fun, easy,\nand reliable, and we can't do it without help from people like you. We\nwelcome contributions from anyone willing to work in good faith with\nother contributors and the community (see also our\n:ref:`code-of-conduct`).\n\nThere are many ways to contribute, no contribution is too small, and\nall contributions are valued.  For example, you could:\n\n- Hang out in our `chatroom <https://gitter.im/python-trio/general>`__\n  and help people with questions.\n- Sign up for our `forum <https://trio.discourse.group>`__, set up\n  your notifications so you notice interesting conversations, and join\n  in.\n- Answer questions on StackOverflow (`recent questions\n  <https://stackexchange.com/filters/289914/trio-project-tags-on-stackoverflow-filter>`__).\n- Use Trio in a project, and give us feedback on what worked and what\n  didn't.\n- Write a blog post about your experiences with Trio, good or bad.\n- Release open-source programs and libraries that use Trio.\n- Improve documentation.\n- Comment on issues.\n- Add tests.\n- Fix bugs.\n- Add features.\n\nWe want contributing to be enjoyable and mutually beneficial; this\ndocument tries to give you some tips to help that happen, and applies\nto all of the projects under the `python-trio organization on Github\n<https://github.com/python-trio>`__. If you have thoughts on how it\ncan be improved then please let us know.\n\n\nGetting started\n---------------\n\nIf you're new to open source in general, you might find it useful to\ncheck out `opensource.guide's How to Contribute to Open Source\ntutorial <https://opensource.guide/how-to-contribute/>`__, or if\nvideo's more your thing, `egghead.io has a short free video course\n<https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github>`__.\n\nTrio and associated projects are developed on GitHub, under the\n`python-trio <https://github.com/python-trio>`__ organization. Code\nand documentation changes are made through pull requests (see\n:ref:`preparing-pull-requests` below).\n\nWe also have an unusual policy for managing commit rights: anyone\nwhose pull request is merged is automatically invited to join the\nGitHub organization, and gets commit rights to all of our\nrepositories. See :ref:`joining-the-team` below for more details.\n\nIf you're looking for a good place to start, then check out our issues\nlabeled `good first issue\n<https://github.com/search?utf8=%E2%9C%93&q=user%3Apython-trio+label%3A%22good+first+issue%22+state%3Aopen&type=Issues&ref=advsearch&l=&l=>`__,\nor feel free to ask `on the forum <https://trio.discourse.group>`__ or\n`in chat <https://gitter.im/python-trio/general>`__.\n\n\nProviding support\n-----------------\n\nWhen helping others use Trio, please remember that you are\nrepresenting our community, and we want this to be a friendly and\nwelcoming place.\n\nConcurrency is *really confusing* when you're first learning. When\ntalking to beginners, remember that you were a beginner once too, and\nthe whole goal here is to make a top-tier concurrency library that's\naccessible to everyone and a joy to use. If people are showing up with\nbeginner questions, *that means we're succeeding*. How we respond to\nquestions is part of that developer experience, just as much as our\nAPI, documentation, or testing tools. And as a bonus, helping\nbeginners is often the best way to discover ideas for improvements. If\nyou start getting burned out and cranky, we've all been there, and\nit's OK to take a break until you feel better. But it's not OK to take\nthat out on random users.\n\nPlease remember that the authors and users of competing projects are\nsmart, thoughtful people doing their best to balance complicated and\nconflicting requirements, just like us. Of course it's totally fine to\nmake specific technical critiques (\"In project X, this is handled by\ndoing Y, Trio does Z instead, which I prefer because...\") or talk\nabout your personal experience (\"I tried using X but I got super\nfrustrated and confused\"), but refrain from generic statements like \"X\nsucks\" or \"I can't believe anyone uses X\".\n\nPlease try not to make assumptions about people's gender, and in\nparticular remember that we're not all dudes. If you don't have a\nspecific reason to assume otherwise, then `singular they\n<https://en.wikipedia.org/wiki/Third-person_pronoun#Singular_they>`__\nmakes a fine pronoun, and there are plenty of gender-neutral\ncollective terms: \"Hey folks\", \"Hi all\", ...\n\nWe also like the Recurse Center's `social rules\n<https://www.recurse.com/manual#sub-sec-social-rules>`__:\n\n* no feigning surprise (also available in a `sweet comic version\n  <https://jvns.ca/blog/2017/04/27/no-feigning-surprise/>`__)\n* no well-actually's\n* no subtle -isms (`more details <https://www.recurse.com/blog/38-subtle-isms-at-hacker-school>`__)\n\n\n.. _preparing-pull-requests:\n\nPreparing pull requests\n-----------------------\n\nIf you want to submit a documentation or code change to one of the\nTrio projects, then that's done by preparing a Github pull request (or\n\"PR\" for short). We'll do our best to review your PR quickly. If it's\nbeen a week or two and you're still waiting for a response, feel free\nto post a comment poking us. (This can just be a comment with the\nsingle word \"ping\"; it's not rude at all.)\n\nHere's a quick checklist for putting together a good PR, with details\nin separate sections below:\n\n* :ref:`pull-request-scope`: Does your PR address a single,\n  self-contained issue?\n\n* :ref:`pull-request-tests`: Are your tests passing? Did you add any\n  necessary tests? Code changes pretty much always require test\n  changes, because if it's worth fixing the code then it's worth\n  adding a test to make sure it stays fixed.\n\n* :ref:`pull-request-formatting`: If you changed Python code, then did\n  you run ``black trio``? (Or for other packages, replace\n  ``trio`` with the package name.)\n\n* :ref:`pull-request-release-notes`: If your change affects\n  user-visible functionality, then did you add a release note to the\n  ``newsfragments/`` directory?\n\n* :ref:`pull-request-docs`: Did you make any necessary documentation\n  updates?\n\n* License: by submitting a PR to a Trio project, you're offering your\n  changes under that project's license. For most projects, that's dual\n  MIT/Apache 2, except for cookiecutter-trio, which is CC0.\n\n\n.. _pull-request-scope:\n\nWhat to put in a PR\n~~~~~~~~~~~~~~~~~~~\n\nEach PR should, as much as possible, address just one issue and be\nself-contained. If you have ten small, unrelated changes, then go\nahead and submit ten PRs – it's much easier to review ten small\nchanges than one big change with them all mixed together, and this way\nif there's some problem with one of the changes it won't hold up all\nthe others.\n\nIf you're uncertain about whether a change is a good idea and want\nsome feedback before putting time into it, feel free to ask in an\nissue or in the chat room. If you have a partial change that you want\nto get feedback on, feel free to submit it as a PR. (In this case it's\ntraditional to start the PR title with ``[WIP]``, for \"work in\nprogress\".)\n\nWhen you are submitting your PR, you can include ``Closes #123``,\n``Fixes: #123`` or\n`some variation <https://help.github.com/en/articles/closing-issues-using-keywords>`__\nin either your commit message or the PR description, in order to\nautomatically close the referenced issue when the PR is merged.\nThis keeps us closer to the desired state where each open issue reflects some\nwork that still needs to be done.\n\n\nEnvironment\n~~~~~~~~~~~\nWe strongly suggest using a virtual environment for managing dependencies,\nfor example with `venv <https://docs.python.org/3/library/venv.html>`__. So to\nset up your environment and install dependencies, you should run something like:\n\n.. code-block:: shell\n\n   cd path/to/trio/checkout/\n   python -m venv .venv # create virtual env in .venv\n   source .venv/bin/activate # activate it\n   pip install -e . # install trio, needed for pytest plugin\n   pip install -r test-requirements.txt # install test requirements\n\nyou rarely need to recreate the virtual environment, but you need to re-activate it\nin future terminals. You might also need to re-install from test-requirements.txt if\nthe versions in it get updated.\n\n.. _pull-request-tests:\n\nTests\n~~~~~\n\nWe use `pytest <https://pytest.org/>`__ for testing. To run the tests\nlocally, you should run:\n\n.. code-block:: shell\n\n   source .venv/bin/activate # if not already activated\n   pytest src\n\nThis doesn't try to be completely exhaustive – it only checks that\nthings work on your machine, and it will skip some slow tests. But it's\na good way to quickly check that things seem to be working, and we'll\nautomatically run the full test suite when your PR is submitted, so\nyou'll have a chance to see and fix any remaining issues then.\n\nEvery change should have 100% coverage for both code and tests. But,\nyou can use ``# pragma: no cover`` to mark lines where\nlack-of-coverage isn't something that we'd want to fix (as opposed to\nit being merely hard to fix). For example:\n\n.. code-block:: python\n\n    if ...:\n        ...\n    else:  # pragma: no cover\n        raise AssertionError(\"this can't happen!\")\n\nWe use Codecov to track coverage, because it makes it easy to combine\ncoverage from running in different configurations. Running coverage\nlocally can be useful\n\n.. code-block:: shell\n\n   coverage run -m pytest\n   coverage combine\n   coverage report\n\nbut don't be surprised if you get lower coverage than when looking at Codecov\nreports, because there are some lines that are only executed on\nWindows, or macOS, or PyPy, or CPython, or... you get the idea. After\nyou create a PR, Codecov will automatically report back with the\ncoverage, so you can check how you're really doing. (But note that the\nresults can be inaccurate until all the tests are passing. If the\ntests failed, then fix that before worrying about coverage.)\n\nSome rules for writing good tests:\n\n* `Tests MUST pass deterministically\n  <https://github.com/python-trio/trio/issues/200>`__. Flakey tests\n  make for miserable developers. One common source of indeterminism is\n  scheduler ordering; if you're having trouble with this, then\n  :mod:`trio.testing` provides powerful tools to help control\n  ordering, like :func:`trio.testing.wait_all_tasks_blocked`,\n  :class:`trio.testing.Sequencer`, and :class:`trio.testing.MockClock`\n  (usually used as a fixture: ``async def\n  test_whatever(autojump_clock): ...``). And if you need more tools\n  than this then we should add them.\n\n* (Trio package only) Slow tests – anything that takes more than about\n  0.25 seconds – should be marked with ``@slow``. This makes it so they\n  only run if you do ``pytest trio --run-slow``. Our CI scripts do\n  run slow tests, so you can be sure that the code will still be\n  thoroughly tested, and this way you don't have to sit around waiting\n  for a few irrelevant multi-second tests to run while you're iterating\n  on a change locally.\n\n  You can check for slow tests by passing ``--durations=10`` to\n  pytest. Most tests should take 0.01 seconds or less.\n\n* Speaking of waiting around for tests: Tests should never sleep\n  unless *absolutely* necessary. However, calling :func:`trio.sleep`\n  when using ``autojump_clock`` is fine, because that's not really\n  sleeping, and doesn't waste developers time waiting for the test to\n  run.\n\n* We like tests to exercise real functionality. For example, if you're\n  adding subprocess spawning functionality, then your tests should\n  spawn at least one process! Sometimes this is tricky – for example,\n  Trio's :class:`KeyboardInterrupt` tests have to jump through quite\n  some hoops to generate real SIGINT signals at the right times to\n  exercise different paths. But it's almost always worth it.\n\n* For cases where real testing isn't relevant or sufficient, then we\n  strongly prefer fakes or stubs over mocks. Useful articles:\n\n  * `Test Doubles - Fakes, Mocks and Stubs\n    <https://dev.to/milipski/test-doubles---fakes-mocks-and-stubs>`__\n\n  * `Mocks aren't stubs\n    <https://martinfowler.com/articles/mocksArentStubs.html>`__\n\n  * `Write test doubles you can trust using verified fakes\n    <https://codewithoutrules.com/2016/07/31/verified-fakes/>`__\n\n  Most major features have both real tests and tests using fakes or\n  stubs. For example, :class:`~trio.SSLStream` has some tests that\n  use Trio to make a real socket connection to real SSL server\n  implemented using blocking I/O, because it sure would be\n  embarrassing if that didn't work. And then there are also a bunch of\n  tests that use a fake in-memory transport stream where we have\n  complete control over timing and can make sure all the subtle edge\n  cases work correctly.\n\nWriting reliable tests for obscure corner cases is often harder than\nimplementing a feature in the first place, but stick with it: it's\nworth it! And don't be afraid to ask for help. Sometimes a fresh pair\nof eyes can be helpful when trying to come up with devious tricks.\n\n\n.. _pull-request-formatting:\n\nCode formatting\n~~~~~~~~~~~~~~~\n\nInstead of wasting time arguing about code formatting, we use `black\n<https://github.com/psf/black>`__ as well as other tools to automatically\nformat all our code to a standard style. While you're editing code you\ncan be as sloppy as you like about whitespace; and then before you commit,\njust run:\n\n.. code-block:: text\n\n    pip install -U pre-commit\n    pre-commit\n\nto fix it up. (And don't worry if you forget – when you submit a pull\nrequest then we'll automatically check and remind you.) Hopefully this\nwill let you focus on more important style issues like choosing good\nnames, writing useful comments, and making sure your docstrings are\nnicely formatted. (black doesn't reformat comments or docstrings.)\n\nIf you would like, you can even have pre-commit run before you commit by\nrunning:\n\n.. code-block:: text\n\n    pre-commit install\n\nand now pre-commit will run before git commits. You can uninstall the\npre-commit hook at any time by running:\n\n.. code-block:: text\n\n    pre-commit uninstall\n\n\nVery occasionally, you'll want to override black formatting. To do so,\nyou can can add ``# fmt: off`` and ``# fmt: on`` comments.\n\nIf you want to see what changes black will make, you can use:\n\n.. code-block:: text\n\n    black --diff trio\n\n(``--diff`` displays a diff, versus the default mode which fixes files\nin-place.)\n\n\nAdditionally, in some cases it is necessary to disable isort changing the\norder of imports. To do so you can add ``# isort: split`` comments.\nFor more information, please see `isort's docs <https://pycqa.github.io/isort/docs/configuration/action_comments.html>`__.\n\n\n.. _pull-request-release-notes:\n\nRelease notes\n~~~~~~~~~~~~~\n\nWe use `towncrier <https://github.com/hawkowl/towncrier>`__ to manage\nour `release notes <https://trio.readthedocs.io/en/latest/history.html>`__.\nBasically, every pull request that has a user\nvisible effect should add a short file to the ``newsfragments/``\ndirectory describing the change, with a name like ``<ISSUE\nNUMBER>.<TYPE>.rst``. See `newsfragments/README.rst\n<https://github.com/python-trio/trio/blob/main/newsfragments/README.rst>`__\nfor details. This way we can keep a good list of changes as we go,\nwhich makes the release manager happy, which means we get more\nfrequent releases, which means your change gets into users' hands\nfaster.\n\n\n.. _pull-request-commit-messages:\n\nCommit messages\n~~~~~~~~~~~~~~~\n\nWe don't enforce any particular format on commit messages. In your\ncommit messages, try to give the context to explain *why* a change was\nmade.\n\nThe target audience for release notes is users, who want to find out\nabout changes that might affect how they use the library, or who are\ntrying to figure out why something changed after they upgraded.\n\nThe target audience for commit messages is some hapless developer\n(think: you in six months... or five years) who is trying to figure\nout why some code looks the way it does. Including links to issues and\nany other discussion that led up to the commit is *strongly*\nrecommended.\n\n\n.. _pull-request-docs:\n\nDocumentation\n~~~~~~~~~~~~~\n\nWe take pride in providing friendly and comprehensive documentation.\nDocumentation is stored in ``docs/source/*.rst`` and is rendered using\n`Sphinx <http://www.sphinx-doc.org/>`__ with the `sphinxcontrib-trio\n<https://sphinxcontrib-trio.readthedocs.io/en/latest/>`__ extension.\nDocumentation is hosted at `Read the Docs\n<https://readthedocs.org/>`__, who take care of automatically\nrebuilding it after every commit.\n\nFor docstrings, we use `the Google docstring format\n<https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html#example-google-style-python-docstrings>`__.\nIf you add a new function or class, there's no mechanism for\nautomatically adding that to the docs: you'll have to at least add a\nline like ``.. autofunction:: <your function>`` in the appropriate\nplace. In many cases it's also nice to add some longer-form narrative\ndocumentation around that.\n\nWe enable Sphinx's \"nitpick mode\", which turns dangling references\ninto an error – this helps catch typos. (This will be automatically\nchecked when your PR is submitted.) If you intentionally want to allow\na dangling reference, you can add it to the `nitpick_ignore\n<http://www.sphinx-doc.org/en/stable/config.html#confval-nitpick_ignore>`__\nwhitelist in ``docs/source/conf.py``.\n\nTo build the docs locally, use our handy ``docs-requirements.txt``\nfile to install all of the required packages (possibly using a\nvirtualenv). After that, build the docs using ``make html`` in the\ndocs directory. The whole process might look something like this:\n\n.. code-block:: text\n\n    cd path/to/project/checkout/\n    pip install -r docs-requirements.txt\n    cd docs\n    make html\n\nYou can then browse the docs using Python's builtin http server:\n``python -m http.server 8000 --bind 127.0.0.1 --directory build/html``\nand then opening ``http://127.0.0.1:8000/`` in your web browser.\n\n.. _joining-the-team:\n\nJoining the team\n----------------\n\nAfter your first PR is merged, you should receive a Github invitation\nto join the ``python-trio`` organization. If you don't, that's not\nyour fault, it's because we made a mistake on our end. Give us a\nnudge on chat or `send @njsmith an email <mailto:njs@pobox.com>`__ and\nwe'll fix it.\n\nIt's totally up to you whether you accept or not, and if you do\naccept, you're welcome to participate as much or as little as you\nwant. We're offering the invitation because we'd love for you to join\nus in making Python concurrency more friendly and robust, but there's\nno pressure: life is too short to spend volunteer time on things that\nyou don't find fulfilling.\n\nAt this point people tend to have questions.\n\n**How can you trust me with this kind of power? What if I mess\neverything up?!?**\n\nRelax, you got this! And we've got your back. Remember, it's just\nsoftware, and everything's in version control: worst case we'll just\nroll things back and brainstorm ways to avoid the issue happening\nagain. We think it's more important to welcome people and help them\ngrow than to worry about the occasional minor mishap.\n\n**I don't think I really deserve this.**\n\nIt's up to you, but we wouldn't be offering if we didn't think\nyou did.\n\n**What exactly happens if I accept? Does it mean I'll break everything\nif I click the wrong button?**\n\nConcretely, if you accept the invitation, this does three things:\n\n* It lets you manage incoming issues on all of the ``python-trio``\n  projects by labelling them, closing them, etc.\n\n* It lets you merge pull requests on all of the ``python-trio``\n  projects by clicking Github's big green \"Merge\" button, but only if\n  all their tests have passed.\n\n* It automatically subscribes you to notifications on the\n  ``python-trio`` repositories (but you can unsubscribe again if you\n  want through the Github interface)\n\nNote that it does *not* allow you to push changes directly to Github\nwithout submitting a PR, and it doesn't let you merge broken PRs –\nthis is enforced through Github's \"branch protection\" feature, and it\napplies to everyone from the newest contributor up to the project\nfounder.\n\n**Okay, that's what I CAN do, but what SHOULD I do?**\n\nShort answer: whatever you feel comfortable with.\n\nWe do have one rule, which is the same one most F/OSS projects use:\ndon't merge your own PRs. We find that having another person look at\neach PR leads to better quality.\n\nBeyond that, it all comes down to what you feel up to. If you don't\nfeel like you know enough to review a complex code change, then you\ndon't have to – you can just look it over and make some comments, even\nif you don't feel up to making the final merge/no-merge decision. Or\nyou can just stick to merging trivial doc fixes and adding tags to\nissues, that's helpful too. If after hanging around for a while you\nstart to feel like you have better handle on how things work and want\nto start doing more, that's excellent; if it doesn't happen, that's\nfine too.\n\nIf at any point you're unsure about whether doing something would be\nappropriate, feel free to ask. For example, it's *totally OK* if the\nfirst time you review a PR, you want someone else to check over your\nwork before you hit the merge button.\n\nThe best essay I know about reviewing pull request's is Sage Sharp's\n`The gentle art of patch review\n<http://sage.thesharps.us/2014/09/01/the-gentle-art-of-patch-review/>`__.\nThe `node.js guide\n<https://github.com/nodejs/node/blob/master/doc/guides/contributing/pull-requests.md#reviewing-pull-requests>`__\nalso has some good suggestions, and `so does this blog post\n<http://verraes.net/2013/10/pre-merge-code-reviews/>`__.\n\n\nManaging issues\n---------------\n\nAs issues come in, they need to be responded to, tracked, and –\nhopefully! – eventually closed.\n\nAs a general rule, each open issue should represent some kind of task\nthat we need to do. Sometimes that task might be \"figure out what to\ndo here\", or even \"figure out whether we want to address this issue\";\nsometimes it will be \"answer this person's question\". But if there's\nno followup to be done, then the issue should be closed.\n\n\nIssue labels\n~~~~~~~~~~~~\n\nThe Trio repository in particular uses a number of labels to try and\nkeep track of issues. The current list is somewhat ad hoc, and may or\nmay not remain useful over time – if you think of a new label that\nwould be useful, a better name for an existing label, or think a label\nhas outlived its usefulness, then speak up.\n\n* `good first issue\n  <https://github.com/python-trio/trio/labels/good%20first%20issue>`__:\n  Used to mark issues that are relatively straightforward, and could\n  be good places for a new contributor to start.\n\n* `todo soon\n  <https://github.com/python-trio/trio/labels/todo%20soon>`__: This\n  marks issues where there aren't questions left about whether or how\n  to do it, it's just waiting for someone to dig in and do the work.\n\n* `missing piece\n  <https://github.com/python-trio/trio/labels/missing%20piece>`__:\n  This generally marks significant self-contained chunks of missing\n  functionality. If you're looking for a more ambitious project to\n  work on, this might be useful.\n\n* `potential API breaker\n  <https://github.com/python-trio/trio/labels/potential%20API%20breaker>`__:\n  What it says. This is useful because these are issues that we'll\n  want to make sure to review aggressively as Trio starts to\n  stabilize, and certainly before we reach 1.0.\n\n* `design discussion\n  <https://github.com/python-trio/trio/labels/design%20discussion>`__:\n  This marks issues where there's significant design questions to be\n  discussed; if you like meaty theoretical debates and discussions of\n  API design, then browsing this might be interesting.\n\n* `polish <https://github.com/python-trio/trio/labels/polish>`__:\n  Marks issues that it'd be nice to resolve eventually, because it's\n  the Right Thing To Do, but it's addressing a kind of edge case thing\n  that isn't necessary for a minimum viable product. Sometimes\n  overlaps with \"user happiness\".\n\n* `user happiness\n  <https://github.com/python-trio/trio/labels/user%20happiness>`__:\n  From the name alone, this could apply to any bug (users certainly\n  are happier when you fix bugs!), but that's not what we mean. This\n  label is used for issues involving places where users stub their\n  toes, or for the kinds of quality-of-life features that leave users\n  surprised and excited – e.g. fancy testing tools that Just Work.\n\n\nGovernance\n----------\n\n`Nathaniel J. Smith <https://github.com/njsmith>`__ is the Trio `BDFL\n<https://en.wikipedia.org/wiki/Benevolent_dictator_for_life>`__. If\nthe project grows to the point where we'd benefit from more structure,\nthen we'll figure something out.\n\n\n.. Possible references for future additions:\n\n   \"\"\"\n   Jumping into an unfamiliar codebase (or any for that matter) for the first time can be scary.\n   Plus, if it's your first time contributing to open source, it can even be scarier!\n\n   But, we at webpack believe:\n\n       Any (even non-technical) individual should feel welcome to contribute.\n       However you decide to contribute, it should be fun and enjoyable for you!\n       Even after your first commit, you will walk away understanding more about webpack or JavaScript.\n       Consequently, you could become a better developer, writer,\n         designer, etc. along the way, and we are committed to helping\n         foster this growth.\n   \"\"\"\n\n   imposter syndrome disclaimer\n   https://github.com/Unidata/MetPy#contributing\n\n   checklist\n   https://github.com/nayafia/contributing-template/blob/master/CONTRIBUTING-template.md\n\n   https://medium.com/the-node-js-collection/healthy-open-source-967fa8be7951\n\n   http://sweng.the-davies.net/Home/rustys-api-design-manifesto\n"
  },
  {
    "path": "docs/source/design.rst",
    "content": "Design and internals\n====================\n\n.. currentmodule:: trio\n\nHere we'll discuss Trio's overall design and architecture: how it fits\ntogether and why we made the decisions we did. If all you want to do\nis use Trio, then you don't need to read this – though you might find\nit interesting. The main target audience here is (a) folks who want to\nread the code and potentially contribute, (b) anyone working on\nsimilar libraries who want to understand what we're up to, (c) anyone\ninterested in I/O library design generally.\n\nThere are many valid approaches to writing an async I/O library. This\nis ours.\n\n\nHigh-level design principles\n----------------------------\n\nTrio's two overriding goals are **usability** and **correctness**: we\nwant to make it *easy* to get things *right*.\n\nOf course there are lots of other things that matter too, like speed,\nmaintainability, etc. We want those too, as much as we can get. But\nsometimes these things come in conflict, and when that happens, these\nare our priorities.\n\nIn some sense the entire rest of this document is a description of how\nthese play out, but to give a simple example: Trio's\n``KeyboardInterrupt`` handling machinery is a bit tricky and hard to\ntest, so it scores poorly on simplicity and maintainability. But we\nthink the usability+correctness gains outweigh this.\n\nThere are some subtleties here. Notice that it's specifically \"easy to\nget things right\". There are situations (e.g. writing one-off scripts)\nwhere the most \"usable\" tool is the one that will happily ignore\nerrors and keep going no matter what, or that doesn't bother with\nresource cleanup. (Cf. the success of PHP.) This is a totally valid\nuse case and valid definition of usability, but it's not the one we\nuse: we think it's easier to build reliable and correct systems if\nexceptions propagate until handled and if the system `catches you when\nyou make potentially dangerous resource handling errors\n<https://github.com/python-trio/trio/issues/265>`__, so that's what we\noptimize for.\n\nIt's also worth saying something about speed, since it often looms\nlarge in comparisons between I/O libraries. This is a rather subtle\nand complex topic.\n\nIn general, speed is certainly important – but the fact that people\nsometimes use Python instead of C is a pretty good indicator that\nusability often trumps speed in practice. We want to make Trio fast,\nbut it's not an accident that it's left off our list of overriding\ngoals at the top: if necessary we are willing to accept some slowdowns\nin the service of usability and reliability.\n\nTo break things down in more detail:\n\nFirst of all, there are the cases where speed directly impacts\ncorrectness, like when you hit an accidental ``O(N**2)`` algorithm and\nyour program effectively locks up. Trio is very careful to use\nalgorithms and data structures that have good worst-case behavior\n(even if this might mean sacrificing a few percentage points of speed\nin the average case).\n\nSimilarly, when there's a conflict, we care more about 99th percentile\nlatencies than we do about raw throughput, because insufficient\nthroughput – if it's consistent! – can often be budgeted for and\nhandled with horizontal scaling, but once you lose latency it's gone\nforever, and latency spikes can easily cross over to become a\ncorrectness issue (e.g., an RPC server that responds slowly enough to\ntrigger timeouts is effectively non-functional). Again, of course,\nthis doesn't mean we don't care about throughput – but sometimes\nengineering requires making trade-offs, especially for early-stage\nprojects that haven't had time to optimize for all use cases yet.\n\nAnd finally: we care about speed on real-world applications quite a\nbit, but speed on microbenchmarks is just about our lowest\npriority. We aren't interested in competing to build \"the fastest echo\nserver in the West\". I mean, it's nice if it happens or whatever, and\nmicrobenchmarks are an invaluable tool for understanding a system's\nbehavior. But if you play that game to win then it's very easy to get\nyourself into a situation with seriously misaligned incentives, where\nyou have to start compromising on features and correctness in order to\nget a speedup that's totally irrelevant to real-world applications. In\nmost cases (we suspect) it's the application code that's the\nbottleneck, and you'll get more of a win out of running the whole app\nunder PyPy than out of any heroic optimizations to the I/O\nlayer. (And this is why Trio *does* place a priority on PyPy\ncompatibility.)\n\nAs a matter of tactics, we also note that at this stage in Trio's\nlifecycle, it'd probably be a mistake to worry about speed too\nmuch. It doesn't make sense to spend lots of effort optimizing an API\nwhose semantics are still in flux.\n\n\nUser-level API principles\n-------------------------\n\nBasic principles\n~~~~~~~~~~~~~~~~\n\nTrio is very much a continuation of the ideas explored in `this blog\npost\n<https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/>`__,\nand in particular the `principles identified there\n<https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#review-and-summing-up-what-is-async-await-native-anyway>`__\nthat make curio easier to use correctly than asyncio. So Trio also\nadopts these rules, in particular:\n\n* The only form of concurrency is the task.\n\n* Tasks are guaranteed to run to completion.\n\n* Task spawning is always explicit. No callbacks, no implicit\n  concurrency, no futures/deferreds/promises/other APIs that involve\n  callbacks. All APIs are `\"causal\"\n  <https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#review-and-summing-up-what-is-async-await-native-anyway>`__\n  except for those that are explicitly used for task spawning.\n\n* Exceptions are used for error handling; ``try``/``finally``\n  and ``with`` blocks for handling cleanup.\n\n\nCancel points and schedule points\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe first major place that Trio departs from curio is in its decision\nto make a much larger fraction of the API use sync functions rather\nthan async functions, and to provide strong conventions about cancel\npoints and schedule points. (At this point, there are a lot of ways\nthat Trio and curio have diverged. But this was really the origin –\nthe tipping point where I realized that exploring these ideas would\nrequire a new library, and couldn't be done inside curio.) The full\nreasoning here takes some unpacking.\n\nFirst, some definitions: a *cancel point* is a point where your code\nchecks if it has been cancelled – e.g., due to a timeout having\nexpired – and potentially raises a :exc:`Cancelled` error. A *schedule\npoint* is a point where the current task can potentially be suspended,\nand another task allowed to run.\n\nIn curio, the convention is that all operations that interact with the\nrun loop in any way are syntactically async, and it's undefined which\nof these operations are cancel/schedule points; users are instructed\nto assume that any of them *might* be cancel/schedule points, but with\na few exceptions there's no guarantee that any of them are unless they\nactually block. (I.e., whether a given call acts as a cancel/schedule\npoint is allowed to vary across curio versions and also depending on\nruntime factors like network load.)\n\nBut when using an async library, there are good reasons why you need\nto be aware of cancel and schedule points. They introduce a set of\ncomplex and partially conflicting constraints on your code:\n\nYou need to make sure that every task passes through a cancel\npoint regularly, because otherwise timeouts become ineffective\nand your code becomes subject to DoS attacks and other\nproblems. So for correctness, it's important to make sure you\nhave enough cancel points.\n\nBut... every cancel point also increases the chance of subtle\nbugs in your program, because it's a place where you have to be\nprepared to handle a :exc:`Cancelled` exception and clean up\nproperly. And while we try to make this as easy as possible,\nthese kinds of clean-up paths are notorious for getting missed\nin testing and harboring subtle bugs. So the more cancel points\nyou have, the harder it is to make sure your code is correct.\n\nSimilarly, you need to make sure that every task passes through\na schedule point regularly, because otherwise this task could\nend up hogging the event loop and preventing other code from\nrunning, causing a latency spike. So for correctness, it's\nimportant to make sure you have enough schedule points.\n\nBut... you have to be careful here too, because every schedule\npoint is a point where arbitrary other code could run, and\nalter your program's state out from under you, introducing\nclassic concurrency bugs. So as you add more schedule points,\nit `becomes exponentially harder to reason about how your code\nis interleaved and be sure that it's correct\n<https://glyph.twistedmatrix.com/2014/02/unyielding.html>`__.\n\nSo an important question for an async I/O library is: how do we help\nthe user manage these trade-offs?\n\nTrio's answer is informed by two further observations:\n\nFirst, any time a task blocks (e.g., because it does an ``await\nsock.recv()`` but there's no data available to receive), that\nhas to be a cancel point (because if the I/O never arrives, we\nneed to be able to time out), and it has to be a schedule point\n(because the whole idea of asynchronous programming is that\nwhen one task is waiting we can switch to another task to get\nsomething useful done).\n\nAnd second, a function which sometimes counts as a cancel/schedule\npoint, and sometimes doesn't, is the worst of both worlds: you have\nput in the effort to make sure your code handles cancellation or\ninterleaving correctly, but you can't count on it to help meet latency\nrequirements.\n\nWith all that in mind, Trio takes the following approach:\n\nRule 1: to reduce the number of concepts to keep track of, we collapse\ncancel points and schedule points together. Every point that is a\ncancel point is also a schedule point and vice versa. These are\ndistinct concepts both theoretically and in the actual implementation,\nbut we hide that distinction from the user so that there's only one\nconcept they need to keep track of.\n\nRule 2: Cancel+schedule points are determined *statically*. A Trio\nprimitive is either *always* a cancel+schedule point, or *never* a\ncancel+schedule point, regardless of runtime conditions. This is\nbecause we want it to be possible to determine whether some code has\n\"enough\" cancel/schedule points by reading the source code.\n\nIn fact, to make this even simpler, we make it so you don't even have\nto look at the function arguments: each *function* is either a\ncancel+schedule point on *every* call or on *no* calls.\n\n(Pragmatic exception: a Trio primitive is not required to act as a\ncancel+schedule point when it raises an exception, even if it would\nact as one in the case of a successful return. See `issue 474\n<https://github.com/python-trio/trio/issues/474>`__ for more details;\nbasically, requiring checkpoints on all exception paths added a lot of\nimplementation complexity with negligible user-facing benefit.)\n\nObservation: since blocking is always a cancel+schedule point, rule 2\nimplies that any function that *sometimes* blocks is *always* a\ncancel+schedule point.\n\nSo that gives us a number of cancel+schedule points: all the functions\nthat can block. Are there any others? Trio's answer is: no. It's easy\nto add new points explicitly (throw in a ``sleep(0)`` or whatever) but\nhard to get rid of them when you don't want them. (And this is a real\nissue – \"too many potential cancel points\" is definitely a tension\n`I've felt\n<https://github.com/dabeaz/curio/issues/149#issuecomment-269745283>`__\nwhile trying to build things like task supervisors in curio.) And we\nexpect that most Trio programs will execute potentially-blocking\noperations \"often enough\" to produce reasonable behavior. So, rule 3:\nthe *only* cancel+schedule points are the potentially-blocking\noperations.\n\nAnd now that we know where our cancel+schedule points are, there's the\nquestion of how to effectively communicate this information to the\nuser. We want some way to mark out a category of functions that might\nblock or trigger a task switch, so that they're clearly distinguished\nfrom functions that don't do this. Wouldn't it be nice if there were\nsome Python feature, that naturally divided functions into two\ncategories, and maybe put some sort of special syntactic marking on\nwith the functions that can do weird things like block and task\nswitch...? What a coincidence, that's exactly how async functions\nwork! Rule 4: in Trio, only the potentially blocking functions are\nasync. So e.g. :meth:`Event.wait` is async, but :meth:`Event.set` is\nsync.\n\nSumming up: out of what's actually a pretty vast space of design\npossibilities, we declare by fiat that when it comes to Trio\nprimitives, all of these categories are identical:\n\n* async functions\n* functions that can, under at least some circumstances, block\n* functions where the caller needs to be prepared to handle\n  potential :exc:`Cancelled` exceptions\n* functions that are guaranteed to notice any pending cancellation\n* functions where you need to be prepared for a potential task switch\n* functions that are guaranteed to take care of switching tasks if\n  appropriate\n\nThis requires some non-trivial work internally – it actually takes a\nfair amount of care to make those 4 cancel/schedule categories line\nup, and there are some shenanigans required to let sync and async APIs\nboth interact with the run loop on an equal footing. But this is all\ninvisible to the user, we feel that it pays off in terms of usability\nand correctness.\n\nThere is one exception to these rules, for async context\nmanagers. Context managers are composed of two operations – enter and\nexit – and sometimes only one of these is potentially\nblocking. (Examples: ``async with lock:`` can block when entering but\nnever when exiting; ``async with open_nursery() as ...:`` can block\nwhen exiting but never when entering.) But, Python doesn't have\n\"half-asynchronous\" context managers: either both operations are\nasync-flavored, or neither is. In Trio we take a pragmatic approach:\nfor this kind of async context manager, we enforce the above rules\nonly on the potentially blocking operation, and the other operation is\nallowed to be syntactically ``async`` but semantically\nsynchronous. And async context managers should always document which\nof their operations are schedule+cancel points.\n\n\nExceptions always propagate\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAnother rule that Trio follows is that *exceptions must always\npropagate*. This is like the `zen\n<https://www.python.org/dev/peps/pep-0020/>`__ line about \"Errors\nshould never pass silently\", except that in every other concurrency\nlibrary for Python (threads, asyncio, curio, ...), it's fairly common\nto end up with an undeliverable exception, which just gets printed to\nstderr and then discarded. While we understand the pragmatic\nconstraints that motivated these libraries to adopt this approach, we\nfeel that there are far too many situations where no human will ever\nlook at stderr and notice the problem, and insist that Trio APIs find\na way to propagate exceptions \"up the stack\" – whatever that might\nmean.\n\nThis is often a challenging rule to follow – for example, the call\nsoon code has to jump through some hoops to make it happen – but its\nmost dramatic influence can seen in Trio's task-spawning interface,\nwhere it motivates the use of \"nurseries\":\n\n.. code-block:: python\n\n   async def parent():\n       async with trio.open_nursery() as nursery:\n           nursery.start_soon(child)\n\n(See :ref:`tasks` for full details.)\n\nIf you squint you can see the conceptual influence of Erlang's \"task\nlinking\" and \"task tree\" ideas here, though the details are different.\n\nThis design also turns out to enforce a remarkable, unexpected\ninvariant.\n\nIn `the blog post\n<https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#c-c-c-c-causality-breaker>`__\nI called out a nice feature of curio's spawning API, which is that\nsince spawning is the only way to break causality, and in curio\n``spawn`` is async, which means that in curio sync functions are\nguaranteed to be causal. One limitation though is that this invariant\nis actually not very predictive: in curio there are lots of async\nfunctions that could spawn off children and violate causality, but\nmost of them don't, but there's no clear marker for the ones that do.\n\nOur API doesn't quite give that guarantee, but actually a better\none. In Trio:\n\n* Sync functions can't create nurseries, because nurseries require an\n  ``async with``\n\n* Any async function can create a nursery and start new tasks... but\n  creating a nursery *allows task starting but does not permit\n  causality breaking*, because the children have to exit before the\n  function is allowed to return. So we can preserve causality without\n  having to give up concurrency!\n\n* The only way to violate causality (which is an important feature,\n  just one that needs to be handled carefully) is to explicitly create\n  a nursery object in one task and then pass it into another task. And\n  this provides a very clear and precise signal about where the funny\n  stuff is happening – just watch for the nursery object getting\n  passed around.\n\n\nIntrospection, debugging, testing\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTools for introspection and debugging are critical to achieving\nusability and correctness in practice, so they should be first-class\nconsiderations in Trio.\n\nSimilarly, the availability of powerful testing tools has a huge\nimpact on usability and correctness; we consider testing helpers to be\nvery much in scope for the Trio project.\n\n\nSpecific style guidelines\n-------------------------\n\n* As noted above, functions that don't block should be sync-colored,\n  and functions that might block should be async-colored and\n  unconditionally act as cancel+schedule points.\n\n* Any function that takes a callable to run should have a signature\n  like:\n\n  .. code-block:: python\n\n     def call_the_thing(fn, *args, kwonly1, kwonly2):\n         ...\n\n  where ``fn(*args)`` is the thing to be called, and ``kwonly1``,\n  ``kwonly2``, are keyword-only arguments that belong to\n  ``call_the_thing``. This applies even if ``call_the_thing`` doesn't\n  take any arguments of its own, i.e. in this case its signature looks\n  like:\n\n  .. code-block:: python\n\n     def call_the_thing(fn, *args):\n         ...\n\n  This allows users to skip faffing about with\n  :func:`functools.partial` in most cases, while still providing an\n  unambiguous and extensible way to pass arguments to the caller.\n  (Hat-tip to asyncio, who we stole this convention from.)\n\n* Whenever it makes sense, Trio classes should have a method called\n  ``statistics()`` which returns an immutable object with named fields\n  containing internal statistics about the object that are useful for\n  debugging or introspection (:ref:`examples <synchronization>`).\n\n* Functions or methods whose purpose is to wait for a condition to\n  become true should be called ``wait_<condition>``. This avoids\n  ambiguities like \"does ``await readable()`` *check* readability\n  (returning a bool) or *wait for* readability?\".\n\n  Sometimes this leads to the slightly funny looking ``await\n  wait_...``. Sorry. As far as I can tell all the alternatives are\n  worse, and you get used to the convention pretty quick.\n\n* If it's desirable to have both blocking and non-blocking versions of\n  a function, then they look like:\n\n  .. code-block:: python\n\n     async def OPERATION(arg1, arg2):\n         ...\n\n     def OPERATION_nowait(arg1, arg2):\n         ...\n\n  and the ``nowait`` version raises :exc:`trio.WouldBlock` if it would block.\n\n* ...we should, but currently don't, have a solid convention to\n  distinguish between functions that take an async callable and those\n  that take a sync callable. See `issue #68\n  <https://github.com/python-trio/trio/issues/68>`__.\n\n\nA brief tour of Trio's internals\n--------------------------------\n\nIf you want to understand how Trio is put together internally, then\nthe first thing to know is that there's a very strict internal\nlayering: the ``trio._core`` package is a fully self-contained\nimplementation of the core scheduling/cancellation/IO handling logic,\nand then the other ``trio.*`` modules are implemented in terms of the\nAPI it exposes. (If you want to see what this API looks like, then\n``import trio; print(trio._core.__all__)``). Everything exported from\n``trio._core`` is *also* exported as part of the ``trio``,\n``trio.lowlevel``, or ``trio.testing`` namespaces. (See their\nrespective ``__init__.py`` files for details; there's a test to\nenforce this.)\n\nRationale: currently, Trio is a new project in a novel part of the\ndesign space, so we don't make any stability guarantees. But the goal\nis to reach the point where we *can* declare the API stable. It's\nunlikely that we'll be able to quickly explore all possible corners of\nthe design space and cover all possible types of I/O. So instead, our\nstrategy is to make sure that it's possible for independent packages\nto add new features on top of Trio. Enforcing the ``trio`` vs\n``trio._core`` split is a way of `eating our own dogfood\n<https://en.wikipedia.org/wiki/Eating_your_own_dog_food>`__: basic\nfunctionality like :class:`trio.Lock` and :mod:`trio.socket` is\nactually implemented solely in terms of public APIs. And the hope is\nthat by doing this, we increase the chances that someone who comes up\nwith a better kind of queue or wants to add some new functionality\nlike, say, file system change watching, will be able to do that on top\nof our public APIs without having to modify Trio internals.\n\n\nInside ``trio._core``\n~~~~~~~~~~~~~~~~~~~~~\n\nThe ``_ki.py`` module implements the core infrastructure for safe handling\nof :class:`KeyboardInterrupt`.  It's largely independent of the rest of Trio,\nand could (possibly should?) be extracted into its own independent package.\n\nThe most important submodule, where everything is integrated, is\n``_run.py``. (This is also by far the largest submodule; it'd be nice\nto factor bits of it out where possible, but it's tricky because the\ncore functionality genuinely is pretty intertwined.) Notably, this is\nwhere cancel scopes, nurseries, and :class:`~trio.lowlevel.Task` are\ndefined; it's also where the scheduler state and :func:`trio.run`\nlive.\n\nThe one thing that *isn't* in ``_run.py`` is I/O handling. This is\ndelegated to an ``IOManager`` class, of which there are currently\nthree implementations:\n\n* ``EpollIOManager`` in ``_io_epoll.py`` (used on Linux, illumos)\n\n* ``KqueueIOManager`` in ``_io_kqueue.py`` (used on macOS, \\*BSD)\n\n* ``WindowsIOManager`` in ``_io_windows.py`` (used on Windows)\n\nThe epoll and kqueue backends take advantage of the epoll and kqueue\nwrappers in the stdlib :mod:`select` module. The windows backend uses\nCFFI to access to the Win32 API directly (see\n``trio/_core/_windows_cffi.py``). In general, we prefer to go directly\nto the raw OS functionality rather than use :mod:`selectors`, for\nseveral reasons:\n\n* Controlling our own fate: I/O handling is pretty core to what Trio\n  is about, and :mod:`selectors` is (as of 2017-03-01) somewhat buggy\n  (e.g. `issue 29256 <https://bugs.python.org/issue29256>`__, `issue\n  29255 <https://bugs.python.org/issue29255>`__). Which isn't a big\n  deal on its own, but since :mod:`selectors` is part of the standard\n  library we can't fix it and ship an updated version; we're stuck\n  with whatever we get. We want more control over our users'\n  experience than that.\n\n* Impedance mismatch: the :mod:`selectors` API isn't particularly\n  well-fitted to how we want to use it. For example, kqueue natively\n  treats an interest in readability of some fd as a separate thing\n  from an interest in that same fd's writability, which neatly matches\n  Trio's model. :class:`selectors.KqueueSelector` goes to some effort\n  internally to lump together all interests in a single fd, and to use\n  it we'd then we'd have to jump through more hoops to reverse\n  this. Of course, the native epoll API is fd-centric in the same way\n  as the :mod:`selectors` API so we do still have to write code to\n  jump through these hoops, but the point is that the :mod:`selectors`\n  abstractions aren't providing a lot of extra value.\n\n* (Most important) Access to raw platform capabilities:\n  :mod:`selectors` is highly inadequate on Windows, and even on\n  Unix-like systems it hides a lot of power (e.g. kqueue can do a lot\n  more than just check fd readability/writability!).\n\nThe ``IOManager`` layer provides a fairly raw exposure of the capabilities\nof each system, with public API functions that vary between different\nbackends. (This is somewhat inspired by how :mod:`os` works.) These\npublic APIs are then exported as part of :mod:`trio.lowlevel`, and\nhigher-level APIs like :mod:`trio.socket` abstract over these\nsystem-specific APIs to provide a uniform experience.\n\nCurrently the choice of backend is made statically at import time, and\nthere is no provision for \"pluggable\" backends. The intuition here is\nthat we'd rather focus our energy on making one set of solid, official\nbackends that provide a high-quality experience out-of-the-box on all\nsupported systems.\n"
  },
  {
    "path": "docs/source/glossary.rst",
    "content": ":orphan:\n\n.. _glossary:\n\n********\nGlossary\n********\n\n.. glossary::\n\n   asynchronous file object\n       This is an object with an API identical to a :term:`file object`, with\n       the exception that all methods that do I/O are async functions.\n\n       The main ways to create an asynchronous file object are by using the\n       :func:`trio.open_file` function or the :meth:`trio.Path.open`\n       method. See :ref:`async-file-io` for more details.\n"
  },
  {
    "path": "docs/source/history.rst",
    "content": "Release history\n===============\n\n.. currentmodule:: trio\n\n.. towncrier release notes start\n\ntrio 0.33.0 (2026-02-14)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Start supporting Android's new ``\"android\"`` `sys.platform`. (`#3357 <https://github.com/python-trio/trio/issues/3357>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Both :class:`trio.testing.RaisesGroup` and :class:`trio.testing.Matcher` have been deprecated. Pytest alternatives ``pytest.RaisesGroup`` and ``pytest.RaisesExc`` (respectively) are considered correct replacement. (`#3326 <https://github.com/python-trio/trio/issues/3326>`__)\n\n\ntrio 0.32.0 (2025-10-31)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Allow `trio.CapacityLimiter` to have zero total_tokens. (`#3321 <https://github.com/python-trio/trio/issues/3321>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fixed a bug where iterating over an ``@as_safe_channel``-derived ``ReceiveChannel``\n  would raise `~trio.BrokenResourceError` if the channel was closed by another task.\n  It now shuts down cleanly. (`#3331 <https://github.com/python-trio/trio/issues/3331>`__)\n- `trio.lowlevel.Task.iter_await_frames` now works on completed tasks, by\n  returning an empty list of frames if the underlying coroutine has been closed.\n  Previously, it raised an internal error. (`#3337 <https://github.com/python-trio/trio/issues/3337>`__)\n\n\nRemovals without deprecations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Drop support for Python 3.9. (`#3345 <https://github.com/python-trio/trio/issues/3345>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Decrease indentation for exception groups raised in `trio.as_safe_channel`. (`#3332 <https://github.com/python-trio/trio/issues/3332>`__)\n\n\nTrio 0.31.0 (2025-09-09)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- :exc:`Cancelled` strings can now display the source and reason for a cancellation. Trio-internal sources of cancellation will set this string, and :meth:`CancelScope.cancel` now has a ``reason`` string parameter that can be used to attach info to any :exc:`Cancelled` to help in debugging. (`#3232 <https://github.com/python-trio/trio/issues/3232>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Make ctrl+c work in more situations in the Trio REPL (``python -m trio``). (`#3007 <https://github.com/python-trio/trio/issues/3007>`__)\n- Allow pickling `trio.Cancelled`, as they can show up when you want to pickle something else. This does not rule out pickling other ``NoPublicConstructor`` objects -- create an issue if necessary. (`#3248 <https://github.com/python-trio/trio/issues/3248>`__)\n- Decrease import time on Windows by around 10%. (`#3263 <https://github.com/python-trio/trio/issues/3263>`__)\n- Handle unwrapping SystemExit/KeyboardInterrupt exception gracefully in utility function ``raise_single_exception_from_group`` that reraises last exception from group. (`#3275 <https://github.com/python-trio/trio/issues/3275>`__)\n- Ensure that the DTLS server does not mutate SSL context. (`#3277 <https://github.com/python-trio/trio/issues/3277>`__)\n- Avoid having `trio.as_safe_channel` raise if closing the generator wrapped\n  `GeneratorExit` in a `BaseExceptionGroup`. (`#3324 <https://github.com/python-trio/trio/issues/3324>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Implement ``bool(trio.Event)`` and have it raise a `DeprecationWarning` and tell users to use `trio.Event.is_set` instead. This is an alternative to ``mypy --enable-error-code=truthy-bool`` for users who don't use type checking. (`#3322 <https://github.com/python-trio/trio/issues/3322>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- When misnesting nurseries you now get a helpful :exc:`RuntimeError` instead of a catastrophic :exc:`TrioInternalError`. (`#3307 <https://github.com/python-trio/trio/issues/3307>`__)\n\n\nTrio 0.30.0 (2025-04-20)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Add :func:`@trio.as_safe_channel <trio.as_safe_channel>`, a wrapper that can be used to make async generators safe.\n  This will be the suggested fix for the flake8-async lint rule `ASYNC900 <https://flake8-async.readthedocs.io/en/latest/rules.html#async900>`_. (`#3197 <https://github.com/python-trio/trio/issues/3197>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Allow `trio` to be a `types.ModuleType` and still have deprecated attributes. (`#2135 <https://github.com/python-trio/trio/issues/2135>`__)\n- Fixed socket module for some older systems which lack ``socket.AI_NUMERICSERV``.\n  Now trio works on legacy (pre-Lion) macOS. (`#3133 <https://github.com/python-trio/trio/issues/3133>`__)\n- Update type hints for `trio.run_process` and `trio.lowlevel.open_process`. (`#3183 <https://github.com/python-trio/trio/issues/3183>`__)\n- Don't mutate the global runner when MockClock is created. (`#3205 <https://github.com/python-trio/trio/issues/3205>`__)\n- Fix incorrect return type hint for :meth:`Nursery.start() <trio.Nursery.start>`. (`#3224 <https://github.com/python-trio/trio/issues/3224>`__)\n\n\nImproved documentation\n~~~~~~~~~~~~~~~~~~~~~~\n\n- Update wording in documentation to more accurately reflect Trio's maturity. (`#3216 <https://github.com/python-trio/trio/issues/3216>`__)\n\n\nTrio 0.29.0 (2025-02-14)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Add :func:`trio.lowlevel.in_trio_run` and :func:`trio.lowlevel.in_trio_task` and document the semantics (and differences) thereof. See :ref:`the documentation <trio_contexts>`. (`#2757 <https://github.com/python-trio/trio/issues/2757>`__)\n- If `trio.testing.RaisesGroup` does not get the expected exceptions it now raises an `AssertionError` with a helpful message, instead of letting the raised exception/group fall through. The raised exception is available in the ``__context__`` of the `AssertionError` and can be seen in the traceback. (`#3145 <https://github.com/python-trio/trio/issues/3145>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Clear Trio's cache of worker threads upon `os.fork`. (`#2764 <https://github.com/python-trio/trio/issues/2764>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Stop using ctypes to mutate tracebacks for ``strict_exception_groups=False``'s exception collapsing. (`#405 <https://github.com/python-trio/trio/issues/405>`__)\n- Fixed spelling error in Windows error code enum for ``ERROR_INVALID_PARAMETER``. (`#3166 <https://github.com/python-trio/trio/issues/3166>`__)\n- Publicly re-export ``__version__`` for type checking purposes. (`#3186 <https://github.com/python-trio/trio/issues/3186>`__)\n- The typing of :func:`trio.abc.HostnameResolver.getaddrinfo` has been corrected to\n  match that of the stdlib `socket.getaddrinfo`, which was updated in mypy 1.15 (via\n  a typeshed update) to include the possibility of ``tuple[int, bytes]`` for the\n  ``sockaddr`` field of the result. This happens in situations where Python was compiled\n  with ``--disable-ipv6``.\n\n  Additionally, the static typing of :func:`trio.to_thread.run_sync`,\n  :func:`trio.from_thread.run` and :func:`trio.from_thread.run_sync` has been\n  improved and should reflect the underlying function being run. (`#3201 <https://github.com/python-trio/trio/issues/3201>`__)\n\n\nTrio 0.28.0 (2024-12-25)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- :func:`inspect.iscoroutinefunction` and the like now give correct answers when\n  called on KI-protected functions. (`#2670 <https://github.com/python-trio/trio/issues/2670>`__)\n- Rework KeyboardInterrupt protection to track code objects, rather than frames,\n  as protected or not. The new implementation no longer needs to access\n  ``frame.f_locals`` dictionaries, so it won't artificially extend the lifetime of\n  local variables. Since KeyboardInterrupt protection is now imposed statically\n  (when a protected function is defined) rather than each time the function runs,\n  its previously-noticeable performance overhead should now be near zero.\n  The lack of a call-time wrapper has some other benefits as well:\n\n  * :func:`inspect.iscoroutinefunction` and the like now give correct answers when\n    called on KI-protected functions.\n\n  * Calling a synchronous KI-protected function no longer pushes an additional stack\n    frame, so tracebacks are clearer.\n\n  * A synchronous KI-protected function invoked from C code (such as a weakref\n    finalizer) is now guaranteed to start executing; previously there would be a brief\n    window in which KeyboardInterrupt could be raised before the protection was\n    established.\n\n  One minor drawback of the new approach is that multiple instances of the same\n  closure share a single KeyboardInterrupt protection state (because they share a\n  single code object). That means that if you apply\n  `@enable_ki_protection <trio.lowlevel.enable_ki_protection>` to some of them\n  and not others, you won't get the protection semantics you asked for. See the\n  documentation of `@enable_ki_protection <trio.lowlevel.enable_ki_protection>`\n  for more details and a workaround. (`#3108 <https://github.com/python-trio/trio/issues/3108>`__)\n- Rework foreign async generator finalization to track async generator\n  ids rather than mutating ``ag_frame.f_locals``. This fixes an issue\n  with the previous implementation: locals' lifetimes will no longer be\n  extended by materialization in the ``ag_frame.f_locals`` dictionary that\n  the previous finalization dispatcher logic needed to access to do its work. (`#3112 <https://github.com/python-trio/trio/issues/3112>`__)\n- Ensure that Pyright recognizes our underscore prefixed attributes for attrs classes. (`#3114 <https://github.com/python-trio/trio/issues/3114>`__)\n- Fix `trio.testing.RaisesGroup`'s typing. (`#3141 <https://github.com/python-trio/trio/issues/3141>`__)\n\n\nImproved documentation\n~~~~~~~~~~~~~~~~~~~~~~\n\n- Improve error message when run after gevent's monkey patching. (`#3087 <https://github.com/python-trio/trio/issues/3087>`__)\n- Document that :func:`trio.sleep_forever` is guaranteed to raise an exception now. (`#3113 <https://github.com/python-trio/trio/issues/3113>`__)\n\n\nRemovals without deprecations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove workaround for OpenSSL 1.1.1 DTLS ClientHello bug. (`#3097 <https://github.com/python-trio/trio/issues/3097>`__)\n- Drop support for Python 3.8. (`#3104 <https://github.com/python-trio/trio/issues/3104>`__) (`#3106 <https://github.com/python-trio/trio/issues/3106>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Switch to using :pep:`570` for positional-only arguments for `~trio.socket.SocketType`'s methods. (`#3094 <https://github.com/python-trio/trio/issues/3094>`__)\n- Improve type annotations in several places by removing `Any` usage. (`#3121 <https://github.com/python-trio/trio/issues/3121>`__)\n- Get and enforce 100% coverage (`#3159 <https://github.com/python-trio/trio/issues/3159>`__)\n\n\nTrio 0.27.0 (2024-10-17)\n------------------------\n\nBreaking changes\n~~~~~~~~~~~~~~~~\n\n- :func:`trio.move_on_after` and :func:`trio.fail_after` previously set the deadline relative to initialization time, instead of more intuitively upon entering the context manager. This might change timeouts if a program relied on this behavior. If you want to restore previous behavior you should instead use ``trio.move_on_at(trio.current_time() + ...)``.\n  flake8-async has a new rule to catch this, in case you're supporting older trio versions. See :ref:`ASYNC122`. (`#2512 <https://github.com/python-trio/trio/issues/2512>`__)\n\n\nFeatures\n~~~~~~~~\n\n- :meth:`CancelScope.relative_deadline` and :meth:`CancelScope.is_relative` added, as well as a ``relative_deadline`` parameter to ``__init__``. This allows initializing scopes ahead of time, but where the specified relative deadline doesn't count down until the scope is entered. (`#2512 <https://github.com/python-trio/trio/issues/2512>`__)\n- :class:`trio.Lock` and :class:`trio.StrictFIFOLock` will now raise :exc:`trio.BrokenResourceError` when :meth:`trio.Lock.acquire` would previously stall due to the owner of the lock exiting without releasing the lock. (`#3035 <https://github.com/python-trio/trio/issues/3035>`__)\n- `trio.move_on_at`, `trio.move_on_after`, `trio.fail_at` and `trio.fail_after` now accept *shield* as a keyword argument. If specified, it provides an initial value for the `~trio.CancelScope.shield` attribute of the `trio.CancelScope` object created by the context manager. (`#3052 <https://github.com/python-trio/trio/issues/3052>`__)\n- Added :func:`trio.lowlevel.add_parking_lot_breaker` and :func:`trio.lowlevel.remove_parking_lot_breaker` to allow creating custom lock/semaphore implementations that will break their underlying parking lot if a task exits unexpectedly. :meth:`trio.lowlevel.ParkingLot.break_lot` is also added, to allow breaking a parking lot intentionally. (`#3081 <https://github.com/python-trio/trio/issues/3081>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Allow sockets to bind any ``os.PathLike`` object. (`#3041 <https://github.com/python-trio/trio/issues/3041>`__)\n- Update ``trio.lowlevel.open_process``'s documentation to allow bytes. (`#3076 <https://github.com/python-trio/trio/issues/3076>`__)\n- Update :func:`trio.sleep_forever` to be `NoReturn`. (`#3095 <https://github.com/python-trio/trio/issues/3095>`__)\n\n\nImproved documentation\n~~~~~~~~~~~~~~~~~~~~~~\n\n- Add docstrings for memory channels' ``statistics()`` and ``aclose`` methods. (`#3101 <https://github.com/python-trio/trio/issues/3101>`__)\n\n\nTrio 0.26.2 (2024-08-08)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Remove remaining ``hash`` usage and fix test configuration issue that prevented it from being caught. (`#3053 <https://github.com/python-trio/trio/issues/3053>`__)\n\n\nTrio 0.26.1 (2024-08-05)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Switched ``attrs`` usage off of ``hash``, which is now deprecated. (`#3053 <https://github.com/python-trio/trio/issues/3053>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Use PyPI's Trusted Publishers to make releases. (`#2980 <https://github.com/python-trio/trio/issues/2980>`__)\n\n\nTrio 0.26.0 (2024-07-05)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Added an interactive interpreter ``python -m trio``.\n\n  This makes it easier to try things and experiment with trio in the a Python repl.\n  Use the ``await`` keyword without needing to call ``trio.run()``\n\n  .. code-block:: console\n\n     $ python -m trio\n     Trio 0.21.0+dev, Python 3.10.6\n     Use \"await\" directly instead of \"trio.run()\".\n     Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n     >>> import trio\n     >>> await trio.sleep(1); print(\"hi\")  # prints after one second\n     hi\n\n  See :ref:`interactive debugging` for further detail. (`#2972 <https://github.com/python-trio/trio/issues/2972>`__)\n- :class:`trio.testing.RaisesGroup` can now catch an unwrapped exception with ``unwrapped=True``. This means that the behaviour of :ref:`except* <except_star>` can be fully replicated in combination with ``flatten_subgroups=True`` (formerly ``strict=False``). (`#2989 <https://github.com/python-trio/trio/issues/2989>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fixed a bug where :class:`trio.testing.RaisesGroup(..., strict=False) <trio.testing.RaisesGroup>` would check the number of exceptions in the raised `ExceptionGroup` before flattening subgroups, leading to incorrectly failed matches.\n  It now properly supports end (``$``) regex markers in the ``match`` message, by no longer including \" (x sub-exceptions)\" in the string it matches against. (`#2989 <https://github.com/python-trio/trio/issues/2989>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Deprecated ``strict`` parameter from :class:`trio.testing.RaisesGroup`, previous functionality of ``strict=False`` is now in ``flatten_subgroups=True``. (`#2989 <https://github.com/python-trio/trio/issues/2989>`__)\n\n\nTrio 0.25.1 (2024-05-16)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Fix crash when importing trio in embedded Python on Windows, and other installs that remove docstrings. (`#2987 <https://github.com/python-trio/trio/issues/2987>`__)\n\n\nTrio 0.25.0 (2024-03-17)\n------------------------\n\nBreaking changes\n~~~~~~~~~~~~~~~~\n\n- The :ref:`strict_exception_groups <strict_exception_groups>` parameter now defaults to `True` in `trio.run` and `trio.lowlevel.start_guest_run`. `trio.open_nursery` still defaults to the same value as was specified in `trio.run`/`trio.lowlevel.start_guest_run`, but if you didn't specify it there then all subsequent calls to `trio.open_nursery` will change.\n  This is unfortunately very tricky to change with a deprecation period, as raising a `DeprecationWarning` whenever :ref:`strict_exception_groups <strict_exception_groups>` is not specified would raise a lot of unnecessary warnings.\n\n  Notable side effects of changing code to run with ``strict_exception_groups==True``\n\n  * If an iterator raises `StopAsyncIteration` or `StopIteration` inside a nursery, then python will not recognize wrapped instances of those for stopping iteration.\n  * `trio.run_process` is now documented that it can raise an `ExceptionGroup`. It previously could do this in very rare circumstances, but with :ref:`strict_exception_groups <strict_exception_groups>` set to `True` it will now do so whenever exceptions occur in ``deliver_cancel`` or with problems communicating with the subprocess.\n\n    * Errors in opening the process is now done outside the internal nursery, so if code previously ran with ``strict_exception_groups=True`` there are cases now where an `ExceptionGroup` is *no longer* added.\n  * `trio.TrioInternalError` ``.__cause__`` might be wrapped in one or more `ExceptionGroups <ExceptionGroup>` (`#2786 <https://github.com/python-trio/trio/issues/2786>`__)\n\n\nFeatures\n~~~~~~~~\n\n- Add `trio.testing.wait_all_threads_completed`, which blocks until no threads are running tasks. This is intended to be used in the same way as `trio.testing.wait_all_tasks_blocked`. (`#2937 <https://github.com/python-trio/trio/issues/2937>`__)\n- :class:`Path` is now a subclass of :class:`pathlib.PurePath`, allowing it to interoperate with other standard\n  :mod:`pathlib` types.\n\n  Instantiating :class:`Path` now returns a concrete platform-specific subclass, one of :class:`PosixPath` or\n  :class:`WindowsPath`, matching the behavior of :class:`pathlib.Path`. (`#2959 <https://github.com/python-trio/trio/issues/2959>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- The pthread functions are now correctly found on systems using vanilla versions of musl libc. (`#2939 <https://github.com/python-trio/trio/issues/2939>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- use the regular readme for the PyPI long_description (`#2866 <https://github.com/python-trio/trio/issues/2866>`__)\n\n\nTrio 0.24.0 (2024-01-10)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- New helper classes: :class:`~.testing.RaisesGroup` and :class:`~.testing.Matcher`.\n\n  In preparation for changing the default of ``strict_exception_groups`` to `True`, we're introducing a set of helper classes that can be used in place of `pytest.raises <https://docs.pytest.org/en/stable/reference/reference.html#pytest.raises>`_ in tests, to check for an expected `ExceptionGroup`.\n  These are provisional, and only planned to be supplied until there's a good solution in ``pytest``. See https://github.com/pytest-dev/pytest/issues/11538 (`#2785 <https://github.com/python-trio/trio/issues/2785>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ``MultiError`` has been fully removed, and all relevant trio functions now raise ExceptionGroups instead. This should not affect end users that have transitioned to using ``except*`` or catching ExceptionGroup/BaseExceptionGroup. (`#2891 <https://github.com/python-trio/trio/issues/2891>`__)\n\n\nTrio 0.23.2 (2023-12-14)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- `TypeVarTuple <https://docs.python.org/3.12/library/typing.html#typing.TypeVarTuple>`_ is now used to fully type :meth:`nursery.start_soon() <trio.Nursery.start_soon>`, :func:`trio.run`, :func:`trio.to_thread.run_sync`, and other similar functions accepting ``(func, *args)``. This means type checkers will be able to verify types are used correctly. :meth:`nursery.start() <trio.Nursery.start>` is not fully typed yet however. (`#2881 <https://github.com/python-trio/trio/issues/2881>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Make pyright recognize :func:`open_memory_channel` as generic. (`#2873 <https://github.com/python-trio/trio/issues/2873>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Moved the metadata into :pep:`621`-compliant :file:`pyproject.toml`. (`#2860 <https://github.com/python-trio/trio/issues/2860>`__)\n- do not depend on exceptiongroup pre-release (`#2861 <https://github.com/python-trio/trio/issues/2861>`__)\n- Move .coveragerc into pyproject.toml (`#2867 <https://github.com/python-trio/trio/issues/2867>`__)\n\n\nTrio 0.23.1 (2023-11-04)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Don't crash on import in Anaconda interpreters. (`#2855 <https://github.com/python-trio/trio/issues/2855>`__)\n\n\nTrio 0.23.0 (2023-11-03)\n------------------------\n\nHeadline features\n~~~~~~~~~~~~~~~~~\n\n- Add type hints. (`#543 <https://github.com/python-trio/trio/issues/543>`__)\n\n\nFeatures\n~~~~~~~~\n\n- When exiting a nursery block, the parent task always waits for child\n  tasks to exit. This wait cannot be cancelled. However, previously, if\n  you tried to cancel it, it *would* inject a `Cancelled` exception,\n  even though it wasn't cancelled. Most users probably never noticed\n  either way, but injecting a `Cancelled` here is not really useful, and\n  in some rare cases caused confusion or problems, so Trio no longer\n  does that. (`#1457 <https://github.com/python-trio/trio/issues/1457>`__)\n- If called from a thread spawned by `trio.to_thread.run_sync`, `trio.from_thread.run` and\n  `trio.from_thread.run_sync` now reuse the task and cancellation status of the host task;\n  this means that context variables and cancel scopes naturally propagate 'through'\n  threads spawned by Trio. You can also use `trio.from_thread.check_cancelled`\n  to efficiently check for cancellation without reentering the Trio thread. (`#2392 <https://github.com/python-trio/trio/issues/2392>`__)\n- :func:`trio.lowlevel.start_guest_run` now does a bit more setup of the guest run\n  before it returns to its caller, so that the caller can immediately make calls to\n  :func:`trio.current_time`, :func:`trio.lowlevel.spawn_system_task`,\n  :func:`trio.lowlevel.current_trio_token`, etc. (`#2696 <https://github.com/python-trio/trio/issues/2696>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- When a starting function raises before calling :func:`trio.TaskStatus.started`,\n  :func:`trio.Nursery.start` will no longer wrap the exception in an undocumented\n  :exc:`ExceptionGroup`. Previously, :func:`trio.Nursery.start` would incorrectly\n  raise an :exc:`ExceptionGroup` containing it when using ``trio.run(...,\n  strict_exception_groups=True)``. (`#2611 <https://github.com/python-trio/trio/issues/2611>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- To better reflect the underlying thread handling semantics,\n  the keyword argument for `trio.to_thread.run_sync` that was\n  previously called ``cancellable`` is now named ``abandon_on_cancel``.\n  It still does the same thing -- allow the thread to be abandoned\n  if the call to `trio.to_thread.run_sync` is cancelled -- but since we now\n  have other ways to propagate a cancellation without abandoning\n  the thread, \"cancellable\" has become somewhat of a misnomer.\n  The old ``cancellable`` name is now deprecated. (`#2841 <https://github.com/python-trio/trio/issues/2841>`__)\n- Deprecated support for ``math.inf`` for the ``backlog`` argument in ``open_tcp_listeners``, making its docstring correct in the fact that only ``TypeError`` is raised if invalid arguments are passed. (`#2842 <https://github.com/python-trio/trio/issues/2842>`__)\n\n\nRemovals without deprecations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Drop support for Python3.7 and PyPy3.7/3.8. (`#2668 <https://github.com/python-trio/trio/issues/2668>`__)\n- Removed special ``MultiError`` traceback handling for IPython. As of `version 8.15 <https://ipython.readthedocs.io/en/stable/whatsnew/version8.html#ipython-8-15>`_ `ExceptionGroup` is handled natively. (`#2702 <https://github.com/python-trio/trio/issues/2702>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Trio now indicates its presence to `sniffio` using the ``sniffio.thread_local``\n  interface that is preferred since sniffio v1.3.0. This should be less likely\n  than the previous approach to cause :func:`sniffio.current_async_library` to\n  return incorrect results due to unintended inheritance of contextvars. (`#2700 <https://github.com/python-trio/trio/issues/2700>`__)\n- On windows, if SIO_BASE_HANDLE failed and SIO_BSP_HANDLE_POLL didn't return a different socket, runtime error will now raise from the OSError that indicated the issue so that in the event it does happen it might help with debugging. (`#2807 <https://github.com/python-trio/trio/issues/2807>`__)\n\n\nTrio 0.22.2 (2023-07-13)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Fix ``PermissionError`` when importing `trio` due to trying to access ``pthread``. (`#2688 <https://github.com/python-trio/trio/issues/2688>`__)\n\n\nTrio 0.22.1 (2023-07-02)\n------------------------\n\nBreaking changes\n~~~~~~~~~~~~~~~~\n\n- Timeout functions now raise `ValueError` if passed `math.nan`. This includes `trio.sleep`, `trio.sleep_until`, `trio.move_on_at`, `trio.move_on_after`, `trio.fail_at` and `trio.fail_after`. (`#2493 <https://github.com/python-trio/trio/issues/2493>`__)\n\n\nFeatures\n~~~~~~~~\n\n- Added support for naming threads created with `trio.to_thread.run_sync`, requires pthreads so is only available on POSIX platforms with glibc installed. (`#1148 <https://github.com/python-trio/trio/issues/1148>`__)\n- `trio.socket.socket` now prints the address it tried to connect to upon failure. (`#1810 <https://github.com/python-trio/trio/issues/1810>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fixed a crash that can occur when running Trio within an embedded Python interpreter, by handling the `TypeError` that is raised when trying to (re-)install a C signal handler. (`#2333 <https://github.com/python-trio/trio/issues/2333>`__)\n- Fix :func:`sniffio.current_async_library` when Trio tasks are spawned from a non-Trio context (such as when using trio-asyncio). Previously, a regular Trio task would inherit the non-Trio library name, and spawning a system task would cause the non-Trio caller to start thinking it was Trio. (`#2462 <https://github.com/python-trio/trio/issues/2462>`__)\n- Issued a new release as in the git tag for 0.22.0, ``trio.__version__`` is incorrectly set to 0.21.0+dev. (`#2485 <https://github.com/python-trio/trio/issues/2485>`__)\n\n\nImproved documentation\n~~~~~~~~~~~~~~~~~~~~~~\n\n- Documented that :obj:`Nursery.start_soon` does not guarantee task ordering. (`#970 <https://github.com/python-trio/trio/issues/970>`__)\n\n\nTrio 0.22.0 (2022-09-28)\n------------------------\n\nHeadline features\n~~~~~~~~~~~~~~~~~\n\n- ``MultiError`` has been deprecated in favor of the standard :exc:`BaseExceptionGroup`\n  (introduced in :pep:`654`). On Python versions below 3.11, this exception and its\n  derivative :exc:`ExceptionGroup` are provided by the backport_. Trio still raises\n  ``MultiError``, but it has been refactored into a subclass of :exc:`BaseExceptionGroup`\n  which users should catch instead of ``MultiError``. Uses of the ``MultiError.filter()``\n  class method should be replaced with :meth:`BaseExceptionGroup.split`. Uses of the\n  ``MultiError.catch()`` class method should be replaced with either ``except*`` clauses\n  (on Python 3.11+) or the ``exceptiongroup.catch()`` context manager provided by the\n  backport_.\n\n  See the :ref:`updated documentation <exceptiongroups>` for details.\n  (`#2211 <https://github.com/python-trio/trio/issues/2211>`__)\n\n  .. _backport: https://pypi.org/project/exceptiongroup/\n\n\nFeatures\n~~~~~~~~\n\n- Added support for `Datagram TLS\n  <https://en.wikipedia.org/wiki/Datagram_Transport_Layer_Security>`__,\n  for secure communication over UDP. Currently requires `PyOpenSSL\n  <https://pypi.org/p/pyopenssl>`__. (`#2010 <https://github.com/python-trio/trio/issues/2010>`__)\n\n\nTrio 0.21.0 (2022-06-07)\n----------------------------\n\nFeatures\n~~~~~~~~\n\n- Trio now supports Python 3.11. (`#2270\n  <https://github.com/python-trio/trio/issues/2270>`__, `#2318\n  <https://github.com/python-trio/trio/issues/2318>`__, `#2319\n  <https://github.com/python-trio/trio/issues/2319>`__)\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove support for Python 3.6. (`#2210 <https://github.com/python-trio/trio/issues/2210>`__)\n\n\nTrio 0.20.0 (2022-02-21)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- You can now conveniently spawn a child process in a background task\n  and interact it with on the fly using ``process = await\n  nursery.start(run_process, ...)``. See `run_process` for more details.\n  We recommend most users switch to this new API. Also note that:\n\n  - ``trio.open_process`` has been deprecated in favor of\n    `trio.lowlevel.open_process`,\n  - The ``aclose`` method on `Process` has been deprecated along with\n    ``async with process_obj``. (`#1104 <https://github.com/python-trio/trio/issues/1104>`__)\n- Now context variables set with `contextvars` are preserved when running functions\n  in a worker thread with `trio.to_thread.run_sync`, or when running\n  functions from the worker thread in the parent Trio thread with\n  `trio.from_thread.run`, and `trio.from_thread.run_sync`.\n  This is done by automatically copying the `contextvars` context.\n  `trio.lowlevel.spawn_system_task` now also receives an optional ``context`` argument. (`#2160 <https://github.com/python-trio/trio/issues/2160>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Trio now avoids creating cyclic garbage when a ``MultiError`` is generated and\n  filtered, including invisibly within the cancellation system. This means errors raised\n  through nurseries and cancel scopes should result in less GC latency. (`#2063 <https://github.com/python-trio/trio/issues/2063>`__)\n- Trio now deterministically cleans up file descriptors that were opened before\n  subprocess creation fails. Previously, they would remain open until the next run of\n  the garbage collector. (`#2193 <https://github.com/python-trio/trio/issues/2193>`__)\n- Add compatibility with OpenSSL 3.0 on newer Python and PyPy versions by working\n  around ``SSLEOFError`` not being raised properly. (`#2203 <https://github.com/python-trio/trio/issues/2203>`__)\n- Fix a bug that could cause `Process.wait` to hang on Linux systems using pidfds, if\n  another task were to access `Process.returncode` after the process exited but before\n  ``wait`` woke up (`#2209 <https://github.com/python-trio/trio/issues/2209>`__)\n\n\nTrio 0.19.0 (2021-06-15)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Trio now supports Python 3.10. (`#1921 <https://github.com/python-trio/trio/issues/1921>`__)\n- Use slots for :class:`~.lowlevel.Task` which should make them slightly smaller and faster. (`#1927 <https://github.com/python-trio/trio/issues/1927>`__)\n- Make :class:`~.Event` more lightweight by using less objects (about 2 rather\n  than 5, including a nested ParkingLot and attribute dicts) and simpler\n  structures (set rather than OrderedDict).  This may benefit applications that\n  create a large number of event instances, such as with the \"replace event\n  object on every set()\" idiom. (`#1948 <https://github.com/python-trio/trio/issues/1948>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- The event loop now holds on to references of coroutine frames for only\n  the minimum necessary period of time. (`#1864 <https://github.com/python-trio/trio/issues/1864>`__)\n- The :class:`~.lowlevel.TrioToken` class can now be used as a target of a weak reference. (`#1924 <https://github.com/python-trio/trio/issues/1924>`__)\n\n\nTrio 0.18.0 (2021-01-11)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Add synchronous ``.close()`` methods and context manager (``with x``) support\n  for `.MemorySendChannel` and `.MemoryReceiveChannel`. (`#1797 <https://github.com/python-trio/trio/issues/1797>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Previously, on Windows, Trio programs using thousands of sockets at the same time could trigger extreme slowdowns in the Windows kernel. Now, Trio works around this issue, so you should be able to use as many sockets as you want. (`#1280 <https://github.com/python-trio/trio/issues/1280>`__)\n- :func:`trio.from_thread.run` no longer crashes the Trio run if it is\n  executed after the system nursery has been closed but before the run\n  has finished. Calls made at this time will now raise\n  `trio.RunFinishedError`.  This fixes a regression introduced in\n  Trio 0.17.0.  The window in question is only one scheduler tick long in\n  most cases, but may be longer if async generators need to be cleaned up. (`#1738 <https://github.com/python-trio/trio/issues/1738>`__)\n- Fix a crash in pypy-3.7 (`#1765 <https://github.com/python-trio/trio/issues/1765>`__)\n- Trio now avoids creating cyclic garbage as often. This should have a\n  minimal impact on most programs, but can slightly reduce how often the\n  cycle collector GC runs on CPython, which can reduce latency spikes. (`#1770 <https://github.com/python-trio/trio/issues/1770>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove deprecated ``max_refill_bytes`` from :class:`SSLStream`. (`#959 <https://github.com/python-trio/trio/issues/959>`__)\n- Remove the deprecated ``tiebreaker`` argument to `trio.testing.wait_all_tasks_blocked`. (`#1558 <https://github.com/python-trio/trio/issues/1558>`__)\n- Remove the deprecated ``trio.hazmat`` module. (`#1722 <https://github.com/python-trio/trio/issues/1722>`__)\n- Stop allowing subclassing public classes. This behavior was deprecated in 0.15.0. (`#1726 <https://github.com/python-trio/trio/issues/1726>`__)\n\n\nTrio 0.17.0 (2020-09-15)\n------------------------\n\nHeadline features\n~~~~~~~~~~~~~~~~~\n\n- Trio now supports automatic :ref:`async generator finalization\n  <async-generators>`, so more async generators will work even if you\n  don't wrap them in ``async with async_generator.aclosing():``\n  blocks. Please see the documentation for important caveats; in\n  particular, yielding within a nursery or cancel scope remains\n  unsupported. (`#265 <https://github.com/python-trio/trio/issues/265>`__)\n\n\nFeatures\n~~~~~~~~\n\n- `trio.open_tcp_stream` has a new ``local_address=`` keyword argument\n  that can be used on machines with multiple IP addresses to control\n  which IP is used for the outgoing connection. (`#275 <https://github.com/python-trio/trio/issues/275>`__)\n- If you pass a raw IP address into ``sendto``, it no longer spends any\n  time trying to resolve the hostname. If you're using UDP, this should\n  substantially reduce your per-packet overhead. (`#1595 <https://github.com/python-trio/trio/issues/1595>`__)\n- `trio.lowlevel.checkpoint` is now much faster. (`#1613 <https://github.com/python-trio/trio/issues/1613>`__)\n- We switched to a new, lower-overhead data structure to track upcoming\n  timeouts, which should make your programs faster. (`#1629 <https://github.com/python-trio/trio/issues/1629>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- On macOS and BSDs, explicitly close our wakeup socketpair when we're\n  done with it. (`#1621 <https://github.com/python-trio/trio/issues/1621>`__)\n- Trio can now be imported when `sys.excepthook` is a `functools.partial` instance, which might occur in a\n  ``pytest-qt`` test function. (`#1630 <https://github.com/python-trio/trio/issues/1630>`__)\n- The thread cache didn't release its reference to the previous job. (`#1638 <https://github.com/python-trio/trio/issues/1638>`__)\n- On Windows, Trio now works around the buggy behavior of certain\n  Layered Service Providers (system components that can intercept\n  network activity) that are built on top of a commercially available\n  library called Komodia Redirector. This benefits users of products\n  such as Astrill VPN and Qustodio parental controls. Previously, Trio\n  would crash on startup when run on a system where such a product was\n  installed. (`#1659 <https://github.com/python-trio/trio/issues/1659>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove ``wait_socket_*``, ``notify_socket_closing``, ``notify_fd_closing``, ``run_sync_in_worker_thread`` and ``current_default_worker_thread_limiter``. They were deprecated in 0.12.0. (`#1596 <https://github.com/python-trio/trio/issues/1596>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- When using :ref:`instruments <instrumentation>`, you now only \"pay for what you use\":\n  if there are no instruments installed that override a particular hook such as\n  :meth:`~trio.abc.Instrument.before_task_step`, then Trio doesn't waste any effort\n  on checking its instruments when the event corresponding to that hook occurs.\n  Previously, installing any instrument would incur all the instrumentation overhead,\n  even for hooks no one was interested in. (`#1340 <https://github.com/python-trio/trio/issues/1340>`__)\n\n\nTrio 0.16.0 (2020-06-10)\n------------------------\n\nHeadline features\n~~~~~~~~~~~~~~~~~\n\n- If you want to use Trio, but are stuck with some other event loop like\n  Qt or PyGame, then good news: now you can have both. For details, see:\n  :ref:`guest-mode`. (`#399 <https://github.com/python-trio/trio/issues/399>`__)\n\n\nFeatures\n~~~~~~~~\n\n- To speed up `trio.to_thread.run_sync`, Trio now caches and reuses\n  worker threads.\n\n  And in case you have some exotic use case where you need to spawn\n  threads manually, but want to take advantage of Trio's cache, you can\n  do that using the new `trio.lowlevel.start_thread_soon`. (`#6 <https://github.com/python-trio/trio/issues/6>`__)\n- Tasks spawned with `nursery.start() <trio.Nursery.start>` aren't treated as\n  direct children of their nursery until they call ``task_status.started()``.\n  This is visible through the task tree introspection attributes such as\n  `Task.parent_nursery <trio.lowlevel.Task.parent_nursery>`. Sometimes, though,\n  you want to know where the task is going to wind up, even if it hasn't finished\n  initializing yet. To support this, we added a new attribute\n  `Task.eventual_parent_nursery <trio.lowlevel.Task.eventual_parent_nursery>`.\n  For a task spawned with :meth:`~trio.Nursery.start` that hasn't yet called\n  ``started()``, this is the nursery that the task was nominally started in,\n  where it will be running once it finishes starting up. In all other cases,\n  it is ``None``. (`#1558 <https://github.com/python-trio/trio/issues/1558>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Added a helpful error message if an async function is passed to `trio.to_thread.run_sync`. (`#1573 <https://github.com/python-trio/trio/issues/1573>`__)\n\n\nDeprecations and removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove ``BlockingTrioPortal``: it was deprecated in 0.12.0. (`#1574 <https://github.com/python-trio/trio/issues/1574>`__)\n- The ``tiebreaker`` argument to `trio.testing.wait_all_tasks_blocked`\n  has been deprecated. This is a highly obscure feature that was\n  probably never used by anyone except `trio.testing.MockClock`, and\n  `~trio.testing.MockClock` doesn't need it anymore. (`#1587 <https://github.com/python-trio/trio/issues/1587>`__)\n- Remove the deprecated ``trio.ssl`` and ``trio.subprocess`` modules. (`#1594 <https://github.com/python-trio/trio/issues/1594>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- We refactored `trio.testing.MockClock` so that it no longer needs to\n  run an internal task to manage autojumping. This should be mostly\n  invisible to users, but there is one semantic change: the interaction\n  between `trio.testing.wait_all_tasks_blocked` and the autojump clock\n  was fixed. Now, the autojump will always wait until after all\n  `~trio.testing.wait_all_tasks_blocked` calls have finished before\n  firing, instead of it depending on which threshold values you passed. (`#1587 <https://github.com/python-trio/trio/issues/1587>`__)\n\n\nTrio 0.15.1 (2020-05-22)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- Fix documentation build. (This must be a new release tag to get readthedocs\n  \"stable\" to include the changes from 0.15.0.)\n\n- Added a helpful error message if an async function is passed to `trio.from_thread.run_sync` or a sync function to `trio.from_thread.run`. (`#1244 <https://github.com/python-trio/trio/issues/1244>`__)\n\n\nTrio 0.15.0 (2020-05-19)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Previously, when `trio.run_process` was cancelled, it always killed\n  the subprocess immediately. Now, on Unix, it first gives the process a\n  chance to clean up by sending ``SIGTERM``, and only escalates to\n  ``SIGKILL`` if the process is still running after 5 seconds. But if\n  you prefer the old behavior, or want to adjust the timeout, then don't\n  worry: you can now pass a custom ``deliver_cancel=`` argument to\n  define your own process killing policy. (`#1104 <https://github.com/python-trio/trio/issues/1104>`__)\n- It turns out that creating a subprocess can block the parent process\n  for a surprisingly long time. So ``trio.open_process`` now uses a worker\n  thread to avoid blocking the event loop. (`#1109 <https://github.com/python-trio/trio/issues/1109>`__)\n- We've added FreeBSD to the list of platforms we support and test on. (`#1118 <https://github.com/python-trio/trio/issues/1118>`__)\n- On Linux kernels v5.3 or newer, `trio.Process.wait` now uses `the\n  pidfd API <https://lwn.net/Articles/794707/>`__ to track child\n  processes. This shouldn't have any user-visible change, but it makes\n  working with subprocesses faster and use less memory. (`#1241 <https://github.com/python-trio/trio/issues/1241>`__)\n- The `trio.Process.returncode` attribute is now automatically updated\n  as needed, instead of only when you call `~trio.Process.poll` or\n  `~trio.Process.wait`. Also, ``repr(process_object)`` now always\n  contains up-to-date information about the process status. (`#1315 <https://github.com/python-trio/trio/issues/1315>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- On Ubuntu systems, the system Python includes a custom\n  unhandled-exception hook to perform `crash reporting\n  <https://wiki.ubuntu.com/Apport>`__. Unfortunately, Trio wants to use\n  the same hook to print nice ``MultiError`` tracebacks, causing a\n  conflict. Previously, Trio would detect the conflict, print a warning,\n  and you just wouldn't get nice ``MultiError`` tracebacks. Now, Trio has\n  gotten clever enough to integrate its hook with Ubuntu's, so the two\n  systems should Just Work together. (`#1065 <https://github.com/python-trio/trio/issues/1065>`__)\n- Fixed an over-strict test that caused failures on Alpine Linux.\n  Started testing against Alpine in CI. (`#1499 <https://github.com/python-trio/trio/issues/1499>`__)\n- Calling `open_signal_receiver` with no arguments used to succeed without listening for any signals. This was confusing, so now it raises TypeError instead. (`#1526 <https://github.com/python-trio/trio/issues/1526>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove support for Python 3.5. (`#75 <https://github.com/python-trio/trio/issues/75>`__)\n- It turns out that everyone got confused by the name ``trio.hazmat``.\n  So that name has been deprecated, and the new name is\n  :mod:`trio.lowlevel`. (`#476 <https://github.com/python-trio/trio/issues/476>`__)\n- Most of the public classes that Trio exports – like `trio.Lock`,\n  `trio.SocketStream`, and so on – weren't designed with subclassing in\n  mind. And we've noticed that some users were trying to subclass them\n  anyway, and ending up with fragile code that we're likely to\n  accidentally break in the future, or else be stuck unable to make\n  changes for fear of breaking subclasses.\n\n  There are also some classes that were explicitly designed to be\n  subclassed, like the ones in ``trio.abc``. Subclassing these is still\n  supported. However, for all other classes, attempts to subclass will\n  now raise a deprecation warning, and in the future will raise an\n  error.\n\n  If this causes problems for you, feel free to drop by our `chat room\n  <https://gitter.im/python-trio/general>`__ or file a bug, to discuss\n  alternatives or make a case for why some particular class should be\n  designed to support subclassing. (`#1044 <https://github.com/python-trio/trio/issues/1044>`__)\n- If you want to create a `trio.Process` object, you now have to call\n  ``trio.open_process``; calling ``trio.Process()`` directly was\n  deprecated in v0.12.0 and has now been removed. (`#1109 <https://github.com/python-trio/trio/issues/1109>`__)\n- Remove ``clear`` method on `trio.Event`: it was deprecated in 0.12.0. (`#1498 <https://github.com/python-trio/trio/issues/1498>`__)\n\n\nTrio 0.14.0 (2020-04-27)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- If you're using Trio's low-level interfaces like\n  `trio.hazmat.wait_readable <trio.lowlevel.wait_readable>` or similar, and then you close a socket or\n  file descriptor, you're supposed to call `trio.hazmat.notify_closing\n  <trio.lowlevel.notify_closing>`\n  first so Trio can clean up properly. But what if you forget? In the\n  past, Trio would tend to either deadlock or explode spectacularly.\n  Now, it's much more robust to this situation, and should generally\n  survive. (But note that \"survive\" is not the same as \"give you the\n  results you were expecting\", so you should still call\n  `~trio.lowlevel.notify_closing` when appropriate. This is about harm\n  reduction and making it easier to debug this kind of mistake, not\n  something you should rely on.)\n\n  If you're using higher-level interfaces outside of the `trio.hazmat <trio.lowlevel>`\n  module, then you don't need to worry about any of this; those\n  interfaces already take care of calling `~trio.lowlevel.notify_closing`\n  for you. (`#1272 <https://github.com/python-trio/trio/issues/1272>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- A bug related to the following methods has been introduced in version 0.12.0:\n\n  - `trio.Path.iterdir`\n  - `trio.Path.glob`\n  - `trio.Path.rglob`\n\n  The iteration of the blocking generators produced by pathlib was performed in\n  the trio thread. With this fix, the previous behavior is restored: the blocking\n  generators are converted into lists in a thread dedicated to blocking IO calls. (`#1308 <https://github.com/python-trio/trio/issues/1308>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Deprecate Python 3.5 (`#1408 <https://github.com/python-trio/trio/pull/1408>`__)\n- Remove ``trio.open_cancel_scope`` which was deprecated in 0.11.0. (`#1458 <https://github.com/python-trio/trio/issues/1458>`__)\n\n\nTrio 0.13.0 (2019-11-02)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- On Windows, the `IOCP subsystem\n  <https://docs.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports>`__\n  is generally the best way to implement async I/O operations – but it's\n  historically been weak at providing ``select``\\-style readiness\n  notifications, like `trio.hazmat.wait_readable <trio.lowlevel.wait_readable>` and\n  `~trio.lowlevel.wait_writable`. We aren't willing to give those up, so\n  previously Trio's Windows backend used a hybrid of ``select`` + IOCP.\n  This was complex, slow, and had `limited scalability\n  <https://github.com/python-trio/trio/issues/3>`__.\n\n  Fortunately, we found a way to implement ``wait_*`` with IOCP, so\n  Trio's Windows backend has been completely rewritten, and now uses\n  IOCP exclusively. As a user, the only difference you should notice is\n  that Trio should now be faster on Windows, and can handle many more\n  sockets. This also simplified the code internally, which should allow\n  for more improvements in the future.\n\n  However, this is somewhat experimental, so if you use Windows then\n  please keep an eye out and let us know if you run into any problems! (`#52 <https://github.com/python-trio/trio/issues/52>`__)\n- Use slots for memory channel state and statistics which should make memory channels slightly smaller and faster. (`#1195 <https://github.com/python-trio/trio/issues/1195>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- OpenSSL has a bug in its handling of TLS 1.3 session tickets that can\n  cause deadlocks or data loss in some rare edge cases. These edge cases\n  most frequently happen during tests. (Upstream bug reports: `openssl/openssl#7948\n  <https://github.com/openssl/openssl/issues/7948>`__, `openssl/openssl#7967\n  <https://github.com/openssl/openssl/issues/7967>`__.) `trio.SSLStream`\n  now works around this issue, so you don't have to worry about it. (`#819 <https://github.com/python-trio/trio/issues/819>`__)\n- Trio now uses `signal.set_wakeup_fd` on all platforms. This is mostly\n  an internal refactoring with no user-visible effect, but in theory it\n  should fix a few extremely-rare race conditions on Unix that could\n  have caused signal delivery to be delayed. (`#109 <https://github.com/python-trio/trio/issues/109>`__)\n- Trio no longer crashes when an async function is implemented in C or\n  Cython and then passed directly to `trio.run` or\n  ``nursery.start_soon``. (`#550 <https://github.com/python-trio/trio/issues/550>`__, `#1191 <https://github.com/python-trio/trio/issues/1191>`__)\n- When a Trio task makes improper use of a non-Trio async library, Trio now causes an exception to be raised within the task at the point of the error, rather than abandoning the task and raising an error in its parent. This improves debuggability and resolves the `TrioInternalError` that would sometimes result from the old strategy. (`#552 <https://github.com/python-trio/trio/issues/552>`__)\n- In 0.12.0 we deprecated ``trio.run_sync_in_worker_thread`` in favor of\n  `trio.to_thread.run_sync`. But, the deprecation message listed the\n  wrong name for the replacement. The message now gives the correct name. (`#810 <https://github.com/python-trio/trio/issues/810>`__)\n- Fix regression introduced with cancellation changes in 0.12.0, where a\n  `trio.CancelScope` which isn't cancelled could catch a propagating\n  `trio.Cancelled` exception if shielding were changed while the\n  cancellation was propagating. (`#1175 <https://github.com/python-trio/trio/issues/1175>`__)\n- Fix a crash that could happen when using ``MockClock`` with autojump\n  enabled and a non-zero rate. (`#1190 <https://github.com/python-trio/trio/issues/1190>`__)\n- If you nest >1000 cancel scopes within each other, Trio now handles\n  that gracefully instead of crashing with a ``RecursionError``. (`#1235 <https://github.com/python-trio/trio/issues/1235>`__)\n- Fixed the hash behavior of `trio.Path` to match `pathlib.Path`. Previously `trio.Path`'s hash was inherited from `object` instead of from `pathlib.PurePath`. Thus, hashing two `trio.Path`\\'s or a `trio.Path` and a `pathlib.Path` with the same underlying path would yield different results. (`#1259 <https://github.com/python-trio/trio/issues/1259>`__)\n\n\nTrio 0.12.1 (2019-08-01)\n------------------------\n\nBugfixes\n~~~~~~~~\n\n- In v0.12.0, we accidentally moved ``BlockingTrioPortal`` from ``trio``\n  to ``trio.hazmat``. It's now been restored to its proper position.\n  (It's still deprecated though, and will issue a warning if you use it.) (`#1167 <https://github.com/python-trio/trio/issues/1167>`__)\n\n\nTrio 0.12.0 (2019-07-31)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- If you have a `~trio.abc.ReceiveStream` object, you can now use\n  ``async for data in stream: ...`` instead of calling\n  `~trio.abc.ReceiveStream.receive_some`. Each iteration gives an\n  arbitrary sized chunk of bytes. And the best part is, the loop\n  automatically exits when you reach EOF, so you don't have to check for\n  it yourself anymore. Relatedly, you no longer need to pick a magic\n  buffer size value before calling\n  `~trio.abc.ReceiveStream.receive_some`; you can ``await\n  stream.receive_some()`` with no arguments, and the stream will\n  automatically pick a reasonable size for you. (`#959 <https://github.com/python-trio/trio/issues/959>`__)\n- Threading interfaces have been reworked:\n  ``run_sync_in_worker_thread`` is now `trio.to_thread.run_sync`, and\n  instead of ``BlockingTrioPortal``, use `trio.from_thread.run` and\n  `trio.from_thread.run_sync`. What's neat about this is that these\n  cooperate, so if you're in a thread created by `to_thread.run_sync`,\n  it remembers which Trio created it, and you can call\n  ``trio.from_thread.*`` directly without having to pass around a\n  ``BlockingTrioPortal`` object everywhere. (`#810 <https://github.com/python-trio/trio/issues/810>`__)\n- We cleaned up the distinction between the \"abstract channel interface\"\n  and the \"memory channel\" concrete implementation.\n  `trio.abc.SendChannel` and `trio.abc.ReceiveChannel` have been slimmed\n  down, `trio.MemorySendChannel` and `trio.MemoryReceiveChannel` are now\n  public types that can be used in type hints, and there's a new\n  `trio.abc.Channel` interface for future bidirectional channels. (`#719 <https://github.com/python-trio/trio/issues/719>`__)\n- Add :func:`trio.run_process` as a high-level helper for running a process\n  and waiting for it to finish, like the standard :func:`subprocess.run` does. (`#822 <https://github.com/python-trio/trio/issues/822>`__)\n- On Linux, when wrapping a bare file descriptor in a Trio socket object,\n  Trio now auto-detects the correct ``family``, ``type``, and ``protocol``.\n  This is useful, for example, when implementing `systemd socket activation\n  <http://0pointer.de/blog/projects/socket-activation.html>`__. (`#251 <https://github.com/python-trio/trio/issues/251>`__)\n- Trio sockets have a new method `~trio.socket.SocketType.is_readable` that allows\n  you to check whether a socket is readable. This is useful for HTTP/1.1 clients. (`#760 <https://github.com/python-trio/trio/issues/760>`__)\n- We no longer use runtime code generation to dispatch core functions\n  like `current_time`. Static analysis tools like mypy and pylint should\n  now be able to recognize and analyze all of Trio's top-level functions\n  (though some class attributes are still dynamic... we're working on it). (`#805 <https://github.com/python-trio/trio/issues/805>`__)\n- Add `trio.hazmat.FdStream <trio.lowlevel.FdStream>` for wrapping a Unix file descriptor as a `~trio.abc.Stream`. (`#829 <https://github.com/python-trio/trio/issues/829>`__)\n- Trio now gives a reasonable traceback and error message in most cases\n  when its invariants surrounding cancel scope nesting have been\n  violated. (One common source of such violations is an async generator\n  that yields within a cancel scope.) The previous behavior was an\n  inscrutable chain of TrioInternalErrors. (`#882 <https://github.com/python-trio/trio/issues/882>`__)\n- ``MultiError`` now defines its ``exceptions`` attribute in ``__init__()``\n  to better support linters and code autocompletion. (`#1066 <https://github.com/python-trio/trio/issues/1066>`__)\n- Use ``__slots__`` in more places internally, which should make Trio slightly faster. (`#984 <https://github.com/python-trio/trio/issues/984>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Destructor methods (``__del__``) are now protected against ``KeyboardInterrupt``. (`#676 <https://github.com/python-trio/trio/issues/676>`__)\n- The :class:`trio.Path` methods :meth:`~trio.Path.glob` and\n  :meth:`~trio.Path.rglob` now return iterables of :class:`trio.Path`\n  (not :class:`pathlib.Path`). (`#917 <https://github.com/python-trio/trio/issues/917>`__)\n- Inspecting the :attr:`~trio.CancelScope.cancel_called` attribute of a\n  not-yet-exited cancel scope whose deadline is in the past now always\n  returns ``True``, like you might expect. (Previously it would return\n  ``False`` for not-yet-entered cancel scopes, and for active cancel\n  scopes until the first checkpoint after their deadline expiry.) (`#958 <https://github.com/python-trio/trio/issues/958>`__)\n- The :class:`trio.Path` classmethods, :meth:`~trio.Path.home` and\n  :meth:`~trio.Path.cwd`, are now async functions.  Previously, a bug\n  in the forwarding logic meant :meth:`~trio.Path.cwd` was synchronous\n  and :meth:`~trio.Path.home` didn't work at all. (`#960 <https://github.com/python-trio/trio/issues/960>`__)\n- An exception encapsulated within a ``MultiError`` doesn't need to be\n  hashable anymore.\n\n  .. note::\n\n     This is only supported if you are running python >= 3.6.4. You can\n     refer to `this github PR <https://github.com/python/cpython/pull/4014>`_\n     for details. (`#1005 <https://github.com/python-trio/trio/issues/1005>`__)\n\n\nImproved Documentation\n~~~~~~~~~~~~~~~~~~~~~~\n\n- To help any user reading through Trio's function implementations, start using public names (not _core) whenever possible. (`#1017 <https://github.com/python-trio/trio/issues/1017>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- The ``clear`` method on `trio.Event` has been deprecated. (`#637 <https://github.com/python-trio/trio/issues/637>`__)\n- ``BlockingTrioPortal`` has been deprecated in favor of the new\n  `trio.from_thread`.  (`#810\n  <https://github.com/python-trio/trio/issues/810>`__)\n- ``run_sync_in_worker_thread`` is deprecated in favor of\n  `trio.to_thread.run_sync`.  (`#810\n  <https://github.com/python-trio/trio/issues/810>`__)\n- ``current_default_worker_thread_limiter`` is deprecated in favor of\n  `trio.to_thread.current_default_thread_limiter`. (`#810\n  <https://github.com/python-trio/trio/issues/810>`__)\n- Give up on trying to have different low-level waiting APIs on Unix and\n  Windows. All platforms now have `trio.hazmat.wait_readable <trio.lowlevel.wait_readable>`,\n  `trio.hazmat.wait_writable <trio.lowlevel.wait_writable>`, and\n  `trio.hazmat.notify_closing <trio.lowlevel.notify_closing>`. The old\n  platform-specific synonyms ``wait_socket_*``,\n  ``notify_socket_closing``, and ``notify_fd_closing`` have been\n  deprecated. (`#878 <https://github.com/python-trio/trio/issues/878>`__)\n- It turns out that it's better to treat subprocess spawning as an async\n  operation. Therefore, direct construction of `Process` objects has\n  been deprecated. Use ``trio.open_process`` instead. (`#1109 <https://github.com/python-trio/trio/issues/1109>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- The plumbing of Trio's cancellation system has been substantially overhauled\n  to improve performance and ease future planned improvements. Notably, there is\n  no longer any internal concept of a \"cancel stack\", and checkpoints now take\n  constant time regardless of the cancel scope nesting depth. (`#58 <https://github.com/python-trio/trio/issues/58>`__)\n- We've slightly relaxed our definition of which Trio operations act as\n  :ref:`checkpoints <checkpoint-rule>`. A Trio async function that exits by\n  throwing an exception is no longer guaranteed to execute a checkpoint;\n  it might or might not. The rules are unchanged for async functions that\n  don't exit with an exception, async iterators, and async context managers.\n  :func:`trio.testing.assert_checkpoints` has been updated to reflect the\n  new behavior: if its ``with`` block exits with an exception, no assertion\n  is made. (`#474 <https://github.com/python-trio/trio/issues/474>`__)\n- Calling ``str`` on a :exc:`trio.Cancelled` exception object returns \"Cancelled\" instead of an empty string. (`#674 <https://github.com/python-trio/trio/issues/674>`__)\n- Change the default timeout in :func:`trio.open_tcp_stream` to 0.250 seconds, for consistency with RFC 8305. (`#762 <https://github.com/python-trio/trio/issues/762>`__)\n- On win32 we no longer set SO_EXCLUSIVEADDRUSE when binding a socket in :exc:`trio.open_tcp_listeners`. (`#928 <https://github.com/python-trio/trio/issues/928>`__)\n- Any attempt to inherit from `CancelScope` or `Nursery` now raises\n  `TypeError`.  (Trio has never been able to safely support subclassing\n  here; this change just makes it more obvious.)\n  Also exposed as public classes for type-checking, etc. (`#1021 <https://github.com/python-trio/trio/issues/1021>`__)\n\n\nTrio 0.11.0 (2019-02-09)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Add support for \"unbound cancel scopes\": you can now construct a\n  :class:`trio.CancelScope` without entering its context, e.g., so you\n  can pass it to another task which will use it to wrap some work that\n  you want to be able to cancel from afar. (`#607 <https://github.com/python-trio/trio/issues/607>`__)\n- The test suite now passes with openssl v1.1.1. Unfortunately this\n  required temporarily disabling TLS v1.3 during tests; see openssl bugs\n  `#7948 <https://github.com/openssl/openssl/issues/7948>`__ and `#7967\n  <https://github.com/openssl/openssl/issues/7967>`__. We believe TLS\n  v1.3 should work in most real use cases, but will be monitoring the\n  situation. (`#817 <https://github.com/python-trio/trio/issues/817>`__)\n- Add :attr:`trio.Process.stdio`, which is a :class:`~trio.StapledStream` of\n  :attr:`~trio.Process.stdin` and :attr:`~trio.Process.stdout` if both of those\n  are available, and ``None`` otherwise. This is intended to make it more\n  ergonomic to speak a back-and-forth protocol with a subprocess. (`#862 <https://github.com/python-trio/trio/issues/862>`__)\n- :class:`trio.Process` on POSIX systems no longer accepts the error-prone\n  combination of ``shell=False`` with a ``command`` that's a single string,\n  or ``shell=True`` with a ``command`` that's a sequence of strings.\n  These forms are accepted by the underlying :class:`subprocess.Popen`\n  constructor but don't do what most users expect. Also, added an explanation\n  of :ref:`quoting <subprocess-quoting>` to the documentation. (`#863 <https://github.com/python-trio/trio/issues/863>`__)\n- Added an internal mechanism for pytest-trio's\n  `Hypothesis <https://hypothesis.readthedocs.io>`__ integration\n  to make the task scheduler reproducible and avoid flaky tests. (`#890 <https://github.com/python-trio/trio/issues/890>`__)\n- :class:`~trio.abc.SendChannel`, :class:`~trio.abc.ReceiveChannel`, :class:`~trio.abc.Listener`,\n  and :func:`~trio.open_memory_channel` can now be referenced using a generic type parameter\n  (the type of object sent over the channel or produced by the listener) using :pep:`484` syntax:\n  ``trio.abc.SendChannel[bytes]``, ``trio.abc.Listener[trio.SocketStream]``,\n  ``trio.open_memory_channel[MyMessage](5)``, etc. The added type information does not change\n  the runtime semantics, but permits better integration with external static type checkers. (`#908 <https://github.com/python-trio/trio/issues/908>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fixed several bugs in the new Unix subprocess pipe support, where\n  (a) operations on a closed pipe could accidentally affect another\n  unrelated pipe due to internal file-descriptor reuse, (b) in very rare\n  circumstances, two tasks calling ``send_all`` on the same pipe at the\n  same time could end up with intermingled data instead of a\n  :exc:`BusyResourceError`. (`#661 <https://github.com/python-trio/trio/issues/661>`__)\n- Stop :func:`trio.open_tcp_listeners` from crashing on systems that have\n  disabled IPv6. (`#853 <https://github.com/python-trio/trio/issues/853>`__)\n- Fixed support for multiple tasks calling :meth:`trio.Process.wait`\n  simultaneously; on kqueue platforms it would previously raise an exception. (`#854 <https://github.com/python-trio/trio/issues/854>`__)\n- :exc:`trio.Cancelled` exceptions now always propagate until they reach\n  the outermost unshielded cancelled scope, even if more cancellations\n  occur or shielding is changed between when the :exc:`~trio.Cancelled`\n  is delivered and when it is caught. (`#860 <https://github.com/python-trio/trio/issues/860>`__)\n- If you have a :class:`SocketStream` that's already been closed, then\n  ``await socket_stream.send_all(b\"\")`` will now correctly raise\n  :exc:`ClosedResourceError`. (`#874 <https://github.com/python-trio/trio/issues/874>`__)\n- Simplified the Windows subprocess pipe ``send_all`` code, and in the\n  process fixed a theoretical bug where closing a pipe at just the wrong\n  time could produce errors or cause data to be redirected to the wrong\n  pipe. (`#883 <https://github.com/python-trio/trio/issues/883>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Deprecate ``trio.open_cancel_scope`` in favor of :class:`trio.CancelScope`,\n  which more clearly reflects that creating a cancel scope is just an ordinary\n  object construction and does not need to be immediately paired with entering it. (`#607 <https://github.com/python-trio/trio/issues/607>`__)\n- The submodules ``trio.ssl`` and ``trio.subprocess`` are now deprecated.\n  Their nontrivial contents (:class:`~trio.Process`, :class:`~trio.SSLStream`,\n  and :class:`~trio.SSLListener`) have been moved to the main :mod:`trio`\n  namespace. For the numerous constants, exceptions, and other helpers\n  that were previously reexported from the standard :mod:`ssl` and\n  :mod:`subprocess` modules, you should now use those modules directly. (`#852 <https://github.com/python-trio/trio/issues/852>`__)\n- Remove all the APIs deprecated in 0.9.0 or earlier (``trio.Queue``,\n  ``trio.catch_signals()``, ``trio.BrokenStreamError``, and\n  ``trio.ResourceBusyError``), except for ``trio.hazmat.UnboundedQueue``,\n  which stays for now since it is used by the obscure lowlevel functions\n  ``monitor_completion_queue()`` and ``monitor_kevent()``. (`#918 <https://github.com/python-trio/trio/issues/918>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Entering a cancel scope whose deadline is in the past now immediately\n  cancels it, so :exc:`~trio.Cancelled` will be raised by the first\n  checkpoint in the cancel scope rather than the second one.\n  This also affects constructs like ``with trio.move_on_after(0):``. (`#320 <https://github.com/python-trio/trio/issues/320>`__)\n\n\nTrio 0.10.0 (2019-01-07)\n------------------------\n\nFeatures\n~~~~~~~~\n\n- Initial :ref:`subprocess support <subprocess>`. Add\n  :class:`trio.subprocess.Process <trio.Process>`, an async wrapper around the stdlib\n  :class:`subprocess.Popen` class, which permits spawning subprocesses and\n  communicating with them over standard Trio streams. ``trio.subprocess``\n  also reexports all the stdlib :mod:`subprocess` exceptions and constants for\n  convenience. (`#4 <https://github.com/python-trio/trio/issues/4>`__)\n- You can now create an unbounded :class:`CapacityLimiter` by initializing with\n  `math.inf` (`#618 <https://github.com/python-trio/trio/issues/618>`__)\n- New :mod:`trio.hazmat <trio.lowlevel>` features to allow cleanly switching live coroutine\n  objects between Trio and other coroutine runners. Frankly, we're not even\n  sure this is a good idea, but we want to `try it out in trio-asyncio\n  <https://github.com/python-trio/trio-asyncio/issues/42>`__, so here we are.\n  For details see :ref:`live-coroutine-handoff`. (`#649\n  <https://github.com/python-trio/trio/issues/649>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fixed a race condition on macOS, where Trio's TCP listener would crash if an\n  incoming TCP connection was closed before the listener had a chance to accept\n  it. (`#609 <https://github.com/python-trio/trio/issues/609>`__)\n- :func:`trio.open_tcp_stream` has been refactored to clean up unsuccessful\n  connection attempts more reliably. (`#809\n  <https://github.com/python-trio/trio/issues/809>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remove the APIs deprecated in 0.5.0. (``ClosedStreamError``,\n  ``ClosedListenerError``, ``Result``) (`#812\n  <https://github.com/python-trio/trio/issues/812>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- There are a number of methods on :class:`trio.ssl.SSLStream <trio.SSLStream>`\n  that report information about the negotiated TLS connection, like\n  ``selected_alpn_protocol``, and thus cannot succeed until after the handshake\n  has been performed. Previously, we returned None from these methods, like the\n  stdlib :mod:`ssl` module does, but this is confusing, because that can also\n  be a valid return value. Now we raise :exc:`trio.ssl.NeedHandshakeError\n  <trio.NeedHandshakeError>`\n  instead. (`#735 <https://github.com/python-trio/trio/issues/735>`__)\n\n\nTrio 0.9.0 (2018-10-12)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- New and improved APIs for inter-task communication:\n  :class:`trio.abc.SendChannel`, :class:`trio.abc.ReceiveChannel`, and\n  :func:`trio.open_memory_channel` (which replaces ``trio.Queue``). This\n  interface uses separate \"sender\" and \"receiver\" objects, for\n  consistency with other communication interfaces like\n  :class:`~trio.abc.Stream`. Also, the two objects can now be closed\n  individually, making it much easier to gracefully shut down a channel.\n  Also, check out the nifty ``clone`` API to make it easy to manage\n  shutdown in multiple-producer/multiple-consumer scenarios. Also, the\n  API has been written to allow for future channel implementations that\n  send objects across process boundaries. Also, it supports unbounded\n  buffering if you really need it. Also, help I can't stop writing also.\n  See :ref:`channels` for more details. (`#497\n  <https://github.com/python-trio/trio/issues/497>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ``trio.Queue`` and ``trio.hazmat.UnboundedQueue`` have been deprecated, in\n  favor of :func:`trio.open_memory_channel`. (`#497\n  <https://github.com/python-trio/trio/issues/497>`__)\n\n\nTrio 0.8.0 (2018-10-01)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- Trio's default internal clock is now based on :func:`time.perf_counter`\n  instead of :func:`time.monotonic`. This makes time-keeping more precise on\n  Windows, and has no effect on other platforms. (`#33\n  <https://github.com/python-trio/trio/issues/33>`__)\n- Reworked :mod:`trio`, :mod:`trio.testing`, and :mod:`trio.socket` namespace\n  construction, making them more understandable by static analysis tools. This\n  should improve tab completion in editors, reduce false positives from pylint,\n  and is a first step towards providing type hints. (`#542\n  <https://github.com/python-trio/trio/issues/542>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ``ResourceBusyError`` is now a deprecated alias for the new\n  :exc:`BusyResourceError`, and ``BrokenStreamError`` is a deprecated alias for\n  the new :exc:`BrokenResourceError`. (`#620\n  <https://github.com/python-trio/trio/issues/620>`__)\n\n\nTrio 0.7.0 (2018-09-03)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- The length of typical exception traces coming from Trio has been\n  greatly reduced. This was done by eliminating many of the exception\n  frames related to details of the implementation. For examples, see\n  the `blog post\n  <https://vorpus.org/blog/beautiful-tracebacks-in-trio-v070/>`__.\n  (`#56 <https://github.com/python-trio/trio/issues/56>`__)\n- New and improved signal catching API: :func:`open_signal_receiver`. (`#354\n  <https://github.com/python-trio/trio/issues/354>`__)\n- The low level ``trio.hazmat.wait_socket_readable``,\n  ``wait_socket_writable``, and\n  ``notify_socket_close`` now work on bare socket descriptors,\n  instead of requiring a :func:`socket.socket` object. (`#400\n  <https://github.com/python-trio/trio/issues/400>`__)\n- If you're using :func:`trio.hazmat.wait_task_rescheduled <trio.lowlevel.wait_task_rescheduled>` and other low-level\n  routines to implement a new sleeping primitive, you can now use the new\n  :data:`trio.hazmat.Task.custom_sleep_data <trio.lowlevel.Task.custom_sleep_data>` attribute to pass arbitrary data\n  between the sleeping task, abort function, and waking task. (`#616\n  <https://github.com/python-trio/trio/issues/616>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Prevent crashes when used with Sentry (raven-python). (`#599\n  <https://github.com/python-trio/trio/issues/599>`__)\n- The nursery context manager was rewritten to avoid use of\n  ``@asynccontextmanager`` and ``@async_generator``. This reduces extraneous frames\n  in exception traces and addresses bugs regarding `StopIteration` and\n  `StopAsyncIteration` exceptions not propagating correctly. (`#612\n  <https://github.com/python-trio/trio/issues/612>`__)\n- Updates the formatting of exception messages raised by\n  :func:`trio.open_tcp_stream` to correctly handle a hostname passed in as\n  bytes, by converting the hostname to a string. (`#633\n  <https://github.com/python-trio/trio/issues/633>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ``trio.catch_signals`` has been deprecated in favor of\n  :func:`open_signal_receiver`. The main differences are: it takes\n  \\*-args now to specify the list of signals (so\n  ``open_signal_receiver(SIGINT)`` instead of\n  ``catch_signals({SIGINT})``), and, the async iterator now yields\n  individual signals, instead of \"batches\" (`#354\n  <https://github.com/python-trio/trio/issues/354>`__)\n- Remove all the APIs deprecated in 0.3.0 and 0.4.0. (`#623\n  <https://github.com/python-trio/trio/issues/623>`__)\n\n\nTrio 0.6.0 (2018-08-13)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- Add :func:`trio.hazmat.WaitForSingleObject <trio.lowlevel.WaitForSingleObject>` async function to await Windows\n  handles. (`#233 <https://github.com/python-trio/trio/issues/233>`__)\n- The `sniffio <https://github.com/python-trio/sniffio>`__ library can now\n  detect when Trio is running. (`#572\n  <https://github.com/python-trio/trio/issues/572>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Make trio.socket._SocketType.connect *always* close the socket on\n  cancellation (`#247 <https://github.com/python-trio/trio/issues/247>`__)\n- Fix a memory leak in :class:`trio.CapacityLimiter`, that could occur when\n  ``acquire`` or ``acquire_on_behalf_of`` was cancelled. (`#548\n  <https://github.com/python-trio/trio/issues/548>`__)\n- Some version of macOS have a buggy ``getaddrinfo`` that was causing spurious\n  test failures; we now detect those systems and skip the relevant test when\n  found. (`#580 <https://github.com/python-trio/trio/issues/580>`__)\n- Prevent crashes when used with Sentry (raven-python). (`#599\n  <https://github.com/python-trio/trio/issues/599>`__)\n\n\nTrio 0.5.0 (2018-07-20)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- Suppose one task is blocked trying to use a resource – for example, reading\n  from a socket – and while it's doing this, another task closes the resource.\n  Previously, this produced undefined behavior. Now, closing a resource causes\n  pending operations on that resource to terminate immediately with a\n  :exc:`ClosedResourceError`. ``ClosedStreamError`` and ``ClosedListenerError``\n  are now aliases for :exc:`ClosedResourceError`, and deprecated. For this to\n  work, Trio needs to know when a resource has been closed. To facilitate this,\n  new functions have been added: ``trio.hazmat.notify_fd_close`` and\n  ``trio.hazmat.notify_socket_close``. If you're using Trio's built-in\n  wrappers like :class:`~trio.SocketStream` or :mod:`trio.socket`, then you don't\n  need to worry about this, but if you're using the low-level functions like\n  :func:`trio.hazmat.wait_readable <trio.lowlevel.wait_readable>`, you should make sure to call these\n  functions at appropriate times. (`#36\n  <https://github.com/python-trio/trio/issues/36>`__)\n- Tasks created by :func:`~trio.lowlevel.spawn_system_task` now no longer inherit\n  the creator's :mod:`contextvars` context, instead using one created at\n  :func:`~trio.run`. (`#289\n  <https://github.com/python-trio/trio/issues/289>`__)\n- Add support for ``trio.Queue`` with ``capacity=0``. Queue's implementation\n  is also faster now. (`#473\n  <https://github.com/python-trio/trio/issues/473>`__)\n- Switch to using standalone `Outcome\n  <https://github.com/python-trio/outcome>`__ library for Result objects.\n  (`#494 <https://github.com/python-trio/trio/issues/494>`__)\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ``trio.hazmat.Result``, ``trio.hazmat.Value`` and\n  ``trio.hazmat.Error`` have been replaced by the equivalent\n  classes in the `Outcome <https://github.com/python-trio/outcome>`__ library.\n\nTrio 0.4.0 (2018-04-10)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- Add unix client socket support. (`#401\n  <https://github.com/python-trio/trio/issues/401>`__)\n- Add support for :mod:`contextvars` (see :ref:`task-local storage\n  <task-local-storage>`), and add :class:`trio.hazmat.RunVar <trio.lowlevel.RunVar>` as a similar API\n  for run-local variables. Deprecate ``trio.TaskLocal`` and\n  ``trio.hazmat.RunLocal`` in favor of these new APIs. (`#420\n  <https://github.com/python-trio/trio/issues/420>`__)\n- Add :func:`trio.hazmat.current_root_task <trio.lowlevel.current_root_task>` to get the root task. (`#452\n  <https://github.com/python-trio/trio/issues/452>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fix KeyboardInterrupt handling when threading state has been modified by a\n  3rd-party library. (`#461\n  <https://github.com/python-trio/trio/issues/461>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Attempting to explicitly raise :exc:`trio.Cancelled` will cause a :exc:`RuntimeError`.\n  :meth:`cancel_scope.cancel() <trio.CancelScope.cancel>` should\n  be used instead. (`#342 <https://github.com/python-trio/trio/issues/342>`__)\n\n\nMiscellaneous internal changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Simplify implementation of primitive traps like :func:`~trio.lowlevel.wait_task_rescheduled`\n  (`#395 <https://github.com/python-trio/trio/issues/395>`__)\n\n\nTrio 0.3.0 (2017-12-28)\n-----------------------\n\nFeatures\n~~~~~~~~\n\n- **Simplified nurseries**: In Trio, the rule used to be that \"parenting is a\n  full time job\", meaning that after a task opened a nursery and spawned some\n  children into it, it had to immediately block in ``__aexit__`` to supervise\n  the new children, or else exception propagation wouldn't work. Also there was\n  some elaborate machinery to let you replace this supervision logic with your\n  own custom supervision logic. Thanks to new advances in task-rearing\n  technology, **parenting is no longer a full time job!** Now the supervision\n  happens automatically in the background, and essentially the body of a\n  ``async with trio.open_nursery()`` block acts just like a task running inside\n  the nursery. This is important: it makes it possible for libraries to\n  abstract over nursery creation. For example, if you have a Websocket library\n  that needs to run a background task to handle Websocket pings, you can now do\n  that with ``async with open_websocket(...) as ws: ...``, and that can run a\n  task in the background without your users having to worry about parenting it.\n  And don't worry, you can still make custom supervisors; it turned out all\n  that spiffy machinery was actually redundant and didn't provide much value.\n  (`#136 <https://github.com/python-trio/trio/issues/136>`__)\n- Trio socket methods like ``bind`` and ``connect`` no longer require\n  \"pre-resolved\" numeric addresses; you can now pass regular hostnames and Trio\n  will implicitly resolve them for you. (`#377\n  <https://github.com/python-trio/trio/issues/377>`__)\n\n\nBugfixes\n~~~~~~~~\n\n- Fixed some corner cases in Trio socket method implicit name resolution to\n  better match stdlib behavior. Example: ``sock.bind((\"\", port))`` now binds to\n  the wildcard address instead of raising an error. (`#277\n  <https://github.com/python-trio/trio/issues/277>`__)\n\n\nDeprecations and Removals\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Removed everything that was deprecated in 0.2.0; see the 0.2.0\n  release notes below for details.\n- As was foretold in the v0.2.0 release notes, the ``bind`` method on Trio\n  sockets is now async. Please update your calls or – better yet – switch to\n  our shiny new high-level networking API, like :func:`serve_tcp`. (`#241\n  <https://github.com/python-trio/trio/issues/241>`__)\n- The ``resolve_local_address`` and ``resolve_remote_address`` methods\n  on Trio sockets have been deprecated; these are unnecessary now that\n  you can just pass your hostnames directly to the socket methods you\n  want to use. (`#377\n  <https://github.com/python-trio/trio/issues/377>`__)\n\n\nTrio 0.2.0 (2017-12-06)\n-----------------------\n\nTrio 0.2.0 contains changes from 14 contributors, and brings major new\nfeatures and bug fixes, as well as a number of deprecations and a very\nsmall number of backwards incompatible changes. We anticipate that\nthese should be easy to adapt to, but make sure to read about them\nbelow, and if you're using Trio then remember to `read and subscribe\nto issue #1 <https://github.com/python-trio/trio/issues/1>`__.\n\n\nHighlights\n~~~~~~~~~~\n\n* Added a comprehensive API for async filesystem I/O: see\n  :ref:`async-file-io` (`gh-20\n  <https://github.com/python-trio/trio/pull/20>`__)\n\n* The new nursery :meth:`~Nursery.start` method makes it\n  easy to perform controlled start-up of long-running tasks. For\n  example, given an appropriate ``http_server_on_random_open_port``\n  function, you could write:\n\n  .. code-block:: python\n\n      port = await nursery.start(http_server_on_random_open_port)\n\n  and this would start the server running in the background in the\n  nursery, and then give you back the random port it selected – but\n  not until it had finished initializing and was ready to accept\n  requests!\n\n* Added a :ref:`new abstract API for byte streams\n  <abstract-stream-api>`, and :mod:`trio.testing` gained helpers for\n  creating fake streams for :ref:`testing your protocol implementation\n  <virtual-streams>` and checking that your custom stream\n  implementation :ref:`follows the stream contract\n  <testing-custom-streams>`.\n\n* If you're currently using :mod:`trio.socket` then you should\n  :ref:`switch to using our new high-level networking API instead\n  <high-level-networking>`. It takes care of many tiresome details, it's\n  fully integrated with the abstract stream API, and it provides\n  niceties like a state-of-the-art `Happy Eyeballs implementation\n  <https://en.wikipedia.org/wiki/Happy_Eyeballs>`__ in\n  :func:`open_tcp_stream` and server helpers that integrate with\n  ``nursery.start``.\n\n* We've also added comprehensive support for SSL/TLS encryption,\n  including SNI (both client and server side), STARTTLS, renegotiation\n  during full-duplex usage (subject to OpenSSL limitations), and\n  applying encryption to arbitrary :class:`~trio.abc.Stream`\\s, which\n  allows for interesting applications like `TLS-over-TLS\n  <https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/>`__.\n  See: :func:`trio.open_ssl_over_tcp_stream`,\n  :func:`trio.serve_ssl_over_tcp`,\n  :func:`trio.open_ssl_over_tcp_listeners`, and ``trio.ssl``.\n\n  Interesting fact: the test suite for ``trio.ssl`` has so far\n  found bugs in CPython's ssl module, PyPy's ssl module, PyOpenSSL,\n  and OpenSSL. (``trio.ssl`` doesn't use PyOpenSSL.) Trio's test\n  suite is fairly thorough.\n\n* You know thread-local storage? Well, Trio now has an equivalent:\n  :ref:`task-local storage <task-local-storage>`. There's also the\n  related, but more obscure, run-local storage; see\n  :class:`~trio.lowlevel.RunLocal`. (`#2\n  <https://github.com/python-trio/trio/pull/2>`__)\n\n* Added a new :ref:`guide to for contributors <contributing>`.\n\n\nBreaking changes and deprecations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTrio has matured into a stable, production-quality foundation for\nasync I/O in Python. While we strive to maintain stability, we may\nmake occasional breaking changes to improve the library. Whenever\npossible, we provide deprecation warnings on a reasonable timeline to\nease transitions. If you use Trio, we recommend `subscribing to issue\n#1 <https://github.com/python-trio/trio/issues/1>`__ to stay informed\nabout changes. We also welcome feedback on how our deprecation process\nis working and whether it could be improved.\n\nThe tl;dr is: stop using ``socket.bind`` if you can, and then fix\neverything your test suite warns you about.\n\nUpcoming breaking changes without warnings (i.e., stuff that works in\n0.2.0, but won't work in 0.3.0):\n\n* In the next release, the ``bind`` method on Trio socket objects will\n  become async (`#241\n  <https://github.com/python-trio/trio/issues/241>`__). Unfortunately,\n  there's no good way to provide a warning here. We recommend\n  switching to the new highlevel networking APIs like\n  :func:`serve_tcp`, which will insulate you from this change.\n\nBreaking changes (i.e., stuff that could theoretically break a program\nthat worked on 0.1.0):\n\n* :mod:`trio.socket` no longer attempts to normalize or modernize\n  socket options across different platforms. The high-level networking\n  API now handles that, freeing :mod:`trio.socket` to focus on giving\n  you raw, unadulterated BSD sockets.\n\n* When a socket ``sendall`` call was cancelled, it used to attach some\n  metadata to the exception reporting how much data was actually sent.\n  It no longer does this, because in common configurations like an\n  :class:`~trio.SSLStream` wrapped around a\n  :class:`~trio.SocketStream` it becomes ambiguous which \"level\" the\n  partial metadata applies to, leading to confusion and bugs. There is\n  no longer any way to tell how much data was sent after a ``sendall``\n  is cancelled.\n\n* The :func:`trio.socket.getprotobyname` function is now async, like\n  it should have been all along. I doubt anyone will ever use it, but\n  that's no reason not to get the details right.\n\n* The :mod:`trio.socket` functions ``getservbyport``,\n  ``getservbyname``, and ``getfqdn`` have been removed, because they\n  were obscure, buggy, and obsolete. Use\n  :func:`~trio.socket.getaddrinfo` instead.\n\nUpcoming breaking changes with warnings (i.e., stuff that in 0.2.0\n*will* work but will print loud complaints, and that won't work in\n0.3.0):\n\n* For consistency with the new ``start`` method, the nursery ``spawn``\n  method is being renamed to ``start_soon`` (`#284\n  <https://github.com/python-trio/trio/issues/284>`__)\n\n* ``trio.socket.sendall`` is deprecated; use ``trio.open_tcp_stream``\n  and ``SocketStream.send_all`` instead (`#291\n  <https://github.com/python-trio/trio/issues/291>`__)\n\n* Trio now consistently uses ``run`` for functions that take and run\n  an async function (like :func:`trio.run`!), and ``run_sync`` for\n  functions that take and run a synchronous function. As part of this:\n\n  * ``run_in_worker_thread`` is becoming\n    ``run_sync_in_worker_thread``\n\n  * We took the opportunity to refactor ``run_in_trio_thread`` and\n    ``await_in_trio_thread`` into the new class\n    ``trio.BlockingTrioPortal``\n\n  * The hazmat function ``current_call_soon_thread_and_signal_safe``\n    is being replaced by :class:`trio.hazmat.TrioToken <trio.lowlevel.TrioToken>`\n\n  See `#68 <https://github.com/python-trio/trio/issues/68>`__ for\n  details.\n\n* ``trio.Queue``\\'s ``join`` and ``task_done`` methods are\n  deprecated without replacement (`#321\n  <https://github.com/python-trio/trio/issues/321>`__)\n\n* Trio 0.1.0 provided a set of built-in mechanisms for waiting for and\n  tracking the result of individual tasks. We haven't yet found any\n  cases where using this actually led to simpler code, though, and\n  this feature is blocking useful improvements, so the following are\n  being deprecated without replacement:\n\n  * ``nursery.zombies``\n  * ``nursery.monitor``\n  * ``nursery.reap``\n  * ``nursery.reap_and_unwrap``\n  * ``task.result``\n  * ``task.add_monitor``\n  * ``task.discard_monitor``\n  * ``task.wait``\n\n  This also lets us move a number of lower-level features out of the\n  main :mod:`trio` namespace and into :mod:`trio.hazmat <trio.lowlevel>`:\n\n  * ``trio.Task`` → :class:`trio.hazmat.Task <trio.lowlevel.Task>`\n  * ``trio.current_task`` → :func:`trio.hazmat.current_task <trio.lowlevel.current_task>`\n  * ``trio.Result`` → ``trio.hazmat.Result``\n  * ``trio.Value`` → ``trio.hazmat.Value``\n  * ``trio.Error`` → ``trio.hazmat.Error``\n  * ``trio.UnboundedQueue`` → ``trio.hazmat.UnboundedQueue``\n\n  In addition, several introspection attributes are being renamed:\n\n  * ``nursery.children`` → ``nursery.child_tasks``\n  * ``task.parent_task`` → use ``task.parent_nursery.parent_task`` instead\n\n  See `#136 <https://github.com/python-trio/trio/issues/136>`__ for\n  more details.\n\n* To consolidate introspection functionality in :mod:`trio.hazmat <trio.lowlevel>`,\n  the following functions are moving:\n\n  * ``trio.current_clock`` → :func:`trio.hazmat.current_clock <trio.lowlevel.current_clock>`\n  * ``trio.current_statistics`` →\n    :func:`trio.hazmat.current_statistics <trio.lowlevel.current_statistics>`\n\n  See `#317 <https://github.com/python-trio/trio/issues/317>`__ for\n  more details.\n\n* It was decided that 0.1.0's \"yield point\" terminology was confusing;\n  we now use :ref:`\"checkpoint\" <checkpoints>` instead. As part of\n  this, the following functions in :mod:`trio.hazmat <trio.lowlevel>` are changing\n  names:\n\n  * ``yield_briefly`` → :func:`~trio.hazmat.checkpoint <trio.lowlevel.checkpoint>`\n  * ``yield_briefly_no_cancel`` → :func:`~trio.lowlevel.cancel_shielded_checkpoint`\n  * ``yield_if_cancelled`` → :func:`~trio.lowlevel.checkpoint_if_cancelled`\n  * ``yield_indefinitely`` → :func:`~trio.lowlevel.wait_task_rescheduled`\n\n  In addition, the following functions in :mod:`trio.testing` are\n  changing names:\n\n  * ``assert_yields`` → :func:`~trio.testing.assert_checkpoints`\n  * ``assert_no_yields`` → :func:`~trio.testing.assert_no_checkpoints`\n\n  See `#157 <https://github.com/python-trio/trio/issues/157>`__ for\n  more details.\n\n* ``trio.format_exception`` is deprecated; use\n  :func:`traceback.format_exception` instead (`#347\n  <https://github.com/python-trio/trio/pull/347>`__).\n\n* ``trio.current_instruments`` is deprecated. For adding or removing\n  instrumentation at run-time, see :func:`trio.hazmat.add_instrument <trio.lowlevel.add_instrument>`\n  and :func:`trio.hazmat.remove_instrument <trio.lowlevel.remove_instrument>` (`#257\n  <https://github.com/python-trio/trio/issues/257>`__)\n\nUnfortunately, a limitation in PyPy3 5.8 breaks our deprecation\nhandling for some renames. (Attempting to use the old names will give\nan unhelpful error instead of a helpful warning.) This does not affect\nCPython, or PyPy3 5.9+.\n\n\nOther changes\n~~~~~~~~~~~~~\n\n* ``run_sync_in_worker_thread`` now has a :ref:`robust mechanism\n  for applying capacity limits to the number of concurrent threads\n  <worker-thread-limiting>` (`#10\n  <https://github.com/python-trio/trio/issues/170>`__, `#57\n  <https://github.com/python-trio/trio/issues/57>`__, `#156\n  <https://github.com/python-trio/trio/issues/156>`__)\n\n* New support for tests to cleanly hook hostname lookup and socket\n  operations: see :ref:`virtual-network-hooks`. In addition,\n  ``trio.socket.SocketType`` is now an empty abstract base class, with\n  the actual socket class made private. This shouldn't effect anyone,\n  since the only thing you could directly use it for in the first\n  place was ``isinstance`` checks, and those still work (`#170\n  <https://github.com/python-trio/trio/issues/170>`__)\n\n* New class :class:`StrictFIFOLock`\n\n* New exception ``ResourceBusyError``\n\n* The :class:`trio.hazmat.ParkingLot <trio.lowlevel.ParkingLot>` class (which is used to\n  implement many of Trio's synchronization primitives) was rewritten\n  to be simpler and faster (`#272\n  <https://github.com/python-trio/trio/issues/272>`__, `#287\n  <https://github.com/python-trio/trio/issues/287>`__)\n\n* It's generally true that if you're using Trio you have to use Trio\n  functions, if you're using asyncio you have to use asyncio\n  functions, and so forth. (See the discussion of the \"async sandwich\"\n  in the Trio tutorial for more details.) So for example, this isn't\n  going to work:\n\n  .. code-block:: python\n\n      async def main():\n          # asyncio here\n          await asyncio.sleep(1)\n\n      # trio here\n      trio.run(main)\n\n  Trio now reliably detects if you accidentally do something like\n  this, and gives a helpful error message.\n\n* Trio now also has special error messages for several other common\n  errors, like doing ``trio.run(some_func())`` (should be\n  ``trio.run(some_func)``).\n\n* :mod:`trio.socket` now handles non-ascii domain names using the\n  modern IDNA 2008 standard instead of the obsolete IDNA 2003 standard\n  (`#11 <https://github.com/python-trio/trio/issues/11>`__)\n\n* When an :class:`~trio.abc.Instrument` raises an unexpected error, we\n  now route it through the :mod:`logging` module instead of printing\n  it directly to stderr. Normally this produces exactly the same\n  effect, but this way it's more configurable. (`#306\n  <https://github.com/python-trio/trio/issues/306>`__)\n\n* Fixed a minor race condition in IOCP thread shutdown on Windows\n  (`#81 <https://github.com/python-trio/trio/issues/81>`__)\n\n* Control-C handling on Windows now uses :func:`signal.set_wakeup_fd`\n  and should be more reliable (`#42\n  <https://github.com/python-trio/trio/issues/42>`__)\n\n* :func:`trio.run` takes a new keyword argument\n  ``restrict_keyboard_interrupt_to_checkpoints``\n\n* New attributes allow more detailed introspection of the task tree:\n  ``nursery.child_tasks``, ``Task.child_nurseries``,\n  ``nursery.parent_task``, ``Task.parent_nursery``\n\n* :func:`trio.testing.wait_all_tasks_blocked` now takes a\n  ``tiebreaker=`` argument. The main use is to allow\n  :class:`~trio.testing.MockClock`\\'s auto-jump functionality to avoid\n  interfering with direct use of\n  :func:`~trio.testing.wait_all_tasks_blocked` in the same test.\n\n* ``MultiError.catch()`` now correctly preserves ``__context__``,\n  despite Python's best attempts to stop us (`#165\n  <https://github.com/python-trio/trio/issues/165>`__)\n\n* It is now possible to take weakrefs to :class:`Lock` and many other\n  classes (`#331 <https://github.com/python-trio/trio/issues/331>`__)\n\n* Fix ``sock.accept()`` for IPv6 sockets (`#164\n  <https://github.com/python-trio/trio/issues/164>`__)\n\n* PyCharm (and hopefully other IDEs) can now offer better completions\n  for the :mod:`trio` and :mod:`trio.hazmat <trio.lowlevel>` modules (`#314\n  <https://github.com/python-trio/trio/issues/314>`__)\n\n* Trio now uses `yapf <https://github.com/google/yapf>`__ to\n  standardize formatting across the source tree, so we never have to\n  think about whitespace again.\n\n* Many documentation improvements\n\n\nTrio 0.1.0 (2017-03-10)\n-----------------------\n\n* Initial release.\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": ".. Trio documentation master file, created by\n   sphinx-quickstart on Sat Jan 21 19:11:14 2017.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\n=============================================================\nTrio: a friendly Python library for async concurrency and I/O\n=============================================================\n\nThe Trio project's goal is to produce a production-quality,\n`permissively licensed\n<https://github.com/python-trio/trio/blob/main/LICENSE>`__,\nasync/await-native I/O library for Python. Like all async libraries,\nits main purpose is to help you write programs that do **multiple\nthings at the same time** with **parallelized I/O**. A web spider that\nwants to fetch lots of pages in parallel, a web server that needs to\njuggle lots of downloads and websocket connections at the same time, a\nprocess supervisor monitoring multiple subprocesses... that sort of\nthing. Compared to other libraries, Trio attempts to distinguish\nitself with an obsessive focus on **usability** and\n**correctness**. Concurrency is complicated; we try to make it *easy*\nto get things *right*.\n\nTrio was built from the ground up to take advantage of the `latest\nPython features <https://www.python.org/dev/peps/pep-0492/>`__, and\ndraws inspiration from `many sources\n<https://github.com/python-trio/trio/wiki/Reading-list>`__, in\nparticular Dave Beazley's `Curio <https://curio.readthedocs.io/>`__.\nThe resulting design is radically simpler than older competitors like\n`asyncio <https://docs.python.org/3/library/asyncio.html>`__ and\n`Twisted <https://twistedmatrix.com/>`__, yet just as capable. Trio is\nthe Python I/O library I always wanted; I find it makes building\nI/O-oriented programs easier, less error-prone, and just plain more\nfun. Perhaps you'll find the same.\n\nTrio is a mature and well-tested library, though it retains its\n“experimental” classification to allow for occasional breaking API\nchanges as we push toward a 1.0 release. In practice, such changes are\nrare and typically minor. It is widely used in production environments,\nand we *do* encourage you do use it, but consider `subscribing to issue\n#1 <https://github.com/python-trio/trio/issues/1>`__ to get a warning\nand a chance to give feedback about any compatibility-breaking changes.\n\nVital statistics:\n\n* Supported environments: We test on\n\n  - Python: 3.10+ (CPython and PyPy)\n  - Windows, macOS, Linux (glibc and musl), FreeBSD\n\n  Other environments might also work; give it a try and see.\n\n* Install: ``python3 -m pip install -U trio`` (or on Windows, maybe\n  ``py -3 -m pip install -U trio``). No compiler needed.\n\n* Tutorial and reference manual: https://trio.readthedocs.io\n\n* Bug tracker and source code: https://github.com/python-trio/trio\n\n* Real-time chat: https://gitter.im/python-trio/general\n\n* Discussion forum: https://trio.discourse.group\n\n* License: MIT or Apache 2, your choice\n\n* Contributor guide: https://trio.readthedocs.io/en/latest/contributing.html\n\n* Code of conduct: Contributors are requested to follow our `code of\n  conduct\n  <https://trio.readthedocs.io/en/latest/code-of-conduct.html>`_\n  in all project spaces.\n\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Trio's friendly, yet comprehensive, manual:\n\n   tutorial.rst\n   awesome-trio-libraries.rst\n   reference-core.rst\n   reference-io.rst\n   reference-testing.rst\n   reference-lowlevel.rst\n   design.rst\n   history.rst\n   contributing.rst\n   releasing.rst\n   code-of-conduct.rst\n\n.. once https://github.com/sphinx-doc/sphinx/issues/13733 is merged, add `genindex` above\n\n====================\n Indices and tables\n====================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n* :ref:`glossary`\n"
  },
  {
    "path": "docs/source/local_customization.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom docutils.parsers.rst import directives as directives\nfrom sphinx import addnodes\nfrom sphinx.domains.python import PyClasslike\nfrom sphinx.ext.autodoc import (\n    ClassLevelDocumenter as ClassLevelDocumenter,\n    FunctionDocumenter as FunctionDocumenter,\n    MethodDocumenter as MethodDocumenter,\n    Options as Options,\n)\n\nif TYPE_CHECKING:\n    from sphinx.addnodes import desc_signature\n    from sphinx.application import Sphinx\n\n\"\"\"\n\n.. interface:: The nursery interface\n\n   .. attribute:: blahblah\n\n\"\"\"\n\n\nclass Interface(PyClasslike):\n    def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:\n        signode += addnodes.desc_name(sig, sig)\n        return sig, \"\"\n\n    def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:\n        return f\"{name_cls[0]} (interface in {modname})\"\n\n\ndef setup(app: Sphinx) -> None:\n    app.add_directive_to_domain(\"py\", \"interface\", Interface)\n"
  },
  {
    "path": "docs/source/reference-core/channels-backpressure.py",
    "content": "# Simulate a producer that generates values 10x faster than the\n# consumer can handle them.\n\nimport trio\nimport math\n\n\nasync def producer(send_channel):\n    count = 0\n    while True:\n        # Pretend that we have to do some work to create this message, and it\n        # takes 0.1 seconds:\n        await trio.sleep(0.1)\n        await send_channel.send(count)\n        print(\"Sent message:\", count)\n        count += 1\n\n\nasync def consumer(receive_channel):\n    async for value in receive_channel:\n        print(\"Received message:\", value)\n        # Pretend that we have to do some work to handle this message, and it\n        # takes 1 second\n        await trio.sleep(1)\n\n\nasync def main():\n    send_channel, receive_channel = trio.open_memory_channel(math.inf)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(producer, send_channel)\n        nursery.start_soon(consumer, receive_channel)\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/channels-mpmc-broken.py",
    "content": "# This example usually crashes!\n\nimport trio\nimport random\n\n\nasync def main():\n    async with trio.open_nursery() as nursery:\n        send_channel, receive_channel = trio.open_memory_channel(0)\n        # Start two producers\n        nursery.start_soon(producer, \"A\", send_channel)\n        nursery.start_soon(producer, \"B\", send_channel)\n        # And two consumers\n        nursery.start_soon(consumer, \"X\", receive_channel)\n        nursery.start_soon(consumer, \"Y\", receive_channel)\n\n\nasync def producer(name, send_channel):\n    async with send_channel:\n        for i in range(3):\n            await send_channel.send(f\"{i} from producer {name}\")\n            # Random sleeps help trigger the problem more reliably\n            await trio.sleep(random.random())\n\n\nasync def consumer(name, receive_channel):\n    async with receive_channel:\n        async for value in receive_channel:\n            print(f\"consumer {name} got value {value!r}\")\n            # Random sleeps help trigger the problem more reliably\n            await trio.sleep(random.random())\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/channels-mpmc-fixed.py",
    "content": "import trio\nimport random\n\n\nasync def main():\n    async with trio.open_nursery() as nursery:\n        send_channel, receive_channel = trio.open_memory_channel(0)\n        async with send_channel, receive_channel:\n            # Start two producers, giving each its own private clone\n            nursery.start_soon(producer, \"A\", send_channel.clone())\n            nursery.start_soon(producer, \"B\", send_channel.clone())\n            # And two consumers, giving each its own private clone\n            nursery.start_soon(consumer, \"X\", receive_channel.clone())\n            nursery.start_soon(consumer, \"Y\", receive_channel.clone())\n\n\nasync def producer(name, send_channel):\n    async with send_channel:\n        for i in range(3):\n            await send_channel.send(f\"{i} from producer {name}\")\n            # Random sleeps help trigger the problem more reliably\n            await trio.sleep(random.random())\n\n\nasync def consumer(name, receive_channel):\n    async with receive_channel:\n        async for value in receive_channel:\n            print(f\"consumer {name} got value {value!r}\")\n            # Random sleeps help trigger the problem more reliably\n            await trio.sleep(random.random())\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/channels-shutdown.py",
    "content": "import trio\n\n\nasync def main():\n    async with trio.open_nursery() as nursery:\n        send_channel, receive_channel = trio.open_memory_channel(0)\n        nursery.start_soon(producer, send_channel)\n        nursery.start_soon(consumer, receive_channel)\n\n\nasync def producer(send_channel):\n    async with send_channel:\n        for i in range(3):\n            await send_channel.send(f\"message {i}\")\n\n\nasync def consumer(receive_channel):\n    async with receive_channel:\n        async for value in receive_channel:\n            print(f\"got value {value!r}\")\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/channels-simple.py",
    "content": "import trio\n\n\nasync def main():\n    async with trio.open_nursery() as nursery:\n        # Open a channel:\n        send_channel, receive_channel = trio.open_memory_channel(0)\n        # Start a producer and a consumer, passing one end of the channel to\n        # each of them:\n        nursery.start_soon(producer, send_channel)\n        nursery.start_soon(consumer, receive_channel)\n\n\nasync def producer(send_channel):\n    # Producer sends 3 messages\n    for i in range(3):\n        # The producer sends using 'await send_channel.send(...)'\n        await send_channel.send(f\"message {i}\")\n\n\nasync def consumer(receive_channel):\n    # The consumer uses an 'async for' loop to receive the values:\n    async for value in receive_channel:\n        print(f\"got value {value!r}\")\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/contextvar-example.py",
    "content": "import random\nimport trio\nimport contextvars\n\nrequest_info = contextvars.ContextVar(\"request_info\")\n\n\n# Example logging function that tags each line with the request identifier.\ndef log(msg):\n    # Read from task-local storage:\n    request_tag = request_info.get()\n\n    print(f\"request {request_tag}: {msg}\")\n\n\n# An example \"request handler\" that does some work itself and also\n# spawns some helper tasks to do some concurrent work.\nasync def handle_request(tag):\n    # Write to task-local storage:\n    request_info.set(tag)\n\n    log(\"Request handler started\")\n    await trio.sleep(random.random())\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(concurrent_helper, \"a\")\n        nursery.start_soon(concurrent_helper, \"b\")\n    await trio.sleep(random.random())\n    log(\"Request received finished\")\n\n\nasync def concurrent_helper(job):\n    log(f\"Helper task {job} started\")\n    await trio.sleep(random.random())\n    log(f\"Helper task {job} finished\")\n\n\n# Spawn several \"request handlers\" simultaneously, to simulate a\n# busy server handling multiple requests at the same time.\nasync def main():\n    async with trio.open_nursery() as nursery:\n        for i in range(3):\n            nursery.start_soon(handle_request, i)\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/from-thread-example.py",
    "content": "import trio\n\n\ndef thread_fn(receive_from_trio, send_to_trio):\n    while True:\n        # Since we're in a thread, we can't call methods on Trio\n        # objects directly -- so we use trio.from_thread to call them.\n        try:\n            request = trio.from_thread.run(receive_from_trio.receive)\n        except trio.EndOfChannel:\n            trio.from_thread.run(send_to_trio.aclose)\n            return\n        else:\n            response = request + 1\n            trio.from_thread.run(send_to_trio.send, response)\n\n\nasync def main():\n    send_to_thread, receive_from_trio = trio.open_memory_channel(0)\n    send_to_trio, receive_from_thread = trio.open_memory_channel(0)\n\n    async with trio.open_nursery() as nursery:\n        # In a background thread, run:\n        #   thread_fn(receive_from_trio, send_to_trio)\n        nursery.start_soon(\n            trio.to_thread.run_sync, thread_fn, receive_from_trio, send_to_trio\n        )\n\n        # prints \"1\"\n        await send_to_thread.send(0)\n        print(await receive_from_thread.receive())\n\n        # prints \"2\"\n        await send_to_thread.send(1)\n        print(await receive_from_thread.receive())\n\n        # When we close the channel, it signals the thread to exit.\n        await send_to_thread.aclose()\n\n        # When we exit the nursery, it waits for the background thread to\n        # exit.\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core/thread-contextvars-example.py",
    "content": "import contextvars\nimport time\n\nimport trio\n\nrequest_state = contextvars.ContextVar(\"request_state\")\n\n# Blocking function that should be run on a thread\n# It could be reading or writing files, communicating with a database\n# with a driver not compatible with async / await, etc.\ndef work_in_thread(msg):\n    # Only use request_state.get() inside the worker thread\n    state_value = request_state.get()\n    current_user_id = state_value[\"current_user_id\"]\n    time.sleep(3)  # this would be some blocking call, like reading a file\n    print(f\"Processed user {current_user_id} with message {msg} in a thread worker\")\n    # Modify/mutate the state object, without setting the entire\n    # contextvar with request_state.set()\n    state_value[\"msg\"] = msg\n\n\n# An example \"request handler\" that does some work itself and also\n# spawns some helper tasks in threads to execute blocking code.\nasync def handle_request(current_user_id):\n    # Write to task-local storage:\n    current_state = {\"current_user_id\": current_user_id, \"msg\": \"\"}\n    request_state.set(current_state)\n\n    # Here the current implicit contextvars context will be automatically copied\n    # inside the worker thread\n    await trio.to_thread.run_sync(work_in_thread, f\"Hello {current_user_id}\")\n    # Extract the value set inside the thread in the same object stored in a contextvar\n    new_msg = current_state[\"msg\"]\n    print(\n        f\"New contextvar value from worker thread for user {current_user_id}: {new_msg}\"\n    )\n\n\n# Spawn several \"request handlers\" simultaneously, to simulate a\n# busy server handling multiple requests at the same time.\nasync def main():\n    async with trio.open_nursery() as nursery:\n        for i in range(3):\n            nursery.start_soon(handle_request, i)\n\n\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/reference-core.rst",
    "content": "Trio's core functionality\n=========================\n\n.. module:: trio\n\n\nEntering Trio\n-------------\n\nIf you want to use Trio, then the first thing you have to do is call\n:func:`trio.run`:\n\n.. autofunction:: run\n\n\nGeneral principles\n------------------\n\n.. _checkpoints:\n\nCheckpoints\n~~~~~~~~~~~\n\nWhen writing code using Trio, it's very important to understand the\nconcept of a *checkpoint*. Many of Trio's functions act as checkpoints.\n\nA checkpoint is two things:\n\n1. It's a point where Trio checks for cancellation. For example, if\n   the code that called your function set a timeout, and that timeout\n   has expired, then the next time your function executes a checkpoint\n   Trio will raise a :exc:`Cancelled` exception. See\n   :ref:`cancellation` below for more details.\n\n2. It's a point where the Trio scheduler checks its scheduling policy\n   to see if it's a good time to switch to another task, and\n   potentially does so. (Currently, this check is very simple: the\n   scheduler always switches at every checkpoint. But `this might\n   change in the future\n   <https://github.com/python-trio/trio/issues/32>`__.)\n\nWhen writing Trio code, you need to keep track of where your\ncheckpoints are. Why? First, because checkpoints require extra\nscrutiny: whenever you execute a checkpoint, you need to be prepared\nto handle a :exc:`Cancelled` error, or for another task to run and\n`rearrange some state out from under you\n<https://glyph.twistedmatrix.com/2014/02/unyielding.html>`__. And\nsecond, because you also need to make sure that you have *enough*\ncheckpoints: if your code doesn't pass through a checkpoint on a\nregular basis, then it will be slow to notice and respond to\ncancellation and – much worse – since Trio is a cooperative\nmulti-tasking system where the *only* place the scheduler can switch\ntasks is at checkpoints, it'll also prevent the scheduler from fairly\nallocating time between different tasks and adversely effect the\nresponse latency of all the other code running in the same\nprocess. (Informally we say that a task that does this is \"hogging the\nrun loop\".)\n\nSo when you're doing code review on a project that uses Trio, one of\nthe things you'll want to think about is whether there are enough\ncheckpoints, and whether each one is handled correctly. Of course this\nmeans you need a way to recognize checkpoints. How do you do that?\nThe underlying principle is that any operation that blocks has to be a\ncheckpoint. This makes sense: if an operation blocks, then it might\nblock for a long time, and you'll want to be able to cancel it if a\ntimeout expires; and in any case, while this task is blocked we want\nanother task to be scheduled to run so our code can make full use of\nthe CPU.\n\nBut if we want to write correct code in practice, then this principle\nis a little too sloppy and imprecise to be useful. How do we know\nwhich functions might block?  What if a function blocks sometimes, but\nnot others, depending on the arguments passed / network speed / phase\nof the moon? How do we figure out where the checkpoints are when\nwe're stressed and sleep deprived but still want to get this code\nreview right, and would prefer to reserve our mental energy for\nthinking about the actual logic instead of worrying about checkpoints?\n\n.. _checkpoint-rule:\n\nDon't worry – Trio's got your back. Since checkpoints are important\nand ubiquitous, we make it as simple as possible to keep track of\nthem. Here are the rules:\n\n* Regular (synchronous) functions never contain any checkpoints.\n\n* If you call an async function provided by Trio (``await\n  <something in trio>``), and it doesn't raise an exception,\n  then it *always* acts as a checkpoint. (If it does raise an\n  exception, it might act as a checkpoint or might not.)\n\n  * This includes async iterators: If you write ``async for ... in <a\n    trio object>``, then there will be at least one checkpoint in\n    each iteration of the loop, and it will still checkpoint if the\n    iterable is empty.\n\n  * Partial exception for async context managers:\n    Both the entry and exit of an ``async with`` block are\n    defined as async functions; but for a\n    particular type of async context manager, it's often the\n    case that only one of them is able to block, which means\n    only that one will act as a checkpoint. This is documented\n    on a case-by-case basis.\n\n    * :func:`trio.open_nursery` is a further exception to this rule.\n\n* Third-party async functions / iterators / context managers can act\n  as checkpoints; if you see ``await <something>`` or one of its\n  friends, then that *might* be a checkpoint. So to be safe, you\n  should prepare for scheduling or cancellation happening there.\n\nThe reason we distinguish between Trio functions and other functions\nis that we can't make any guarantees about third party\ncode. Checkpoint-ness is a transitive property: if function A acts as\na checkpoint, and you write a function that calls function A, then\nyour function also acts as a checkpoint. If you don't, then it\nisn't. So there's nothing stopping someone from writing a function\nlike:\n\n.. code-block:: python\n\n   # technically legal, but bad style:\n   async def why_is_this_async():\n       return 7\n\nthat never calls any of Trio's async functions. This is an async\nfunction, but it's not a checkpoint. But why make a function async if\nit never calls any async functions? It's possible, but it's a bad\nidea. If you have a function that's not calling any async functions,\nthen you should make it synchronous. The people who use your function\nwill thank you, because it makes it obvious that your function is not\na checkpoint, and their code reviews will go faster.\n\n(Remember how in the tutorial we emphasized the importance of the\n:ref:`\"async sandwich\" <async-sandwich>`, and the way it means that\n``await`` ends up being a marker that shows when you're calling a\nfunction that calls a function that ... eventually calls one of Trio's\nbuilt-in async functions? The transitivity of async-ness is a\ntechnical requirement that Python imposes, but since it exactly\nmatches the transitivity of checkpoint-ness, we're able to exploit it\nto help you keep track of checkpoints. Pretty sneaky, eh?)\n\nA slightly trickier case is a function like:\n\n.. code-block:: python\n\n   async def sleep_or_not(should_sleep):\n       if should_sleep:\n           await trio.sleep(1)\n       else:\n           pass\n\nHere the function acts as a checkpoint if you call it with\n``should_sleep`` set to a true value, but not otherwise. This is why\nwe emphasize that Trio's own async functions are *unconditional* checkpoints:\nthey *always* check for cancellation and check for scheduling,\nregardless of what arguments they're passed. If you find an async\nfunction in Trio that doesn't follow this rule, then it's a bug and\nyou should `let us know\n<https://github.com/python-trio/trio/issues>`__.\n\nInside Trio, we're very picky about this, because Trio is the\nfoundation of the whole system so we think it's worth the extra effort\nto make things extra predictable. It's up to you how picky you want to\nbe in your code. To give you a more realistic example of what this\nkind of issue looks like in real life, consider this function:\n\n.. code-block:: python\n\n    async def recv_exactly(sock, nbytes):\n        data = bytearray()\n        while nbytes > 0:\n            # recv() reads up to 'nbytes' bytes each time\n            chunk = await sock.recv(nbytes)\n            if not chunk:\n                raise RuntimeError(\"socket unexpected closed\")\n            nbytes -= len(chunk)\n            data += chunk\n        return data\n\nIf called with an ``nbytes`` that's greater than zero, then it will\ncall ``sock.recv`` at least once, and ``recv`` is an async Trio\nfunction, and thus an unconditional checkpoint. So in this case,\n``recv_exactly`` acts as a checkpoint. But if we do ``await\nrecv_exactly(sock, 0)``, then it will immediately return an empty\nbuffer without executing a checkpoint. If this were a function in\nTrio itself, then this wouldn't be acceptable, but you may decide you\ndon't want to worry about this kind of minor edge case in your own\ncode.\n\nIf you do want to be careful, or if you have some CPU-bound code that\ndoesn't have enough checkpoints in it, then it's useful to know that\n``await trio.sleep(0)`` is an idiomatic way to execute a checkpoint\nwithout doing anything else, and that\n:func:`trio.testing.assert_checkpoints` can be used to test that an\narbitrary block of code contains a checkpoint.\n\n\nThread safety\n~~~~~~~~~~~~~\n\nThe vast majority of Trio's API is *not* thread safe: it can only be\nused from inside a call to :func:`trio.run`. This manual doesn't\nbother documenting this on individual calls; unless specifically noted\notherwise, you should assume that it isn't safe to call any Trio\nfunctions from anywhere except the Trio thread. (But :ref:`see below\n<threads>` if you really do need to work with threads.)\n\n\n.. _time-and-clocks:\n\nTime and clocks\n---------------\n\nEvery call to :func:`run` has an associated clock.\n\nBy default, Trio uses an unspecified monotonic clock, but this can be\nchanged by passing a custom clock object to :func:`run` (e.g. for\ntesting).\n\nYou should not assume that Trio's internal clock matches any other\nclock you have access to, including the clocks of simultaneous calls\nto :func:`trio.run` happening in other processes or threads!\n\nThe default clock is currently implemented as :func:`time.perf_counter`\nplus a large random offset. The idea here is to catch code that\naccidentally uses :func:`time.perf_counter` early, which should help keep\nour options open for `changing the clock implementation later\n<https://github.com/python-trio/trio/issues/33>`__, and (more importantly)\nmake sure you can be confident that custom clocks like\n:class:`trio.testing.MockClock` will work with third-party libraries\nyou don't control.\n\n.. autofunction:: current_time\n\n.. autofunction:: sleep\n.. autofunction:: sleep_until\n.. autofunction:: sleep_forever\n\nIf you're a mad scientist or otherwise feel the need to take direct\ncontrol over the PASSAGE OF TIME ITSELF, then you can implement a\ncustom :class:`~trio.abc.Clock` class:\n\n.. autoclass:: trio.abc.Clock\n   :members:\n\n\n.. _cancellation:\n\nCancellation and timeouts\n-------------------------\n\nTrio has a rich, composable system for cancelling work, either\nexplicitly or when a timeout expires.\n\n\nA simple timeout example\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn the simplest case, you can apply a timeout to a block of code:\n\n.. code-block:: python\n\n   with trio.move_on_after(30):\n       result = await do_http_get(\"https://...\")\n       print(\"result is\", result)\n   print(\"with block finished\")\n\nWe refer to :func:`move_on_after` as creating a \"cancel scope\", which\ncontains all the code that runs inside the ``with`` block. If the HTTP\nrequest takes more than 30 seconds to run, then it will be cancelled:\nwe'll abort the request and we *won't* see ``result is ...`` printed\non the console; instead we'll go straight to printing the ``with block\nfinished`` message.\n\n.. note::\n\n   Note that this is a single 30 second timeout for the entire body of\n   the ``with`` statement. This is different from what you might have\n   seen with other Python libraries, where timeouts often refer to\n   something `more complicated\n   <https://requests.kennethreitz.org/en/master/user/quickstart/#timeouts>`__. We\n   think this way is easier to reason about.\n\nHow does this work? There's no magic here: Trio is built using\nordinary Python functionality, so we can't just abandon the code\ninside the ``with`` block. Instead, we take advantage of Python's\nstandard way of aborting a large and complex piece of code: we raise\nan exception.\n\nHere's the idea: whenever you call a cancellable function like ``await\ntrio.sleep(...)`` or ``await sock.recv(...)`` – see :ref:`checkpoints`\n– then the first thing that function does is to check if there's a\nsurrounding cancel scope whose timeout has expired, or otherwise been\ncancelled. If so, then instead of performing the requested operation,\nthe function fails immediately with a :exc:`Cancelled` exception. In\nthis example, this probably happens somewhere deep inside the bowels\nof ``do_http_get``. The exception then propagates out like any normal\nexception (you could even catch it if you wanted, but that's generally\na bad idea), until it reaches the ``with move_on_after(...):``. And at\nthis point, the :exc:`Cancelled` exception has done its job – it's\nsuccessfully unwound the whole cancelled scope – so\n:func:`move_on_after` catches it, and execution continues as normal\nafter the ``with`` block. And this all works correctly even if you\nhave nested cancel scopes, because every :exc:`Cancelled` object\ncarries an invisible marker that makes sure that the cancel scope that\ntriggered it is the only one that will catch it.\n\n\nHandling cancellation\n~~~~~~~~~~~~~~~~~~~~~\n\nPretty much any code you write using Trio needs to have some strategy\nto handle :exc:`Cancelled` exceptions – even if you didn't set a\ntimeout, then your caller might (and probably will).\n\nYou can catch :exc:`Cancelled`, but you shouldn't! Or more precisely,\nif you do catch it, then you should do some cleanup and then re-raise\nit or otherwise let it continue propagating (unless you encounter an\nerror, in which case it's OK to let that propagate instead). To help\nremind you of this fact, :exc:`Cancelled` inherits from\n:exc:`BaseException`, like :exc:`KeyboardInterrupt` and\n:exc:`SystemExit` do, so that it won't be caught by catch-all ``except\nException:`` blocks.\n\nIt's also important in any long-running code to make sure that you\nregularly check for cancellation, because otherwise timeouts won't\nwork! This happens implicitly every time you call a cancellable\noperation; see :ref:`below <cancellable-primitives>` for details. If\nyou have a task that has to do a lot of work without any I/O, then you\ncan use ``await sleep(0)`` to insert an explicit cancel+schedule\npoint.\n\nHere's a rule of thumb for designing good Trio-style (\"trionic\"?)\nAPIs: if you're writing a reusable function, then you shouldn't take a\n``timeout=`` parameter, and instead let your caller worry about\nit. This has several advantages. First, it leaves the caller's options\nopen for deciding how they prefer to handle timeouts – for example,\nthey might find it easier to work with absolute deadlines instead of\nrelative timeouts. If they're the ones calling into the cancellation\nmachinery, then they get to pick, and you don't have to worry about\nit. Second, and more importantly, this makes it easier for others to\nreuse your code. If you write a ``http_get`` function, and then I come\nalong later and write a ``log_in_to_twitter`` function that needs to\ninternally make several ``http_get`` calls, I don't want to have to\nfigure out how to configure the individual timeouts on each of those\ncalls – and with Trio's timeout system, it's totally unnecessary.\n\nOf course, this rule doesn't apply to APIs that need to impose\ninternal timeouts. For example, if you write a ``start_http_server``\nfunction, then you probably should give your caller some way to\nconfigure timeouts on individual requests.\n\n\nCancellation semantics\n~~~~~~~~~~~~~~~~~~~~~~\n\nYou can freely nest cancellation blocks, and each :exc:`Cancelled`\nexception \"knows\" which block it belongs to. So long as you don't stop\nit, the exception will keep propagating until it reaches the block\nthat raised it, at which point it will stop automatically.\n\nHere's an example:\n\n.. code-block:: python\n\n   print(\"starting...\")\n   with trio.move_on_after(5):\n       with trio.move_on_after(10):\n           await trio.sleep(20)\n           print(\"sleep finished without error\")\n       print(\"move_on_after(10) finished without error\")\n   print(\"move_on_after(5) finished without error\")\n\nIn this code, the outer scope will expire after 5 seconds, causing the\n:func:`sleep` call to return early with a :exc:`Cancelled`\nexception. Then this exception will propagate through the ``with\nmove_on_after(10)`` line until it's caught by the ``with\nmove_on_after(5)`` context manager. So this code will print:\n\n.. code-block:: none\n\n   starting...\n   move_on_after(5) finished without error\n\nThe end result is that Trio has successfully cancelled exactly the\nwork that was happening within the scope that was cancelled.\n\nLooking at this, you might wonder how you can tell whether the inner\nblock timed out – perhaps you want to do something different, like try\na fallback procedure or report a failure to our caller. To make this\neasier, :func:`move_on_after`\\´s ``__enter__`` function returns an\nobject representing this cancel scope, which we can use to check\nwhether this scope caught a :exc:`Cancelled` exception:\n\n.. code-block:: python\n\n   with trio.move_on_after(5) as cancel_scope:\n       await trio.sleep(10)\n   print(cancel_scope.cancelled_caught)  # prints \"True\"\n\nThe ``cancel_scope`` object also allows you to check or adjust this\nscope's deadline, explicitly trigger a cancellation without waiting\nfor the deadline, check if the scope has already been cancelled, and\nso forth – see :class:`CancelScope` below for the full details.\n\n.. _blocking-cleanup-example:\n\nCancellations in Trio are \"level triggered\", meaning that once a block\nhas been cancelled, *all* cancellable operations in that block will\nkeep raising :exc:`Cancelled`. This helps avoid some pitfalls around\nresource clean-up. For example, imagine that we have a function that\nconnects to a remote server and sends some messages, and then cleans\nup on the way out:\n\n.. code-block:: python\n\n   with trio.move_on_after(TIMEOUT):\n       conn = make_connection()\n       try:\n           await conn.send_hello_msg()\n       finally:\n           await conn.send_goodbye_msg()\n\nNow suppose that the remote server stops responding, so our call to\n``await conn.send_hello_msg()`` hangs forever. Fortunately, we were\nclever enough to put a timeout around this code, so eventually the\ntimeout will expire and ``send_hello_msg`` will raise\n:exc:`Cancelled`. But then, in the ``finally`` block, we make another\nblocking operation, which will also hang forever! At this point, if we\nwere using :mod:`asyncio` or another library with \"edge-triggered\"\ncancellation, we'd be in trouble: since our timeout already fired, it\nwouldn't fire again, and at this point our application would lock up\nforever. But in Trio, this *doesn't* happen: the ``await\nconn.send_goodbye_msg()`` call is still inside the cancelled block, so\nit will also raise :exc:`Cancelled`.\n\nOf course, if you really want to make another blocking call in your\ncleanup handler, Trio will let you; it's trying to prevent you from\naccidentally shooting yourself in the foot. Intentional foot-shooting\nis no problem (or at least – it's not Trio's problem). To do this,\ncreate a new scope, and set its :attr:`~CancelScope.shield`\nattribute to :data:`True`:\n\n.. code-block:: python\n\n   with trio.move_on_after(TIMEOUT):\n       conn = make_connection()\n       try:\n           await conn.send_hello_msg()\n       finally:\n           with trio.move_on_after(CLEANUP_TIMEOUT, shield=True) as cleanup_scope:\n               await conn.send_goodbye_msg()\n\nSo long as you're inside a scope with ``shield = True`` set, then\nyou'll be protected from outside cancellations. Note though that this\n*only* applies to *outside* cancellations: if ``CLEANUP_TIMEOUT``\nexpires then ``await conn.send_goodbye_msg()`` will still be\ncancelled, and if ``await conn.send_goodbye_msg()`` call uses any\ntimeouts internally, then those will continue to work normally as\nwell. This is a pretty advanced feature that most people probably\nwon't use, but it's there for the rare cases where you need it.\n\n\n.. _cancellable-primitives:\n\nCancellation and primitive operations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe've talked a lot about what happens when an operation is cancelled,\nand how you need to be prepared for this whenever calling a\ncancellable operation... but we haven't gone into the details about\nwhich operations are cancellable, and how exactly they behave when\nthey're cancelled.\n\nHere's the rule: if it's in the ``trio`` namespace, and you use ``await``\nto call it, then it's cancellable (see :ref:`checkpoints`\nabove). Cancellable means:\n\n* If you try to call it when inside a cancelled scope, then it will\n  raise :exc:`Cancelled`.\n\n* If it blocks, and while it's blocked then one of the scopes around\n  it becomes cancelled, it will return early and raise\n  :exc:`Cancelled`.\n\n* Raising :exc:`Cancelled` means that the operation *did not\n  happen*. If a Trio socket's ``send`` method raises :exc:`Cancelled`,\n  then no data was sent. If a Trio socket's ``recv`` method raises\n  :exc:`Cancelled` then no data was lost – it's still sitting in the\n  socket receive buffer waiting for you to call ``recv`` again. And so\n  forth.\n\nThere are a few idiosyncratic cases where external constraints make it\nimpossible to fully implement these semantics. These are always\ndocumented. There is also one systematic exception:\n\n* Async cleanup operations – like ``__aexit__`` methods or async close\n  methods – are cancellable just like anything else *except* that if\n  they are cancelled, they still perform a minimum level of cleanup\n  before raising :exc:`Cancelled`.\n\nFor example, closing a TLS-wrapped socket normally involves sending a\nnotification to the remote peer, so that they can be cryptographically\nassured that you really meant to close the socket, and your connection\nwasn't just broken by a man-in-the-middle attacker. But handling this\nrobustly is a bit tricky. Remember our :ref:`example\n<blocking-cleanup-example>` above where the blocking\n``send_goodbye_msg`` caused problems? That's exactly how closing a TLS\nsocket works: if the remote peer has disappeared, then our code may\nnever be able to actually send our shutdown notification, and it would\nbe nice if it didn't block forever trying. Therefore, the method for\nclosing a TLS-wrapped socket will *try* to send that notification –\nand if it gets cancelled, then it will give up on sending the message,\nbut *will* still close the underlying socket before raising\n:exc:`Cancelled`, so at least you don't leak that resource.\n\n\nCancellation API details\n~~~~~~~~~~~~~~~~~~~~~~~~\n\n:func:`move_on_after` and all the other cancellation facilities provided\nby Trio are ultimately implemented in terms of :class:`CancelScope`\nobjects.\n\n.. autoclass:: trio.CancelScope\n\n   .. autoattribute:: deadline\n\n   .. autoattribute:: relative_deadline\n\n   .. autoattribute:: shield\n\n   .. automethod:: is_relative()\n\n   .. automethod:: cancel()\n\n   .. attribute:: cancelled_caught\n\n      Readonly :class:`bool`. Records whether this scope caught a\n      :exc:`~trio.Cancelled` exception. This requires two things: (1)\n      the ``with`` block exited with a :exc:`~trio.Cancelled`\n      exception, and (2) this scope is the one that was responsible\n      for triggering this :exc:`~trio.Cancelled` exception.\n\n   .. autoattribute:: cancel_called\n\nOften there is no need to create :class:`CancelScope` object. Trio\nalready includes :attr:`~trio.Nursery.cancel_scope` attribute in a\ntask-related :class:`Nursery` object. We will cover nurseries later in\nthe manual.\n\nTrio also provides several convenience functions for the common\nsituation of just wanting to impose a timeout on some code:\n\n.. autofunction:: move_on_after\n   :with: cancel_scope\n\n.. autofunction:: move_on_at\n   :with: cancel_scope\n\n.. autofunction:: fail_after\n   :with: cancel_scope\n\n.. autofunction:: fail_at\n   :with: cancel_scope\n\nCheat sheet\n+++++++++++\n\n* If you want to impose a timeout on a function, but you don't care\n  whether it timed out or not:\n\n  .. code-block:: python\n\n     with trio.move_on_after(TIMEOUT):\n         await do_whatever()\n     # carry on!\n\n* If you want to impose a timeout on a function, and then do some\n  recovery if it timed out:\n\n  .. code-block:: python\n\n     with trio.move_on_after(TIMEOUT) as cancel_scope:\n         await do_whatever()\n     if cancel_scope.cancelled_caught:\n         # The operation timed out, try something else\n         try_to_recover()\n\n* If you want to impose a timeout on a function, and then if it times\n  out then just give up and raise an error for your caller to deal\n  with:\n\n  .. code-block:: python\n\n     with trio.fail_after(TIMEOUT):\n         await do_whatever()\n\nIt's also possible to check what the current effective deadline is,\nwhich is sometimes useful:\n\n.. autofunction:: current_effective_deadline\n\n.. _tasks:\n\nTasks let you do multiple things at once\n----------------------------------------\n\nOne of Trio's core design principles is: *no implicit\nconcurrency*. Every function executes in a straightforward,\ntop-to-bottom manner, finishing each operation before moving on to the\nnext – *like Guido intended*.\n\nBut, of course, the entire point of an async library is to let you do\nmultiple things at once. The one and only way to do that in Trio is\nthrough the task spawning interface. So if you want your program to\nwalk *and* chew gum, this is the section for you.\n\n\nNurseries and spawning\n~~~~~~~~~~~~~~~~~~~~~~\n\nMost libraries for concurrent programming let you start new child\ntasks (or threads, or whatever) willy-nilly, whenever and where-ever\nyou feel like it. Trio is a bit different: you can't start a child\ntask unless you're prepared to be a responsible parent. The way you\ndemonstrate your responsibility is by creating a nursery:\n\n.. code-block:: python\n\n   async with trio.open_nursery() as nursery:\n       ...\n\nAnd once you have a reference to a nursery object, you can start\nchildren in that nursery:\n\n.. code-block:: python\n\n   async def child():\n       ...\n\n   async def parent():\n       async with trio.open_nursery() as nursery:\n           # Make two concurrent calls to child()\n           nursery.start_soon(child)\n           nursery.start_soon(child)\n\nThis means that tasks form a tree: when you call :func:`run`, then\nthis creates an initial task, and all your other tasks will be\nchildren, grandchildren, etc. of the initial task.\n\nEssentially, the body of the ``async with`` block acts like an initial\ntask that's running inside the nursery, and then each call to\n``nursery.start_soon`` adds another task that runs in parallel. Two\ncrucial things to keep in mind:\n\n* If any task inside the nursery finishes with an unhandled exception,\n  then the nursery immediately cancels all the tasks inside the\n  nursery.\n\n* Since all of the tasks are running concurrently inside the ``async\n  with`` block, the block does not exit until *all* tasks have\n  completed. If you've used other concurrency frameworks, then you can\n  think of it as, the de-indentation at the end of the ``async with``\n  automatically \"joins\" (waits for) all of the tasks in the nursery.\n\n* Once all the tasks have finished, then:\n\n  * The nursery is marked as \"closed\", meaning that no new tasks can\n    be started inside it.\n\n  * Any unhandled exceptions are re-raised inside the parent task, grouped into a\n    single :exc:`BaseExceptionGroup` or :exc:`ExceptionGroup` exception.\n\nSince all tasks are descendents of the initial task, one consequence\nof this is that :func:`run` can't finish until all tasks have\nfinished.\n\n.. note::\n\n   A return statement will not cancel the nursery if it still has tasks running:\n\n   .. code-block:: python\n\n     async def main():\n         async with trio.open_nursery() as nursery:\n             nursery.start_soon(trio.sleep, 5)\n             return\n\n     trio.run(main)\n\n   This code will wait 5 seconds (for the child task to finish), and then return.\n\nChild tasks and cancellation\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn Trio, child tasks inherit the parent nursery's cancel scopes. So in\nthis example, both the child tasks will be cancelled when the timeout\nexpires:\n\n.. code-block:: python\n\n   with trio.move_on_after(TIMEOUT):\n       async with trio.open_nursery() as nursery:\n           nursery.start_soon(child1)\n           nursery.start_soon(child2)\n\nNote that what matters here is the scopes that were active when\n:func:`open_nursery` was called, *not* the scopes active when\n``start_soon`` is called. So for example, the timeout block below does\nnothing at all:\n\n.. code-block:: python\n\n   async with trio.open_nursery() as nursery:\n       with trio.move_on_after(TIMEOUT):  # don't do this!\n           nursery.start_soon(child)\n\nWhy is this so? Well, ``start_soon()`` returns as soon as it has scheduled the new task to start running. The flow of execution in the parent then continues on to exit the ``with trio.move_on_after(TIMEOUT):`` block, at which point Trio forgets about the timeout entirely. In order for the timeout to apply to the child task, Trio must be able to tell that its associated cancel scope will stay open for at least as long as the child task is executing. And Trio can only know that for sure if the cancel scope block is outside the nursery block.\n\nYou might wonder why Trio can't just remember \"this task should be cancelled in ``TIMEOUT`` seconds\", even after the ``with trio.move_on_after(TIMEOUT):`` block is gone. The reason has to do with :ref:`how cancellation is implemented <cancellation>`. Recall that cancellation is represented by a `Cancelled` exception, which eventually needs to be caught by the cancel scope that caused it. (Otherwise, the exception would take down your whole program!) In order to be able to cancel the child tasks, the cancel scope has to be able to \"see\" the `Cancelled` exceptions that they raise -- and those exceptions come out of the ``async with open_nursery()`` block, not out of the call to ``start_soon()``.\n\nIf you want a timeout to apply to one task but not another, then you need to put the cancel scope in that individual task's function -- ``child()``, in this example.\n\n.. _exceptiongroups:\n\nErrors in multiple child tasks\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNormally, in Python, only one thing happens at a time, which means\nthat only one thing can go wrong at a time. Trio has no such\nlimitation. Consider code like:\n\n.. code-block:: python\n\n    async def broken1():\n        d = {}\n        return d[\"missing\"]\n\n    async def broken2():\n        seq = range(10)\n        return seq[20]\n\n    async def parent():\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(broken1)\n            nursery.start_soon(broken2)\n\n``broken1`` raises ``KeyError``. ``broken2`` raises\n``IndexError``. Obviously ``parent`` should raise some error, but\nwhat? The answer is that both exceptions are grouped in an :exc:`ExceptionGroup`.\n:exc:`ExceptionGroup` and its parent class :exc:`BaseExceptionGroup` are used to\nencapsulate multiple exceptions being raised at once.\n\nTo catch individual exceptions encapsulated in an exception group, the ``except*``\nclause was introduced in Python 3.11 (:pep:`654`). Here's how it works:\n\n.. code-block:: python\n\n    try:\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(broken1)\n            nursery.start_soon(broken2)\n    except* KeyError as excgroup:\n        for exc in excgroup.exceptions:\n            ...  # handle each KeyError\n    except* IndexError as excgroup:\n        for exc in excgroup.exceptions:\n            ...  # handle each IndexError\n\nIf you want to reraise exceptions, or raise new ones, you can do so, but be aware that\nexceptions raised in ``except*`` sections will be raised together in a new exception\ngroup.\n\nBut what if you can't use Python 3.11, and therefore ``except*``, just yet?\nThe same exceptiongroup_ library which backports `ExceptionGroup`  also lets\nyou approximate this behavior with exception handler callbacks:\n\n.. code-block:: python\n\n    from exceptiongroup import catch\n\n    def handle_keyerrors(excgroup):\n        for exc in excgroup.exceptions:\n            ...  # handle each KeyError\n\n    def handle_indexerrors(excgroup):\n        for exc in excgroup.exceptions:\n            ...  # handle each IndexError\n\n    with catch({\n        KeyError: handle_keyerrors,\n        IndexError: handle_indexerrors\n    }):\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(broken1)\n            nursery.start_soon(broken2)\n\nThe semantics for the handler functions are equal to ``except*`` blocks, except for\nsetting local variables. If you need to set local variables, you need to declare them\ninside the handler function(s) with the ``nonlocal`` keyword:\n\n.. code-block:: python\n\n    def handle_keyerrors(excgroup):\n        nonlocal myflag\n        myflag = True\n\n    myflag = False\n    with catch({KeyError: handle_keyerrors}):\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(broken1)\n\n.. _handling_exception_groups:\n\nDesigning for multiple errors\n+++++++++++++++++++++++++++++\n\nStructured concurrency is still a relatively new design pattern, but there are a few\napproaches we've identified for how you (or your users) might want to handle groups of\nexceptions. Note that the final pattern, simply raising an `ExceptionGroup`, is the most\ncommon - and nurseries automatically do that for you.\n\n**First**, you might want to 'defer to' a particular exception type, raising just that if\nthere is any such instance in the group.  For example: `KeyboardInterrupt` has a clear\nmeaning for the surrounding code, could reasonably take priority over errors of other\ntypes, and whether you have one or several of them doesn't really matter.\n\nThis pattern can often be implemented using a decorator or a context manager, such\nas :func:`trio_util.multi_error_defer_to` or :func:`trio_util.defer_to_cancelled`.\nNote however that re-raising a 'leaf' exception will discard whatever part of the\ntraceback is attached to the `ExceptionGroup` itself, so we don't recommend this for\nerrors that will be presented to humans.\n\n..\n    TODO: what about `Cancelled`?  It's relevantly similar to `KeyboardInterrupt`,\n    but if you have multiple Cancelleds destined for different scopes, it seems\n    like it might be bad to abandon all-but-one of those - we might try to execute\n    some more code which then itself gets cancelled again, and incur more cleanup.\n    That's only a mild inefficiency though, and the semantics are fine overall.\n\n**Second**, you might want to treat the concurrency inside your code as an implementation\ndetail which is hidden from your users - for example, abstracting a protocol which\ninvolves sending and receiving data to a simple receive-only interface, or implementing\na context manager which maintains some background tasks for the length of the\n``async with`` block.\n\nThe simple option here is to ``raise MySpecificError from group``, allowing users to\nhandle your library-specific error.  This is simple and reliable, but doesn't completely\nhide the nursery.  *Do not* unwrap single exceptions if there could ever be multiple\nexceptions though; that always ends in latent bugs and then tears.\n\nThe more complex option is to ensure that only one exception can in fact happen at a time.\nThis is *very hard*, for example you'll need to handle `KeyboardInterrupt` somehow, and\nwe strongly recommend having a ``raise PleaseReportBug from group`` fallback just in case\nyou get a group containing more than one exception.\nThis is useful when writing a context manager which starts some background tasks, and then\nyields to user code which effectively runs 'inline' in the body of the nursery block.\nIn this case, the background tasks can be wrapped with e.g. the `outcome\n<https://pypi.org/project/outcome/>`__ library to ensure that only one exception\ncan be raised (from end-user code); and then you can either ``raise SomeInternalError``\nif a background task failed, or unwrap the user exception if that was the only error.\n\n..\n    For more on this pattern, see https://github.com/python-trio/trio/issues/2929\n    and the linked issue on trio-websocket.  We may want to provide a nursery mode\n    which handles this automatically; it's annoying but not too complicated and\n    seems like it might be a good feature to ship for such cases.\n\n**Third and most often**, the existence of a nursery in your code is not just an\nimplementation detail, and callers *should* be prepared to handle multiple exceptions\nin the form of an `ExceptionGroup`, whether with ``except*`` or manual inspection\nor by just letting it propagate to *their* callers.  Because this is so common,\nit's nurseries' default behavior and you don't need to do anything.\n\n.. _strict_exception_groups:\n\nHistorical Note: \"non-strict\" ExceptionGroups\n+++++++++++++++++++++++++++++++++++++++++++++\n\nIn early versions of Trio, the ``except*`` syntax hadn't be dreamt up yet, and we\nhadn't worked with structured concurrency for long or in large codebases.\nAs a concession to convenience, some APIs would therefore raise single exceptions,\nand only wrap concurrent exceptions in the old ``trio.MultiError`` type if there\nwere two or more.\n\nUnfortunately, the results were not good: calling code often didn't realize that\nsome function *could* raise a ``MultiError``, and therefore handle only the common\ncase - with the result that things would work well in testing, and then crash under\nheavier load (typically in production).  `asyncio.TaskGroup` learned from this\nexperience and *always* wraps errors into an `ExceptionGroup`, as does ``anyio``,\nand as of Trio 0.25 that's our default behavior too.\n\nWe currently support a compatibility argument ``strict_exception_groups=False`` to\n`trio.run` and `trio.open_nursery`, which restores the old behavior (although\n``MultiError`` itself has been fully removed).  We strongly advise against it for\nnew code, and encourage existing uses to migrate - we consider the option deprecated\nand plan to remove it after a period of documented and then runtime warnings.\n\n.. _exceptiongroup: https://pypi.org/project/exceptiongroup/\n\nSpawning tasks without becoming a parent\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSometimes it doesn't make sense for the task that starts a child to\ntake on responsibility for watching it. For example, a server task may\nwant to start a new task for each connection, but it can't listen for\nconnections and supervise children at the same time.\n\nThe solution here is simple once you see it: there's no requirement\nthat a nursery object stay in the task that created it! We can write\ncode like this:\n\n.. code-block:: python\n\n   async def new_connection_listener(handler, nursery):\n       while True:\n           conn = await get_new_connection()\n           nursery.start_soon(handler, conn)\n\n   async def server(handler):\n       async with trio.open_nursery() as nursery:\n           nursery.start_soon(new_connection_listener, handler, nursery)\n\nNotice that ``server`` opens a nursery and passes it to\n``new_connection_listener``, and then ``new_connection_listener`` is\nable to start new tasks as \"siblings\" of itself. Of course, in this\ncase, we could just as well have written:\n\n.. code-block:: python\n\n   async def server(handler):\n       async with trio.open_nursery() as nursery:\n           while True:\n               conn = await get_new_connection()\n               nursery.start_soon(handler, conn)\n\n\\...but sometimes things aren't so simple, and this trick comes in\nhandy.\n\nOne thing to remember, though: cancel scopes are inherited from the\nnursery, **not** from the task that calls ``start_soon``. So in this\nexample, the timeout does *not* apply to ``child`` (or to anything\nelse):\n\n.. code-block:: python\n\n   async def do_spawn(nursery):\n       with trio.move_on_after(TIMEOUT):  # don't do this, it has no effect\n           nursery.start_soon(child)\n\n   async with trio.open_nursery() as nursery:\n       nursery.start_soon(do_spawn, nursery)\n\n\nCustom supervisors\n~~~~~~~~~~~~~~~~~~\n\nThe default cleanup logic is often sufficient for simple cases, but\nwhat if you want a more sophisticated supervisor? For example, maybe\nyou have `Erlang envy <http://learnyousomeerlang.com/supervisors>`__\nand want features like automatic restart of crashed tasks. Trio itself\ndoesn't provide these kinds of features, but you can build them on\ntop; Trio's goal is to enforce basic hygiene and then get out of your\nway. (Specifically: Trio won't let you build a supervisor that exits\nand leaves orphaned tasks behind, and if you have an unhandled\nexception due to bugs or laziness then Trio will make sure they\npropagate.) And then you can wrap your fancy supervisor up in a\nlibrary and put it on PyPI, because supervisors are tricky and there's\nno reason everyone should have to write their own.\n\nFor example, here's a function that takes a list of functions, runs\nthem all concurrently, and returns the result from the one that\nfinishes first:\n\n.. code-block:: python\n\n   async def race(*async_fns):\n       if not async_fns:\n           raise ValueError(\"must pass at least one argument\")\n\n       winner = None\n\n       async def jockey(async_fn, cancel_scope):\n           nonlocal winner\n           winner = await async_fn()\n           cancel_scope.cancel()\n\n       async with trio.open_nursery() as nursery:\n           for async_fn in async_fns:\n               nursery.start_soon(jockey, async_fn, nursery.cancel_scope)\n\n       return winner\n\nThis works by starting a set of tasks which each try to run their\nfunction. As soon as the first function completes its execution, the task will set the nonlocal variable ``winner``\nfrom the outer scope to the result of the function, and cancel the other tasks using the passed in cancel scope. Once all tasks\nhave been cancelled (which exits the nursery block), the variable ``winner`` will be returned.\n\nHere if one or more of the racing functions raises an unhandled\nexception then Trio's normal handling kicks in: it cancels the others\nand then propagates the exception. If you want different behavior, you\ncan get that by adding a ``try`` block to the ``jockey`` function to\ncatch exceptions and handle them however you like.\n\n\nTask-related API details\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe nursery API\n+++++++++++++++\n\n.. autofunction:: open_nursery\n   :async-with: nursery\n\n\n.. autoclass:: Nursery()\n   :members: child_tasks, parent_task\n\n   .. automethod:: start(async_fn, *args, name = None)\n\n   .. automethod:: start_soon(async_fn, *args, name = None)\n\n.. attribute:: TASK_STATUS_IGNORED\n   :type: TaskStatus\n\n   See :meth:`Nursery.start`.\n\n.. autoclass:: TaskStatus(Protocol[StatusT])\n   :members:\n\n.. _task-local-storage:\n\nTask-local storage\n------------------\n\nSuppose you're writing a server that responds to network requests, and\nyou log some information about each request as you process it. If the\nserver is busy and there are multiple requests being handled at the\nsame time, then you might end up with logs like this:\n\n.. code-block:: none\n\n   Request handler started\n   Request handler started\n   Request handler finished\n   Request handler finished\n\nIn this log, it's hard to know which lines came from which\nrequest. (Did the request that started first also finish first, or\nnot?) One way to solve this is to assign each request a unique\nidentifier, and then include this identifier in each log message:\n\n.. code-block:: none\n\n   request 1: Request handler started\n   request 2: Request handler started\n   request 2: Request handler finished\n   request 1: Request handler finished\n\nThis way we can see that request 1 was slow: it started before request\n2 but finished afterwards. (You can also get `much fancier\n<https://opentracing.io/docs/>`__, but this is enough for an\nexample.)\n\nNow, here's the problem: how does the logging code know what the\nrequest identifier is? One approach would be to explicitly pass it\naround to every function that might want to emit logs... but that's\nbasically every function, because you never know when you might need\nto add a ``log.debug(...)`` call to some utility function buried deep\nin the call stack, and when you're in the middle of a debugging a\nnasty problem that last thing you want is to have to stop first and\nrefactor everything to pass through the request identifier! Sometimes\nthis is the right solution, but other times it would be much more\nconvenient if we could store the identifier in a global variable, so\nthat the logging function could look it up whenever it needed\nit. Except... a global variable can only have one value at a time, so\nif we have multiple handlers running at once then this isn't going to\nwork. What we need is something that's *like* a global variable, but\nthat can have different values depending on which request handler is\naccessing it.\n\nTo solve this problem, Python has a module in the standard\nlibrary: :mod:`contextvars`.\n\nHere's a toy example demonstrating how to use :mod:`contextvars`:\n\n.. literalinclude:: reference-core/contextvar-example.py\n\nExample output (yours may differ slightly):\n\n.. code-block:: none\n\n   request 1: Request handler started\n   request 2: Request handler started\n   request 0: Request handler started\n   request 2: Helper task a started\n   request 2: Helper task b started\n   request 1: Helper task a started\n   request 1: Helper task b started\n   request 0: Helper task b started\n   request 0: Helper task a started\n   request 2: Helper task b finished\n   request 2: Helper task a finished\n   request 2: Request received finished\n   request 0: Helper task a finished\n   request 1: Helper task a finished\n   request 1: Helper task b finished\n   request 1: Request received finished\n   request 0: Helper task b finished\n   request 0: Request received finished\n\nFor more information, read the\n`contextvars docs <https://docs.python.org/3/library/contextvars.html>`__.\n\n\n.. _synchronization:\n\nSynchronizing and communicating between tasks\n---------------------------------------------\n\nTrio provides a standard set of synchronization and inter-task\ncommunication primitives. These objects' APIs are generally modelled\noff of the analogous classes in the standard library, but with some\ndifferences.\n\n\nBlocking and non-blocking methods\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe standard library synchronization primitives have a variety of\nmechanisms for specifying timeouts and blocking behavior, and of\nsignaling whether an operation returned due to success versus a\ntimeout.\n\nIn Trio, we standardize on the following conventions:\n\n* We don't provide timeout arguments. If you want a timeout, then use\n  a cancel scope.\n\n* For operations that have a non-blocking variant, the blocking and\n  non-blocking variants are different methods with names like ``X``\n  and ``X_nowait``, respectively. (This is similar to\n  :class:`queue.Queue`, but unlike most of the classes in\n  :mod:`threading`.) We like this approach because it allows us to\n  make the blocking version async and the non-blocking version sync.\n\n* When a non-blocking method cannot succeed (the channel is empty, the\n  lock is already held, etc.), then it raises :exc:`trio.WouldBlock`.\n  There's no equivalent to the :exc:`queue.Empty` versus\n  :exc:`queue.Full` distinction – we just have the one exception that\n  we use consistently.\n\n\nFairness\n~~~~~~~~\n\nThese classes are all guaranteed to be \"fair\", meaning that when it\ncomes time to choose who will be next to acquire a lock, get an item\nfrom a queue, etc., then it always goes to the task which has been\nwaiting longest. It's `not entirely clear\n<https://github.com/python-trio/trio/issues/54>`__ whether this is the\nbest choice, but for now that's how it works.\n\nAs an example of what this means, here's a small program in which two\ntasks compete for a lock. Notice that the task which releases the lock\nalways immediately attempts to re-acquire it, before the other task has\na chance to run. (And remember that we're doing cooperative\nmulti-tasking here, so it's actually *deterministic* that the task\nreleasing the lock will call :meth:`~Lock.acquire` before the other\ntask wakes up; in Trio releasing a lock is not a checkpoint.)  With\nan unfair lock, this would result in the same task holding the lock\nforever and the other task being starved out. But if you run this,\nyou'll see that the two tasks politely take turns:\n\n.. code-block:: python\n\n   # fairness-demo.py\n\n   import trio\n\n   async def loopy_child(number, lock):\n       while True:\n           async with lock:\n               print(f\"Child {number} has the lock!\")\n               await trio.sleep(0.5)\n\n   async def main():\n       async with trio.open_nursery() as nursery:\n           lock = trio.Lock()\n           nursery.start_soon(loopy_child, 1, lock)\n           nursery.start_soon(loopy_child, 2, lock)\n\n   trio.run(main)\n\n\nBroadcasting an event with :class:`Event`\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. autoclass:: Event\n   :members:\n\n.. autoclass:: EventStatistics\n   :members:\n\n.. _channels:\n\nUsing channels to pass values between tasks\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n*Channels* allow you to safely and conveniently send objects between\ndifferent tasks. They're particularly useful for implementing\nproducer/consumer patterns.\n\nThe core channel API is defined by the abstract base classes\n:class:`trio.abc.SendChannel` and :class:`trio.abc.ReceiveChannel`.\nYou can use these to implement your own custom channels, that do\nthings like pass objects between processes or over the network. But in\nmany cases, you just want to pass objects between different tasks\ninside a single process, and for that you can use\n:func:`trio.open_memory_channel`:\n\n.. autofunction:: open_memory_channel(max_buffer_size)\n\n.. note:: If you've used the :mod:`threading` or :mod:`asyncio`\n   modules, you may be familiar with :class:`queue.Queue` or\n   :class:`asyncio.Queue`. In Trio, :func:`open_memory_channel` is\n   what you use when you're looking for a queue. The main difference\n   is that Trio splits the classic queue interface up into two\n   objects. The advantage of this is that it makes it possible to put\n   the two ends in different processes without rewriting your code,\n   and that we can close the two sides separately.\n\n`MemorySendChannel` and `MemoryReceiveChannel` also expose several\nmore features beyond the core channel interface:\n\n.. autoclass:: MemorySendChannel\n   :members:\n\n.. autoclass:: MemoryReceiveChannel\n   :members:\n\n.. autoclass:: MemoryChannelStatistics\n   :members:\n\nA simple channel example\n++++++++++++++++++++++++\n\nHere's a simple example of how to use memory channels:\n\n.. literalinclude:: reference-core/channels-simple.py\n\nIf you run this, it prints:\n\n.. code-block:: none\n\n   got value \"message 0\"\n   got value \"message 1\"\n   got value \"message 2\"\n\nAnd then it hangs forever. (Use control-C to quit.)\n\n\n.. _channel-shutdown:\n\nClean shutdown with channels\n++++++++++++++++++++++++++++\n\nOf course we don't generally like it when programs hang. What\nhappened? The problem is that the producer sent 3 messages and then\nexited, but the consumer has no way to tell that the producer is gone:\nfor all it knows, another message might be coming along any moment. So\nit hangs forever waiting for the 4th message.\n\nHere's a new version that fixes this: it produces the same output as\nthe previous version, and then exits cleanly. The only change is the\naddition of ``async with`` blocks inside the producer and consumer:\n\n.. literalinclude:: reference-core/channels-shutdown.py\n   :emphasize-lines: 12,18\n\nThe really important thing here is the producer's ``async with`` .\nWhen the producer exits, this closes the ``send_channel``, and that\ntells the consumer that no more messages are coming, so it can cleanly\nexit its ``async for`` loop. Then the program shuts down because both\ntasks have exited.\n\nWe also added an ``async with`` to the consumer. This isn't as\nimportant, but it can help us catch mistakes or other problems. For\nexample, suppose that the consumer exited early for some reason –\nmaybe because of a bug. Then the producer would be sending messages\ninto the void, and might get stuck indefinitely. But, if the consumer\ncloses its ``receive_channel``, then the producer will get a\n:exc:`BrokenResourceError` to alert it that it should stop sending\nmessages because no-one is listening.\n\nIf you want to see the effect of the consumer exiting early, try\nadding a ``break`` statement to the ``async for`` loop – you should\nsee a :exc:`BrokenResourceError` from the producer.\n\n\n.. _channel-mpmc:\n\nManaging multiple producers and/or multiple consumers\n+++++++++++++++++++++++++++++++++++++++++++++++++++++\n\nYou can also have multiple producers, and multiple consumers, all\nsharing the same channel. However, this makes shutdown a little more\ncomplicated.\n\nFor example, consider this naive extension of our previous example,\nnow with two producers and two consumers:\n\n.. literalinclude:: reference-core/channels-mpmc-broken.py\n\nThe two producers, A and B, send 3 messages apiece. These are then\nrandomly distributed between the two consumers, X and Y. So we're\nhoping to see some output like:\n\n.. code-block:: none\n\n   consumer Y got value '0 from producer B'\n   consumer X got value '0 from producer A'\n   consumer Y got value '1 from producer A'\n   consumer Y got value '1 from producer B'\n   consumer X got value '2 from producer B'\n   consumer X got value '2 from producer A'\n\nHowever, on most runs, that's not what happens – the first part of the\noutput is OK, and then when we get to the end the program crashes with\n:exc:`ClosedResourceError`. If you run the program a few times, you'll\nsee that sometimes the traceback shows ``send`` crashing, and other\ntimes it shows ``receive`` crashing, and you might even find that on\nsome runs it doesn't crash at all.\n\nHere's what's happening: suppose that producer A finishes first. It\nexits, and its ``async with`` block closes the ``send_channel``. But\nwait! Producer B was still using that ``send_channel``... so the next\ntime B calls ``send``, it gets a :exc:`ClosedResourceError`.\n\nSometimes, though if we're lucky, the two producers might finish at\nthe same time (or close enough), so they both make their last ``send``\nbefore either of them closes the ``send_channel``.\n\nBut, even if that happens, we're not out of the woods yet! After the\nproducers exit, the two consumers race to be the first to notice that\nthe ``send_channel`` has closed. Suppose that X wins the race. It\nexits its ``async for`` loop, then exits the ``async with`` block...\nand closes the ``receive_channel``, while Y is still using it. Again,\nthis causes a crash.\n\nWe could avoid this by using some complicated bookkeeping to make sure\nthat only the *last* producer and the *last* consumer close their\nchannel endpoints... but that would be tiresome and fragile.\nFortunately, there's a better way! Here's a fixed version of our\nprogram above:\n\n.. literalinclude:: reference-core/channels-mpmc-fixed.py\n   :emphasize-lines: 8, 10, 11, 13, 14\n\nThis example demonstrates using the `MemorySendChannel.clone` and\n`MemoryReceiveChannel.clone` methods. What these do is create copies\nof our endpoints, that act just like the original – except that they\ncan be closed independently. And the underlying channel is only closed\nafter *all* the clones have been closed. So this completely solves our\nproblem with shutdown, and if you run this program, you'll see it\nprint its six lines of output and then exits cleanly.\n\nNotice a small trick we use: the code in ``main`` creates clone\nobjects to pass into all the child tasks, and then closes the original\nobjects using ``async with``. Another option is to pass clones into\nall-but-one of the child tasks, and then pass the original object into\nthe last task, like:\n\n.. code-block:: python\n\n   # Also works, but is more finicky:\n   send_channel, receive_channel = trio.open_memory_channel(0)\n   nursery.start_soon(producer, \"A\", send_channel.clone())\n   nursery.start_soon(producer, \"B\", send_channel)\n   nursery.start_soon(consumer, \"X\", receive_channel.clone())\n   nursery.start_soon(consumer, \"Y\", receive_channel)\n\nBut this is more error-prone, especially if you use a loop to spawn\nthe producers/consumers.\n\nJust make sure that you don't write:\n\n.. code-block:: python\n\n   # Broken, will cause program to hang:\n   send_channel, receive_channel = trio.open_memory_channel(0)\n   nursery.start_soon(producer, \"A\", send_channel.clone())\n   nursery.start_soon(producer, \"B\", send_channel.clone())\n   nursery.start_soon(consumer, \"X\", receive_channel.clone())\n   nursery.start_soon(consumer, \"Y\", receive_channel.clone())\n\nHere we pass clones into the tasks, but never close the original\nobjects. That means we have 3 send channel objects (the original + two\nclones), but we only close 2 of them, so the consumers will hang\naround forever waiting for that last one to be closed.\n\n\n.. _channel-buffering:\n\nBuffering in channels\n+++++++++++++++++++++\n\nWhen you call :func:`open_memory_channel`, you have to specify how\nmany values can be buffered internally in the channel. If the buffer\nis full, then any task that calls :meth:`~trio.abc.SendChannel.send`\nwill stop and wait for another task to call\n:meth:`~trio.abc.ReceiveChannel.receive`. This is useful because it\nproduces *backpressure*: if the channel producers are running faster\nthan the consumers, then it forces the producers to slow down.\n\nYou can disable buffering entirely, by doing\n``open_memory_channel(0)``. In that case any task that calls\n:meth:`~trio.abc.SendChannel.send` will wait until another task calls\n:meth:`~trio.abc.ReceiveChannel.receive`, and vice versa. This is similar to\nhow channels work in the `classic Communicating Sequential Processes\nmodel <https://en.wikipedia.org/wiki/Channel_(programming)>`__, and is\na reasonable default if you aren't sure what size buffer to use.\n(That's why we used it in the examples above.)\n\nAt the other extreme, you can make the buffer unbounded by using\n``open_memory_channel(math.inf)``. In this case,\n:meth:`~trio.abc.SendChannel.send` *always* returns immediately.\nNormally, this is a bad idea. To see why, consider a program where the\nproducer runs more quickly than the consumer:\n\n.. literalinclude:: reference-core/channels-backpressure.py\n\nIf you run this program, you'll see output like:\n\n.. code-block:: none\n\n   Sent message: 0\n   Received message: 0\n   Sent message: 1\n   Sent message: 2\n   Sent message: 3\n   Sent message: 4\n   Sent message: 5\n   Sent message: 6\n   Sent message: 7\n   Sent message: 8\n   Sent message: 9\n   Received message: 1\n   Sent message: 10\n   Sent message: 11\n   Sent message: 12\n   ...\n\nOn average, the producer sends ten messages per second, but the\nconsumer only calls ``receive`` once per second. That means that each\nsecond, the channel's internal buffer has to grow to hold an extra\nnine items. After a minute, the buffer will have ~540 items in it;\nafter an hour, that grows to ~32,400. Eventually, the program will run\nout of memory. And well before we run out of memory, our latency on\nhandling individual messages will become abysmal. For example, at the\none minute mark, the producer is sending message ~600, but the\nconsumer is still processing message ~60. Message 600 will have to sit\nin the channel for ~9 minutes before the consumer catches up and\nprocesses it.\n\nNow try replacing ``open_memory_channel(math.inf)`` with\n``open_memory_channel(0)``, and run it again. We get output like:\n\n.. code-block:: none\n\n   Sent message: 0\n   Received message: 0\n   Received message: 1\n   Sent message: 1\n   Received message: 2\n   Sent message: 2\n   Sent message: 3\n   Received message: 3\n   ...\n\nNow the ``send`` calls wait for the ``receive`` calls to finish, which\nforces the producer to slow down to match the consumer's speed. (It\nmight look strange that some values are reported as \"Received\" before\nthey're reported as \"Sent\"; this happens because the actual\nsend/receive happen at the same time, so which line gets printed first\nis random.)\n\nNow, let's try setting a small but nonzero buffer size, like\n``open_memory_channel(3)``. what do you think will happen?\n\nI get:\n\n.. code-block:: none\n\n   Sent message: 0\n   Received message: 0\n   Sent message: 1\n   Sent message: 2\n   Sent message: 3\n   Received message: 1\n   Sent message: 4\n   Received message: 2\n   Sent message: 5\n   ...\n\nSo you can see that the producer runs ahead by 3 messages, and then\nstops to wait: when the consumer reads message 1, it sends message 4,\nthen when the consumer reads message 2, it sends message 5, and so on.\nOnce it reaches the steady state, this version acts just like our\nprevious version where we set the buffer size to 0, except that it\nuses a bit more memory and each message sits in the buffer for a bit\nlonger before being processed (i.e., the message latency is higher).\n\nOf course real producers and consumers are usually more complicated\nthan this, and in some situations, a modest amount of buffering might\nimprove throughput. But too much buffering wastes memory and increases\nlatency, so if you want to tune your application you should experiment\nto see what value works best for you.\n\n**Why do we even support unbounded buffers then?** Good question!\nDespite everything we saw above, there are times when you actually do\nneed an unbounded buffer. For example, consider a web crawler that\nuses a channel to keep track of all the URLs it still wants to crawl.\nEach crawler runs a loop where it takes a URL from the channel,\nfetches it, checks the HTML for outgoing links, and then adds the new\nURLs to the channel. This creates a *circular flow*, where each\nconsumer is also a producer. In this case, if your channel buffer gets\nfull, then the crawlers will block when they try to add new URLs to\nthe channel, and if all the crawlers got blocked, then they aren't\ntaking any URLs out of the channel, so they're stuck forever in a\ndeadlock. Using an unbounded channel avoids this, because it means\nthat :meth:`~trio.abc.SendChannel.send` never blocks.\n\n\nLower-level synchronization primitives\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPersonally, I find that events and channels are usually enough to\nimplement most things I care about, and lead to easier to read code\nthan the lower-level primitives discussed in this section. But if you\nneed them, they're here. (If you find yourself reaching for these\nbecause you're trying to implement a new higher-level synchronization\nprimitive, then you might also want to check out the facilities in\n:mod:`trio.lowlevel` for a more direct exposure of Trio's underlying\nsynchronization logic. All of classes discussed in this section are\nimplemented on top of the public APIs in :mod:`trio.lowlevel`; they\ndon't have any special access to Trio's internals.)\n\n.. autoclass:: CapacityLimiter\n   :members:\n\n.. autoclass:: Semaphore\n   :members:\n\n.. We have to use :inherited-members: here because all the actual lock\n   methods are stashed in _LockImpl. Weird side-effect of having both\n   Lock and StrictFIFOLock, but wanting both to be marked Final so\n   neither can inherit from the other.\n\n.. autoclass:: Lock\n   :members:\n   :inherited-members:\n\n.. autoclass:: StrictFIFOLock\n   :members:\n\n.. autoclass:: Condition\n   :members:\n\nThese primitives return statistics objects that can be inspected.\n\n.. autoclass:: CapacityLimiterStatistics\n   :members:\n\n.. autoclass:: LockStatistics\n   :members:\n\n.. autoclass:: ConditionStatistics\n   :members:\n\n.. _async-generators:\n\nNotes on async generators\n-------------------------\n\nPython 3.6 added support for *async generators*, which can use\n``await``, ``async for``, and ``async with`` in between their ``yield``\nstatements. As you might expect, you use ``async for`` to iterate\nover them. :pep:`525` has many more details if you want them.\n\nFor example, the following is a roundabout way to print\nthe numbers 0 through 9 with a 1-second delay before each one:\n\n.. code-block:: python\n\n    async def range_slowly(*args):\n        \"\"\"Like range(), but adds a 1-second sleep before each value.\"\"\"\n        for value in range(*args):\n            await trio.sleep(1)\n            yield value\n\n    async def use_it():\n        async for value in range_slowly(10):\n            print(value)\n\n    trio.run(use_it)\n\nTrio supports async generators, but there's several caveats and it's very\nhard to handle them properly. Therefore Trio bundles a helper,\n`trio.as_safe_channel` that does it for you.\n\n\n.. autofunction:: trio.as_safe_channel\n\nThe details behind the problems are described in the following sections.\n\nFinalization\n~~~~~~~~~~~~\n\nIf you iterate over an async generator in its entirety, like the\nexample above does, then the execution of the async generator will\noccur completely in the context of the code that's iterating over it,\nand there aren't too many surprises.\n\nIf you abandon a partially-completed async generator, though, such as\nby ``break``\\ing out of the iteration, things aren't so simple.  The\nasync generator iterator object is still alive, waiting for you to\nresume iterating it so it can produce more values. At some point,\nPython will realize that you've dropped all references to the\niterator, and will call on Trio to throw in a `GeneratorExit` exception\nso that any remaining cleanup code inside the generator has a chance\nto run: ``finally`` blocks, ``__aexit__`` handlers, and so on.\n\nSo far, so good. Unfortunately, Python provides no guarantees about\n*when* this happens. It could be as soon as you break out of the\n``async for`` loop, or an arbitrary amount of time later. It could\neven be after the entire Trio run has finished! Just about the only\nguarantee is that it *won't* happen in the task that was using the\ngenerator. That task will continue on with whatever else it's doing,\nand the async generator cleanup will happen \"sometime later,\nsomewhere else\": potentially with different context variables,\nnot subject to timeouts, and/or after any nurseries you're using have\nbeen closed.\n\nIf you don't like that ambiguity, and you want to ensure that a\ngenerator's ``finally`` blocks and ``__aexit__`` handlers execute as\nsoon as you're done using it, then you'll need to wrap your use of the\ngenerator in something like `async_generator.aclosing()\n<https://async-generator.readthedocs.io/en/latest/reference.html#context-managers>`__:\n\n.. code-block:: python\n\n    # Instead of this:\n    async for value in my_generator():\n        if value == 42:\n            break\n\n    # Do this:\n    async with aclosing(my_generator()) as aiter:\n        async for value in aiter:\n            if value == 42:\n                break\n\nThis is cumbersome, but Python unfortunately doesn't provide any other\nreliable options. If you use ``aclosing()``, then\nyour generator's cleanup code executes in the same context as the\nrest of its iterations, so timeouts, exceptions, and context\nvariables work like you'd expect.\n\nIf you don't use ``aclosing()``, then Trio will do\nits best anyway, but you'll have to contend with the following semantics:\n\n* The cleanup of the generator occurs in a cancelled context, i.e.,\n  all blocking calls executed during cleanup will raise `Cancelled`.\n  This is to compensate for the fact that any timeouts surrounding\n  the original use of the generator have been long since forgotten.\n\n* The cleanup runs without access to any :ref:`context variables\n  <task-local-storage>` that may have been present when the generator\n  was originally being used.\n\n* If the generator raises an exception during cleanup, then it's\n  printed to the ``trio.async_generator_errors`` logger and otherwise\n  ignored.\n\n* If an async generator is still alive at the end of the whole\n  call to :func:`trio.run`, then it will be cleaned up after all\n  tasks have exited and before :func:`trio.run` returns.\n  Since the \"system nursery\" has already been closed at this point,\n  Trio isn't able to support any new calls to\n  :func:`trio.lowlevel.spawn_system_task`.\n\nIf you plan to run your code on PyPy to take advantage of its better\nperformance, you should be aware that PyPy is *far more likely* than\nCPython to perform async generator cleanup at a time well after the\nlast use of the generator. (This is a consequence of the fact that\nPyPy does not use reference counting to manage memory.)  To help catch\nissues like this, Trio will issue a `ResourceWarning` (ignored by\ndefault, but enabled when running under ``python -X dev`` for example)\nfor each async generator that needs to be handled through the fallback\nfinalization path.\n\nCancel scopes and nurseries\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. warning:: You may not write a ``yield`` statement that suspends an async generator\n   inside a `CancelScope` or `Nursery` that was entered within the generator.\n\nThat is, this is OK:\n\n.. code-block:: python\n\n    async def some_agen():\n        with trio.move_on_after(1):\n            await long_operation()\n        yield \"first\"\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(task1)\n            nursery.start_soon(task2)\n        yield \"second\"\n        ...\n\nBut this is not:\n\n.. code-block:: python\n\n    async def some_agen():\n        with trio.move_on_after(1):\n            yield \"first\"\n        async with trio.open_nursery() as nursery:\n            yield \"second\"\n        ...\n\nAsync generators decorated with ``@asynccontextmanager`` to serve as\nthe template for an async context manager are *not* subject to this\nconstraint, because ``@asynccontextmanager`` uses them in a limited\nway that doesn't create problems.\n\nViolating the rule described in this section will sometimes get you a\nuseful error message, but Trio is not able to detect all such cases,\nso sometimes you'll get an unhelpful `TrioInternalError`. (And\nsometimes it will seem to work, which is probably the worst outcome of\nall, since then you might not notice the issue until you perform some\nminor refactoring of the generator or the code that's iterating it, or\njust get unlucky. There is a draft :pep:`789` with accompanying\n`discussion thread\n<https://discuss.python.org/t/preventing-yield-inside-certain-context-managers/1091>`__\nthat would at least make it fail consistently.)\n\nThe reason for the restriction on cancel scopes has to do with the\ndifficulty of noticing when a generator gets suspended and\nresumed. The cancel scopes inside the generator shouldn't affect code\nrunning outside the generator, but Trio isn't involved in the process\nof exiting and reentering the generator, so it would be hard pressed\nto keep its cancellation plumbing in the correct state. Nurseries\nuse a cancel scope internally, so they have all the problems of cancel\nscopes plus a number of problems of their own: for example, when\nthe generator is suspended, what should the background tasks do?\nThere's no good way to suspend them, but if they keep running and throw\nan exception, where can that exception be reraised?\n\nFor more discussion, see\nTrio issues `264 <https://github.com/python-trio/trio/issues/264>`__\n(especially `this comment\n<https://github.com/python-trio/trio/issues/264#issuecomment-418989328>`__)\nand `638 <https://github.com/python-trio/trio/issues/638>`__.\n\n\n.. _threads:\n\nThreads (if you must)\n---------------------\n\nIn a perfect world, all third-party libraries and low-level APIs would\nbe natively async and integrated into Trio, and all would be happiness\nand rainbows.\n\nThat world, alas, does not (yet) exist. Until it does, you may find\nyourself needing to interact with non-Trio APIs that do rude things\nlike \"blocking\".\n\nIn acknowledgment of this reality, Trio provides two useful utilities\nfor working with real, operating-system level,\n:mod:`threading`\\-module-style threads. First, if you're in Trio but\nneed to push some blocking I/O into a thread, there's\n`trio.to_thread.run_sync`. And if you're in a thread and need\nto communicate back with Trio, you can use\n:func:`trio.from_thread.run` and :func:`trio.from_thread.run_sync`.\n\n\n.. _worker-thread-limiting:\n\nTrio's philosophy about managing worker threads\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you've used other I/O frameworks, you may have encountered the\nconcept of a \"thread pool\", which is most commonly implemented as a\nfixed size collection of threads that hang around waiting for jobs to\nbe assigned to them. These solve two different problems: First,\nreusing the same threads over and over is more efficient than\nstarting and stopping a new thread for every job you need done;\nbasically, the pool acts as a kind of cache for idle threads. And\nsecond, having a fixed size avoids getting into a situation where\n100,000 jobs are submitted simultaneously, and then 100,000 threads\nare spawned and the system gets overloaded and crashes. Instead, the N\nthreads start executing the first N jobs, while the other\n(100,000 - N) jobs sit in a queue and wait their turn. Which is\ngenerally what you want, and this is how\n:func:`trio.to_thread.run_sync` works by default.\n\nThe downside of this kind of thread pool is that sometimes, you need\nmore sophisticated logic for controlling how many threads are run at\nonce. For example, you might want a policy like \"at most 20 threads\ntotal, but no more than 3 of those can be running jobs associated with\nthe same user account\", or you might want a pool whose size is\ndynamically adjusted over time in response to system conditions.\n\nIt's even possible for a fixed-size policy to cause unexpected\n`deadlocks <https://en.wikipedia.org/wiki/Deadlock>`__. Imagine a\nsituation where we have two different types of blocking jobs that you\nwant to run in the thread pool, type A and type B. Type A is pretty\nsimple: it just runs and completes pretty quickly. But type B is more\ncomplicated: it has to stop in the middle and wait for some other work\nto finish, and that other work includes running a type A job. Now,\nsuppose you submit N jobs of type B to the pool. They all start\nrunning, and then eventually end up submitting one or more jobs of\ntype A. But since every thread in our pool is already busy, the type A\njobs don't actually start running – they just sit in a queue waiting\nfor the type B jobs to finish. But the type B jobs will never finish,\nbecause they're waiting for the type A jobs. Our system has\ndeadlocked. The ideal solution to this problem is to avoid having type\nB jobs in the first place – generally it's better to keep complex\nsynchronization logic in the main Trio thread. But if you can't do\nthat, then you need a custom thread allocation policy that tracks\nseparate limits for different types of jobs, and make it impossible\nfor type B jobs to fill up all the slots that type A jobs need to run.\n\nSo, we can see that it's important to be able to change the policy\ncontrolling the allocation of threads to jobs. But in many frameworks,\nthis requires implementing a new thread pool from scratch, which is\nhighly non-trivial; and if different types of jobs need different\npolicies, then you may have to create multiple pools, which is\ninefficient because now you effectively have two different thread\ncaches that aren't sharing resources.\n\nTrio's solution to this problem is to split worker thread management\ninto two layers. The lower layer is responsible for taking blocking\nI/O jobs and arranging for them to run immediately on some worker\nthread. It takes care of solving the tricky concurrency problems\ninvolved in managing threads and is responsible for optimizations like\nreusing threads, but has no admission control policy: if you give it\n100,000 jobs, it will spawn 100,000 threads. The upper layer is\nresponsible for providing the policy to make sure that this doesn't\nhappen – but since it *only* has to worry about policy, it can be much\nsimpler. In fact, all there is to it is the ``limiter=`` argument\npassed to :func:`trio.to_thread.run_sync`. This defaults to a global\n:class:`CapacityLimiter` object, which gives us the classic fixed-size\nthread pool behavior. (See\n:func:`trio.to_thread.current_default_thread_limiter`.) But if you\nwant to use \"separate pools\" for type A jobs and type B jobs, then\nit's just a matter of creating two separate :class:`CapacityLimiter`\nobjects and passing them in when running these jobs. Or here's an\nexample of defining a custom policy that respects the global thread\nlimit, while making sure that no individual user can use more than 3\nthreads at a time:\n\n.. code-block:: python\n\n   class CombinedLimiter:\n        def __init__(self, first, second):\n            self._first = first\n            self._second = second\n\n        async def acquire_on_behalf_of(self, borrower):\n            # Acquire both, being careful to clean up properly on error\n            await self._first.acquire_on_behalf_of(borrower)\n            try:\n                await self._second.acquire_on_behalf_of(borrower)\n            except:\n                self._first.release_on_behalf_of(borrower)\n                raise\n\n        def release_on_behalf_of(self, borrower):\n            # Release both, being careful to clean up properly on error\n            try:\n                self._second.release_on_behalf_of(borrower)\n            finally:\n                self._first.release_on_behalf_of(borrower)\n\n\n   # Use a weak value dictionary, so that we don't waste memory holding\n   # limiter objects for users who don't have any worker threads running.\n   USER_LIMITERS = weakref.WeakValueDictionary()\n   MAX_THREADS_PER_USER = 3\n\n   def get_user_limiter(user_id):\n       try:\n           return USER_LIMITERS[user_id]\n       except KeyError:\n           per_user_limiter = trio.CapacityLimiter(MAX_THREADS_PER_USER)\n           global_limiter = trio.current_default_thread_limiter()\n           # IMPORTANT: acquire the per_user_limiter before the global_limiter.\n           # If we get 100 jobs for a user at the same time, we want\n           # to only allow 3 of them at a time to even compete for the\n           # global thread slots.\n           combined_limiter = CombinedLimiter(per_user_limiter, global_limiter)\n           USER_LIMITERS[user_id] = combined_limiter\n           return combined_limiter\n\n\n   async def run_sync_in_thread_for_user(user_id, sync_fn, *args):\n       combined_limiter = get_user_limiter(user_id)\n       return await trio.to_thread.run_sync(sync_fn, *args, limiter=combined_limiter)\n\n\n.. module:: trio.to_thread\n.. currentmodule:: trio\n\nPutting blocking I/O into worker threads\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. autofunction:: trio.to_thread.run_sync\n\n.. autofunction:: trio.to_thread.current_default_thread_limiter\n\n\n.. module:: trio.from_thread\n.. currentmodule:: trio\n\nGetting back into the Trio thread from another thread\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. autofunction:: trio.from_thread.run\n\n.. autofunction:: trio.from_thread.run_sync\n\n\nThis will probably be clearer with an example. Here we demonstrate how\nto spawn a child thread, and then use a :ref:`memory channel\n<channels>` to send messages between the thread and a Trio task:\n\n.. literalinclude:: reference-core/from-thread-example.py\n\n.. note::\n\n   The ``from_thread.run*`` functions reuse the host task that called\n   :func:`trio.to_thread.run_sync` to run your provided function, as long as you're\n   using the default ``abandon_on_cancel=False`` so Trio can be sure that the task will remain\n   around to perform the work. If you pass ``abandon_on_cancel=True`` at the outset, or if\n   you provide a :class:`~trio.lowlevel.TrioToken` when calling back in to Trio, your\n   functions will be executed in a new system task. Therefore, the\n   :func:`~trio.lowlevel.current_task`, :func:`current_effective_deadline`, or other\n   task-tree specific values may differ depending on keyword argument values.\n\nYou can also use :func:`trio.from_thread.check_cancelled` to check for cancellation from\na thread that was spawned by :func:`trio.to_thread.run_sync`. If the call to\n:func:`~trio.to_thread.run_sync` was cancelled, then\n:func:`~trio.from_thread.check_cancelled` will raise :func:`trio.Cancelled`.\nIt's like ``trio.from_thread.run(trio.sleep, 0)``, but much faster.\n\n.. autofunction:: trio.from_thread.check_cancelled\n\nThreads and task-local storage\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen working with threads, you can use the same `contextvars` we discussed above,\nbecause their values are preserved.\n\nThis is done by automatically copying the `contextvars` context when you use any of:\n\n* `trio.to_thread.run_sync`\n* `trio.from_thread.run`\n* `trio.from_thread.run_sync`\n\nThat means that the values of the context variables are accessible even in worker\nthreads, or when sending a function to be run in the main/parent Trio thread using\n`trio.from_thread.run` *from* one of these worker threads.\n\nBut it also means that as the context is not the same but a copy, if you `set` the\ncontext variable value *inside* one of these functions that work in threads, the\nnew value will only be available in that context (that was copied). So, the new value\nwill be available for that function and other internal/children tasks, but the value\nwon't be available in the parent thread.\n\nIf you need to modify values that would live in the context variables and you need to\nmake those modifications from the child threads, you can instead set a mutable object\n(e.g. a dictionary) in the context variable of the top level/parent Trio thread.\nThen in the children, instead of setting the context variable, you can ``get`` the same\nobject, and modify its values. That way you keep the same object in the context\nvariable and only mutate it in child threads.\n\nThis way, you can modify the object content in child threads and still access the\nnew content in the parent thread.\n\nHere's an example:\n\n.. literalinclude:: reference-core/thread-contextvars-example.py\n\nRunning that script will result in the output:\n\n.. code-block:: none\n\n    Processed user 2 with message Hello 2 in a thread worker\n    Processed user 0 with message Hello 0 in a thread worker\n    Processed user 1 with message Hello 1 in a thread worker\n    New contextvar value from worker thread for user 2: Hello 2\n    New contextvar value from worker thread for user 1: Hello 1\n    New contextvar value from worker thread for user 0: Hello 0\n\nIf you are using ``contextvars`` or you are using a library that uses them, now you\nknow how they interact when working with threads in Trio.\n\nBut have in mind that in many cases it might be a lot simpler to *not* use context\nvariables in your own code and instead pass values in arguments, as it might be more\nexplicit and might be easier to reason about.\n\n.. note::\n\n   The context is automatically copied instead of using the same parent context because\n   a single context can't be used in more than one thread, it's not supported by\n   ``contextvars``.\n\n\n.. _interactive debugging:\n\n\nInteractive debugging\n---------------------\n\nWhen you start an interactive Python session to debug any async program\n(whether it's based on ``asyncio``, Trio, or something else), every await\nexpression needs to be inside an async function:\n\n.. code-block:: console\n\n   $ python\n   Python 3.10.6\n   Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n   >>> import trio\n   >>> await trio.sleep(1)\n   File \"<stdin>\", line 1\n   SyntaxError: 'await' outside function\n   >>> async def main():\n   ...     print(\"hello...\")\n   ...     await trio.sleep(1)\n   ...     print(\"world!\")\n   ...\n   >>> trio.run(main)\n   hello...\n   world!\n\nThis can make it difficult to iterate quickly since you have to redefine the\nwhole function body whenever you make a tweak.\n\nTrio provides a modified interactive console that lets you ``await`` at the top\nlevel. You can access this console by running ``python -m trio``:\n\n.. code-block:: console\n\n   $ python -m trio\n   Trio 0.21.0+dev, Python 3.10.6\n   Use \"await\" directly instead of \"trio.run()\".\n   Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n   >>> import trio\n   >>> print(\"hello...\"); await trio.sleep(1); print(\"world!\")\n   hello...\n   world!\n\nIf you are an IPython user, you can use IPython's `autoawait\n<https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-autoawait>`__\nfunction. This can be enabled within the IPython shell by running the magic command\n``%autoawait trio``. To have ``autoawait`` enabled whenever Trio installed, you can\nadd the following to your IPython startup files.\n(e.g. ``~/.ipython/profile_default/startup/10-async.py``)\n\n.. code-block::\n\n   try:\n       import trio\n       get_ipython().run_line_magic(\"autoawait\", \"trio\")\n   except ImportError:\n       pass\n\nExceptions and warnings\n-----------------------\n\n.. autoexception:: Cancelled\n\n.. autoexception:: TooSlowError\n\n.. autoexception:: WouldBlock\n\n.. autoexception:: EndOfChannel\n\n.. autoexception:: BusyResourceError\n\n.. autoexception:: ClosedResourceError\n\n.. autoexception:: BrokenResourceError\n\n.. autoexception:: RunFinishedError\n\n.. autoexception:: TrioInternalError\n\n.. autoexception:: TrioDeprecationWarning\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/reference-io.rst",
    "content": ".. currentmodule:: trio\n\nI/O in Trio\n===========\n\n.. _abstract-stream-api:\n\nThe abstract Stream API\n-----------------------\n\nTrio provides a set of abstract base classes that define a standard\ninterface for unidirectional and bidirectional byte streams.\n\nWhy is this useful? Because it lets you write generic protocol\nimplementations that can work over arbitrary transports, and easily\ncreate complex transport configurations. Here's some examples:\n\n* :class:`trio.SocketStream` wraps a raw socket (like a TCP connection\n  over the network), and converts it to the standard stream interface.\n\n* :class:`trio.SSLStream` is a \"stream adapter\" that can take any\n  object that implements the :class:`trio.abc.Stream` interface, and\n  convert it into an encrypted stream. In Trio the standard way to\n  speak SSL over the network is to wrap an\n  :class:`~trio.SSLStream` around a :class:`~trio.SocketStream`.\n\n* If you spawn a :ref:`subprocess <subprocess>`, you can get a\n  :class:`~trio.abc.SendStream` that lets you write to its stdin, and\n  a :class:`~trio.abc.ReceiveStream` that lets you read from its\n  stdout. If for some reason you wanted to speak SSL to a subprocess,\n  you could use a :class:`StapledStream` to combine its stdin/stdout\n  into a single bidirectional :class:`~trio.abc.Stream`, and then wrap\n  that in an :class:`~trio.SSLStream`:\n\n  .. code-block:: python\n\n     ssl_context = ssl.create_default_context()\n     ssl_context.check_hostname = False\n     s = SSLStream(StapledStream(process.stdin, process.stdout), ssl_context)\n\n* It sometimes happens that you want to connect to an HTTPS server,\n  but you have to go through a web proxy... and the proxy also uses\n  HTTPS. So you end up having to do `SSL-on-top-of-SSL\n  <https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/>`__. In\n  Trio this is trivial – just wrap your first\n  :class:`~trio.SSLStream` in a second\n  :class:`~trio.SSLStream`:\n\n  .. code-block:: python\n\n     # Get a raw SocketStream connection to the proxy:\n     s0 = await open_tcp_stream(\"proxy\", 443)\n\n     # Set up SSL connection to proxy:\n     s1 = SSLStream(s0, proxy_ssl_context, server_hostname=\"proxy\")\n     # Request a connection to the website\n     await s1.send_all(b\"CONNECT website:443 / HTTP/1.0\\r\\n\\r\\n\")\n     await check_CONNECT_response(s1)\n\n     # Set up SSL connection to the real website. Notice that s1 is\n     # already an SSLStream object, and here we're wrapping a second\n     # SSLStream object around it.\n     s2 = SSLStream(s1, website_ssl_context, server_hostname=\"website\")\n     # Make our request\n     await s2.send_all(b\"GET /index.html HTTP/1.0\\r\\n\\r\\n\")\n     ...\n\n* The :mod:`trio.testing` module provides a set of :ref:`flexible\n  in-memory stream object implementations <testing-streams>`, so if\n  you have a protocol implementation to test then you can start\n  two tasks, set up a virtual \"socket\" connecting them, and then do\n  things like inject random-but-repeatable delays into the connection.\n\n\nAbstract base classes\n~~~~~~~~~~~~~~~~~~~~~\n\n.. currentmodule:: trio.abc\n\n.. http://docutils.sourceforge.net/docs/ref/rst/directives.html#list-table\n\n.. list-table:: Overview: abstract base classes for I/O\n   :widths: auto\n   :header-rows: 1\n\n   * - Abstract base class\n     - Inherits from...\n     - Adds these abstract methods...\n     - And these concrete methods.\n     - Example implementations\n   * - :class:`AsyncResource`\n     -\n     - :meth:`~AsyncResource.aclose`\n     - ``__aenter__``, ``__aexit__``\n     - :ref:`async-file-objects`\n   * - :class:`SendStream`\n     - :class:`AsyncResource`\n     - :meth:`~SendStream.send_all`,\n       :meth:`~SendStream.wait_send_all_might_not_block`\n     -\n     - :class:`~trio.testing.MemorySendStream`\n   * - :class:`ReceiveStream`\n     - :class:`AsyncResource`\n     - :meth:`~ReceiveStream.receive_some`\n     - ``__aiter__``, ``__anext__``\n     - :class:`~trio.testing.MemoryReceiveStream`\n   * - :class:`Stream`\n     - :class:`SendStream`, :class:`ReceiveStream`\n     -\n     -\n     - :class:`~trio.SSLStream`\n   * - :class:`HalfCloseableStream`\n     - :class:`Stream`\n     - :meth:`~HalfCloseableStream.send_eof`\n     -\n     - :class:`~trio.SocketStream`, :class:`~trio.StapledStream`\n   * - :class:`Listener`\n     - :class:`AsyncResource`\n     - :meth:`~Listener.accept`\n     -\n     - :class:`~trio.SocketListener`, :class:`~trio.SSLListener`\n   * - :class:`SendChannel`\n     - :class:`AsyncResource`\n     - :meth:`~SendChannel.send`\n     -\n     - `~trio.MemorySendChannel`\n   * - :class:`ReceiveChannel`\n     - :class:`AsyncResource`\n     - :meth:`~ReceiveChannel.receive`\n     - ``__aiter__``, ``__anext__``\n     - `~trio.MemoryReceiveChannel`\n   * - `Channel`\n     - `SendChannel`, `ReceiveChannel`\n     -\n     -\n     -\n\n.. autoclass:: trio.abc.AsyncResource\n   :members:\n\n.. currentmodule:: trio\n\n.. autofunction:: aclose_forcefully\n\n.. currentmodule:: trio.abc\n\n.. autoclass:: trio.abc.SendStream\n   :members:\n   :show-inheritance:\n\n.. autoclass:: trio.abc.ReceiveStream\n   :members:\n   :show-inheritance:\n\n.. autoclass:: trio.abc.Stream\n   :members:\n   :show-inheritance:\n\n.. autoclass:: trio.abc.HalfCloseableStream\n   :members:\n   :show-inheritance:\n\n.. currentmodule:: trio.abc\n\n.. autoclass:: trio.abc.Listener\n   :members:\n   :show-inheritance:\n\n.. autoclass:: trio.abc.SendChannel\n   :members:\n   :show-inheritance:\n\n.. autoclass:: trio.abc.ReceiveChannel\n   :members:\n   :show-inheritance:\n\n.. autoclass:: trio.abc.Channel\n   :members:\n   :show-inheritance:\n\n.. currentmodule:: trio\n\n\nGeneric stream tools\n~~~~~~~~~~~~~~~~~~~~\n\nTrio currently provides a generic helper for writing servers that\nlisten for connections using one or more\n:class:`~trio.abc.Listener`\\s, and a generic utility class for working\nwith streams. And if you want to test code that's written against the\nstreams interface, you should also check out :ref:`testing-streams` in\n:mod:`trio.testing`.\n\n.. autofunction:: serve_listeners\n\n.. autoclass:: StapledStream\n   :members:\n   :show-inheritance:\n\n\n.. _high-level-networking:\n\nSockets and networking\n~~~~~~~~~~~~~~~~~~~~~~\n\nThe high-level network interface is built on top of our stream\nabstraction.\n\n.. autofunction:: open_tcp_stream\n\n.. autofunction:: serve_tcp\n\n.. autofunction:: open_ssl_over_tcp_stream\n\n.. autofunction:: serve_ssl_over_tcp\n\n.. autofunction:: open_unix_socket\n\n.. autoclass:: SocketStream\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\n.. autoclass:: SocketListener\n   :members:\n   :show-inheritance:\n\n.. autofunction:: open_tcp_listeners\n\n.. autofunction:: open_ssl_over_tcp_listeners\n\n\nSSL / TLS support\n~~~~~~~~~~~~~~~~~\n\nTrio provides SSL/TLS support based on the standard library :mod:`ssl`\nmodule. Trio's :class:`SSLStream` and :class:`SSLListener` take their\nconfiguration from a :class:`ssl.SSLContext`, which you can create\nusing :func:`ssl.create_default_context` and customize using the\nother constants and functions in the :mod:`ssl` module.\n\n.. warning:: Avoid instantiating :class:`ssl.SSLContext` directly.\n   A newly constructed :class:`~ssl.SSLContext` has less secure\n   defaults than one returned by :func:`ssl.create_default_context`.\n\nInstead of using :meth:`ssl.SSLContext.wrap_socket`, you\ncreate a :class:`SSLStream`:\n\n.. autoclass:: SSLStream\n   :show-inheritance:\n   :members:\n\nAnd if you're implementing a server, you can use :class:`SSLListener`:\n\n.. autoclass:: SSLListener\n   :show-inheritance:\n   :members:\n\nSome methods on :class:`SSLStream` raise :exc:`NeedHandshakeError` if\nyou call them before the handshake completes:\n\n.. autoexception:: NeedHandshakeError\n\n\nDatagram TLS support\n~~~~~~~~~~~~~~~~~~~~\n\nTrio also has support for Datagram TLS (DTLS), which is like TLS but\nfor unreliable UDP connections. This can be useful for applications\nwhere TCP's reliable in-order delivery is problematic, like\nteleconferencing, latency-sensitive games, and VPNs.\n\nCurrently, using DTLS with Trio requires PyOpenSSL. We hope to\neventually allow the use of the stdlib `ssl` module as well, but\nunfortunately that's not yet possible.\n\n.. warning:: Note that PyOpenSSL is in many ways lower-level than the\n   `ssl` module – in particular, it currently **HAS NO BUILT-IN\n   MECHANISM TO VALIDATE CERTIFICATES**. We *strongly* recommend that\n   you use the `service-identity\n   <https://pypi.org/project/service-identity/>`__ library to validate\n   hostnames and certificates.\n\n.. autoclass:: DTLSEndpoint\n\n   .. automethod:: connect\n\n   .. automethod:: serve\n\n   .. automethod:: close\n\n.. autoclass:: DTLSChannel\n   :show-inheritance:\n\n   .. automethod:: do_handshake\n\n   .. automethod:: send\n\n   .. automethod:: receive\n\n   .. automethod:: close\n\n   .. automethod:: aclose\n\n   .. automethod:: set_ciphertext_mtu\n\n   .. automethod:: get_cleartext_mtu\n\n   .. automethod:: statistics\n\n.. autoclass:: DTLSChannelStatistics\n   :members:\n\n.. module:: trio.socket\n\nLow-level networking with :mod:`trio.socket`\n---------------------------------------------\n\nThe :mod:`trio.socket` module provides Trio's basic low-level\nnetworking API. If you're doing ordinary things with stream-oriented\nconnections over IPv4/IPv6/Unix domain sockets, then you probably want\nto stick to the high-level API described above. If you want to use\nUDP, or exotic address families like ``AF_BLUETOOTH``, or otherwise\nget direct access to all the quirky bits of your system's networking\nAPI, then you're in the right place.\n\n\nTop-level exports\n~~~~~~~~~~~~~~~~~\n\nGenerally, the API exposed by :mod:`trio.socket` mirrors that of the\nstandard library :mod:`socket` module. Most constants (like\n``SOL_SOCKET``) and simple utilities (like :func:`~socket.inet_aton`)\nare simply re-exported unchanged. But there are also some differences,\nwhich are described here.\n\nFirst, Trio provides analogues to all the standard library functions\nthat return socket objects; their interface is identical, except that\nthey're modified to return Trio socket objects instead:\n\n.. autofunction:: socket\n\n.. autofunction:: socketpair\n\n.. autofunction:: fromfd\n\n.. function:: fromshare(data)\n\n   Like :func:`socket.fromshare`, but returns a Trio socket object.\n\nIn addition, there is a new function to directly convert a standard\nlibrary socket into a Trio socket:\n\n.. autofunction:: from_stdlib_socket\n\nUnlike :class:`socket.socket`, :func:`trio.socket.socket` is a\nfunction, not a class; if you want to check whether an object is a\nTrio socket, use ``isinstance(obj, trio.socket.SocketType)``.\n\nFor name lookup, Trio provides the standard functions, but with some\nchanges:\n\n.. autofunction:: getaddrinfo\n\n.. autofunction:: getnameinfo\n\n.. autofunction:: getprotobyname\n\nTrio intentionally DOES NOT include some obsolete, redundant, or\nbroken features:\n\n* :func:`~socket.gethostbyname`, :func:`~socket.gethostbyname_ex`,\n  :func:`~socket.gethostbyaddr`: obsolete; use\n  :func:`~socket.getaddrinfo` and :func:`~socket.getnameinfo` instead.\n\n* :func:`~socket.getservbyport`: obsolete and `buggy\n  <https://bugs.python.org/issue30482>`__; instead, do:\n\n  .. code-block:: python\n\n     _, service_name = await getnameinfo(('127.0.0.1', port), NI_NUMERICHOST)\n\n* :func:`~socket.getservbyname`: obsolete and `buggy\n  <https://bugs.python.org/issue30482>`__; instead, do:\n\n  .. code-block:: python\n\n     await getaddrinfo(None, service_name)\n\n* :func:`~socket.getfqdn`: obsolete; use :func:`getaddrinfo` with the\n  ``AI_CANONNAME`` flag.\n\n* :func:`~socket.getdefaulttimeout`,\n  :func:`~socket.setdefaulttimeout`: instead, use Trio's standard\n  support for :ref:`cancellation`.\n\n* On Windows, ``SO_REUSEADDR`` is not exported, because it's a trap:\n  the name is the same as Unix ``SO_REUSEADDR``, but the semantics are\n  `different and extremely broken\n  <https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx>`__. In\n  the very rare cases where you actually want ``SO_REUSEADDR`` on\n  Windows, then it can still be accessed from the standard library's\n  :mod:`socket` module.\n\n\nSocket objects\n~~~~~~~~~~~~~~\n\n.. class:: SocketType\n\n   .. note:: :class:`trio.socket.SocketType` is an abstract class and\n      cannot be instantiated directly; you get concrete socket objects\n      by calling constructors like :func:`trio.socket.socket`.\n      However, you can use it to check if an object is a Trio socket\n      via ``isinstance(obj, trio.socket.SocketType)``.\n\n   Trio socket objects are overall very similar to the :ref:`standard\n   library socket objects <python:socket-objects>`, with a few\n   important differences:\n\n   First, and most obviously, everything is made \"Trio-style\":\n   blocking methods become async methods, and the following attributes\n   are *not* supported:\n\n   * :meth:`~socket.socket.setblocking`: Trio sockets always act like\n     blocking sockets; if you need to read/write from multiple sockets\n     at once, then create multiple tasks.\n   * :meth:`~socket.socket.settimeout`: see :ref:`cancellation` instead.\n   * :meth:`~socket.socket.makefile`: Python's file-like API is\n     synchronous, so it can't be implemented on top of an async\n     socket.\n   * :meth:`~socket.socket.sendall`: Could be supported, but you're\n     better off using the higher-level\n     :class:`~trio.SocketStream`, and specifically its\n     :meth:`~trio.SocketStream.send_all` method, which also does\n     additional error checking.\n\n   In addition, the following methods are similar to the equivalents\n   in :class:`socket.socket`, but have some Trio-specific quirks:\n\n   .. method:: connect\n      :async:\n\n      Connect the socket to a remote address.\n\n      Similar to :meth:`socket.socket.connect`, except async.\n\n      .. warning::\n\n         Due to limitations of the underlying operating system APIs, it is\n         not always possible to properly cancel a connection attempt once it\n         has begun. If :meth:`connect` is cancelled, and is unable to\n         abort the connection attempt, then it will:\n\n         1. forcibly close the socket to prevent accidental reuse\n         2. raise :exc:`~trio.Cancelled`.\n\n         tl;dr: if :meth:`connect` is cancelled then the socket is\n         left in an unknown state – possibly open, and possibly\n         closed. The only reasonable thing to do is to close it.\n\n   .. method:: is_readable\n\n      Check whether the socket is readable or not.\n\n   .. method:: sendfile\n\n      `Not implemented yet! <https://github.com/python-trio/trio/issues/45>`__\n\n   We also keep track of an extra bit of state, because it turns out\n   to be useful for :class:`trio.SocketStream`:\n\n   .. attribute:: did_shutdown_SHUT_WR\n\n      This :class:`bool` attribute is True if you've called\n      ``sock.shutdown(SHUT_WR)`` or ``sock.shutdown(SHUT_RDWR)``, and\n      False otherwise.\n\n   The following methods are identical to their equivalents in\n   :class:`socket.socket`, except async, and the ones that take address\n   arguments require pre-resolved addresses:\n\n   * :meth:`~socket.socket.accept`\n   * :meth:`~socket.socket.bind`\n   * :meth:`~socket.socket.recv`\n   * :meth:`~socket.socket.recv_into`\n   * :meth:`~socket.socket.recvfrom`\n   * :meth:`~socket.socket.recvfrom_into`\n   * :meth:`~socket.socket.recvmsg` (if available)\n   * :meth:`~socket.socket.recvmsg_into` (if available)\n   * :meth:`~socket.socket.send`\n   * :meth:`~socket.socket.sendto`\n   * :meth:`~socket.socket.sendmsg` (if available)\n\n   All methods and attributes *not* mentioned above are identical to\n   their equivalents in :class:`socket.socket`:\n\n   * :attr:`~socket.socket.family`\n   * :attr:`~socket.socket.type`\n   * :attr:`~socket.socket.proto`\n   * :meth:`~socket.socket.fileno`\n   * :meth:`~socket.socket.listen`\n   * :meth:`~socket.socket.getpeername`\n   * :meth:`~socket.socket.getsockname`\n   * :meth:`~socket.socket.close`\n   * :meth:`~socket.socket.shutdown`\n   * :meth:`~socket.socket.setsockopt`\n   * :meth:`~socket.socket.getsockopt`\n   * :meth:`~socket.socket.dup`\n   * :meth:`~socket.socket.detach`\n   * :meth:`~socket.socket.share`\n   * :meth:`~socket.socket.set_inheritable`\n   * :meth:`~socket.socket.get_inheritable`\n\n\n.. currentmodule:: trio\n\n\n.. _async-file-io:\n\nAsynchronous filesystem I/O\n---------------------------\n\nTrio provides built-in facilities for performing asynchronous\nfilesystem operations like reading or renaming a file. Generally, we\nrecommend that you use these instead of Python's normal synchronous\nfile APIs. But the tradeoffs here are somewhat subtle: sometimes\npeople switch to async I/O, and then they're surprised and confused\nwhen they find it doesn't speed up their program. The next section\nexplains the theory behind async file I/O, to help you better\nunderstand your code's behavior. Or, if you just want to get started,\nyou can :ref:`jump down to the API overview <async-file-io-overview>`.\n\n\nBackground: Why is async file I/O useful? The answer may surprise you\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMany people expect that switching from synchronous file I/O to\nasync file I/O will always make their program faster. This is not\ntrue! If we just look at total throughput, then async file I/O might\nbe faster, slower, or about the same, and it depends in a complicated\nway on things like your exact patterns of disk access, or how much RAM\nyou have. The main motivation for async file I/O is not to improve\nthroughput, but to **reduce the frequency of latency glitches.**\n\nTo understand why, you need to know two things.\n\nFirst, right now no mainstream operating system offers a generic,\nreliable, native API for async file or filesystem operations, so we\nhave to fake it by using threads (specifically,\n:func:`trio.to_thread.run_sync`). This is cheap but isn't free: on a\ntypical PC, dispatching to a worker thread adds something like ~100 µs\nof overhead to each operation. (\"µs\" is pronounced \"microseconds\", and\nthere are 1,000,000 µs in a second. Note that all the numbers here are\ngoing to be rough orders of magnitude to give you a sense of scale; if\nyou need precise numbers for your environment, measure!)\n\n.. file.read benchmark is\n   https://github.com/python-trio/trio/wiki/notes-to-self#file-read-latencypy\n.. Numbers for spinning disks and SSDs are from taking a few random\n   recent reviews from http://www.storagereview.com/best_drives and\n   looking at their \"4K Write Latency\" test results for \"Average MS\"\n   and \"Max MS\":\n   http://www.storagereview.com/samsung_ssd_850_evo_ssd_review\n   http://www.storagereview.com/wd_black_6tb_hdd_review\n\nAnd second, the cost of a disk operation is incredibly\nbimodal. Sometimes, the data you need is already cached in RAM, and\nthen accessing it is very, very fast – calling :class:`io.FileIO`\\'s\n``read`` method on a cached file takes on the order of ~1 µs. But when\nthe data isn't cached, then accessing it is much, much slower: the\naverage is ~100 µs for SSDs and ~10,000 µs for spinning disks, and if\nyou look at tail latencies then for both types of storage you'll see\ncases where occasionally some operation will be 10x or 100x slower\nthan average. And that's assuming your program is the only thing\ntrying to use that disk – if you're on some oversold cloud VM fighting\nfor I/O with other tenants then who knows what will happen. And some\noperations can require multiple disk accesses.\n\nPutting these together: if your data is in RAM then it should be clear\nthat using a thread is a terrible idea – if you add 100 µs of overhead\nto a 1 µs operation, then that's a 100x slowdown! On the other hand,\nif your data's on a spinning disk, then using a thread is *great* –\ninstead of blocking the main thread and all tasks for 10,000 µs, we\nonly block them for 100 µs and can spend the rest of that time running\nother tasks to get useful work done, which can effectively be a 100x\nspeedup.\n\nBut here's the problem: for any individual I/O operation, there's no\nway to know in advance whether it's going to be one of the fast ones\nor one of the slow ones, so you can't pick and choose. When you switch\nto async file I/O, it makes all the fast operations slower, and all\nthe slow operations faster. Is that a win? In terms of overall speed,\nit's hard to say: it depends what kind of disks you're using and your\nkernel's disk cache hit rate, which in turn depends on your file\naccess patterns, how much spare RAM you have, the load on your\nservice, ... all kinds of things. If the answer is important to you,\nthen there's no substitute for measuring your code's actual behavior\nin your actual deployment environment. But what we *can* say is that\nasync disk I/O makes performance much more predictable across a wider\nrange of runtime conditions.\n\n**If you're not sure what to do, then we recommend that you use async\ndisk I/O by default,** because it makes your code more robust when\nconditions are bad, especially with regards to tail latencies; this\nimproves the chances that what your users see matches what you saw in\ntesting. Blocking the main thread stops *all* tasks from running for\nthat time. 10,000 µs is 10 ms, and it doesn't take many 10 ms glitches\nto start adding up to `real money\n<https://google.com/search?q=latency+cost>`__; async disk I/O can help\nprevent those. Just don't expect it to be magic, and be aware of the\ntradeoffs.\n\n\n.. _async-file-io-overview:\n\nAPI overview\n~~~~~~~~~~~~\n\nIf you want to perform general filesystem operations like creating and\nlisting directories, renaming files, or checking file metadata – or if\nyou just want a friendly way to work with filesystem paths – then you\nwant :class:`trio.Path`. It's an asyncified replacement for the\nstandard library's :class:`pathlib.Path`, and provides the same\ncomprehensive set of operations.\n\nFor reading and writing to files and file-like objects, Trio also\nprovides a mechanism for wrapping any synchronous file-like object\ninto an asynchronous interface. If you have a :class:`trio.Path`\nobject you can get one of these by calling its :meth:`~trio.Path.open`\nmethod; or if you know the file's name you can open it directly with\n:func:`trio.open_file`. Alternatively, if you already have an open\nfile-like object, you can wrap it with :func:`trio.wrap_file` – one\ncase where this is especially useful is to wrap :class:`io.BytesIO` or\n:class:`io.StringIO` when writing tests.\n\n\nAsynchronous path objects\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. autoclass:: Path\n   :members:\n   :inherited-members:\n\n.. autoclass:: PosixPath\n\n.. autoclass:: WindowsPath\n\n\n.. _async-file-objects:\n\nAsynchronous file objects\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. Suppress type annotations here, they refer to lots of internal types.\n   The normal Python docs go into better detail.\n.. autofunction:: open_file(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=None, opener=None)\n\n.. autofunction:: wrap_file(file)\n\n.. interface:: Asynchronous file interface\n\n   Trio's asynchronous file objects have an interface that\n   automatically adapts to the object being wrapped. Intuitively, you\n   can mostly treat them like a regular :term:`file object`, except\n   adding an ``await`` in front of any of methods that do I/O. The\n   definition of :term:`file object` is a little vague in Python\n   though, so here are the details:\n\n   * Synchronous attributes/methods: if any of the following\n     attributes or methods are present, then they're re-exported\n     unchanged: ``closed``, ``encoding``, ``errors``, ``fileno``,\n     ``isatty``, ``newlines``, ``readable``, ``seekable``,\n     ``writable``, ``buffer``, ``raw``, ``line_buffering``,\n     ``closefd``, ``name``, ``mode``, ``getvalue``, ``getbuffer``.\n\n   * Async methods: if any of the following methods are present, then\n     they're re-exported as an async method: ``flush``, ``read``,\n     ``read1``, ``readall``, ``readinto``, ``readline``,\n     ``readlines``, ``seek``, ``tell``, ``truncate``, ``write``,\n     ``writelines``, ``readinto1``, ``peek``, ``detach``.\n\n   Special notes:\n\n   * Async file objects implement Trio's\n     :class:`~trio.abc.AsyncResource` interface: you close them by\n     calling :meth:`~trio.abc.AsyncResource.aclose` instead of\n     ``close`` (!!), and they can be used as async context\n     managers. Like all :meth:`~trio.abc.AsyncResource.aclose`\n     methods, the ``aclose`` method on async file objects is\n     guaranteed to close the file before returning, even if it is\n     cancelled or otherwise raises an error.\n\n   * Using the same async file object from multiple tasks\n     simultaneously: because the async methods on async file objects\n     are implemented using threads, it's only safe to call two of them\n     at the same time from different tasks IF the underlying\n     synchronous file object is thread-safe. You should consult the\n     documentation for the object you're wrapping. For objects\n     returned from :func:`trio.open_file` or :meth:`trio.Path.open`,\n     it depends on whether you open the file in binary mode or text\n     mode: `binary mode files are task-safe/thread-safe, text mode\n     files are not\n     <https://docs.python.org/3/library/io.html#multi-threading>`__.\n\n   * Async file objects can be used as async iterators to iterate over\n     the lines of the file:\n\n     .. code-block:: python\n\n        async with await trio.open_file(...) as f:\n            async for line in f:\n                print(line)\n\n   * The ``detach`` method, if present, returns an async file object.\n\n   This should include all the attributes exposed by classes in\n   :mod:`io`. But if you're wrapping an object that has other\n   attributes that aren't on the list above, then you can access them\n   via the ``.wrapped`` attribute:\n\n   .. attribute:: wrapped\n\n      The underlying synchronous file object.\n\n\n.. _subprocess:\n\nSpawning subprocesses\n---------------------\n\nTrio provides support for spawning other programs as subprocesses,\ncommunicating with them via pipes, sending them signals, and waiting\nfor them to exit.\n\nMost of the time, this is done through our high-level interface,\n`trio.run_process`. It lets you either run a process to completion\nwhile optionally capturing the output, or else run it in a background\ntask and interact with it while it's running:\n\n.. autofunction:: trio.run_process\n\n.. autoclass:: trio._subprocess.HasFileno(Protocol)\n\n   .. automethod:: fileno\n\n.. autoclass:: trio._subprocess.StrOrBytesPath\n\n.. autoclass:: trio.Process()\n\n   .. autoattribute:: returncode\n\n   .. automethod:: wait\n\n   .. automethod:: poll\n\n   .. automethod:: kill\n\n   .. automethod:: terminate\n\n   .. automethod:: send_signal\n\n   .. note:: :meth:`~subprocess.Popen.communicate` is not provided as a\n      method on :class:`~trio.Process` objects; call :func:`~trio.run_process`\n      normally for simple capturing, or write the loop yourself if you\n      have unusual needs. :meth:`~subprocess.Popen.communicate` has\n      quite unusual cancellation behavior in the standard library (on\n      some platforms it spawns a background thread which continues to\n      read from the child process even after the timeout has expired)\n      and we wanted to provide an interface with fewer surprises.\n\nIf `trio.run_process` is too limiting, we also offer a low-level API,\n`trio.lowlevel.open_process`. For example, if you want to spawn a\nchild process that will outlive the parent process and be\norphaned, then `~trio.run_process` can't do that, but\n`~trio.lowlevel.open_process` can.\n\n\n.. _subprocess-options:\n\nOptions for starting subprocesses\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAll of Trio's subprocess APIs accept the numerous keyword arguments used\nby the standard :mod:`subprocess` module to control the environment in\nwhich a process starts and the mechanisms used for communicating with\nit.  These may be passed wherever you see ``**options`` in the\ndocumentation below.  See the `full list\n<https://docs.python.org/3/library/subprocess.html#popen-constructor>`__\nor just the `frequently used ones\n<https://docs.python.org/3/library/subprocess.html#frequently-used-arguments>`__\nin the :mod:`subprocess` documentation. (You may need to ``import\nsubprocess`` in order to access constants such as ``PIPE`` or\n``DEVNULL``.)\n\nCurrently, Trio always uses unbuffered byte streams for communicating\nwith a process, so it does not support the ``encoding``, ``errors``,\n``universal_newlines`` (alias ``text``), and ``bufsize``\noptions.\n\n\n.. _subprocess-quoting:\n\nQuoting: more than you wanted to know\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe command to run and its arguments usually must be passed to Trio's\nsubprocess APIs as a sequence of strings, where the first element in\nthe sequence specifies the command to run and the remaining elements\nspecify its arguments, one argument per element. This form is used\nbecause it avoids potential quoting pitfalls; for example, you can run\n``[\"cp\", \"-f\", source_file, dest_file]`` without worrying about\nwhether ``source_file`` or ``dest_file`` contains spaces.\n\nIf you only run subprocesses without ``shell=True`` and on UNIX,\nthat's all you need to know about specifying the command. If you use\n``shell=True`` or run on Windows, you probably should read the\nrest of this section to be aware of potential pitfalls.\n\nWith ``shell=True`` on UNIX, you must specify the command as a single\nstring, which will be passed to the shell as if you'd entered it at an\ninteractive prompt. The advantage of this option is that it lets you\nuse shell features like pipes and redirection without writing code to\nhandle them. For example, you can write ``Process(\"ls | grep\nsome_string\", shell=True)``.  The disadvantage is that you must\naccount for the shell's quoting rules, generally by wrapping in\n:func:`shlex.quote` any argument that might contain spaces, quotes, or\nother shell metacharacters.  If you don't do that, your safe-looking\n``f\"ls | grep {some_string}\"`` might end in disaster when invoked with\n``some_string = \"foo; rm -rf /\"``.\n\nOn Windows, the fundamental API for process spawning (the\n``CreateProcess()`` system call) takes a string, not a list, and it's\nactually up to the child process to decide how it wants to split that\nstring into individual arguments. Since the C language specifies that\n``main()`` should take a list of arguments, *most* programs you\nencounter will follow the rules used by the Microsoft C/C++ runtime.\n:class:`subprocess.Popen`, and thus also Trio, uses these rules\nwhen it converts an argument sequence to a string, and they\nare `documented\n<https://docs.python.org/3/library/subprocess.html#converting-argument-sequence>`__\nalongside the :mod:`subprocess` module. There is no documented\nPython standard library function that can directly perform that\nconversion, so even on Windows, you almost always want to pass an\nargument sequence rather than a string. But if the program you're\nspawning doesn't split its command line back into individual arguments\nin the standard way, you might need to pass a string to work around this.\n(Or you might just be out of luck: as far as I can tell, there's simply\nno way to pass an argument containing a double-quote to a Windows\nbatch file.)\n\nOn Windows with ``shell=True``, things get even more chaotic. Now\nthere are two separate sets of quoting rules applied, one by the\nWindows command shell ``CMD.EXE`` and one by the process being\nspawned, and they're *different*. (And there's no :func:`shlex.quote`\nto save you: it uses UNIX-style quoting rules, even on Windows.)  Most\nspecial characters interpreted by the shell ``&<>()^|`` are not\ntreated as special if the shell thinks they're inside double quotes,\nbut ``%FOO%`` environment variable substitutions still are, and the\nshell doesn't provide any way to write a double quote inside a\ndouble-quoted string. Outside double quotes, any character (including\na double quote) can be escaped using a leading ``^``.  But since a\npipeline is processed by running each command in the pipeline in a\nsubshell, multiple layers of escaping can be needed:\n\n.. code-block:: sh\n\n    echo ^^^&x | find \"x\" | find \"x\"          # prints: &x\n\nAnd if you combine pipelines with () grouping, you can need even more\nlevels of escaping:\n\n.. code-block:: sh\n\n    (echo ^^^^^^^&x | find \"x\") | find \"x\"    # prints: &x\n\nSince process creation takes a single arguments string, ``CMD.EXE``\\'s\nquoting does not influence word splitting, and double quotes are not\nremoved during CMD.EXE's expansion pass. Double quotes are troublesome\nbecause CMD.EXE handles them differently from the MSVC runtime rules; in:\n\n.. code-block:: sh\n\n    prog.exe \"foo \\\"bar\\\" baz\"\n\nthe program will see one argument ``foo \"bar\" baz`` but CMD.EXE thinks\n``bar\\`` is not quoted while ``foo \\`` and ``baz`` are. All of this\nmakes it a formidable task to reliably interpolate anything into a\n``shell=True`` command line on Windows, and Trio falls back on the\n:mod:`subprocess` behavior: If you pass a sequence with\n``shell=True``, it's quoted in the same way as a sequence with\n``shell=False``, and had better not contain any shell metacharacters\nyou weren't planning on.\n\nFurther reading:\n\n* https://stackoverflow.com/questions/30620876/how-to-properly-escape-filenames-in-windows-cmd-exe\n\n* https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts\n\n\nSignals\n-------\n\n.. currentmodule:: trio\n\n.. autofunction:: open_signal_receiver\n   :with: signal_aiter\n"
  },
  {
    "path": "docs/source/reference-lowlevel.rst",
    "content": "=========================================================\n Introspecting and extending Trio with ``trio.lowlevel``\n=========================================================\n\n.. module:: trio.lowlevel\n\n:mod:`trio.lowlevel` contains low-level APIs for introspecting and\nextending Trio. If you're writing ordinary, everyday code, then you\ncan ignore this module completely. But sometimes you need something a\nbit lower level. Here are some examples of situations where you should\nreach for :mod:`trio.lowlevel`:\n\n* You want to implement a new :ref:`synchronization primitive\n  <synchronization>` that Trio doesn't (yet) provide, like a\n  reader-writer lock.\n* You want to extract low-level metrics to monitor the health of your\n  application.\n* You want to use a low-level operating system interface that Trio\n  doesn't (yet) provide its own wrappers for, like watching a\n  filesystem directory for changes.\n* You want to implement an interface for calling between Trio and\n  another event loop within the same process.\n* You're writing a debugger and want to visualize Trio's task tree.\n* You need to interoperate with a C library whose API exposes raw file\n  descriptors.\n\nYou don't need to be scared of :mod:`trio.lowlevel`, as long as you\ntake proper precautions. These are real public APIs, with strictly\ndefined and carefully documented semantics. They're the same tools we\nuse to implement all the nice high-level APIs in the :mod:`trio`\nnamespace. But, be careful. Some of those strict semantics have `nasty\nbig pointy teeth\n<https://en.wikipedia.org/wiki/Rabbit_of_Caerbannog>`__. If you make a\nmistake, Trio may not be able to handle it gracefully; conventions and\nguarantees that are followed strictly in the rest of Trio do not\nalways apply. When you use this module, it's your job to think about\nhow you're going to handle the tricky cases so you can expose a\nfriendly Trio-style API to your users.\n\n\nDebugging and instrumentation\n=============================\n\nTrio tries hard to provide useful hooks for debugging and\ninstrumentation. Some are documented above (the nursery introspection\nattributes, :meth:`trio.Lock.statistics`, etc.). Here are some more.\n\n\nGlobal statistics\n-----------------\n\n.. function:: current_statistics() -> RunStatistics\n\n   Returns an object containing run-loop-level debugging information:\n\n.. autoclass:: RunStatistics()\n\n\n.. _trio_contexts:\n\nChecking for Trio\n-----------------\n\nIf you want to interact with an active Trio run -- perhaps you need to\nknow the :func:`~trio.current_time` or the\n:func:`~trio.lowlevel.current_task` -- then Trio needs to have certain\nstate available to it or else you will get a\n``RuntimeError(\"must be called from async context\")``.\nThis requires that you either be:\n\n* indirectly inside (and on the same thread as) a call to\n  :func:`trio.run`, for run-level information such as the\n  :func:`~trio.current_time` or :func:`~trio.lowlevel.current_clock`;\n  or\n\n* indirectly inside a Trio task, for task-level information such as\n  the :func:`~trio.lowlevel.current_task` or\n  :func:`~trio.current_effective_deadline`.\n\nInternally, this state is provided by thread-local variables tracking\nthe current run and the current task. Sometimes, it's useful to know\nin advance whether a call will fail or to have dynamic information for\nsafeguards against running something inside or outside Trio. To do so,\ncall :func:`trio.lowlevel.in_trio_run` or\n:func:`trio.lowlevel.in_trio_task`, which will provide answers\naccording to the following table.\n\n\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n| situation                                              | :func:`trio.lowlevel.in_trio_run` | :func:`trio.lowlevel.in_trio_task` |\n+========================================================+===================================+====================================+\n| inside a Trio-flavored async function                  | `True`                            | `True`                             |\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n| in a thread without an active call to :func:`trio.run` | `False`                           | `False`                            |\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n| in a guest run's host loop                             | `True`                            | `False`                            |\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n| inside an instrument call                              | `True`                            | depends                            |\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n| in a thread created by :func:`trio.to_thread.run_sync` | `False`                           | `False`                            |\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n| inside an abort function                               | `True`                            | `True`                             |\n+--------------------------------------------------------+-----------------------------------+------------------------------------+\n\n.. autofunction:: in_trio_run\n\n.. autofunction:: in_trio_task\n\nThe current clock\n-----------------\n\n.. autofunction:: current_clock\n\n\n.. _instrumentation:\n\nInstrument API\n--------------\n\nThe instrument API provides a standard way to add custom\ninstrumentation to the run loop. Want to make a histogram of\nscheduling latencies, log a stack trace of any task that blocks the\nrun loop for >50 ms, or measure what percentage of your process's\nrunning time is spent waiting for I/O? This is the place.\n\nThe general idea is that at any given moment, :func:`trio.run`\nmaintains a set of \"instruments\", which are objects that implement the\n:class:`trio.abc.Instrument` interface. When an interesting event\nhappens, it loops over these instruments and notifies them by calling\nan appropriate method. The tutorial has :ref:`a simple example of\nusing this for tracing <tutorial-instrument-example>`.\n\nSince this hooks into Trio at a rather low level, you do have to be\ncareful. The callbacks are run synchronously, and in many cases if\nthey error out then there isn't any plausible way to propagate this\nexception (for instance, we might be deep in the guts of the exception\npropagation machinery...). Therefore our `current strategy\n<https://github.com/python-trio/trio/issues/47>`__ for handling\nexceptions raised by instruments is to (a) log an exception to the\n``\"trio.abc.Instrument\"`` logger, which by default prints a stack\ntrace to standard error and (b) disable the offending instrument.\n\nYou can register an initial list of instruments by passing them to\n:func:`trio.run`. :func:`add_instrument` and\n:func:`remove_instrument` let you add and remove instruments at\nruntime.\n\n.. autofunction:: add_instrument\n\n.. autofunction:: remove_instrument\n\nAnd here's the interface to implement if you want to build your own\n:class:`~trio.abc.Instrument`:\n\n.. autoclass:: trio.abc.Instrument\n   :members:\n\nThe tutorial has a :ref:`fully-worked example\n<tutorial-instrument-example>` of defining a custom instrument to log\nTrio's internal scheduling decisions.\n\n\nLow-level process spawning\n==========================\n\n.. autofunction:: trio.lowlevel.open_process\n\n\nLow-level I/O primitives\n========================\n\nDifferent environments expose different low-level APIs for performing\nasync I/O. :mod:`trio.lowlevel` exposes these APIs in a relatively\ndirect way, so as to allow maximum power and flexibility for higher\nlevel code. However, this means that the exact API provided may vary\ndepending on what system Trio is running on.\n\n\nUniversally available API\n-------------------------\n\nAll environments provide the following functions:\n\n.. function:: wait_readable(obj)\n   :async:\n\n   Block until the kernel reports that the given object is readable.\n\n   On Unix systems, ``obj`` must either be an integer file descriptor,\n   or else an object with a ``.fileno()`` method which returns an\n   integer file descriptor. Any kind of file descriptor can be passed,\n   though the exact semantics will depend on your kernel. For example,\n   this probably won't do anything useful for on-disk files.\n\n   On Windows systems, ``obj`` must either be an integer ``SOCKET``\n   handle, or else an object with a ``.fileno()`` method which returns\n   an integer ``SOCKET`` handle. File descriptors aren't supported,\n   and neither are handles that refer to anything besides a\n   ``SOCKET``.\n\n   :raises trio.BusyResourceError:\n       if another task is already waiting for the given socket to\n       become readable.\n   :raises trio.ClosedResourceError:\n       if another task calls :func:`notify_closing` while this\n       function is still working.\n\n.. function:: wait_writable(obj)\n   :async:\n\n   Block until the kernel reports that the given object is writable.\n\n   See `wait_readable` for the definition of ``obj``.\n\n   :raises trio.BusyResourceError:\n       if another task is already waiting for the given socket to\n       become writable.\n   :raises trio.ClosedResourceError:\n       if another task calls :func:`notify_closing` while this\n       function is still working.\n\n\n.. function:: notify_closing(obj)\n\n   Call this before closing a file descriptor (on Unix) or socket (on\n   Windows). This will cause any `wait_readable` or `wait_writable`\n   calls on the given object to immediately wake up and raise\n   `~trio.ClosedResourceError`.\n\n   This doesn't actually close the object – you still have to do that\n   yourself afterwards. Also, you want to be careful to make sure no\n   new tasks start waiting on the object in between when you call this\n   and when it's actually closed. So to close something properly, you\n   usually want to do these steps in order:\n\n   1. Explicitly mark the object as closed, so that any new attempts\n      to use it will abort before they start.\n   2. Call `notify_closing` to wake up any already-existing users.\n   3. Actually close the object.\n\n   It's also possible to do them in a different order if that's more\n   convenient, *but only if* you make sure not to have any checkpoints in\n   between the steps. This way they all happen in a single atomic\n   step, so other tasks won't be able to tell what order they happened\n   in anyway.\n\n\nUnix-specific API\n-----------------\n\n`FdStream` supports wrapping Unix files (such as a pipe or TTY) as\na stream.\n\nIf you have two different file descriptors for sending and receiving,\nand want to bundle them together into a single bidirectional\n`~trio.abc.Stream`, then use `trio.StapledStream`:\n\n.. code-block:: python\n\n    bidirectional_stream = trio.StapledStream(\n        trio.lowlevel.FdStream(write_fd),\n        trio.lowlevel.FdStream(read_fd)\n    )\n\n.. autoclass:: FdStream\n   :show-inheritance:\n   :members:\n\n\nKqueue-specific API\n-------------------\n\nTODO: these are implemented, but are currently more of a sketch than\nanything real. See `#26\n<https://github.com/python-trio/trio/issues/26>`__.\n\n.. autofunction:: current_kqueue()\n\n.. autofunction:: wait_kevent(ident, filter, abort_func)\n   :async:\n\n.. autofunction:: monitor_kevent(ident, filter)\n   :with: queue\n\n\nWindows-specific API\n--------------------\n\n.. note: this is a function and not `autofunction` since it relies on cffi\n   compiling some things.\n\n.. function:: WaitForSingleObject(handle)\n    :async:\n\n    Async and cancellable variant of `WaitForSingleObject\n    <https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx>`__.\n    Windows only.\n\n    :arg handle:\n        A Win32 object handle, as a Python integer.\n    :raises OSError:\n        If the handle is invalid, e.g. when it is already closed.\n\n\nTODO: these are implemented, but are currently more of a sketch than\nanything real. See `#26\n<https://github.com/python-trio/trio/issues/26>`__ and `#52\n<https://github.com/python-trio/trio/issues/52>`__.\n\n.. autofunction:: register_with_iocp(handle)\n\n.. autofunction:: wait_overlapped(handle, lpOverlapped)\n   :async:\n\n.. autofunction:: write_overlapped(handle, data)\n   :async:\n\n.. autofunction:: readinto_overlapped(handle, data)\n   :async:\n\n.. autofunction:: current_iocp()\n\n.. autofunction:: monitor_completion_key()\n   :with: queue\n\n\nGlobal state: system tasks and run-local variables\n==================================================\n\n.. autoclass:: RunVar\n\n.. autofunction:: spawn_system_task\n\n\nTrio tokens\n===========\n\n.. autoclass:: TrioToken()\n   :members:\n\n.. autofunction:: current_trio_token\n\n\nSpawning threads\n================\n\n.. autofunction:: start_thread_soon\n\n\nSafer KeyboardInterrupt handling\n================================\n\nTrio's handling of control-C is designed to balance usability and\nsafety. On the one hand, there are sensitive regions (like the core\nscheduling loop) where it's simply impossible to handle arbitrary\n:exc:`KeyboardInterrupt` exceptions while maintaining our core\ncorrectness invariants. On the other, if the user accidentally writes\nan infinite loop, we do want to be able to break out of that. Our\nsolution is to install a default signal handler which checks whether\nit's safe to raise :exc:`KeyboardInterrupt` at the place where the\nsignal is received. If so, then we do; otherwise, we schedule a\n:exc:`KeyboardInterrupt` to be delivered to the main task at the next\navailable opportunity (similar to how :exc:`~trio.Cancelled` is\ndelivered).\n\nSo that's great, but – how do we know whether we're in one of the\nsensitive parts of the program or not?\n\nThis is determined on a function-by-function basis. By default:\n\n- The top-level function in regular user tasks is unprotected.\n- The top-level function in system tasks is protected.\n- If a function doesn't specify otherwise, then it inherits the\n  protection state of its caller.\n\nThis means you only need to override the defaults at places where you\ntransition from protected code to unprotected code or vice-versa.\n\nThese transitions are accomplished using two function decorators:\n\n.. function:: disable_ki_protection()\n   :decorator:\n\n   Decorator that marks the given regular function, generator\n   function, async function, or async generator function as\n   unprotected against :exc:`KeyboardInterrupt`, i.e., the code inside\n   this function *can* be rudely interrupted by\n   :exc:`KeyboardInterrupt` at any moment.\n\n   If you have multiple decorators on the same function, then this\n   should be at the bottom of the stack (closest to the actual\n   function).\n\n   An example of where you'd use this is in implementing something\n   like :func:`trio.from_thread.run`, which uses\n   :meth:`TrioToken.run_sync_soon` to get into the Trio\n   thread. :meth:`~TrioToken.run_sync_soon` callbacks are run with\n   :exc:`KeyboardInterrupt` protection enabled, and\n   :func:`trio.from_thread.run` takes advantage of this to safely set up\n   the machinery for sending a response back to the original thread, but\n   then uses :func:`disable_ki_protection` when entering the\n   user-provided function.\n\n.. function:: enable_ki_protection()\n   :decorator:\n\n   Decorator that marks the given regular function, generator\n   function, async function, or async generator function as protected\n   against :exc:`KeyboardInterrupt`, i.e., the code inside this\n   function *won't* be rudely interrupted by\n   :exc:`KeyboardInterrupt`. (Though if it contains any\n   :ref:`checkpoints <checkpoints>`, then it can still receive\n   :exc:`KeyboardInterrupt` at those. This is considered a polite\n   interruption.)\n\n   .. warning::\n\n      Be very careful to only use this decorator on functions that you\n      know will either exit in bounded time, or else pass through a\n      checkpoint regularly. (Of course all of your functions should\n      have this property, but if you mess it up here then you won't\n      even be able to use control-C to escape!)\n\n   If you have multiple decorators on the same function, then this\n   should be at the bottom of the stack (closest to the actual\n   function).\n\n   An example of where you'd use this is on the ``__exit__``\n   implementation for something like a :class:`~trio.Lock`, where a\n   poorly-timed :exc:`KeyboardInterrupt` could leave the lock in an\n   inconsistent state and cause a deadlock.\n\n   Since KeyboardInterrupt protection is tracked per code object, any attempt to\n   conditionally protect the same block of code in different ways is unlikely to behave\n   how you expect. If you try to conditionally protect a closure, it will be\n   unconditionally protected instead::\n\n       def example(protect: bool) -> bool:\n           def inner() -> bool:\n               return trio.lowlevel.currently_ki_protected()\n           if protect:\n               inner = trio.lowlevel.enable_ki_protection(inner)\n           return inner()\n\n       async def amain():\n           assert example(False) == False\n           assert example(True) == True  # once protected ...\n           assert example(False) == True  # ... always protected\n\n       trio.run(amain)\n\n   If you really need conditional protection, you can achieve it by giving each\n   KI-protected instance of the closure its own code object::\n\n       def example(protect: bool) -> bool:\n           def inner() -> bool:\n               return trio.lowlevel.currently_ki_protected()\n           if protect:\n               inner.__code__ = inner.__code__.replace()\n               inner = trio.lowlevel.enable_ki_protection(inner)\n           return inner()\n\n       async def amain():\n           assert example(False) == False\n           assert example(True) == True\n           assert example(False) == False\n\n       trio.run(amain)\n\n   (This isn't done by default because it carries some memory overhead and reduces\n   the potential for specializing optimizations in recent versions of CPython.)\n\n.. autofunction:: currently_ki_protected\n\n\nSleeping and waking\n===================\n\nWait queue abstraction\n----------------------\n\n.. autoclass:: ParkingLot\n   :members:\n   :undoc-members:\n\n.. autoclass:: ParkingLotStatistics\n   :members:\n\n.. autofunction:: add_parking_lot_breaker\n\n.. autofunction:: remove_parking_lot_breaker\n\nLow-level checkpoint functions\n------------------------------\n\n.. autofunction:: checkpoint\n\nThe next two functions are used *together* to make up a checkpoint:\n\n.. autofunction:: checkpoint_if_cancelled\n.. autofunction:: cancel_shielded_checkpoint\n\nThese are commonly used in cases where you have an operation that\nmight-or-might-not block, and you want to implement Trio's standard\ncheckpoint semantics. Example:\n\n.. code-block:: python\n\n   async def operation_that_maybe_blocks():\n       await checkpoint_if_cancelled()\n       try:\n           ret = attempt_operation()\n       except BlockingIOError:\n           # need to block and then retry, which we do below\n           pass\n       else:\n           # operation succeeded, finish the checkpoint then return\n           await cancel_shielded_checkpoint()\n           return ret\n       while True:\n           await wait_for_operation_to_be_ready()\n           try:\n               return attempt_operation()\n           except BlockingIOError:\n               pass\n\nThis logic is a bit convoluted, but accomplishes all of the following:\n\n* Every successful execution path passes through a checkpoint (assuming that\n  ``wait_for_operation_to_be_ready`` is an unconditional checkpoint)\n\n* Our :ref:`cancellation semantics <cancellable-primitives>` say that\n  :exc:`~trio.Cancelled` should only be raised if the operation didn't\n  happen. Using :func:`cancel_shielded_checkpoint` on the early-exit\n  branch accomplishes this.\n\n* On the path where we do end up blocking, we don't pass through any\n  schedule points before that, which avoids some unnecessary work.\n\n* Avoids implicitly chaining the :exc:`BlockingIOError` with any\n  errors raised by ``attempt_operation`` or\n  ``wait_for_operation_to_be_ready``, by keeping the ``while True:``\n  loop outside of the ``except BlockingIOError:`` block.\n\nThese functions can also be useful in other situations. For example,\nwhen :func:`trio.to_thread.run_sync` schedules some work to run in a\nworker thread, it blocks until the work is finished (so it's a\nschedule point), but by default it doesn't allow cancellation. So to\nmake sure that the call always acts as a checkpoint, it calls\n:func:`checkpoint_if_cancelled` before starting the thread.\n\n\nLow-level blocking\n------------------\n\n.. autofunction:: wait_task_rescheduled\n.. autoclass:: Abort\n.. autofunction:: reschedule\n\nHere's an example lock class implemented using\n:func:`wait_task_rescheduled` directly. This implementation has a\nnumber of flaws, including lack of fairness, O(n) cancellation,\nmissing error checking, failure to insert a checkpoint on the\nnon-blocking path, etc. If you really want to implement your own lock,\nthen you should study the implementation of :class:`trio.Lock` and use\n:class:`ParkingLot`, which handles some of these issues for you. But\nthis does serve to illustrate the basic structure of the\n:func:`wait_task_rescheduled` API:\n\n.. code-block:: python\n\n   class NotVeryGoodLock:\n       def __init__(self):\n           self._blocked_tasks = collections.deque()\n           self._held = False\n\n       async def acquire(self):\n           # We might have to try several times to acquire the lock.\n           while self._held:\n               # Someone else has the lock, so we have to wait.\n               task = trio.lowlevel.current_task()\n               self._blocked_tasks.append(task)\n               def abort_fn(_):\n                   self._blocked_tasks.remove(task)\n                   return trio.lowlevel.Abort.SUCCEEDED\n               await trio.lowlevel.wait_task_rescheduled(abort_fn)\n               # At this point the lock was released -- but someone else\n               # might have swooped in and taken it again before we\n               # woke up. So we loop around to check the 'while' condition\n               # again.\n           # if we reach this point, it means that the 'while' condition\n           # has just failed, so we know no-one is holding the lock, and\n           # we can take it.\n           self._held = True\n\n       def release(self):\n           self._held = False\n           if self._blocked_tasks:\n               woken_task = self._blocked_tasks.popleft()\n               trio.lowlevel.reschedule(woken_task)\n\n\nTask API\n========\n\n.. autofunction:: current_root_task()\n\n.. autofunction:: current_task()\n\n.. class:: Task()\n\n   A :class:`Task` object represents a concurrent \"thread\" of\n   execution. It has no public constructor; Trio internally creates a\n   :class:`Task` object for each call to ``nursery.start(...)`` or\n   ``nursery.start_soon(...)``.\n\n   Its public members are mostly useful for introspection and\n   debugging:\n\n   .. attribute:: name\n\n      String containing this :class:`Task`\\'s name. Usually the name\n      of the function this :class:`Task` is running, but can be\n      overridden by passing ``name=`` to ``start`` or ``start_soon``.\n\n   .. attribute:: coro\n\n      This task's coroutine object.\n\n   .. automethod:: iter_await_frames\n\n   .. attribute:: context\n\n      This task's :class:`contextvars.Context` object.\n\n   .. autoattribute:: parent_nursery\n\n   .. autoattribute:: eventual_parent_nursery\n\n   .. autoattribute:: child_nurseries\n\n   .. attribute:: custom_sleep_data\n\n      Trio doesn't assign this variable any meaning, except that it\n      sets it to ``None`` whenever a task is rescheduled. It can be\n      used to share data between the different tasks involved in\n      putting a task to sleep and then waking it up again. (See\n      :func:`wait_task_rescheduled` for details.)\n\n.. _guest-mode:\n\nUsing \"guest mode\" to run Trio on top of other event loops\n==========================================================\n\nWhat is \"guest mode\"?\n---------------------\n\nAn event loop acts as a central coordinator to manage all the IO\nhappening in your program. Normally, that means that your application\nhas to pick one event loop, and use it for everything. But what if you\nlike Trio, but also need to use a framework like `Qt\n<https://en.wikipedia.org/wiki/Qt_(software)>`__ or `PyGame\n<https://www.pygame.org/>`__ that has its own event loop? Then you\nneed some way to run both event loops at once.\n\nIt is possible to combine event loops, but the standard approaches all\nhave significant downsides:\n\n- **Polling:** this is where you use a `busy-loop\n  <https://en.wikipedia.org/wiki/Busy_waiting>`__ to manually check\n  for IO on both event loops many times per second. This adds latency,\n  and wastes CPU time and electricity.\n\n- **Pluggable IO backends:** this is where you reimplement one of the\n  event loop APIs on top of the other, so you effectively end up with\n  just one event loop. This requires a significant amount of work for\n  each pair of event loops you want to integrate, and different\n  backends inevitably end up with inconsistent behavior, forcing users\n  to program against the least-common-denominator. And if the two\n  event loops expose different feature sets, it may not even be\n  possible to implement one in terms of the other.\n\n- **Running the two event loops in separate threads:** This works, but\n  most event loop APIs aren't thread-safe, so in this approach you\n  need to keep careful track of which code runs on which event loop,\n  and remember to use explicit inter-thread messaging whenever you\n  interact with the other loop – or else risk obscure race conditions\n  and data corruption.\n\nThat's why Trio offers a fourth option: **guest mode**. Guest mode\nlets you execute `trio.run` on top of some other \"host\" event loop,\nlike Qt. Its advantages are:\n\n- Efficiency: guest mode is event-driven instead of using a busy-loop,\n  so it has low latency and doesn't waste electricity.\n\n- No need to think about threads: your Trio code runs in the same\n  thread as the host event loop, so you can freely call sync Trio APIs\n  from the host, and call sync host APIs from Trio. For example, if\n  you're making a GUI app with Qt as the host loop, then making a\n  `cancel button <https://doc.qt.io/qt-5/qpushbutton.html>`__ and\n  connecting it to a `trio.CancelScope` is as easy as writing:\n\n  .. code-block:: python\n\n      # Trio code can create Qt objects without any special ceremony...\n      my_cancel_button = QPushButton(\"Cancel\")\n      # ...and Qt can call back to Trio just as easily\n      my_cancel_button.clicked.connect(my_cancel_scope.cancel)\n\n  (For async APIs, it's not that simple, but you can use sync APIs to\n  build explicit bridges between the two worlds, e.g. by passing async\n  functions and their results back and forth through queues.)\n\n- Consistent behavior: guest mode uses the same code as regular Trio:\n  the same scheduler, same IO code, same everything. So you get the\n  full feature set and everything acts the way you expect.\n\n- Simple integration and broad compatibility: pretty much every event\n  loop offers some threadsafe \"schedule a callback\" operation, and\n  that's all you need to use it as a host loop.\n\n\nReally? How is that possible?\n-----------------------------\n\n.. note::\n\n   You can use guest mode without reading this section. It's included\n   for those who enjoy understanding how things work.\n\nAll event loops have the same basic structure. They loop through two\noperations, over and over:\n\n1. Wait for the operating system to notify them that something\n   interesting has happened, like data arriving on a socket or a\n   timeout passing. They do this by invoking a platform-specific\n   ``sleep_until_something_happens()`` system call – ``select``,\n   ``epoll``, ``kqueue``, ``GetQueuedCompletionEvents``, etc.\n\n2. Run all the user tasks that care about whatever happened, then go\n   back to step 1.\n\nThe problem here is step 1. Two different event loops on the same\nthread can take turns running user tasks in step 2, but when they're\nidle and nothing is happening, they can't both invoke their own\n``sleep_until_something_happens()`` function at the same time.\n\nThe \"polling\" and \"pluggable backend\" strategies solve this by hacking\nthe loops so both step 1s can run at the same time in the same thread.\nKeeping everything in one thread is great for step 2, but the step 1\nhacks create problems.\n\nThe \"separate threads\" strategy solves this by moving both steps into\nseparate threads. This makes step 1 work, but the downside is that now\nthe user tasks in step 2 are running separate threads as well, so\nusers are forced to deal with inter-thread coordination.\n\nThe idea behind guest mode is to combine the best parts of each\napproach: we move Trio's step 1 into a separate worker thread, while\nkeeping Trio's step 2 in the main host thread. This way, when the\napplication is idle, both event loops do their\n``sleep_until_something_happens()`` at the same time in their own\nthreads. But when the app wakes up and your code is actually running,\nit all happens in a single thread. The threading trickiness is all\nhandled transparently inside Trio.\n\nConcretely, we unroll Trio's internal event loop into a chain of\ncallbacks, and as each callback finishes, it schedules the next\ncallback onto the host loop or a worker thread as appropriate. So the\nonly thing the host loop has to provide is a way to schedule a\ncallback onto the main thread from a worker thread.\n\nCoordinating between Trio and the host loop does add some overhead.\nThe main cost is switching in and out of the background thread, since\nthis requires cross-thread messaging. This is cheap (on the order of a\nfew microseconds, assuming your host loop is implemented efficiently),\nbut it's not free.\n\nBut, there's a nice optimization we can make: we only *need* the\nthread when our ``sleep_until_something_happens()`` call actually\nsleeps, that is, when the Trio part of your program is idle and has\nnothing to do. So before we switch into the worker thread, we\ndouble-check whether we're idle, and if not, then we skip the worker\nthread and jump directly to step 2. This means that your app only pays\nthe extra thread-switching penalty at moments when it would otherwise\nbe sleeping, so it should have minimal effect on your app's overall\nperformance.\n\nThe total overhead will depend on your host loop, your platform, your\napplication, etc. But we expect that in most cases, apps running in\nguest mode should only be 5-10% slower than the same code using\n`trio.run`. If you find that's not true for your app, then please let\nus know and we'll see if we can fix it!\n\n\n.. _guest-run-implementation:\n\nImplementing guest mode for your favorite event loop\n----------------------------------------------------\n\nLet's walk through what you need to do to integrate Trio's guest mode\nwith your favorite event loop. Treat this section like a checklist.\n\n**Getting started:** The first step is to get something basic working.\nHere's a minimal example of running Trio on top of asyncio, that you\ncan use as a model:\n\n.. code-block:: python\n\n    import asyncio\n    import trio\n\n    # A tiny Trio program\n    async def trio_main():\n        for _ in range(5):\n            print(\"Hello from Trio!\")\n            # This is inside Trio, so we have to use Trio APIs\n            await trio.sleep(1)\n        return \"trio done!\"\n\n    # The code to run it as a guest inside asyncio\n    async def asyncio_main():\n        asyncio_loop = asyncio.get_running_loop()\n\n        def run_sync_soon_threadsafe(fn):\n            asyncio_loop.call_soon_threadsafe(fn)\n\n        def done_callback(trio_main_outcome):\n            print(f\"Trio program ended with: {trio_main_outcome}\")\n\n        # This is where the magic happens:\n        trio.lowlevel.start_guest_run(\n            trio_main,\n            run_sync_soon_threadsafe=run_sync_soon_threadsafe,\n            done_callback=done_callback,\n        )\n\n        # Let the host loop run for a while to give trio_main time to\n        # finish. (WARNING: This is a hack. See below for better\n        # approaches.)\n        #\n        # This function is in asyncio, so we have to use asyncio APIs.\n        await asyncio.sleep(10)\n\n    asyncio.run(asyncio_main())\n\nYou can see we're using asyncio-specific APIs to start up a loop, and\nthen we call `trio.lowlevel.start_guest_run`. This function is very\nsimilar to `trio.run`, and takes all the same arguments. But it has\ntwo differences:\n\nFirst, instead of blocking until ``trio_main`` has finished, it\nschedules ``trio_main`` to start running on top of the host loop, and\nthen returns immediately. So ``trio_main`` is running in the\nbackground – that's why we have to sleep and give it time to finish.\n\nAnd second, it requires two extra keyword arguments:\n``run_sync_soon_threadsafe``, and ``done_callback``.\n\nFor ``run_sync_soon_threadsafe``, we need a function that takes a\nsynchronous callback, and schedules it to run on your host loop. And\nthis function needs to be \"threadsafe\" in the sense that you can\nsafely call it from any thread. So you need to figure out how to write\na function that does that using your host loop's API. For asyncio,\nthis is easy because `~asyncio.loop.call_soon_threadsafe` does exactly\nwhat we need; for your loop, it might be more or less complicated.\n\nFor ``done_callback``, you pass in a function that Trio will\nautomatically invoke when the Trio run finishes, so you know it's done\nand what happened. For this basic starting version, we just print the\nresult; in the next section we'll discuss better alternatives.\n\nAt this stage you should be able to run a simple Trio program inside\nyour host loop. Now we'll turn that prototype into something solid.\n\n\n**Loop lifetimes:** One of the trickiest things in most event loops is\nshutting down correctly. And having two event loops makes this even\nharder!\n\nIf you can, we recommend following this pattern:\n\n- Start up your host loop\n- Immediately call `start_guest_run` to start Trio\n- When Trio finishes and your ``done_callback`` is invoked, shut down\n  the host loop\n- Make sure that nothing else shuts down your host loop\n\nThis way, your two event loops have the same lifetime, and your\nprogram automatically exits when your Trio function finishes.\n\nHere's how we'd extend our asyncio example to implement this pattern:\n\n.. code-block:: python3\n   :emphasize-lines: 8-11,19-22\n\n   # Improved version, that shuts down properly after Trio finishes\n   async def asyncio_main():\n       asyncio_loop = asyncio.get_running_loop()\n\n       def run_sync_soon_threadsafe(fn):\n           asyncio_loop.call_soon_threadsafe(fn)\n\n       # Revised 'done' callback: set a Future\n       done_fut = asyncio_loop.create_future()\n       def done_callback(trio_main_outcome):\n           done_fut.set_result(trio_main_outcome)\n\n       trio.lowlevel.start_guest_run(\n           trio_main,\n           run_sync_soon_threadsafe=run_sync_soon_threadsafe,\n           done_callback=done_callback,\n       )\n\n       # Wait for the guest run to finish\n       trio_main_outcome = await done_fut\n       # Pass through the return value or exception from the guest run\n       return trio_main_outcome.unwrap()\n\nAnd then you can encapsulate all this machinery in a utility function\nthat exposes a `trio.run`-like API, but runs both loops together:\n\n.. code-block:: python\n\n   def trio_run_with_asyncio(trio_main, *args, **trio_run_kwargs):\n       async def asyncio_main():\n           # same as above\n           ...\n\n       return asyncio.run(asyncio_main())\n\nTechnically, it is possible to use other patterns. But there are some\nimportant limitations you have to respect:\n\n- **You must let the Trio program run to completion.** Many event\n  loops let you stop the event loop at any point, and any pending\n  callbacks/tasks/etc. just... don't run. Trio follows a more\n  structured system, where you can cancel things, but the code always\n  runs to completion, so ``finally`` blocks run, resources are cleaned\n  up, etc. If you stop your host loop early, before the\n  ``done_callback`` is invoked, then that cuts off the Trio run in the\n  middle without a chance to clean up. This can leave your code in an\n  inconsistent state, and will definitely leave Trio's internals in an\n  inconsistent state, which will cause errors if you try to use Trio\n  again in that thread.\n\n  Some programs need to be able to quit at any time, for example in\n  response to a GUI window being closed or a user selecting a \"Quit\"\n  from a menu. In these cases, we recommend wrapping your whole\n  program in a `trio.CancelScope`, and cancelling it when you want to\n  quit.\n\n- Each host loop can only have one `start_guest_run` at a time. If you\n  try to start a second one, you'll get an error. If you need to run\n  multiple Trio functions at the same time, then start up a single\n  Trio run, open a nursery, and then start your functions as child\n  tasks in that nursery.\n\n- Unless you or your host loop register a handler for `signal.SIGINT`\n  before starting Trio (this is not common), then Trio will take over\n  delivery of `KeyboardInterrupt`\\s. And since Trio can't tell which\n  host code is safe to interrupt, it will only deliver\n  `KeyboardInterrupt` into the Trio part of your code. This is fine if\n  your program is set up to exit when the Trio part exits, because the\n  `KeyboardInterrupt` will propagate out of Trio and then trigger the\n  shutdown of your host loop, which is just what you want.\n\nGiven these constraints, we think the simplest approach is to always\nstart and stop the two loops together.\n\n**Signal management:** `\"Signals\"\n<https://en.wikipedia.org/wiki/Signal_(IPC)>`__ are a low-level\ninter-process communication primitive. When you hit control-C to kill\na program, that uses a signal. Signal handling in Python has `a lot of\nmoving parts\n<https://vorpus.org/blog/control-c-handling-in-python-and-trio/>`__.\nOne of those parts is `signal.set_wakeup_fd`, which event loops use to\nmake sure that they wake up when a signal arrives so they can respond\nto it. (If you've ever had an event loop ignore you when you hit\ncontrol-C, it was probably because they weren't using\n`signal.set_wakeup_fd` correctly.)\n\nBut, only one event loop can use `signal.set_wakeup_fd` at a time. And\nin guest mode that can cause problems: Trio and the host loop might\nstart fighting over who's using `signal.set_wakeup_fd`.\n\nSome event loops, like asyncio, won't work correctly unless they win\nthis fight. Fortunately, Trio is a little less picky: as long as\n*someone* makes sure that the program wakes up when a signal arrives,\nit should work correctly. So if your host loop wants\n`signal.set_wakeup_fd`, then you should disable Trio's\n`signal.set_wakeup_fd` support, and then both loops will work\ncorrectly.\n\nOn the other hand, if your host loop doesn't use\n`signal.set_wakeup_fd`, then the only way to make everything work\ncorrectly is to *enable* Trio's `signal.set_wakeup_fd` support.\n\nBy default, Trio assumes that your host loop doesn't use\n`signal.set_wakeup_fd`. It does try to detect when this creates a\nconflict with the host loop, and print a warning – but unfortunately,\nby the time it detects it, the damage has already been done. So if\nyou're getting this warning, then you should disable Trio's\n`signal.set_wakeup_fd` support by passing\n``host_uses_signal_set_wakeup_fd=True`` to `start_guest_run`.\n\nIf you aren't seeing any warnings with your initial prototype, you're\n*probably* fine. But the only way to be certain is to check your host\nloop's source. For example, asyncio may or may not use\n`signal.set_wakeup_fd` depending on the Python version and operating\nsystem.\n\n\n**A small optimization:** Finally, consider a small optimization. Some\nevent loops offer two versions of their \"call this function soon\" API:\none that can be used from any thread, and one that can only be used\nfrom the event loop thread, with the latter being cheaper. For\nexample, asyncio has both `~asyncio.loop.call_soon_threadsafe` and\n`~asyncio.loop.call_soon`.\n\nIf you have a loop like this, then you can also pass a\n``run_sync_soon_not_threadsafe=...`` kwarg to `start_guest_run`, and\nTrio will automatically use it when appropriate.\n\nIf your loop doesn't have a split like this, then don't worry about\nit; ``run_sync_soon_not_threadsafe=`` is optional. (If it's not\npassed, then Trio will just use your threadsafe version in all cases.)\n\n**That's it!** If you've followed all these steps, you should now have\na cleanly-integrated hybrid event loop. Go make some cool\nGUIs/games/whatever!\n\n\nLimitations\n-----------\n\nIn general, almost all Trio features should work in guest mode. The\nexception is features which rely on Trio having a complete picture of\neverything that your program is doing, since obviously, it can't\ncontrol the host loop or see what it's doing.\n\nCustom clocks can be used in guest mode, but they only affect Trio\ntimeouts, not host loop timeouts. And the :ref:`autojump clock\n<testing-time>` and related `trio.testing.wait_all_tasks_blocked` can\ntechnically be used in guest mode, but they'll only take Trio tasks\ninto account when decided whether to jump the clock or whether all\ntasks are blocked.\n\n\nReference\n---------\n\n.. autofunction:: start_guest_run\n\n\n.. _live-coroutine-handoff:\n\nHanding off live coroutine objects between coroutine runners\n============================================================\n\nInternally, Python's async/await syntax is built around the idea of\n\"coroutine objects\" and \"coroutine runners\". A coroutine object\nrepresents the state of an async callstack. But by itself, this is\njust a static object that sits there. If you want it to do anything,\nyou need a coroutine runner to push it forward. Every Trio task has an\nassociated coroutine object (see :data:`Task.coro`), and the Trio\nscheduler acts as their coroutine runner.\n\nBut of course, Trio isn't the only coroutine runner in Python –\n:mod:`asyncio` has one, other event loops have them, you can even\ndefine your own.\n\nAnd in some very, very unusual circumstances, it even makes sense to\ntransfer a single coroutine object back and forth between different\ncoroutine runners. That's what this section is about. This is an\n*extremely* exotic use case, and assumes a lot of expertise in how\nPython async/await works internally. For motivating examples, see\n`trio-asyncio issue #42\n<https://github.com/python-trio/trio-asyncio/issues/42>`__, and `trio\nissue #649 <https://github.com/python-trio/trio/issues/649>`__. For\nmore details on how coroutines work, we recommend André Caron's `A\ntale of event loops\n<https://github.com/AndreLouisCaron/a-tale-of-event-loops>`__, or\ngoing straight to :pep:`492` for the full details.\n\n.. autofunction:: permanently_detach_coroutine_object\n\n.. autofunction:: temporarily_detach_coroutine_object\n\n.. autofunction:: reattach_detached_coroutine_object\n"
  },
  {
    "path": "docs/source/reference-testing/across-realtime.out",
    "content": "Clock where time passes at 100 years per second:\n\ntask2: sleeping for 5 years\ntask1: sleeping for 1 year\ntask1: woke up; clock says I've slept 1.0365006048232317 years\ntask1: sleeping for 1 year, 100 times\ntask2: woke up; clock says I've slept 5.0572111969813704 years\ntask2: sleeping for 500 years\ntask1: slept 104.77677842136472 years total\ntask2: slept 505.25014589075 years total\nTotal real time elapsed: 5.053582429885864 seconds\n\nClock where time automatically skips past the boring parts:\n\ntask2: sleeping for 5 years\ntask1: sleeping for 1 year\ntask1: woke up; clock says I've slept 1.0 years\ntask1: sleeping for 1 year, 100 times\ntask2: woke up; clock says I've slept 5.0 years\ntask2: sleeping for 500 years\ntask1: slept 101.0 years total\ntask2: slept 505.0 years total\nTotal real time elapsed: 0.019298791885375977 seconds\n"
  },
  {
    "path": "docs/source/reference-testing/across-realtime.py",
    "content": "# across-realtime.py\n\nimport time\nimport trio\nimport trio.testing\n\nYEAR = 365 * 24 * 60 * 60  # seconds\n\n\nasync def task1():\n    start = trio.current_time()\n\n    print(\"task1: sleeping for 1 year\")\n    await trio.sleep(YEAR)\n\n    duration = trio.current_time() - start\n    print(f\"task1: woke up; clock says I've slept {duration / YEAR} years\")\n\n    print(\"task1: sleeping for 1 year, 100 times\")\n    for _ in range(100):\n        await trio.sleep(YEAR)\n\n    duration = trio.current_time() - start\n    print(f\"task1: slept {duration / YEAR} years total\")\n\n\nasync def task2():\n    start = trio.current_time()\n\n    print(\"task2: sleeping for 5 years\")\n    await trio.sleep(5 * YEAR)\n\n    duration = trio.current_time() - start\n    print(f\"task2: woke up; clock says I've slept {duration / YEAR} years\")\n\n    print(\"task2: sleeping for 500 years\")\n    await trio.sleep(500 * YEAR)\n\n    duration = trio.current_time() - start\n    print(f\"task2: slept {duration / YEAR} years total\")\n\n\nasync def main():\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(task1)\n        nursery.start_soon(task2)\n\n\ndef run_example(clock):\n    real_start = time.perf_counter()\n    trio.run(main, clock=clock)\n    real_duration = time.perf_counter() - real_start\n    print(f\"Total real time elapsed: {real_duration} seconds\")\n\n\nprint(\"Clock where time passes at 100 years per second:\\n\")\nrun_example(trio.testing.MockClock(rate=100 * YEAR))\n\nprint(\"\\nClock where time automatically skips past the boring parts:\\n\")\nrun_example(trio.testing.MockClock(autojump_threshold=0))\n"
  },
  {
    "path": "docs/source/reference-testing.rst",
    "content": "Testing made easier with ``trio.testing``\n=========================================\n\n.. module:: trio.testing\n\nThe :mod:`trio.testing` module provides various utilities to make it\neasier to test Trio code. Unlike the other submodules in the\n:mod:`trio` namespace, :mod:`trio.testing` is *not* automatically\nimported when you do ``import trio``; you must ``import trio.testing``\nexplicitly.\n\n\nTest harness integration\n------------------------\n\n.. decorator:: trio_test\n\n\n.. _testing-time:\n\nTime and timeouts\n-----------------\n\n:class:`trio.testing.MockClock` is a :class:`~trio.abc.Clock` with a\nfew tricks up its sleeve to help you efficiently test code involving\ntimeouts:\n\n* By default, it starts at time 0, and clock time only advances when\n  you explicitly call :meth:`~MockClock.jump`. This provides an\n  extremely controllable clock for testing.\n\n* You can set :attr:`~MockClock.rate` to 1.0 if you want it to start\n  running in real time like a regular clock. You can stop and start\n  the clock within a test. You can set :attr:`~MockClock.rate` to 10.0\n  to make clock time pass at 10x real speed (so e.g. ``await\n  trio.sleep(10)`` returns after 1 second).\n\n* But even more interestingly, you can set\n  :attr:`~MockClock.autojump_threshold` to zero or a small value, and\n  then it will watch the execution of the run loop, and any time\n  things have settled down and everyone's waiting for a timeout, it\n  jumps the clock forward to that timeout. In many cases this allows\n  natural-looking code involving timeouts to be automatically run at\n  near full CPU utilization with no changes. (Thanks to `fluxcapacitor\n  <https://github.com/majek/fluxcapacitor>`__ for this awesome idea.)\n\n* And of course these can be mixed and matched at will.\n\nRegardless of these shenanigans, from \"inside\" Trio the passage of time\nstill seems normal so long as you restrict yourself to Trio's time\nfunctions (see :ref:`time-and-clocks`). Below is an example\ndemonstrating two different ways of making time pass quickly. Notice\nhow in both cases, the two tasks keep a consistent view of reality and\nevents happen in the expected order, despite being wildly divorced\nfrom real time:\n\n.. literalinclude:: reference-testing/across-realtime.py\n\nOutput:\n\n.. literalinclude:: reference-testing/across-realtime.out\n   :language: none\n\n.. autoclass:: MockClock\n   :members:\n\n\nInter-task ordering\n-------------------\n\n.. autoclass:: Sequencer\n\n.. autofunction:: wait_all_tasks_blocked\n\n.. autofunction:: wait_all_threads_completed\n\n.. autofunction:: active_thread_count\n\n\n.. _testing-streams:\n\nStreams\n-------\n\nConnecting to an in-process socket server\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. autofunction:: open_stream_to_socket_listener\n\n\n.. _virtual-streams:\n\nVirtual, controllable streams\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOne particularly challenging problem when testing network protocols is\nmaking sure that your implementation can handle data whose flow gets\nbroken up in weird ways and arrives with weird timings: localhost\nconnections tend to be much better behaved than real networks, so if\nyou only test on localhost then you might get bitten later. To help\nyou out, Trio provides some fully in-memory implementations of the\nstream interfaces (see :ref:`abstract-stream-api`), that let you write\nall kinds of interestingly evil tests.\n\nThere are a few pieces here, so here's how they fit together:\n\n:func:`memory_stream_pair` gives you a pair of connected,\nbidirectional streams. It's like :func:`socket.socketpair`, but\nwithout any involvement from that pesky operating system and its\nnetworking stack.\n\nTo build a bidirectional stream, :func:`memory_stream_pair` uses\ntwo unidirectional streams. It gets these by calling\n:func:`memory_stream_one_way_pair`.\n\n:func:`memory_stream_one_way_pair`, in turn, is implemented using the\nlow-ish level classes :class:`MemorySendStream` and\n:class:`MemoryReceiveStream`. These are implementations of (you\nguessed it) :class:`trio.abc.SendStream` and\n:class:`trio.abc.ReceiveStream` that on their own, aren't attached to\nanything – \"sending\" and \"receiving\" just put data into and get data\nout of a private internal buffer that each object owns. They also have\nsome interesting hooks you can set, that let you customize the\nbehavior of their methods. This is where you can insert the evil, if\nyou want it. :func:`memory_stream_one_way_pair` takes advantage of\nthese hooks in a relatively boring way: it just sets it up so that\nwhen you call ``send_all``, or when you close the send stream, then it\nautomatically triggers a call to :func:`memory_stream_pump`, which is\na convenience function that takes data out of a\n:class:`MemorySendStream`\\´s buffer and puts it into a\n:class:`MemoryReceiveStream`\\´s buffer. But that's just the default –\nyou can replace this with whatever arbitrary behavior you want.\n\nTrio also provides some specialized functions for testing completely\n**un**\\buffered streams: :func:`lockstep_stream_one_way_pair` and\n:func:`lockstep_stream_pair`. These aren't customizable, but they do\nexhibit an extreme kind of behavior that's good at catching out edge\ncases in protocol implementations.\n\n\nAPI details\n~~~~~~~~~~~\n\n.. autoclass:: MemorySendStream\n   :members:\n\n.. autoclass:: MemoryReceiveStream\n   :members:\n\n.. autofunction:: memory_stream_pump\n\n.. autofunction:: memory_stream_one_way_pair\n\n.. autofunction:: memory_stream_pair\n\n.. autofunction:: lockstep_stream_one_way_pair\n\n.. autofunction:: lockstep_stream_pair\n\n\n.. _testing-custom-streams:\n\nTesting custom stream implementations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTrio also provides some functions to help you test your custom stream\nimplementations:\n\n.. autofunction:: check_one_way_stream\n\n.. autofunction:: check_two_way_stream\n\n.. autofunction:: check_half_closeable_stream\n\n\n.. _virtual-network-hooks:\n\nVirtual networking for testing\n------------------------------\n\nIn the previous section you learned how to use virtual in-memory\nstreams to test protocols that are written against Trio's\n:class:`~trio.abc.Stream` abstraction. But what if you have more\ncomplicated networking code – the kind of code that makes connections\nto multiple hosts, or opens a listening socket, or sends UDP packets?\n\nTrio doesn't itself provide a virtual in-memory network implementation\nfor testing – but :mod:`trio.socket` module does provide the hooks you\nneed to write your own! And if you're interested in helping implement\na reusable virtual network for testing, then `please get in touch\n<https://github.com/python-trio/trio/issues/170>`__.\n\nNote that these APIs are actually in :mod:`trio.socket` and\n:mod:`trio.abc`, but we document them here because they're primarily\nintended for testing.\n\n.. currentmodule:: trio.socket\n\n.. autofunction:: trio.socket.set_custom_hostname_resolver\n\n.. currentmodule:: trio.abc\n\n.. autoclass:: trio.abc.HostnameResolver\n   :members:\n\n.. currentmodule:: trio.socket\n\n.. autofunction:: trio.socket.set_custom_socket_factory\n\n.. currentmodule:: trio.abc\n\n.. autoclass:: trio.abc.SocketFactory\n   :members:\n\n.. currentmodule:: trio.testing\n\n\nTesting checkpoints\n--------------------\n\n.. autofunction:: assert_checkpoints\n   :with:\n\n.. autofunction:: assert_no_checkpoints\n   :with:\n\n\nExceptionGroup helpers\n----------------------\n\n.. autoclass:: RaisesGroup\n   :members:\n\n   .. autoattribute:: fail_reason\n\n.. autoclass:: Matcher\n   :members:\n\n   .. autoattribute:: fail_reason\n\n.. autoclass:: trio.testing._raises_group._ExceptionInfo\n   :members:\n"
  },
  {
    "path": "docs/source/releasing.rst",
    "content": ".. _releasing:\n\nPreparing a release\n-------------------\n\nThings to do for releasing:\n\n* announce intent to release on gitter\n\n* check for open issues / pull requests that really should be in the release\n\n   + come back when these are done\n\n   + … or ignore them and do another release next week\n\n* check for deprecations \"long enough ago\" (two months or two releases, whichever is longer)\n\n   + remove affected code\n\n* Do the actual release changeset\n\n   + bump version number\n\n      - increment as per Semantic Versioning rules\n\n      - remove ``+dev`` tag from version number\n\n   + Run ``towncrier``\n\n      - review history change\n\n      - ``git rm`` the now outdated newfragments\n\n   + commit\n\n* push to your personal repository\n\n* create pull request to ``python-trio/trio``'s \"main\" branch\n\n* verify that all checks succeeded\n\n* tag with vVERSION, push tag on ``python-trio/trio`` (not on your personal repository)\n\n* approve the release workflow's publish job\n\n* update version number in the same pull request\n\n   + add ``+dev`` tag to the end\n\n* merge the release pull request\n\n* make a GitHub release (go to the tag and press \"Create release from tag\")\n\n   + paste in the new content in ``history.rst`` and convert it to markdown: turn the parts under section into ``---``, update links to just be the links, and whatever else is necessary.\n\n   + include anything else that might be pertinent, like a link to the commits between the latest and current release.\n\n* announce on gitter\n"
  },
  {
    "path": "docs/source/tutorial/echo-client.py",
    "content": "# echo-client.py\n\nimport sys\nimport trio\n\n# arbitrary, but:\n# - must be in between 1024 and 65535\n# - can't be in use by some other program on your computer\n# - must match what we set in our echo server\nPORT = 12345\n\n\nasync def sender(client_stream):\n    print(\"sender: started!\")\n    while True:\n        data = b\"async can sometimes be confusing, but I believe in you!\"\n        print(f\"sender: sending {data!r}\")\n        await client_stream.send_all(data)\n        await trio.sleep(1)\n\n\nasync def receiver(client_stream):\n    print(\"receiver: started!\")\n    async for data in client_stream:\n        print(f\"receiver: got data {data!r}\")\n    print(\"receiver: connection closed\")\n    sys.exit()\n\n\nasync def parent():\n    print(f\"parent: connecting to 127.0.0.1:{PORT}\")\n    client_stream = await trio.open_tcp_stream(\"127.0.0.1\", PORT)\n    async with client_stream:\n        async with trio.open_nursery() as nursery:\n            print(\"parent: spawning sender...\")\n            nursery.start_soon(sender, client_stream)\n\n            print(\"parent: spawning receiver...\")\n            nursery.start_soon(receiver, client_stream)\n\n\ntrio.run(parent)\n"
  },
  {
    "path": "docs/source/tutorial/echo-server.py",
    "content": "# echo-server.py\n\nimport trio\nfrom itertools import count\n\n# Port is arbitrary, but:\n# - must be in between 1024 and 65535\n# - can't be in use by some other program on your computer\n# - must match what we set in our echo client\nPORT = 12345\n\nCONNECTION_COUNTER = count()\n\n\nasync def echo_server(server_stream):\n    # Assign each connection a unique number to make our debug prints easier\n    # to understand when there are multiple simultaneous connections.\n    ident = next(CONNECTION_COUNTER)\n    print(f\"echo_server {ident}: started\")\n    try:\n        async for data in server_stream:\n            print(f\"echo_server {ident}: received data {data!r}\")\n            await server_stream.send_all(data)\n        print(f\"echo_server {ident}: connection closed\")\n    # FIXME: add discussion of (Base)ExceptionGroup to the tutorial, and use\n    # exceptiongroup.catch() here. (Not important in this case, but important\n    # if the server code uses nurseries internally.)\n    except Exception as exc:\n        # Unhandled exceptions will propagate into our parent and take\n        # down the whole program. If the exception is KeyboardInterrupt,\n        # that's what we want, but otherwise maybe not...\n        print(f\"echo_server {ident}: crashed: {exc!r}\")\n\n\nasync def main():\n    await trio.serve_tcp(echo_server, PORT)\n\n\n# We could also just write 'trio.run(trio.serve_tcp, echo_server, PORT)', but real\n# programs almost always end up doing other stuff too and then we'd have to go\n# back and factor it out into a separate function anyway. So it's simplest to\n# just make it a standalone function from the beginning.\ntrio.run(main)\n"
  },
  {
    "path": "docs/source/tutorial/tasks-intro.py",
    "content": "# tasks-intro.py\n\nimport trio\n\n\nasync def child1():\n    print(\"  child1: started! sleeping now...\")\n    await trio.sleep(1)\n    print(\"  child1: exiting!\")\n\n\nasync def child2():\n    print(\"  child2: started! sleeping now...\")\n    await trio.sleep(1)\n    print(\"  child2: exiting!\")\n\n\nasync def parent():\n    print(\"parent: started!\")\n    async with trio.open_nursery() as nursery:\n        print(\"parent: spawning child1...\")\n        nursery.start_soon(child1)\n\n        print(\"parent: spawning child2...\")\n        nursery.start_soon(child2)\n\n        print(\"parent: waiting for children to finish...\")\n        # -- we exit the nursery block here --\n    print(\"parent: all done!\")\n\n\ntrio.run(parent)\n"
  },
  {
    "path": "docs/source/tutorial/tasks-with-trace.py",
    "content": "# tasks-with-trace.py\n\nimport trio\n\n\nasync def child1():\n    print(\"  child1: started! sleeping now...\")\n    await trio.sleep(1)\n    print(\"  child1: exiting!\")\n\n\nasync def child2():\n    print(\"  child2 started! sleeping now...\")\n    await trio.sleep(1)\n    print(\"  child2 exiting!\")\n\n\nasync def parent():\n    print(\"parent: started!\")\n    async with trio.open_nursery() as nursery:\n        print(\"parent: spawning child1...\")\n        nursery.start_soon(child1)\n\n        print(\"parent: spawning child2...\")\n        nursery.start_soon(child2)\n\n        print(\"parent: waiting for children to finish...\")\n        # -- we exit the nursery block here --\n    print(\"parent: all done!\")\n\n\nclass Tracer(trio.abc.Instrument):\n    def before_run(self):\n        print(\"!!! run started\")\n\n    def _print_with_task(self, msg, task):\n        # repr(task) is perhaps more useful than task.name in general,\n        # but in context of a tutorial the extra noise is unhelpful.\n        print(f\"{msg}: {task.name}\")\n\n    def task_spawned(self, task):\n        self._print_with_task(\"### new task spawned\", task)\n\n    def task_scheduled(self, task):\n        self._print_with_task(\"### task scheduled\", task)\n\n    def before_task_step(self, task):\n        self._print_with_task(\">>> about to run one step of task\", task)\n\n    def after_task_step(self, task):\n        self._print_with_task(\"<<< task step finished\", task)\n\n    def task_exited(self, task):\n        self._print_with_task(\"### task exited\", task)\n\n    def before_io_wait(self, timeout):\n        if timeout:\n            print(f\"### waiting for I/O for up to {timeout} seconds\")\n        else:\n            print(\"### doing a quick check for I/O\")\n        self._sleep_time = trio.current_time()\n\n    def after_io_wait(self, timeout):\n        duration = trio.current_time() - self._sleep_time\n        print(f\"### finished I/O check (took {duration} seconds)\")\n\n    def after_run(self):\n        print(\"!!! run finished\")\n\n\ntrio.run(parent, instruments=[Tracer()])\n"
  },
  {
    "path": "docs/source/tutorial.rst",
    "content": "Tutorial\n========\n\n.. The Trio tutorial\n\n   the spiel about what a concurrent library is\n\n   Traditionally Python is a synchronous language, and we assume\n   you're familiar with that kind of programming, but don't assume any\n   knowledge of concurrent programming. (And even if you are familiar\n   with concurrent programming using another library like Twisted or\n   asyncio, or another language like Go or Erlang, then you should\n   still probably read this, because Trio is different.)\n\n   Trio turns Python into a concurrent language. It takes the core\n   async/await syntax introduced in 3.5, and uses it to add two\n   new pieces of semantics:\n\n   - cancel scopes: a generic system for managing timeouts and\n     cancellation\n   - nurseries: which let your program do multiple things at the same\n     time\n\n   Of course it also provides a complete suite of APIs for doing\n   networking, file I/O, using worker threads,\n\n   We'll go through and explain each of these\n\n   simple cancellation\n   applied to an HTTP request\n     with fail_after(5):\n         response = await asks.get(\"https://httpbin.org/delay/1\")\n         print(response)\n   and then again with /delay/10\n\n   value of async/await: show you where the cancellation exceptions\n   can happen -- see pillar re: explicit cancel points\n\n   (also briefly discuss cancel scopes and cancel() + the query APIs,\n   fail_after vs move_on_after, current_time() and\n   current_effective_deadline())\n\n   simple multi-task concurrency\n   applied to do multiple HTTP requests\n   adding a per-request timeout\n   adding a timeout on the whole thing -- demonstrating wrapping\n       cancel around a nursery\n\n   pillars: implicit concurrency and exception raising\n   and explicit schedule points\n\n   example: the scheduling trace\n\n   implicit concurrency -> use echo example to introduce networking\n   API, and show how to do explicit concurrency\n   and demonstrate start()\n   then point out that you can just use serve_tcp()\n\n   example: catch-all logging in our echo server\n\n   review of the three (or four) core language extensions\n   and how they fit together and\n\n.. currentmodule:: trio\n\nWelcome to the Trio tutorial! Trio is a modern Python library for\nwriting asynchronous applications – that is, programs that want to do\nmultiple things at the same time with parallelized I/O, like a web\nspider that fetches lots of pages in parallel, a web server juggling\nlots of simultaneous downloads... that sort of thing. Here we'll try\nto give a gentle introduction to asynchronous programming with Trio.\n\nWe assume that you're familiar with Python in general, but don't worry\n– we don't assume you know anything about asynchronous programming or\nPython's new ``async/await`` feature.\n\nAlso, unlike many ``async/await`` tutorials, we assume that your goal\nis to *use* Trio to write interesting programs, so we won't go into\nthe nitty-gritty details of how ``async/await`` is implemented inside\nthe Python interpreter. The word \"coroutine\" is never mentioned. The\nfact is, you really don't *need* to know any of that stuff unless you\nwant to *implement* a library like Trio, so we leave it out (though\nwe'll throw in a few links for those who want to dig deeper).\n\nOkay, ready? Let's get started.\n\n\nBefore you begin\n----------------\n\n1. Make sure you're using Python 3.10 or newer.\n\n2. ``python3 -m pip install --upgrade trio`` (or on Windows, maybe\n   ``py -3 -m pip install --upgrade trio`` – `details\n   <https://packaging.python.org/installing/>`__)\n\n3. Can you ``import trio``? If so then you're good to go!\n\n\nIf you get lost or confused...\n------------------------------\n\n...then we want to know! We have a friendly `chat channel\n<https://gitter.im/python-trio/general>`__, you can ask questions\n`using the \"python-trio\" tag on StackOverflow\n<https://stackoverflow.com/questions/ask?tags=python+python-trio>`__, or just\n`file a bug <https://github.com/python-trio/trio/issues/new>`__ (if\nour documentation is confusing, that's our fault, and we want to fix\nit!).\n\n\nAsync functions\n---------------\n\nPython 3.5 added a major new feature: async functions. Using Trio is\nall about writing async functions, so let's start there.\n\nAn async function is defined like a normal function, except you write\n``async def`` instead of ``def``:\n\n.. code-block:: python\n\n   # A regular function\n   def regular_double(x):\n       return 2 * x\n\n   # An async function\n   async def async_double(x):\n       return 2 * x\n\n\"Async\" is short for \"asynchronous\"; we'll sometimes refer to regular\nfunctions like ``regular_double`` as \"synchronous functions\", to\ndistinguish them from async functions.\n\nFrom a user's point of view, there are two differences between an\nasync function and a regular function:\n\n1. To call an async function, you have to use the ``await``\n   keyword. So instead of writing ``regular_double(3)``, you write\n   ``await async_double(3)``.\n\n2. You can't use the ``await`` keyword inside the body of a regular\n   function. If you try it, you'll get a syntax error:\n\n   .. code-block:: python\n\n      def print_double(x):\n          print(await async_double(x))   # <-- SyntaxError here\n\n   But inside an async function, ``await`` is allowed:\n\n   .. code-block:: python\n\n      async def print_double(x):\n          print(await async_double(x))   # <-- OK!\n\nNow, let's think about the consequences here: if you need ``await`` to\ncall an async function, and only async functions can use\n``await``... here's a little table:\n\n=======================  ==================================  ===================\nIf a function like this  wants to call a function like this  is it gonna happen?\n=======================  ==================================  ===================\nsync                     sync                                ✓\nsync                     async                               **NOPE**\nasync                    sync                                ✓\nasync                    async                               ✓\n=======================  ==================================  ===================\n\nSo in summary: As a user, the entire advantage of async functions over\nregular functions is that async functions have a superpower: they can\ncall other async functions.\n\nThis immediately raises two questions: how, and why? Specifically:\n\nWhen your Python program starts up, it's running regular old sync\ncode. So there's a chicken-and-the-egg problem: once we're running an\nasync function we can call other async functions, but *how* do we call\nthat first async function?\n\nAnd, if the only reason to write an async function is that it can call\nother async functions, *why* on earth would we ever use them in\nthe first place? I mean, as superpowers go this seems a bit\npointless. Wouldn't it be simpler to just... not use any async\nfunctions at all?\n\nThis is where an async library like Trio comes in. It provides two\nthings:\n\n1. A runner function, which is a special *synchronous* function that\n   takes and calls an *asynchronous* function. In Trio, this is\n   ``trio.run``:\n\n   .. code-block:: python\n\n      import trio\n\n      async def async_double(x):\n          return 2 * x\n\n      trio.run(async_double, 3)  # returns 6\n\n   So that answers the \"how\" part.\n\n2. A bunch of useful async functions – in particular, functions for\n   doing I/O. So that answers the \"why\": these functions are async,\n   and they're useful, so if you want to use them, you have to write\n   async code. If you think keeping track of these ``async`` and\n   ``await`` things is annoying, then too bad – you've got no choice\n   in the matter! (Well, OK, you could just not use Trio. That's a\n   legitimate option. But it turns out that the ``async/await`` stuff\n   is actually a good thing, for reasons we'll discuss a little bit\n   later.)\n\n   Here's an example function that uses\n   :func:`trio.sleep`. (:func:`trio.sleep` is like :func:`time.sleep`,\n   but with more async.)\n\n   .. code-block:: python\n\n      import trio\n\n      async def double_sleep(x):\n          await trio.sleep(2 * x)\n\n      trio.run(double_sleep, 3)  # does nothing for 6 seconds then returns\n\n.. _async-sandwich:\n\nSo it turns out our ``async_double`` function is actually a bad\nexample. I mean, it works, it's fine, there's nothing *wrong* with it,\nbut it's pointless: it could just as easily be written as a regular\nfunction, and it would be more useful that way. ``double_sleep`` is a\nmuch more typical example: we have to make it async, because it calls\nanother async function. The end result is a kind of async sandwich,\nwith Trio on both sides and our code in the middle:\n\n.. code-block:: none\n\n  trio.run -> double_sleep -> trio.sleep\n\nThis \"sandwich\" structure is typical for async code; in general, it\nlooks like:\n\n.. code-block:: none\n\n  trio.run -> [async function] -> ... -> [async function] -> trio.whatever\n\nIt's exactly the functions on the path between :func:`trio.run` and\n``trio.whatever`` that have to be async. Trio provides the async\nbread, and then your code makes up the async sandwich's tasty async\nfilling. Other functions (e.g., helpers you call along the way) should\ngenerally be regular, non-async functions.\n\n\nWarning: don't forget that ``await``!\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow would be a good time to open up a Python prompt and experiment a\nlittle with writing simple async functions and running them with\n``trio.run``.\n\nAt some point in this process, you'll probably write some code like\nthis, that tries to call an async function but leaves out the\n``await``:\n\n.. code-block:: python\n\n   import time\n   import trio\n\n   async def broken_double_sleep(x):\n       print(\"*yawn* Going to sleep\")\n       start_time = time.perf_counter()\n\n       # Whoops, we forgot the 'await'!\n       trio.sleep(2 * x)\n\n       sleep_time = time.perf_counter() - start_time\n       print(f\"Woke up after {sleep_time:.2f} seconds, feeling well rested!\")\n\n   trio.run(broken_double_sleep, 3)\n\nYou might think that Python would raise an error here, like it does\nfor other kinds of mistakes we sometimes make when calling a\nfunction. Like, if we forgot to pass :func:`trio.sleep` its required\nargument, then we would get a nice :exc:`TypeError` saying so. But\nunfortunately, if you forget an ``await``, you don't get that. What\nyou actually get is:\n\n.. code-block:: pycon\n\n   >>> trio.run(broken_double_sleep, 3)\n   *yawn* Going to sleep\n   Woke up after 0.00 seconds, feeling well rested!\n   __main__:4: RuntimeWarning: coroutine 'sleep' was never awaited\n   >>>\n\nThis is clearly broken – 0.00 seconds is not long enough to feel well\nrested! Yet the code acts like it succeeded – no exception was\nraised. The only clue that something went wrong is that it prints\n``RuntimeWarning: coroutine 'sleep' was never awaited``. Also, the\nexact place where the warning is printed might vary, because it\ndepends on the whims of the garbage collector. If you're using PyPy,\nyou might not even get a warning at all until the next GC collection\nruns:\n\n.. code-block:: pycon\n\n   # On PyPy:\n   >>> trio.run(broken_double_sleep, 3)\n   *yawn* Going to sleep\n   Woke up after 0.00 seconds, feeling well rested!\n   >>> # what the ... ?? not even a warning!\n\n   >>> # but forcing a garbage collection gives us a warning:\n   >>> import gc\n   >>> gc.collect()\n   /home/njs/pypy-3.8-nightly/lib-python/3/importlib/_bootstrap.py:191: RuntimeWarning: coroutine 'sleep' was never awaited\n   if _module_locks.get(name) is wr:    # XXX PyPy fix?\n   0\n\n(If you can't see the warning above, try scrolling right.)\n\nForgetting an ``await`` like this is an *incredibly common\nmistake*. You will mess this up. Everyone does. And Python will not\nhelp you as much as you'd hope 😞. The key thing to remember is: if\nyou see the magic words ``RuntimeWarning: coroutine '...' was never\nawaited``, then this *always* means that you made the mistake of\nleaving out an ``await`` somewhere, and you should ignore all the\nother error messages you see and go fix that first, because there's a\ngood chance the other stuff is just collateral damage. I'm not even\nsure what all that other junk in the PyPy output is. Fortunately I\ndon't need to know, I just need to fix my function!\n\n(\"I thought you said you weren't going to mention coroutines!\" Yes,\nwell, *I* didn't mention coroutines, Python did. Take it up with\nGuido! But seriously, this is unfortunately a place where the internal\nimplementation details do leak out a bit.)\n\nWhy does this happen? In Trio, every time we use ``await`` it's to\ncall an async function, and every time we call an async function we\nuse ``await``. But Python's trying to keep its options open for other\nlibraries that are *ahem* a little less organized about things. So\nwhile for our purposes we can think of ``await trio.sleep(...)`` as a\nsingle piece of syntax, Python thinks of it as two things: first a\nfunction call that returns this weird \"coroutine\" object:\n\n.. code-block:: pycon\n\n   >>> trio.sleep(3)\n   <coroutine object sleep at 0x7f5ac77be6d0>\n\nand then that object gets passed to ``await``, which actually runs the\nfunction. So if you forget ``await``, then two bad things happen: your\nfunction doesn't actually get called, and you get a \"coroutine\" object\nwhere you might have been expecting something else, like a number:\n\n.. code-block:: pycon\n\n   >>> async_double(3) + 1\n   TypeError: unsupported operand type(s) for +: 'coroutine' and 'int'\n\nIf you didn't already mess this up naturally, then give it a try on\npurpose: try writing some code with a missing ``await``, or an extra\n``await``, and see what you get. This way you'll be prepared for when\nit happens to you for real.\n\nAnd remember: watch out for ``RuntimeWarning: coroutine '...' was\nnever awaited``; it means you need to find and fix your missing\n``await``.\n\n\nOkay, let's see something cool already\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSo now we've started using Trio, but so far all we've learned to do is\nwrite functions that print things and sleep for various lengths of\ntime. Interesting enough, but we could just as easily have done that\nwith :func:`time.sleep`. ``async/await`` is useless!\n\nWell, not really. Trio has one more trick up its sleeve, that makes\nasync functions more powerful than regular functions: it can run\nmultiple async functions *at the same time*. Here's an example:\n\n.. _tutorial-example-tasks-intro:\n\n.. literalinclude:: tutorial/tasks-intro.py\n   :linenos:\n\nThere's a lot going on in here, so we'll take it one step at a\ntime. In the first part, we define two async functions ``child1`` and\n``child2``. These should look familiar from the last section:\n\n.. literalinclude:: tutorial/tasks-intro.py\n   :linenos:\n   :lineno-match:\n   :start-at: async def child1\n   :end-at: child2: exiting\n\nNext, we define ``parent`` as an async function that's going to call\n``child1`` and ``child2`` concurrently:\n\n.. literalinclude:: tutorial/tasks-intro.py\n   :linenos:\n   :lineno-match:\n   :start-at: async def parent\n   :end-at: all done!\n\nIt does this by using a mysterious ``async with`` statement to create\na \"nursery\", and then \"spawns\" ``child1`` and ``child2`` into the\nnursery.\n\nLet's start with this ``async with`` thing. It's actually pretty\nsimple. In regular Python, a statement like ``with someobj: ...``\ninstructs the interpreter to call ``someobj.__enter__()`` at the\nbeginning of the block, and to call ``someobj.__exit__()`` at the end\nof the block. We call ``someobj`` a \"context manager\". An ``async\nwith`` does exactly the same thing, except that where a regular\n``with`` statement calls regular methods, an ``async with`` statement\ncalls async methods: at the start of the block it does ``await\nsomeobj.__aenter__()`` and at that end of the block it does ``await\nsomeobj.__aexit__()``. In this case we call ``someobj`` an \"async\ncontext manager\". So in short: ``with`` blocks are a shorthand for\ncalling some functions, and since with async/await Python now has two\nkinds of functions, it also needs two kinds of ``with`` blocks. That's\nall there is to it! If you understand async functions, then you\nunderstand ``async with``.\n\n.. note::\n\n   This example doesn't use them, but while we're here we might as\n   well mention the one other piece of new syntax that async/await\n   added: ``async for``. It's basically the same idea as ``async\n   with`` versus ``with``: An ``async for`` loop is just like a\n   ``for`` loop, except that where a ``for`` loop does\n   ``iterator.__next__()`` to fetch the next item, an ``async for``\n   does ``await async_iterator.__anext__()``. Now you understand all\n   of async/await. Basically just remember that it involves making\n   sandwiches and sticking the word \"async\" in front of everything,\n   and you'll do fine.\n\nNow that we understand ``async with``, let's look at ``parent`` again:\n\n.. literalinclude:: tutorial/tasks-intro.py\n   :linenos:\n   :lineno-match:\n   :start-at: async def parent\n   :end-at: all done!\n\nThere are only 4 lines of code that really do anything here. On line\n20, we use :func:`trio.open_nursery` to get a \"nursery\" object, and\nthen inside the ``async with`` block we call ``nursery.start_soon`` twice,\non lines 22 and 25. There are actually two ways to call an async\nfunction: the first one is the one we already saw, using ``await\nasync_fn()``; the new one is ``nursery.start_soon(async_fn)``: it asks Trio\nto start running this async function, *but then returns immediately\nwithout waiting for the function to finish*. So after our two calls to\n``nursery.start_soon``, ``child1`` and ``child2`` are now running in the\nbackground. And then at line 28, the commented line, we hit the end of\nthe ``async with`` block, and the nursery's ``__aexit__`` function\nruns. What this does is force ``parent`` to stop here and wait for all\nthe children in the nursery to exit. This is why you have to use\n``async with`` to get a nursery: it gives us a way to make sure that\nthe child calls can't run away and get lost. One reason this is\nimportant is that if there's a bug or other problem in one of the\nchildren, and it raises an exception, then it lets us propagate that\nexception into the parent; in many other frameworks, exceptions like\nthis are just discarded. Trio never discards exceptions.\n\nOk! Let's try running it and see what we get:\n\n.. code-block:: none\n\n   parent: started!\n   parent: spawning child1...\n   parent: spawning child2...\n   parent: waiting for children to finish...\n     child2: started! sleeping now...\n     child1: started! sleeping now...\n       [... 1 second passes ...]\n     child1: exiting!\n     child2: exiting!\n   parent: all done!\n\n(Your output might have the order of the \"started\" and/or \"exiting\"\nlines swapped compared to mine.)\n\nNotice that ``child1`` and ``child2`` both start together and then\nboth exit together. And, even though we made two calls to\n``trio.sleep(1)``, the program finished in just one second total.\nSo it looks like ``child1`` and ``child2`` really are running at the\nsame time!\n\nNow, if you're familiar with programming using threads, this might\nlook familiar – and that's intentional. But it's important to realize\nthat *there are no threads here*. All of this is happening in a single\nthread. To remind ourselves of this, we use slightly different\nterminology: instead of spawning two \"threads\", we say that we spawned\ntwo \"tasks\". There are two differences between tasks and threads: (1)\nmany tasks can take turns running on a single thread, and (2) with\nthreads, the Python interpreter/operating system can switch which\nthread is running whenever they feel like it; with tasks, we can only\nswitch at certain designated places we call :ref:`\"checkpoints\"\n<checkpoints>`. In the next section, we'll dig into what this means.\n\n\n.. _tutorial-instrument-example:\n\nTask switching illustrated\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe big idea behind async/await-based libraries like Trio is to run\nlots of tasks simultaneously on a single thread by switching between\nthem at appropriate places – so for example, if we're implementing a\nweb server, then one task could be sending an HTTP response at the\nsame time as another task is waiting for new connections. If all you\nwant to do is use Trio, then you don't need to understand all the\nnitty-gritty detail of how this switching works – but it's very useful\nto have at least a general intuition about what Trio is doing \"under\nthe hood\" when your code is executing. To help build that intuition,\nlet's look more closely at how Trio ran our example from the last\nsection.\n\nFortunately, Trio provides a :ref:`rich set of tools for inspecting\nand debugging your programs <instrumentation>`. Here we want to watch\n:func:`trio.run` at work, which we can do by writing a class we'll\ncall ``Tracer``, which implements Trio's :class:`~trio.abc.Instrument`\ninterface. Its job is to log various events as they happen:\n\n.. literalinclude:: tutorial/tasks-with-trace.py\n   :pyobject: Tracer\n\nThen we re-run our example program from the previous section, but this\ntime we pass :func:`trio.run` a ``Tracer`` object:\n\n.. literalinclude:: tutorial/tasks-with-trace.py\n   :start-at: trio.run\n\nThis generates a *lot* of output, so we'll go through it one step at a\ntime.\n\nFirst, there's a bit of chatter while Trio gets ready to run our\ncode. Most of this is irrelevant to us for now, but in the middle you\ncan see that Trio has created a task for the ``__main__.parent``\nfunction, and \"scheduled\" it (i.e., made a note that it should be run\nsoon):\n\n.. code-block:: none\n\n   $ python3 tutorial/tasks-with-trace.py\n   !!! run started\n   ### new task spawned: <init>\n   ### task scheduled: <init>\n   ### doing a quick check for I/O\n   ### finished I/O check (took 1.1122087016701698e-05 seconds)\n   >>> about to run one step of task: <init>\n   ### new task spawned: <call soon task>\n   ### task scheduled: <call soon task>\n   ### new task spawned: __main__.parent\n   ### task scheduled: __main__.parent\n   <<< task step finished: <init>\n   ### doing a quick check for I/O\n   ### finished I/O check (took 6.4980704337358475e-06 seconds)\n\nOnce the initial housekeeping is done, Trio starts running the\n``parent`` function, and you can see ``parent`` creating the two child\ntasks. Then it hits the end of the ``async with`` block, and pauses:\n\n.. code-block:: none\n\n   >>> about to run one step of task: __main__.parent\n   parent: started!\n   parent: spawning child1...\n   ### new task spawned: __main__.child1\n   ### task scheduled: __main__.child1\n   parent: spawning child2...\n   ### new task spawned: __main__.child2\n   ### task scheduled: __main__.child2\n   parent: waiting for children to finish...\n   <<< task step finished: __main__.parent\n\nControl then goes back to :func:`trio.run`, which logs a bit more\ninternal chatter:\n\n.. code-block:: none\n\n   >>> about to run one step of task: <call soon task>\n   <<< task step finished: <call soon task>\n   ### doing a quick check for I/O\n   ### finished I/O check (took 5.476875230669975e-06 seconds)\n\nAnd then gives the two child tasks a chance to run:\n\n.. code-block:: none\n\n   >>> about to run one step of task: __main__.child2\n     child2 started! sleeping now...\n   <<< task step finished: __main__.child2\n\n   >>> about to run one step of task: __main__.child1\n     child1: started! sleeping now...\n   <<< task step finished: __main__.child1\n\nEach task runs until it hits the call to :func:`trio.sleep`, and then\nsuddenly we're back in :func:`trio.run` deciding what to run next. How\ndoes this happen? The secret is that :func:`trio.run` and\n:func:`trio.sleep` work together to make it happen: :func:`trio.sleep`\nhas access to some special magic that lets it pause itself,\nso it sends a note to :func:`trio.run` requesting to be\nwoken again after 1 second, and then suspends the task. And once the\ntask is suspended, Python gives control back to :func:`trio.run`,\nwhich decides what to do next. (If this sounds similar to the way that\ngenerators can suspend execution by doing a ``yield``, then that's not\na coincidence: inside the Python interpreter, there's a lot of overlap\nbetween the implementation of generators and async functions.)\n\n.. note::\n\n   You might wonder whether you can mix-and-match primitives from\n   different async libraries. For example, could we use\n   :func:`trio.run` together with :func:`asyncio.sleep`? The answer is\n   no, we can't, and the paragraph above explains why: the two sides\n   of our async sandwich have a private language they use to talk to\n   each other, and different libraries use different languages. So if\n   you try to call :func:`asyncio.sleep` from inside a\n   :func:`trio.run`, then Trio will get very confused indeed and\n   probably blow up in some dramatic way.\n\nOnly async functions have access to the special magic for suspending a\ntask, so only async functions can cause the program to switch to a\ndifferent task. What this means is that if a call *doesn't* have an ``await``\non it, then you know that it *can't* be a place where your task will\nbe suspended. This makes tasks much `easier to reason about\n<https://glyph.twistedmatrix.com/2014/02/unyielding.html>`__ than\nthreads, because there are far fewer ways that tasks can be\ninterleaved with each other and stomp on each others' state. (For\nexample, in Trio a statement like ``a += 1`` is always atomic – even\nif ``a`` is some arbitrarily complicated custom object!) Trio also\nmakes some :ref:`further guarantees beyond that <checkpoints>`, but\nthat's the big one.\n\nAnd now you also know why ``parent`` had to use an ``async with`` to\nopen the nursery: if we had used a regular ``with`` block, then it\nwouldn't have been able to pause at the end and wait for the children\nto finish; we need our cleanup function to be async, which is exactly\nwhat ``async with`` gives us.\n\nNow, back to our execution point. To recap: at this point ``parent``\nis waiting on ``child1`` and ``child2``, and both children are\nsleeping. So :func:`trio.run` checks its notes, and sees that there's\nnothing to be done until those sleeps finish – unless possibly some\nexternal I/O event comes in. If that happened, then it might give us\nsomething to do. Of course we aren't doing any I/O here so it won't\nhappen, but in other situations it could. So next it calls an\noperating system primitive to put the whole process to sleep:\n\n.. code-block:: none\n\n   ### waiting for I/O for up to 0.9999009938910604 seconds\n\nAnd in fact no I/O does arrive, so one second later we wake up again,\nand Trio checks its notes again. At this point it checks the current\ntime, compares it to the notes that :func:`trio.sleep` sent saying\nwhen the two child tasks should be woken up again, and realizes\nthat they've slept for long enough, so it schedules them to run soon:\n\n.. code-block:: none\n\n   ### finished I/O check (took 1.0006483688484877 seconds)\n   ### task scheduled: __main__.child1\n   ### task scheduled: __main__.child2\n\nAnd then the children get to run, and this time they run to\ncompletion. Remember how ``parent`` is waiting for them to finish?\nNotice how ``parent`` gets scheduled when the first child exits:\n\n.. code-block:: none\n\n   >>> about to run one step of task: __main__.child1\n     child1: exiting!\n   ### task scheduled: __main__.parent\n   ### task exited: __main__.child1\n   <<< task step finished: __main__.child1\n\n   >>> about to run one step of task: __main__.child2\n     child2 exiting!\n   ### task exited: __main__.child2\n   <<< task step finished: __main__.child2\n\nThen, after another check for I/O, ``parent`` wakes up. The nursery\ncleanup code notices that all its children have exited, and lets the\nnursery block finish. And then ``parent`` makes a final print and\nexits:\n\n.. code-block:: none\n\n   ### doing a quick check for I/O\n   ### finished I/O check (took 9.045004844665527e-06 seconds)\n\n   >>> about to run one step of task: __main__.parent\n   parent: all done!\n   ### task scheduled: <init>\n   ### task exited: __main__.parent\n   <<< task step finished: __main__.parent\n\nAnd finally, after a bit more internal bookkeeping, :func:`trio.run`\nexits too:\n\n.. code-block:: none\n\n   ### doing a quick check for I/O\n   ### finished I/O check (took 5.996786057949066e-06 seconds)\n   >>> about to run one step of task: <init>\n   ### task scheduled: <call soon task>\n   ### task scheduled: <init>\n   <<< task step finished: <init>\n   ### doing a quick check for I/O\n   ### finished I/O check (took 6.258022040128708e-06 seconds)\n   >>> about to run one step of task: <call soon task>\n   ### task exited: <call soon task>\n   <<< task step finished: <call soon task>\n   >>> about to run one step of task: <init>\n   ### task exited: <init>\n   <<< task step finished: <init>\n   !!! run finished\n\nYou made it!\n\nThat was a lot of text, but again, you don't need to understand\neverything here to use Trio – in fact, Trio goes to great lengths to\nmake each task feel like it executes in a simple, linear way. (Just\nlike your operating system goes to great lengths to make it feel like\nyour single-threaded code executes in a simple linear way, even though\nunder the covers the operating system juggles between different\nthreads and processes in essentially the same way Trio does.) But it\nis useful to have a rough model in your head of how the code you write\nis actually executed, and – most importantly – the consequences of\nthat for parallelism.\n\nAlternatively, if this has just whetted your appetite and you want to\nknow more about how ``async/await`` works internally, then `this blog\npost\n<https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/>`__\nis a good deep dive, or check out `this great walkthrough\n<https://github.com/AndreLouisCaron/a-tale-of-event-loops>`__ to see\nhow to build a simple async I/O framework from the ground up.\n\n\nA kinder, gentler GIL\n---------------------\n\nSpeaking of parallelism – let's zoom out for a moment and talk about\nhow async/await compares to other ways of handling concurrency in\nPython.\n\nAs we've already noted, Trio tasks are conceptually rather similar to\nPython's built-in threads, as provided by the :mod:`threading`\nmodule. And in all common Python implementations, threads have a\nfamous limitation: the Global Interpreter Lock, or \"GIL\" for\nshort. The GIL means that even if you use multiple threads, your code\nstill (mostly) ends up running on a single core. People tend to find\nthis frustrating.\n\nBut from Trio's point of view, the problem with the GIL isn't that it\nrestricts parallelism. Of course it would be nice if Python had better\noptions for taking advantage of multiple cores, but that's an\nextremely difficult problem to solve, and in the meantime there are\nlots of problems where a single core is totally adequate – or where if\nit isn't, then process-level or machine-level parallelism works fine.\n\nNo, the problem with the GIL is that it's a *lousy deal*: we give up\non using multiple cores, and in exchange we get... almost all the same\nchallenges and mind-bending bugs that come with real parallel\nprogramming, and – to add insult to injury – `pretty poor scalability\n<https://twitter.com/hynek/status/771790449057132544>`__. Threads in\nPython just aren't that appealing.\n\nTrio doesn't make your code run on multiple cores; in fact, as we saw\nabove, it's baked into Trio's design that when it has multiple tasks,\nthey take turns, so at each moment only one of them is actively running.\nWe're not so much overcoming the GIL as embracing it. But if you're\nwilling to accept that, plus a bit of extra work to put these new\n``async`` and ``await`` keywords in the right places, then in exchange\nyou get:\n\n* Excellent scalability: Trio can run 10,000+ tasks simultaneously\n  without breaking a sweat, so long as their total CPU demands don't\n  exceed what a single core can provide. (This is common in, for\n  example, network servers that have lots of clients connected, but\n  only a few active at any given time.)\n\n* Fancy features: most threading systems are implemented in C and\n  restricted to whatever features the operating system provides. In\n  Trio our logic is all in Python, which makes it possible to\n  implement powerful and ergonomic features like :ref:`Trio's\n  cancellation system <cancellation>`.\n\n* Code that's easier to reason about: the ``await`` keyword means that\n  potential task-switching points are explicitly marked within each\n  function. This can make Trio code `dramatically easier to reason\n  about <https://glyph.twistedmatrix.com/2014/02/unyielding.html>`__\n  than the equivalent program using threads.\n\nCertainly it's not appropriate for every app... but there are a lot of\nsituations where the trade-offs here look pretty appealing.\n\nThere is one downside that's important to keep in mind, though. Making\ncheckpoints explicit gives you more control over how your tasks can be\ninterleaved – but with great power comes great responsibility. With\nthreads, the runtime environment is responsible for making sure that\neach thread gets its fair share of running time. With Trio, if some\ntask runs off and does stuff for seconds on end without executing a\ncheckpoint, then... all your other tasks will just have to wait.\n\nHere's an example of how this can go wrong. Take our :ref:`example\nfrom above <tutorial-example-tasks-intro>`, and replace the calls to\n:func:`trio.sleep` with calls to :func:`time.sleep`. If we run our\nmodified program, we'll see something like:\n\n.. code-block:: none\n\n   parent: started!\n   parent: spawning child1...\n   parent: spawning child2...\n   parent: waiting for children to finish...\n     child2 started! sleeping now...\n       [... pauses for 1 second ...]\n     child2 exiting!\n     child1: started! sleeping now...\n       [... pauses for 1 second ...]\n     child1: exiting!\n   parent: all done!\n\nOne of the major reasons why Trio has such a rich\n:ref:`instrumentation API <tutorial-instrument-example>` is to make it\npossible to write debugging tools to catch issues like this.\n\n\nNetworking with Trio\n--------------------\n\nNow let's take what we've learned and use it to do some I/O, which is\nwhere async/await really shines.\n\nThe traditional toy application for demonstrating network APIs is an\n\"echo server\": a program that awaits arbitrary data from  remote clients,\nand then sends that same data right back. (Probably a more relevant example\nthese days would be an application that does lots of concurrent HTTP\nrequests, but for that `you need an HTTP library\n<https://github.com/python-trio/trio/issues/236#issuecomment-310784001>`__\nsuch as `asks <https://asks.readthedocs.io>`__, so we'll stick\nwith the echo server tradition.)\n\nIn this tutorial, we present both ends of the pipe: the client, and the\nserver. The client periodically sends data to the server, and displays its\nanswers. The server awaits connections; when a client connects, it recopies\nthe received data back on the pipe.\n\n\nAn echo client\n~~~~~~~~~~~~~~\n\n\nTo start with, here's an example echo *client*, i.e., the program that\nwill send some data at our echo server and get responses back:\n\n.. _tutorial-echo-client-example:\n\n.. literalinclude:: tutorial/echo-client.py\n   :linenos:\n\nNote that this code will not work without a TCP server such as the one\nwe'll implement below.\n\nThe overall structure here should be familiar, because it's just like\nour :ref:`last example <tutorial-example-tasks-intro>`: we have a\nparent task, which spawns two child tasks to do the actual work, and\nthen at the end of the ``async with`` block it switches into full-time\nparenting mode while waiting for them to finish. But now instead of\njust calling :func:`trio.sleep`, the children use some of Trio's\nnetworking APIs.\n\nLet's look at the parent first:\n\n.. literalinclude:: tutorial/echo-client.py\n   :linenos:\n   :lineno-match:\n   :pyobject: parent\n\nFirst we call :func:`trio.open_tcp_stream` to make a TCP connection to\nthe server. ``127.0.0.1`` is a magic `IP address\n<https://en.wikipedia.org/wiki/IP_address>`__ meaning \"the computer\nI'm running on\", so this connects us to whatever program on the local\ncomputer is using ``PORT`` as its contact point. This function returns\nan object implementing Trio's :class:`~trio.abc.Stream` interface,\nwhich gives us methods to send and receive bytes, and to close the\nconnection when we're done. We use an ``async with`` block to make\nsure that we do close the connection – not a big deal in a toy example\nlike this, but it's a good habit to get into, and Trio is designed to\nmake ``with`` and ``async with`` blocks easy to use.\n\nFinally, we start up two child tasks, and pass each of them a\nreference to the stream. (This is also a good example of how\n``nursery.start_soon`` lets you pass positional arguments to the\nspawned function.)\n\nOur first task's job is to send data to the server:\n\n.. literalinclude:: tutorial/echo-client.py\n   :linenos:\n   :lineno-match:\n   :pyobject: sender\n\nIt uses a loop that alternates between calling ``await\nclient_stream.send_all(...)`` to send some data (this is the method\nyou use for sending data on any kind of Trio stream), and then\nsleeping for a second to avoid making the output scroll by too fast on\nyour terminal.\n\nAnd the second task's job is to process the data the server sends back:\n\n.. literalinclude:: tutorial/echo-client.py\n   :linenos:\n   :lineno-match:\n   :pyobject: receiver\n\nIt uses an ``async for`` loop to fetch data from the server.\nAlternatively, it could use `~trio.abc.ReceiveStream.receive_some`,\nwhich is the opposite of `~trio.abc.SendStream.send_all`, but using\n``async for`` saves some boilerplate.\n\nAnd now we're ready to look at the server.\n\n\n.. _tutorial-echo-server-example:\n\nAn echo server\n~~~~~~~~~~~~~~\n\nAs usual, let's look at the whole thing first, and then we'll discuss\nthe pieces:\n\n.. literalinclude:: tutorial/echo-server.py\n   :linenos:\n\nLet's start with ``main``, which is just one line long:\n\n.. literalinclude:: tutorial/echo-server.py\n   :linenos:\n   :lineno-match:\n   :pyobject: main\n\nWhat this does is call :func:`serve_tcp`, which is a convenience\nfunction Trio provides that runs forever (or at least until you hit\ncontrol-C or otherwise cancel it). This function does several helpful\nthings:\n\n* It creates a nursery internally, so that our server will be able to\n  handle multiple connections at the same time.\n\n* It listens for incoming TCP connections on the specified ``PORT``.\n\n* Whenever a connection arrives, it starts a new task running the\n  function we pass (in this example it's ``echo_server``), and passes\n  it a stream representing that connection.\n\n* When each task exits, it makes sure to close the corresponding\n  connection. (That's why you don't see any ``async with\n  server_stream`` in the server – :func:`serve_tcp` takes care of this\n  for us.)\n\nSo :func:`serve_tcp` is pretty handy! This part works pretty much the\nsame for any server, whether it's an echo server, HTTP server, SSH\nserver, or whatever, so it makes sense to bundle it all up together in\na helper function like this.\n\nNow let's look at ``echo_server``, which handles each client\nconnection – so if there are multiple clients, there might be multiple\ncalls to ``echo_server`` running at the same time. This is where we\nimplement our server's \"echo\" behavior. This should be pretty\nstraightforward to understand, because it uses the same stream\nfunctions we saw in the last section:\n\n.. literalinclude:: tutorial/echo-server.py\n   :linenos:\n   :lineno-match:\n   :pyobject: echo_server\n\nThe argument ``server_stream`` is provided by :func:`serve_tcp`, and\nis the other end of the connection we made in the client: so the data\nthat the client passes to ``send_all`` will come out here. Then we\nhave a ``try`` block discussed below, and finally the server loop\nwhich alternates between reading some data from the socket and then\nsending it back out again (unless the socket was closed, in which case\nwe quit).\n\nSo what's that ``try`` block for? Remember that in Trio, like Python\nin general, exceptions keep propagating until they're caught. Here we\nthink it's plausible there might be unexpected exceptions, and we want\nto isolate that to making just this one task crash, without taking\ndown the whole program. For example, if the client closes the\nconnection at the wrong moment then it's possible this code will end\nup calling ``send_all`` on a closed connection and get a\n:exc:`BrokenResourceError`; that's unfortunate, and in a more serious\nprogram we might want to handle it more explicitly, but it doesn't\nindicate a problem for any *other* connections. On the other hand, if\nthe exception is something like a :exc:`KeyboardInterrupt`, we *do*\nwant that to propagate out into the parent task and cause the whole\nprogram to exit. To express this, we use a ``try`` block with an\n``except Exception:`` handler.\n\nIn general, Trio leaves it up to you to decide whether and how you\nwant to handle exceptions, just like Python in general.\n\n\nTry it out\n~~~~~~~~~~\n\nOpen a few terminals, run ``echo-server.py`` in one, run\n``echo-client.py`` in another, and watch the messages scroll by! When\nyou get bored, you can exit by hitting control-C.\n\nSome things to try:\n\n* Open several terminals, and run multiple clients at the same time,\n  all talking to the same server.\n\n* See how the server reacts when you hit control-C on the client.\n\n* See how the client reacts when you hit control-C on the server.\n\n\nFlow control in our echo client and server\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nHere's a question you might be wondering about: why does our client\nuse two separate tasks for sending and receiving, instead of a single\ntask that alternates between them – like the server has? For example,\nour client could use a single task like:\n\n.. code-block:: python\n\n   # Can you spot the two problems with this code?\n   async def send_and_receive(client_stream):\n       while True:\n           data = ...\n           await client_stream.send_all(data)\n           received = await client_stream.receive_some()\n           if not received:\n               sys.exit()\n           await trio.sleep(1)\n\nIt turns out there are two problems with this – one minor and one\nmajor. Both relate to flow control. The minor problem is that when we\ncall ``receive_some`` here we're not waiting for *all* the data to be\navailable; ``receive_some`` returns as soon as *any* data is available. If\n``data`` is small, then our operating systems / network / server will\n*probably* keep it all together in a single chunk, but there's no\nguarantee. If the server sends ``hello`` then we might get ``hello``,\nor ``he`` ``llo``, or ``h`` ``e`` ``l`` ``l`` ``o``, or ... bottom\nline, any time we're expecting more than one byte of data, we have to\nbe prepared to call ``receive_some`` multiple times.\n\nAnd where this would go especially wrong is if we find ourselves in\nthe situation where ``data`` is big enough that it passes some\ninternal threshold, and the operating system or network decide to\nalways break it up into multiple pieces. Now on each pass through the\nloop, we send ``len(data)`` bytes, but read less than that. The result\nis something like a memory leak: we'll end up with more and more data\nbacked up in the network, until eventually something breaks.\n\n.. note:: If you're curious *how* things break, then you can use\n   `~trio.abc.ReceiveStream.receive_some`\\'s optional argument to put\n   a limit on how many bytes you read each time, and see what happens.\n\nWe could fix this by keeping track of how much data we're expecting at\neach moment, and then keep calling ``receive_some`` until we get it all:\n\n.. code-block:: python\n\n   expected = len(data)\n   while expected > 0:\n       received = await client_stream.receive_some(expected)\n       if not received:\n           sys.exit(1)\n       expected -= len(received)\n\nThis is a bit cumbersome, but it would solve this problem.\n\nThere's another problem, though, that's deeper. We're still\nalternating between sending and receiving. Notice that when we send\ndata, we use ``await``: this means that sending can potentially\n*block*. Why does this happen? Any data that we send goes first into\nan operating system buffer, and from there onto the network, and then\nanother operating system buffer on the receiving computer, before the\nreceiving program finally calls ``receive_some`` to take the data out\nof these buffers. If we call ``send_all`` with a small amount of data,\nthen it goes into these buffers and ``send_all`` returns immediately.\nBut if we send enough data fast enough, eventually the buffers fill\nup, and ``send_all`` will block until the remote side calls\n``receive_some`` and frees up some space.\n\nNow let's think about this from the server's point of view. Each time\nit calls ``receive_some``, it gets some data that it needs to send\nback. And until it sends it back, the data that is sitting around takes up\nmemory. Computers have finite amounts of RAM, so if our server is well\nbehaved then at some point it needs to stop calling ``receive_some``\nuntil it gets rid of some of the old data by doing its own call to\n``send_all``. So for the server, really the only viable option is to\nalternate between receiving and sending.\n\nBut we need to remember that it's not just the client's call to\n``send_all`` that might block: the server's call to ``send_all`` can\nalso get into a situation where it blocks until the client calls\n``receive_some``. So if the server is waiting for ``send_all`` to\nfinish before it calls ``receive_some``, and our client also waits for\n``send_all`` to finish before it calls ``receive_some``,... we have a\nproblem! The client won't call ``receive_some`` until the server has\ncalled ``receive_some``, and the server won't call ``receive_some``\nuntil the client has called ``receive_some``. If our client is written\nto alternate between sending and receiving, and the chunk of data it's\ntrying to send is large enough (e.g. 10 megabytes will probably do it\nin most configurations), then the two processes will `deadlock\n<https://en.wikipedia.org/wiki/Deadlock>`__.\n\nMoral: Trio gives you powerful tools to manage sequential and\nconcurrent execution. In this example we saw that the server needs\n``send`` and ``receive_some`` to alternate in sequence, while the\nclient needs them to run concurrently, and both were straightforward\nto implement. But when you're implementing network code like this then\nit's important to think carefully about flow control and buffering,\nbecause it's up to you to choose the right execution mode!\n\nOther popular async libraries like `Twisted\n<https://twistedmatrix.com/>`__ and :mod:`asyncio` tend to paper over\nthese kinds of issues by throwing in unbounded buffers everywhere.\nThis can avoid deadlocks, but can introduce its own problems and in\nparticular can make it difficult to keep `memory usage and latency\nunder control\n<https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#three-bugs>`__.\nWhile both approaches have their advantages, Trio takes the position\nthat it's better to expose the underlying problem as directly as\npossible and provide good tools to confront it head-on.\n\n.. note::\n\n   If you want to try and make the deadlock happen on purpose to see\n   for yourself, and you're using Windows, then you might need to\n   split the ``send_all`` call up into two calls that each send half of\n   the data. This is because Windows has a `somewhat unusual way of\n   handling buffering\n   <https://stackoverflow.com/questions/28785626/what-is-the-size-of-a-socket-send-buffer-in-windows>`__.\n\n\nWhen things go wrong: timeouts, cancellation and exceptions in concurrent tasks\n-------------------------------------------------------------------------------\n\nTODO: give an example using :func:`fail_after`\n\nTODO: explain :exc:`Cancelled`\n\nTODO: explain how cancellation is also used when one child raises an\nexception\n\nTODO: maybe a brief discussion of :exc:`KeyboardInterrupt` handling?\n\n..\n   Timeouts\n   --------\n\n   XX todo\n\n   timeout example:\n\n   .. code-block:: python\n\n      async def counter():\n          for i in range(100000):\n              print(i)\n              await trio.sleep(1)\n\n      async def main():\n          with trio.fail_after(10):\n              await counter()\n\n   you can stick anything inside a timeout block, even child tasks\n\n     [show something like the first example but with a timeout – they\n     both get cancelled, the cancelleds get packed into an ExceptionGroup, and\n     then the timeout block catches the cancelled]\n\n   brief discussion of KI?\n   tasks-with-trace.py + control-C is pretty interesting\n   or maybe leave it for a blog post?\n"
  },
  {
    "path": "docs/source/typevars.py",
    "content": "\"\"\"Transform references to typevars to avoid missing reference errors.\n\nSee https://github.com/sphinx-doc/sphinx/issues/7722 also.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport re\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport trio\nfrom sphinx.errors import NoUri\n\nif TYPE_CHECKING:\n    from sphinx.addnodes import Element, pending_xref\n    from sphinx.application import Sphinx\n    from sphinx.environment import BuildEnvironment\n\n\ndef identify_typevars(trio_folder: Path) -> None:\n    \"\"\"Record all typevars in trio.\"\"\"\n    for filename in trio_folder.rglob(\"*.py\"):\n        with open(filename, encoding=\"utf8\") as f:\n            for line in f:\n                # A simple regex should be sufficient to find them all, no need to actually parse.\n                match = re.search(\n                    r\"\\b(TypeVar|TypeVarTuple|ParamSpec)\\(['\\\"]([^'\\\"]+)['\\\"]\",\n                    line,\n                )\n                if match is not None:\n                    relative = \"trio\" / filename.relative_to(trio_folder)\n                    relative = relative.with_suffix(\"\")\n                    if relative.name == \"__init__\":  # Package, remove.\n                        relative = relative.parent\n                    kind = match.group(1)\n                    name = match.group(2)\n                    typevars_qualified[f'{\".\".join(relative.parts)}.{name}'] = kind\n                    existing = typevars_named.setdefault(name, kind)\n                    if existing != kind:\n                        print(\"Mismatch: {} = {}, {}\", name, existing, kind)\n\n\n# All our typevars, so we can suppress reference errors for them.\ntypevars_qualified: dict[str, str] = {}\ntypevars_named: dict[str, str] = {}\n\n\ndef lookup_reference(\n    app: Sphinx,\n    env: BuildEnvironment,\n    node: pending_xref,\n    contnode: Element,\n) -> Element | None:\n    \"\"\"Handle missing references.\"\"\"\n    # If this is a typing_extensions object, redirect to typing.\n    # Most things there are backports, so the stdlib docs should have an entry.\n    target: str = node[\"reftarget\"]\n    if target.startswith(\"typing_extensions.\"):\n        new_node = node.copy()\n        new_node[\"reftarget\"] = f\"typing.{target[18:]}\"\n        # This fires off this same event, with our new modified node in order to fetch the right\n        # URL to use.\n        return app.emit_firstresult(  # type: ignore[no-any-return]\n            \"missing-reference\",\n            env,\n            new_node,\n            contnode,\n            allowed_exceptions=(NoUri,),\n        )\n\n    try:\n        typevar_type = typevars_qualified[target]\n    except KeyError:\n        # Imports might mean the typevar was defined in a different module or something.\n        # Fall back to checking just by name.\n        dot = target.rfind(\".\")\n        stem = target[dot + 1 :] if dot >= 0 else target\n        try:\n            typevar_type = typevars_named[stem]\n        except KeyError:\n            # Let other handlers deal with this name, it's not a typevar.\n            return None\n\n    # Found a typevar. Redirect to the stdlib docs for that kind of var.\n    new_node = node.copy()\n    new_node[\"reftarget\"] = f\"typing.{typevar_type}\"\n    new_node = app.emit_firstresult(\n        \"missing-reference\",\n        env,\n        new_node,\n        contnode,\n        allowed_exceptions=(NoUri,),\n    )\n    reftitle = new_node[\"reftitle\"]\n    # Is normally \"(in Python 3.XX)\", make it say typevar/paramspec/etc\n    paren = \"(\" if reftitle.startswith(\"(\") else \"\"\n    new_node[\"reftitle\"] = f\"{paren}{typevar_type}, {reftitle.lstrip('(')}\"\n    # Add a CSS class, for restyling.\n    new_node[\"classes\"].append(\"typevarref\")\n    return new_node  # type: ignore[no-any-return]\n\n\ndef setup(app: Sphinx) -> None:\n    identify_typevars(Path(trio.__file__).parent)\n    app.connect(\"missing-reference\", lookup_reference, -10)\n"
  },
  {
    "path": "docs-requirements.in",
    "content": "# RTD is currently installing 1.5.3, which has a bug in :lineno-match: (??)\n# sphinx 5.3 doesn't work with our _NoValue workaround\nsphinx >= 6.0\njinja2\n# >= is necessary to prevent `uv` from selecting a `Sphinx` version this does not support\nsphinx_rtd_theme >= 3\nsphinxcontrib-jquery\nsphinxcontrib-trio\ntowncrier\nsphinx-codeautolink\n\n# Trio's own dependencies\ncffi; os_name == \"nt\"\nattrs >= 23.2.0\nsortedcontainers\nidna\noutcome\nsniffio\nexceptiongroup >= 1.0.0rc9\n\n# See note in test-requirements.in\nimmutables >= 0.6\n\n# types used in annotations\npyOpenSSL\n"
  },
  {
    "path": "docs-requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv pip compile --universal --python-version=3.11 docs-requirements.in -o docs-requirements.txt\nalabaster==1.0.0\n    # via sphinx\nattrs==25.4.0\n    # via\n    #   -r docs-requirements.in\n    #   outcome\nbabel==2.18.0\n    # via sphinx\nbeautifulsoup4==4.14.3\n    # via sphinx-codeautolink\ncertifi==2026.2.25\n    # via requests\ncffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy'\n    # via\n    #   -r docs-requirements.in\n    #   cryptography\ncharset-normalizer==3.4.6\n    # via requests\nclick==8.3.1\n    # via towncrier\ncolorama==0.4.6 ; sys_platform == 'win32'\n    # via\n    #   click\n    #   sphinx\ncryptography==46.0.5\n    # via pyopenssl\ndocutils==0.22.4\n    # via\n    #   sphinx\n    #   sphinx-rtd-theme\nexceptiongroup==1.3.1\n    # via -r docs-requirements.in\nidna==3.11\n    # via\n    #   -r docs-requirements.in\n    #   requests\nimagesize==2.0.0\n    # via sphinx\nimmutables==0.21\n    # via -r docs-requirements.in\njinja2==3.1.6\n    # via\n    #   -r docs-requirements.in\n    #   sphinx\n    #   towncrier\nmarkupsafe==3.0.3\n    # via jinja2\noutcome==1.3.0.post0\n    # via -r docs-requirements.in\npackaging==26.0\n    # via sphinx\npycparser==3.0 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy')\n    # via cffi\npygments==2.19.2\n    # via sphinx\npyopenssl==26.0.0\n    # via -r docs-requirements.in\nrequests==2.32.5\n    # via sphinx\nroman-numerals==4.1.0\n    # via sphinx\nsniffio==1.3.1\n    # via -r docs-requirements.in\nsnowballstemmer==3.0.1\n    # via sphinx\nsortedcontainers==2.4.0\n    # via -r docs-requirements.in\nsoupsieve==2.8.3\n    # via beautifulsoup4\nsphinx==9.0.4 ; python_full_version < '3.12'\n    # via\n    #   -r docs-requirements.in\n    #   sphinx-codeautolink\n    #   sphinx-rtd-theme\n    #   sphinxcontrib-jquery\n    #   sphinxcontrib-trio\nsphinx==9.1.0 ; python_full_version >= '3.12'\n    # via\n    #   -r docs-requirements.in\n    #   sphinx-codeautolink\n    #   sphinx-rtd-theme\n    #   sphinxcontrib-jquery\n    #   sphinxcontrib-trio\nsphinx-codeautolink==0.17.5\n    # via -r docs-requirements.in\nsphinx-rtd-theme==3.1.0\n    # via -r docs-requirements.in\nsphinxcontrib-applehelp==2.0.0\n    # via sphinx\nsphinxcontrib-devhelp==2.0.0\n    # via sphinx\nsphinxcontrib-htmlhelp==2.1.0\n    # via sphinx\nsphinxcontrib-jquery==4.1\n    # via\n    #   -r docs-requirements.in\n    #   sphinx-rtd-theme\nsphinxcontrib-jsmath==1.0.1\n    # via sphinx\nsphinxcontrib-qthelp==2.0.0\n    # via sphinx\nsphinxcontrib-serializinghtml==2.0.0\n    # via sphinx\nsphinxcontrib-trio==1.2.0\n    # via -r docs-requirements.in\ntowncrier==25.8.0\n    # via -r docs-requirements.in\ntyping-extensions==4.15.0\n    # via\n    #   beautifulsoup4\n    #   exceptiongroup\n    #   pyopenssl\nurllib3==2.6.3\n    # via requests\n"
  },
  {
    "path": "newsfragments/.gitkeep",
    "content": ""
  },
  {
    "path": "newsfragments/3261.bugfix.rst",
    "content": "``Nursery.start()`` now preserves the ``__cause__`` and ``__context__`` of\nexceptions raised before ``task_status.started()`` is called.\n"
  },
  {
    "path": "newsfragments/README.rst",
    "content": "This directory collects \"newsfragments\": short files that each contain\na snippet of ReST-formatted text that will be added to the next\nrelease notes. This should be a description of aspects of the change\n(if any) that are relevant to users. (This contrasts with your commit\nmessage and PR description, which are a description of the change as\nrelevant to people working on the code itself.)\n\nEach file should be named like ``<ISSUE>.<TYPE>.rst``, where\n``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of:\n\n* ``headline``: a major new feature we want to highlight for users\n* ``breaking``: any breaking changes that happen without a proper\n  deprecation period (note: deprecations, and removal of previously\n  deprecated features after an appropriate time, go in the\n  ``deprecated`` category instead)\n* ``feature``: any new feature that doesn't qualify for ``headline``\n* ``removal``: removing support for old python versions, or other removals with no deprecation period.\n* ``bugfix``\n* ``doc``\n* ``deprecated``\n* ``misc``\n\nSo for example: ``123.headline.rst``, ``456.bugfix.rst``,\n``789.deprecated.rst``\n\nIf your PR fixes an issue, use that number here. If there is no issue,\nthen after you submit the PR and get the PR number you can add a\nnewsfragment using that instead.\n\nYour text can use all the same markup that we use in our Sphinx docs.\nFor example, you can use double-backticks to mark code snippets, or\nsingle-backticks to link to a function/class/module.\n\nTo check how your formatting looks, the easiest way is to make the PR,\nand then after the CI checks run, click on the \"Read the Docs build\"\ndetails link, and navigate to the release history page.\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\n# setuptools v77 adds PEP 639 support\nrequires = [\"setuptools >= 77\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"trio\"\ndescription = \"A friendly Python library for async concurrency and I/O\"\nauthors = [{name = \"Nathaniel J. Smith\", email = \"njs@pobox.com\"}]\nlicense = \"MIT OR Apache-2.0\"\nlicense-files = [\"LICENSE*\"]\nkeywords = [\n    \"async\",\n    \"io\",\n    \"networking\",\n    \"trio\",\n]\nclassifiers = [\n    \"Development Status :: 4 - Beta\",\n    \"Framework :: Trio\",\n    \"Intended Audience :: Developers\",\n    \"Operating System :: POSIX :: Linux\",\n    \"Operating System :: MacOS :: MacOS X\",\n    \"Operating System :: POSIX :: BSD\",\n    \"Operating System :: Microsoft :: Windows\",\n    \"Programming Language :: Python :: Implementation :: CPython\",\n    \"Programming Language :: Python :: Implementation :: PyPy\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n    \"Programming Language :: Python :: 3.12\",\n    \"Programming Language :: Python :: 3.13\",\n    \"Programming Language :: Python :: 3.14\",\n    \"Topic :: System :: Networking\",\n    \"Typing :: Typed\",\n]\nrequires-python = \">=3.10\"\ndependencies = [\n    # attrs 19.2.0 adds `eq` option to decorators\n    # attrs 20.1.0 adds @frozen\n    # attrs 21.1.0 adds a dataclass transform for type-checkers\n    # attrs 21.3.0 adds `import attrs`\n    \"attrs >= 23.2.0\",\n    \"sortedcontainers\",\n    \"idna\",\n    \"outcome\",\n    \"sniffio >= 1.3.0\",\n    # cffi 1.12 adds from_buffer(require_writable=True) and ffi.release()\n    # cffi 1.14 fixes memory leak inside ffi.getwinerror()\n    # cffi is required on Windows, except on PyPy where it is built-in\n    \"cffi>=1.14; os_name == 'nt' and implementation_name != 'pypy'\",\n    \"exceptiongroup; python_version < '3.11'\",\n]\ndynamic = [\"version\"]\n\n[project.readme]\nfile = \"README.rst\"\ncontent-type = \"text/x-rst\"\n\n[project.urls]\nHomepage = \"https://github.com/python-trio/trio\"\nDocumentation = \"https://trio.readthedocs.io/\"\nChangelog = \"https://trio.readthedocs.io/en/latest/history.html\"\n\n[project.entry-points.hypothesis]\ntrio = \"trio._core._run:_hypothesis_plugin_setup\"\n\n[tool.setuptools]\n# This means, just install *everything* you see under trio/, even if it\n# doesn't look like a source file, so long as it appears in MANIFEST.in:\ninclude-package-data = true\n\n[tool.setuptools.dynamic]\nversion = {attr = \"trio._version.__version__\"}\n\n[tool.black]\nforce-exclude = '''\n(\n  ^/docs/source/reference-.*\n  | ^/docs/source/tutorial\n  | ^/src/trio/_core/_generated_windows_ffi.py\n)\n'''\n\n[tool.codespell]\nignore-words-list = 'astroid,crasher,asend'\n\n[tool.typos]\ndefault.extend-ignore-re = [\n    \"(?Rm)^.*(#|//)\\\\s*spellchecker:disable-line$\",\n]\n\n[tool.typos.default.extend-identifiers]\nIPPROTO_ND = \"IPPROTO_ND\"\n\n[tool.typos.default.extend-words]\nastroid = \"astroid\"\ncrasher = \"crasher\"\nasend = \"asend\"\n\n[tool.ruff]\nrespect-gitignore = true\nfix = true\n\n# The directories to consider when resolving first vs. third-party imports.\n# Does not control what files to include/exclude!\nsrc = [\"src/trio\"]\n\ninclude = [\"*.py\", \"*.pyi\", \"**/pyproject.toml\"]\n\nextend-exclude = [\n  \"docs/source/reference-*\",\n  \"docs/source/tutorial/*\",\n  \"src/trio/_core/_generated_windows_ffi.py\"\n]\n\n[tool.ruff.lint]\npreview = true\nallowed-confusables = [\"–\"]\n\nselect = [\n    \"A\",     # flake8-builtins\n    \"ANN\",   # flake8-annotations\n    \"ASYNC\", # flake8-async\n    \"B\",     # flake8-bugbear\n    \"C4\",    # flake8-comprehensions\n    \"COM\",   # flake8-commas\n    \"E\",     # Error\n    \"EXE\",   # flake8-executable\n    \"F\",     # pyflakes\n    \"FA\",    # flake8-future-annotations\n    \"FLY\",   # flynt\n    \"FURB\",  # refurb\n    \"I\",     # isort\n    \"ICN\",   # flake8-import-conventions\n    \"PERF\",  # Perflint\n    \"PIE\",   # flake8-pie\n    \"PT\",    # flake8-pytest-style\n    \"PYI\",   # flake8-pyi\n    \"Q\",     # flake8-quotes\n    \"RUF\",   # Ruff-specific rules\n    \"SIM\",   # flake8-simplify\n    \"TC\",    # flake8-type-checking\n    \"UP\",    # pyupgrade\n    \"W\",     # Warning\n    \"YTT\",   # flake8-2020\n]\nextend-ignore = [\n    \"A002\",    # builtin-argument-shadowing\n    \"ANN401\",  # any-type (mypy's `disallow_any_explicit` is better)\n    \"E402\",    # module-import-not-at-top-of-file (usually OS-specific)\n    \"E501\",    # line-too-long\n    \"F403\",    # undefined-local-with-import-star\n    \"F405\",    # undefined-local-with-import-star-usage\n    \"PERF203\", # try-except-in-loop (not always possible to refactor)\n    \"PT012\",   # multiple statements in pytest.raises block\n    \"SIM117\",  # multiple-with-statements (messes up lots of context-based stuff and looks bad)\n    \"RUF067\",  # non-empty-init-module, since we need lots of logic in our __init__\n\n    # conflicts with formatter (ruff recommends these be disabled)\n    \"COM812\",\n]\n\n[tool.ruff.lint.per-file-ignores]\n# F401 is ignoring unused imports. For these particular files,\n# these are public APIs where we are importing everything we want\n# to export for public use.\n'src/trio/__init__.py' = ['F401']\n'src/trio/_core/__init__.py' = ['F401']\n'src/trio/abc.py' = ['F401', 'A005']\n'src/trio/lowlevel.py' = ['F401']\n'src/trio/socket.py' = ['F401', 'A005']\n'src/trio/testing/__init__.py' = ['F401']\n\n# RUF029 is ignoring tests that are marked as async functions but\n# do not use an await in their function bodies. There are several\n# places where internal trio synchronous code relies on being\n# called from an async function, where current task is set up.\n#\n# RUF069 is ignoring exact float comparison, because we use that\n# in combination with autojump clocks\n'src/trio/_tests/*.py' = ['RUF029', 'RUF069']\n'src/trio/_core/_tests/*.py' = ['RUF029', 'RUF069']\n# A005 is ignoring modules that shadow stdlib modules.\n'src/trio/_abc.py' = ['A005']\n'src/trio/_socket.py' = ['A005']\n'src/trio/_ssl.py' = ['A005']\n\n[tool.ruff.lint.isort]\ncombine-as-imports = true\n\n[tool.ruff.lint.flake8-pytest-style]\nfixture-parentheses = false\n\n[tool.mypy]\npython_version = \"3.10\"\nfiles = [\"src/trio/\", \"docs/source/*.py\"]\n\n# Be flexible about dependencies that don't have stubs yet (like pytest)\nignore_missing_imports = true\n\n# Be strict about use of Mypy\nenable_error_code = [\"truthy-bool\", \"mutable-override\"]\nlocal_partial_types = true\nwarn_unused_ignores = true\nwarn_unused_configs = true\nwarn_redundant_casts = true\nwarn_return_any = true\n\n# Avoid subtle backsliding\ndisallow_any_decorated = true\ndisallow_any_explicit = true\ndisallow_any_generics = true\ndisallow_any_unimported = true\ndisallow_incomplete_defs = true\ndisallow_subclassing_any = true\ndisallow_untyped_calls = true\ndisallow_untyped_decorators = true\ndisallow_untyped_defs = true\ncheck_untyped_defs = true\n\n[tool.pyright]\npythonVersion = \"3.10\"\nreportUnnecessaryTypeIgnoreComment = true\ntypeCheckingMode = \"strict\"\n\n[tool.pytest.ini_options]\naddopts = [\"--strict-markers\", \"--strict-config\", \"-p trio._tests.pytest_plugin\", \"--import-mode=importlib\"]\nfaulthandler_timeout = 60\nfilterwarnings = [\n  \"error\",\n  # https://gitter.im/python-trio/general?at=63bb8d0740557a3d5c688d67\n  'ignore:You are using cryptography on a 32-bit Python on a 64-bit Windows Operating System. Cryptography will be significantly faster if you switch to using a 64-bit Python.:UserWarning',\n  # https://github.com/berkerpeksag/astor/issues/217\n  'ignore:ast.Num is deprecated:DeprecationWarning:astor',\n]\njunit_family = \"xunit2\"\nmarkers = [\"redistributors_should_skip: tests that should be skipped by downstream redistributors\"]\nxfail_strict = true\n\n[tool.towncrier]\ndirectory = \"newsfragments\"\nfilename = \"docs/source/history.rst\"\nissue_format = \"`#{issue} <https://github.com/python-trio/trio/issues/{issue}>`__\"\n# Usage:\n# - PRs should drop a file like \"issuenumber.feature\" in newsfragments\n# (or \"bugfix\", \"doc\", \"removal\", \"misc\"; misc gets no text, we can\n# customize this)\n# - At release time after bumping version number, run: towncrier\n# (or towncrier --draft)\npackage = \"trio\"\npackage_dir = \"src\"\nunderlines = [\"-\", \"~\", \"^\"]\n\n[[tool.towncrier.type]]\ndirectory = \"headline\"\nname = \"Headline features\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"breaking\"\nname = \"Breaking changes\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"feature\"\nname = \"Features\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"bugfix\"\nname = \"Bugfixes\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"doc\"\nname = \"Improved documentation\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"deprecated\"\nname = \"Deprecations and removals\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"removal\"\nname = \"Removals without deprecations\"\nshowcontent = true\n\n[[tool.towncrier.type]]\ndirectory = \"misc\"\nname = \"Miscellaneous internal changes\"\nshowcontent = true\n\n[tool.coverage.html]\nshow_contexts = true\nskip_covered = false\n\n[tool.coverage.paths]\nsource = [\"src\", \"**/site-packages\"]\nattrs-plugin = [\n  \"tests/_trio_check_attrs_aliases.py\",\n  \"_trio_check_attrs_aliases.py\",\n]\n\n[tool.coverage.run]\nbranch = true\nomit = [\n    # Omit the generated files in trio/_core starting with _generated_\n    \"*/trio/_core/_generated_*\",\n    # Type tests aren't intended to be run, just passed to type checkers.\n    \"*/type_tests/*\",\n    # Script used to check type completeness that isn't run in tests\n    \"*/trio/_tests/check_type_completeness.py\",\n    # Script to generate a CFFI interface for the Windows kernel\n    \"*/trio/_tools/windows_ffi_build.py\",\n]\n# The test suite spawns subprocesses to test some stuff, so make sure\n# this doesn't corrupt the coverage files\nparallel = true\nplugins = []\nrelative_files = true\nsource = [\".\"]\nsource_pkgs = [\"trio\", \"_trio_check_attrs_aliases\"]\n\n[tool.coverage.report]\nprecision = 1\nskip_covered = true\nskip_empty = true\nshow_missing = true\nexclude_also = [\n  '^\\s*@pytest\\.mark\\.xfail',\n   \"abc.abstractmethod\",\n   \"if TYPE_CHECKING.*:\",\n   \"if _t.TYPE_CHECKING:\",\n   \"if t.TYPE_CHECKING:\",\n   \"@overload\",\n   'class .*\\bProtocol\\b.*\\):',\n   \"raise NotImplementedError\",\n   '.*if \"sphinx.ext.autodoc\" in sys.modules:',\n   'TODO: test this line',\n   'if __name__ == \"__main__\":',\n   'pass',\n]\npartial_branches = [\n    \"pragma: no branch\",\n    \"if not TYPE_CHECKING:\",\n    \"if not _t.TYPE_CHECKING:\",\n    \"if not t.TYPE_CHECKING:\",\n    \"if .* or not TYPE_CHECKING:\",\n    \"if .* or not _t.TYPE_CHECKING:\",\n    \"if .* or not t.TYPE_CHECKING:\",\n   'TODO: test this branch',\n]\n"
  },
  {
    "path": "src/trio/__init__.py",
    "content": "\"\"\"Trio - A friendly Python library for async concurrency and I/O\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n# General layout:\n#\n# trio/_core/... is the self-contained core library. It does various\n# shenanigans to export a consistent \"core API\", but parts of the core API are\n# too low-level to be recommended for regular use.\n#\n# trio/*.py define a set of more usable tools on top of this. They import from\n# trio._core and from each other.\n#\n# This file pulls together the friendly public API, by re-exporting the more\n# innocuous bits of the _core API + the higher-level tools from trio/*.py.\n#\n# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)\n#\n# must be imported early to avoid circular import\nfrom ._core import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED  # isort: split\n\n# Submodules imported by default\nfrom . import abc, from_thread, lowlevel, socket, to_thread\nfrom ._channel import (\n    MemoryChannelStatistics as MemoryChannelStatistics,\n    MemoryReceiveChannel as MemoryReceiveChannel,\n    MemorySendChannel as MemorySendChannel,\n    as_safe_channel as as_safe_channel,\n    open_memory_channel as open_memory_channel,\n)\nfrom ._core import (\n    BrokenResourceError as BrokenResourceError,\n    BusyResourceError as BusyResourceError,\n    Cancelled as Cancelled,\n    CancelScope as CancelScope,\n    ClosedResourceError as ClosedResourceError,\n    EndOfChannel as EndOfChannel,\n    Nursery as Nursery,\n    RunFinishedError as RunFinishedError,\n    TaskStatus as TaskStatus,\n    TrioInternalError as TrioInternalError,\n    WouldBlock as WouldBlock,\n    current_effective_deadline as current_effective_deadline,\n    current_time as current_time,\n    open_nursery as open_nursery,\n    run as run,\n)\nfrom ._deprecate import TrioDeprecationWarning as TrioDeprecationWarning\nfrom ._dtls import (\n    DTLSChannel as DTLSChannel,\n    DTLSChannelStatistics as DTLSChannelStatistics,\n    DTLSEndpoint as DTLSEndpoint,\n)\nfrom ._file_io import open_file as open_file, wrap_file as wrap_file\nfrom ._highlevel_generic import (\n    StapledStream as StapledStream,\n    aclose_forcefully as aclose_forcefully,\n)\nfrom ._highlevel_open_tcp_listeners import (\n    open_tcp_listeners as open_tcp_listeners,\n    serve_tcp as serve_tcp,\n)\nfrom ._highlevel_open_tcp_stream import open_tcp_stream as open_tcp_stream\nfrom ._highlevel_open_unix_stream import open_unix_socket as open_unix_socket\nfrom ._highlevel_serve_listeners import serve_listeners as serve_listeners\nfrom ._highlevel_socket import (\n    SocketListener as SocketListener,\n    SocketStream as SocketStream,\n)\nfrom ._highlevel_ssl_helpers import (\n    open_ssl_over_tcp_listeners as open_ssl_over_tcp_listeners,\n    open_ssl_over_tcp_stream as open_ssl_over_tcp_stream,\n    serve_ssl_over_tcp as serve_ssl_over_tcp,\n)\nfrom ._path import Path as Path, PosixPath as PosixPath, WindowsPath as WindowsPath\nfrom ._signals import open_signal_receiver as open_signal_receiver\nfrom ._ssl import (\n    NeedHandshakeError as NeedHandshakeError,\n    SSLListener as SSLListener,\n    SSLStream as SSLStream,\n)\nfrom ._subprocess import Process as Process, run_process as run_process\nfrom ._sync import (\n    CapacityLimiter as CapacityLimiter,\n    CapacityLimiterStatistics as CapacityLimiterStatistics,\n    Condition as Condition,\n    ConditionStatistics as ConditionStatistics,\n    Event as Event,\n    EventStatistics as EventStatistics,\n    Lock as Lock,\n    LockStatistics as LockStatistics,\n    Semaphore as Semaphore,\n    StrictFIFOLock as StrictFIFOLock,\n)\nfrom ._timeouts import (\n    TooSlowError as TooSlowError,\n    fail_after as fail_after,\n    fail_at as fail_at,\n    move_on_after as move_on_after,\n    move_on_at as move_on_at,\n    sleep as sleep,\n    sleep_forever as sleep_forever,\n    sleep_until as sleep_until,\n)\nfrom ._version import __version__ as __version__\n\n# Not imported by default, but mentioned here so static analysis tools like\n# pylint will know that it exists.\nif TYPE_CHECKING:\n    from . import testing\n\nfrom . import _deprecate as _deprecate\n\n_deprecate.deprecate_attributes(__name__, {})\n\n# Having the public path in .__module__ attributes is important for:\n# - exception names in printed tracebacks\n# - sphinx :show-inheritance:\n# - deprecation warnings\n# - pickle\n# - probably other stuff\nfrom ._util import fixup_module_metadata\n\nfixup_module_metadata(__name__, globals())\nfixup_module_metadata(lowlevel.__name__, lowlevel.__dict__)\nfixup_module_metadata(socket.__name__, socket.__dict__)\nfixup_module_metadata(abc.__name__, abc.__dict__)\nfixup_module_metadata(from_thread.__name__, from_thread.__dict__)\nfixup_module_metadata(to_thread.__name__, to_thread.__dict__)\ndel fixup_module_metadata\ndel TYPE_CHECKING\n"
  },
  {
    "path": "src/trio/__main__.py",
    "content": "from trio._repl import main\n\nmain(locals())\n"
  },
  {
    "path": "src/trio/_abc.py",
    "content": "from __future__ import annotations\n\nimport socket\nfrom abc import ABC, abstractmethod\nfrom typing import TYPE_CHECKING, Generic, TypeVar\n\nimport trio\n\nif TYPE_CHECKING:\n    from types import TracebackType\n\n    from typing_extensions import Self\n\n    # both of these introduce circular imports if outside a TYPE_CHECKING guard\n    from ._socket import SocketType\n    from .lowlevel import Task\n\n\nclass Clock(ABC):\n    \"\"\"The interface for custom run loop clocks.\"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    def start_clock(self) -> None:\n        \"\"\"Do any setup this clock might need.\n\n        Called at the beginning of the run.\n\n        \"\"\"\n\n    @abstractmethod\n    def current_time(self) -> float:\n        \"\"\"Return the current time, according to this clock.\n\n        This is used to implement functions like :func:`trio.current_time` and\n        :func:`trio.move_on_after`.\n\n        Returns:\n            float: The current time.\n\n        \"\"\"\n\n    @abstractmethod\n    def deadline_to_sleep_time(self, deadline: float) -> float:\n        \"\"\"Compute the real time until the given deadline.\n\n        This is called before we enter a system-specific wait function like\n        :func:`select.select`, to get the timeout to pass.\n\n        For a clock using wall-time, this should be something like::\n\n           return deadline - self.current_time()\n\n        but of course it may be different if you're implementing some kind of\n        virtual clock.\n\n        Args:\n            deadline (float): The absolute time of the next deadline,\n                according to this clock.\n\n        Returns:\n            float: The number of real seconds to sleep until the given\n            deadline. May be :data:`math.inf`.\n\n        \"\"\"\n\n\nclass Instrument(ABC):  # noqa: B024  # conceptually is ABC\n    \"\"\"The interface for run loop instrumentation.\n\n    Instruments don't have to inherit from this abstract base class, and all\n    of these methods are optional. This class serves mostly as documentation.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    def before_run(self) -> None:\n        \"\"\"Called at the beginning of :func:`trio.run`.\"\"\"\n        return\n\n    def after_run(self) -> None:\n        \"\"\"Called just before :func:`trio.run` returns.\"\"\"\n        return\n\n    def task_spawned(self, task: Task) -> None:\n        \"\"\"Called when the given task is created.\n\n        Args:\n            task (trio.lowlevel.Task): The new task.\n\n        \"\"\"\n        return\n\n    def task_scheduled(self, task: Task) -> None:\n        \"\"\"Called when the given task becomes runnable.\n\n        It may still be some time before it actually runs, if there are other\n        runnable tasks ahead of it.\n\n        Args:\n            task (trio.lowlevel.Task): The task that became runnable.\n\n        \"\"\"\n        return\n\n    def before_task_step(self, task: Task) -> None:\n        \"\"\"Called immediately before we resume running the given task.\n\n        Args:\n            task (trio.lowlevel.Task): The task that is about to run.\n\n        \"\"\"\n        return\n\n    def after_task_step(self, task: Task) -> None:\n        \"\"\"Called when we return to the main run loop after a task has yielded.\n\n        Args:\n            task (trio.lowlevel.Task): The task that just ran.\n\n        \"\"\"\n        return\n\n    def task_exited(self, task: Task) -> None:\n        \"\"\"Called when the given task exits.\n\n        Args:\n            task (trio.lowlevel.Task): The finished task.\n\n        \"\"\"\n        return\n\n    def before_io_wait(self, timeout: float) -> None:\n        \"\"\"Called before blocking to wait for I/O readiness.\n\n        Args:\n            timeout (float): The number of seconds we are willing to wait.\n\n        \"\"\"\n        return\n\n    def after_io_wait(self, timeout: float) -> None:\n        \"\"\"Called after handling pending I/O.\n\n        Args:\n            timeout (float): The number of seconds we were willing to\n                wait. This much time may or may not have elapsed, depending on\n                whether any I/O was ready.\n\n        \"\"\"\n        return\n\n\nclass HostnameResolver(ABC):\n    \"\"\"If you have a custom hostname resolver, then implementing\n    :class:`HostnameResolver` allows you to register this to be used by Trio.\n\n    See :func:`trio.socket.set_custom_hostname_resolver`.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def getaddrinfo(\n        self,\n        host: bytes | None,\n        port: bytes | str | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> list[\n        tuple[\n            socket.AddressFamily,\n            socket.SocketKind,\n            int,\n            str,\n            tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n        ]\n    ]:\n        \"\"\"A custom implementation of :func:`~trio.socket.getaddrinfo`.\n\n        Called by :func:`trio.socket.getaddrinfo`.\n\n        If ``host`` is given as a numeric IP address, then\n        :func:`~trio.socket.getaddrinfo` may handle the request itself rather\n        than calling this method.\n\n        Any required IDNA encoding is handled before calling this function;\n        your implementation can assume that it will never see U-labels like\n        ``\"café.com\"``, and only needs to handle A-labels like\n        ``b\"xn--caf-dma.com\"``.\"\"\"  # spellchecker:disable-line\n\n    @abstractmethod\n    async def getnameinfo(\n        self,\n        sockaddr: tuple[str, int] | tuple[str, int, int, int],\n        flags: int,\n    ) -> tuple[str, str]:\n        \"\"\"A custom implementation of :func:`~trio.socket.getnameinfo`.\n\n        Called by :func:`trio.socket.getnameinfo`.\n\n        \"\"\"\n\n\nclass SocketFactory(ABC):\n    \"\"\"If you write a custom class implementing the Trio socket interface,\n    then you can use a :class:`SocketFactory` to get Trio to use it.\n\n    See :func:`trio.socket.set_custom_socket_factory`.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    def socket(\n        self,\n        family: socket.AddressFamily | int = socket.AF_INET,\n        type: socket.SocketKind | int = socket.SOCK_STREAM,\n        proto: int = 0,\n    ) -> SocketType:\n        \"\"\"Create and return a socket object.\n\n        Your socket object must inherit from :class:`trio.socket.SocketType`,\n        which is an empty class whose only purpose is to \"mark\" which classes\n        should be considered valid Trio sockets.\n\n        Called by :func:`trio.socket.socket`.\n\n        Note that unlike :func:`trio.socket.socket`, this does not take a\n        ``fileno=`` argument. If a ``fileno=`` is specified, then\n        :func:`trio.socket.socket` returns a regular Trio socket object\n        instead of calling this method.\n\n        \"\"\"\n\n\nclass AsyncResource(ABC):\n    \"\"\"A standard interface for resources that needs to be cleaned up, and\n    where that cleanup may require blocking operations.\n\n    This class distinguishes between \"graceful\" closes, which may perform I/O\n    and thus block, and a \"forceful\" close, which cannot. For example, cleanly\n    shutting down a TLS-encrypted connection requires sending a \"goodbye\"\n    message; but if a peer has become non-responsive, then sending this\n    message might block forever, so we may want to just drop the connection\n    instead. Therefore the :meth:`aclose` method is unusual in that it\n    should always close the connection (or at least make its best attempt)\n    *even if it fails*; failure indicates a failure to achieve grace, not a\n    failure to close the connection.\n\n    Objects that implement this interface can be used as async context\n    managers, i.e., you can write::\n\n      async with create_resource() as some_async_resource:\n          ...\n\n    Entering the context manager is synchronous (not a checkpoint); exiting it\n    calls :meth:`aclose`. The default implementations of\n    ``__aenter__`` and ``__aexit__`` should be adequate for all subclasses.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def aclose(self) -> None:\n        \"\"\"Close this resource, possibly blocking.\n\n        IMPORTANT: This method may block in order to perform a \"graceful\"\n        shutdown. But, if this fails, then it still *must* close any\n        underlying resources before returning. An error from this method\n        indicates a failure to achieve grace, *not* a failure to close the\n        connection.\n\n        For example, suppose we call :meth:`aclose` on a TLS-encrypted\n        connection. This requires sending a \"goodbye\" message; but if the peer\n        has become non-responsive, then our attempt to send this message might\n        block forever, and eventually time out and be cancelled. In this case\n        the :meth:`aclose` method on :class:`~trio.SSLStream` will\n        immediately close the underlying transport stream using\n        :func:`trio.aclose_forcefully` before raising :exc:`~trio.Cancelled`.\n\n        If the resource is already closed, then this method should silently\n        succeed.\n\n        Once this method completes, any other pending or future operations on\n        this resource should generally raise :exc:`~trio.ClosedResourceError`,\n        unless there's a good reason to do otherwise.\n\n        See also: :func:`trio.aclose_forcefully`.\n\n        \"\"\"\n\n    async def __aenter__(self) -> Self:\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        await self.aclose()\n\n\nclass SendStream(AsyncResource):\n    \"\"\"A standard interface for sending data on a byte stream.\n\n    The underlying stream may be unidirectional, or bidirectional. If it's\n    bidirectional, then you probably want to also implement\n    :class:`ReceiveStream`, which makes your object a :class:`Stream`.\n\n    :class:`SendStream` objects also implement the :class:`AsyncResource`\n    interface, so they can be closed by calling :meth:`~AsyncResource.aclose`\n    or using an ``async with`` block.\n\n    If you want to send Python objects rather than raw bytes, see\n    :class:`SendChannel`.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        \"\"\"Sends the given data through the stream, blocking if necessary.\n\n        Args:\n          data (bytes, bytearray, or memoryview): The data to send.\n\n        Raises:\n          trio.BusyResourceError: if another task is already executing a\n              :meth:`send_all`, :meth:`wait_send_all_might_not_block`, or\n              :meth:`HalfCloseableStream.send_eof` on this stream.\n          trio.BrokenResourceError: if something has gone wrong, and the stream\n              is broken.\n          trio.ClosedResourceError: if you previously closed this stream\n              object, or if another task closes this stream object while\n              :meth:`send_all` is running.\n\n        Most low-level operations in Trio provide a guarantee: if they raise\n        :exc:`trio.Cancelled`, this means that they had no effect, so the\n        system remains in a known state. This is **not true** for\n        :meth:`send_all`. If this operation raises :exc:`trio.Cancelled` (or\n        any other exception for that matter), then it may have sent some, all,\n        or none of the requested data, and there is no way to know which.\n\n        \"\"\"\n\n    @abstractmethod\n    async def wait_send_all_might_not_block(self) -> None:\n        \"\"\"Block until it's possible that :meth:`send_all` might not block.\n\n        This method may return early: it's possible that after it returns,\n        :meth:`send_all` will still block. (In the worst case, if no better\n        implementation is available, then it might always return immediately\n        without blocking. It's nice to do better than that when possible,\n        though.)\n\n        This method **must not** return *late*: if it's possible for\n        :meth:`send_all` to complete without blocking, then it must\n        return. When implementing it, err on the side of returning early.\n\n        Raises:\n          trio.BusyResourceError: if another task is already executing a\n              :meth:`send_all`, :meth:`wait_send_all_might_not_block`, or\n              :meth:`HalfCloseableStream.send_eof` on this stream.\n          trio.BrokenResourceError: if something has gone wrong, and the stream\n              is broken.\n          trio.ClosedResourceError: if you previously closed this stream\n              object, or if another task closes this stream object while\n              :meth:`wait_send_all_might_not_block` is running.\n\n        Note:\n\n          This method is intended to aid in implementing protocols that want\n          to delay choosing which data to send until the last moment. E.g.,\n          suppose you're working on an implementation of a remote display server\n          like `VNC\n          <https://en.wikipedia.org/wiki/Virtual_Network_Computing>`__, and\n          the network connection is currently backed up so that if you call\n          :meth:`send_all` now then it will sit for 0.5 seconds before actually\n          sending anything. In this case it doesn't make sense to take a\n          screenshot, then wait 0.5 seconds, and then send it, because the\n          screen will keep changing while you wait; it's better to wait 0.5\n          seconds, then take the screenshot, and then send it, because this\n          way the data you deliver will be more\n          up-to-date. Using :meth:`wait_send_all_might_not_block` makes it\n          possible to implement the better strategy.\n\n          If you use this method, you might also want to read up on\n          ``TCP_NOTSENT_LOWAT``.\n\n          Further reading:\n\n          * `Prioritization Only Works When There's Pending Data to Prioritize\n            <https://insouciant.org/tech/prioritization-only-works-when-theres-pending-data-to-prioritize/>`__\n\n          * WWDC 2015: Your App and Next Generation Networks: `slides\n            <http://devstreaming.apple.com/videos/wwdc/2015/719ui2k57m/719/719_your_app_and_next_generation_networks.pdf?dl=1>`__,\n            `video and transcript\n            <https://developer.apple.com/videos/play/wwdc2015/719/>`__\n\n        \"\"\"\n\n\nclass ReceiveStream(AsyncResource):\n    \"\"\"A standard interface for receiving data on a byte stream.\n\n    The underlying stream may be unidirectional, or bidirectional. If it's\n    bidirectional, then you probably want to also implement\n    :class:`SendStream`, which makes your object a :class:`Stream`.\n\n    :class:`ReceiveStream` objects also implement the :class:`AsyncResource`\n    interface, so they can be closed by calling :meth:`~AsyncResource.aclose`\n    or using an ``async with`` block.\n\n    If you want to receive Python objects rather than raw bytes, see\n    :class:`ReceiveChannel`.\n\n    `ReceiveStream` objects can be used in ``async for`` loops. Each iteration\n    will produce an arbitrary sized chunk of bytes, like calling\n    `receive_some` with no arguments. Every chunk will contain at least one\n    byte, and the loop automatically exits when reaching end-of-file.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:\n        \"\"\"Wait until there is data available on this stream, and then return\n        some of it.\n\n        A return value of ``b\"\"`` (an empty bytestring) indicates that the\n        stream has reached end-of-file. Implementations should be careful that\n        they return ``b\"\"`` if, and only if, the stream has reached\n        end-of-file!\n\n        Args:\n          max_bytes (int): The maximum number of bytes to return. Must be\n              greater than zero. Optional; if omitted, then the stream object\n              is free to pick a reasonable default.\n\n        Returns:\n          bytes or bytearray: The data received.\n\n        Raises:\n          trio.BusyResourceError: if two tasks attempt to call\n              :meth:`receive_some` on the same stream at the same time.\n          trio.BrokenResourceError: if something has gone wrong, and the stream\n              is broken.\n          trio.ClosedResourceError: if you previously closed this stream\n              object, or if another task closes this stream object while\n              :meth:`receive_some` is running.\n\n        \"\"\"\n\n    def __aiter__(self) -> Self:\n        return self\n\n    async def __anext__(self) -> bytes | bytearray:\n        data = await self.receive_some()\n        if not data:\n            raise StopAsyncIteration\n        return data\n\n\nclass Stream(SendStream, ReceiveStream):\n    \"\"\"A standard interface for interacting with bidirectional byte streams.\n\n    A :class:`Stream` is an object that implements both the\n    :class:`SendStream` and :class:`ReceiveStream` interfaces.\n\n    If implementing this interface, you should consider whether you can go one\n    step further and implement :class:`HalfCloseableStream`.\n\n    \"\"\"\n\n    __slots__ = ()\n\n\nclass HalfCloseableStream(Stream):\n    \"\"\"This interface extends :class:`Stream` to also allow closing the send\n    part of the stream without closing the receive part.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def send_eof(self) -> None:\n        \"\"\"Send an end-of-file indication on this stream, if possible.\n\n        The difference between :meth:`send_eof` and\n        :meth:`~AsyncResource.aclose` is that :meth:`send_eof` is a\n        *unidirectional* end-of-file indication. After you call this method,\n        you shouldn't try sending any more data on this stream, and your\n        remote peer should receive an end-of-file indication (eventually,\n        after receiving all the data you sent before that). But, they may\n        continue to send data to you, and you can continue to receive it by\n        calling :meth:`~ReceiveStream.receive_some`. You can think of it as\n        calling :meth:`~AsyncResource.aclose` on just the\n        :class:`SendStream` \"half\" of the stream object (and in fact that's\n        literally how :class:`trio.StapledStream` implements it).\n\n        Examples:\n\n        * On a socket, this corresponds to ``shutdown(..., SHUT_WR)`` (`man\n          page <https://linux.die.net/man/2/shutdown>`__).\n\n        * The SSH protocol provides the ability to multiplex bidirectional\n          \"channels\" on top of a single encrypted connection. A Trio\n          implementation of SSH could expose these channels as\n          :class:`HalfCloseableStream` objects, and calling :meth:`send_eof`\n          would send an ``SSH_MSG_CHANNEL_EOF`` request (see `RFC 4254 §5.3\n          <https://tools.ietf.org/html/rfc4254#section-5.3>`__).\n\n        * On an SSL/TLS-encrypted connection, the protocol doesn't provide any\n          way to do a unidirectional shutdown without closing the connection\n          entirely, so :class:`~trio.SSLStream` implements\n          :class:`Stream`, not :class:`HalfCloseableStream`.\n\n        If an EOF has already been sent, then this method should silently\n        succeed.\n\n        Raises:\n          trio.BusyResourceError: if another task is already executing a\n              :meth:`~SendStream.send_all`,\n              :meth:`~SendStream.wait_send_all_might_not_block`, or\n              :meth:`send_eof` on this stream.\n          trio.BrokenResourceError: if something has gone wrong, and the stream\n              is broken.\n          trio.ClosedResourceError: if you previously closed this stream\n              object, or if another task closes this stream object while\n              :meth:`send_eof` is running.\n\n        \"\"\"\n\n\n# A regular invariant generic type\nT = TypeVar(\"T\")\n\n# The type of object produced by a ReceiveChannel (covariant because\n# ReceiveChannel[Derived] can be passed to someone expecting\n# ReceiveChannel[Base])\nReceiveType = TypeVar(\"ReceiveType\", covariant=True)\n\n# The type of object accepted by a SendChannel (contravariant because\n# SendChannel[Base] can be passed to someone expecting\n# SendChannel[Derived])\nSendType = TypeVar(\"SendType\", contravariant=True)\n\n# The type of object produced by a Listener (covariant plus must be\n# an AsyncResource)\nT_resource = TypeVar(\"T_resource\", bound=AsyncResource, covariant=True)\n\n\nclass Listener(AsyncResource, Generic[T_resource]):\n    \"\"\"A standard interface for listening for incoming connections.\n\n    :class:`Listener` objects also implement the :class:`AsyncResource`\n    interface, so they can be closed by calling :meth:`~AsyncResource.aclose`\n    or using an ``async with`` block.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def accept(self) -> T_resource:\n        \"\"\"Wait until an incoming connection arrives, and then return it.\n\n        Returns:\n          AsyncResource: An object representing the incoming connection. In\n          practice this is generally some kind of :class:`Stream`,\n          but in principle you could also define a :class:`Listener` that\n          returned, say, channel objects.\n\n        Raises:\n          trio.BusyResourceError: if two tasks attempt to call\n              :meth:`accept` on the same listener at the same time.\n          trio.ClosedResourceError: if you previously closed this listener\n              object, or if another task closes this listener object while\n              :meth:`accept` is running.\n\n        Listeners don't generally raise :exc:`~trio.BrokenResourceError`,\n        because for listeners there is no general condition of \"the\n        network/remote peer broke the connection\" that can be handled in a\n        generic way, like there is for streams. Other errors *can* occur and\n        be raised from :meth:`accept` – for example, if you run out of file\n        descriptors then you might get an :class:`OSError` with its errno set\n        to ``EMFILE``.\n\n        \"\"\"\n\n\nclass SendChannel(AsyncResource, Generic[SendType]):\n    \"\"\"A standard interface for sending Python objects to some receiver.\n\n    `SendChannel` objects also implement the `AsyncResource` interface, so\n    they can be closed by calling `~AsyncResource.aclose` or using an ``async\n    with`` block.\n\n    If you want to send raw bytes rather than Python objects, see\n    `SendStream`.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def send(self, value: SendType) -> None:\n        \"\"\"Attempt to send an object through the channel, blocking if necessary.\n\n        Args:\n          value (object): The object to send.\n\n        Raises:\n          trio.BrokenResourceError: if something has gone wrong, and the\n              channel is broken. For example, you may get this if the receiver\n              has already been closed.\n          trio.ClosedResourceError: if you previously closed this\n              :class:`SendChannel` object, or if another task closes it while\n              :meth:`send` is running.\n          trio.BusyResourceError: some channels allow multiple tasks to call\n              `send` at the same time, but others don't. If you try to call\n              `send` simultaneously from multiple tasks on a channel that\n              doesn't support it, then you can get `~trio.BusyResourceError`.\n\n        \"\"\"\n\n\nclass ReceiveChannel(AsyncResource, Generic[ReceiveType]):\n    \"\"\"A standard interface for receiving Python objects from some sender.\n\n    You can iterate over a :class:`ReceiveChannel` using an ``async for``\n    loop::\n\n       async for value in receive_channel:\n           ...\n\n    This is equivalent to calling :meth:`receive` repeatedly. The loop exits\n    without error when `receive` raises `~trio.EndOfChannel`.\n\n    `ReceiveChannel` objects also implement the `AsyncResource` interface, so\n    they can be closed by calling `~AsyncResource.aclose` or using an ``async\n    with`` block.\n\n    If you want to receive raw bytes rather than Python objects, see\n    `ReceiveStream`.\n\n    \"\"\"\n\n    __slots__ = ()\n\n    @abstractmethod\n    async def receive(self) -> ReceiveType:\n        \"\"\"Attempt to receive an incoming object, blocking if necessary.\n\n        Returns:\n          object: Whatever object was received.\n\n        Raises:\n          trio.EndOfChannel: if the sender has been closed cleanly, and no\n              more objects are coming. This is not an error condition.\n          trio.ClosedResourceError: if you previously closed this\n              :class:`ReceiveChannel` object.\n          trio.BrokenResourceError: if something has gone wrong, and the\n              channel is broken.\n          trio.BusyResourceError: some channels allow multiple tasks to call\n              `receive` at the same time, but others don't. If you try to call\n              `receive` simultaneously from multiple tasks on a channel that\n              doesn't support it, then you can get `~trio.BusyResourceError`.\n\n        \"\"\"\n\n    def __aiter__(self) -> Self:\n        return self\n\n    async def __anext__(self) -> ReceiveType:\n        try:\n            return await self.receive()\n        except trio.EndOfChannel:\n            raise StopAsyncIteration from None\n\n\n# these are necessary for Sphinx's :show-inheritance: with type args.\n# (this should be removed if possible)\n# see: https://github.com/python/cpython/issues/123250\nSendChannel.__module__ = SendChannel.__module__.replace(\"_abc\", \"abc\")\nReceiveChannel.__module__ = ReceiveChannel.__module__.replace(\"_abc\", \"abc\")\nListener.__module__ = Listener.__module__.replace(\"_abc\", \"abc\")\n\n\nclass Channel(SendChannel[T], ReceiveChannel[T]):\n    \"\"\"A standard interface for interacting with bidirectional channels.\n\n    A `Channel` is an object that implements both the `SendChannel` and\n    `ReceiveChannel` interfaces, so you can both send and receive objects.\n\n    \"\"\"\n\n    __slots__ = ()\n\n\n# see above\nChannel.__module__ = Channel.__module__.replace(\"_abc\", \"abc\")\n"
  },
  {
    "path": "src/trio/_channel.py",
    "content": "from __future__ import annotations\n\nimport sys\nfrom collections import OrderedDict, deque\nfrom collections.abc import AsyncGenerator, Callable  # noqa: TC003  # Needed for Sphinx\nfrom contextlib import AbstractAsyncContextManager, asynccontextmanager\nfrom functools import wraps\nfrom math import inf\nfrom typing import (\n    TYPE_CHECKING,\n    Generic,\n)\n\nimport attrs\nfrom outcome import Error, Value\n\nimport trio\n\nfrom ._abc import ReceiveChannel, ReceiveType, SendChannel, SendType, T\nfrom ._core import Abort, BrokenResourceError, RaiseCancelT, Task, enable_ki_protection\nfrom ._util import (\n    MultipleExceptionError,\n    NoPublicConstructor,\n    final,\n    raise_single_exception_from_group,\n)\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup\n\nif TYPE_CHECKING:\n    from types import TracebackType\n\n    from typing_extensions import ParamSpec, Self\n\n    P = ParamSpec(\"P\")\nelif \"sphinx.ext.autodoc\" in sys.modules:\n    # P needs to exist for Sphinx to parse the type hints successfully.\n    try:\n        from typing_extensions import ParamSpec\n    except ImportError:\n        P = ...  # This is valid in Callable, though not correct\n    else:\n        P = ParamSpec(\"P\")\n\n\n# written as a class so you can say open_memory_channel[int](5)\n@final\nclass open_memory_channel(tuple[\"MemorySendChannel[T]\", \"MemoryReceiveChannel[T]\"]):\n    \"\"\"Open a channel for passing objects between tasks within a process.\n\n    Memory channels are lightweight, cheap to allocate, and entirely\n    in-memory. They don't involve any operating-system resources, or any kind\n    of serialization. They just pass Python objects directly between tasks\n    (with a possible stop in an internal buffer along the way).\n\n    Channel objects can be closed by calling `~trio.abc.AsyncResource.aclose`\n    or using ``async with``. They are *not* automatically closed when garbage\n    collected. Closing memory channels isn't mandatory, but it is generally a\n    good idea, because it helps avoid situations where tasks get stuck waiting\n    on a channel when there's no-one on the other side. See\n    :ref:`channel-shutdown` for details.\n\n    Memory channel operations are all atomic with respect to\n    cancellation, either `~trio.abc.ReceiveChannel.receive` will\n    successfully return an object, or it will raise :exc:`Cancelled`\n    while leaving the channel unchanged.\n\n    Args:\n      max_buffer_size (int or math.inf): The maximum number of items that can\n        be buffered in the channel before :meth:`~trio.abc.SendChannel.send`\n        blocks. Choosing a sensible value here is important to ensure that\n        backpressure is communicated promptly and avoid unnecessary latency;\n        see :ref:`channel-buffering` for more details. If in doubt, use 0.\n\n    Returns:\n      A pair ``(send_channel, receive_channel)``. If you have\n      trouble remembering which order these go in, remember: data\n      flows from left → right.\n\n    In addition to the standard channel methods, all memory channel objects\n    provide a ``statistics()`` method, which returns an object with the\n    following fields:\n\n    * ``current_buffer_used``: The number of items currently stored in the\n      channel buffer.\n    * ``max_buffer_size``: The maximum number of items allowed in the buffer,\n      as passed to :func:`open_memory_channel`.\n    * ``open_send_channels``: The number of open\n      :class:`MemorySendChannel` endpoints pointing to this channel.\n      Initially 1, but can be increased by\n      :meth:`MemorySendChannel.clone`.\n    * ``open_receive_channels``: Likewise, but for open\n      :class:`MemoryReceiveChannel` endpoints.\n    * ``tasks_waiting_send``: The number of tasks blocked in ``send`` on this\n      channel (summing over all clones).\n    * ``tasks_waiting_receive``: The number of tasks blocked in ``receive`` on\n      this channel (summing over all clones).\n    \"\"\"\n\n    def __new__(  # type: ignore[misc]  # \"must return a subtype\"\n        cls,\n        max_buffer_size: int | float,  # noqa: PYI041\n    ) -> tuple[MemorySendChannel[T], MemoryReceiveChannel[T]]:\n        if max_buffer_size != inf and not isinstance(max_buffer_size, int):\n            raise TypeError(\"max_buffer_size must be an integer or math.inf\")\n        if max_buffer_size < 0:\n            raise ValueError(\"max_buffer_size must be >= 0\")\n        state: MemoryChannelState[T] = MemoryChannelState(max_buffer_size)\n        return (\n            MemorySendChannel[T]._create(state),\n            MemoryReceiveChannel[T]._create(state),\n        )\n\n    def __init__(self, max_buffer_size: int | float) -> None:  # noqa: PYI041\n        ...\n\n\n@attrs.frozen\nclass MemoryChannelStatistics:\n    current_buffer_used: int\n    max_buffer_size: int | float\n    open_send_channels: int\n    open_receive_channels: int\n    tasks_waiting_send: int\n    tasks_waiting_receive: int\n\n\n@attrs.define\nclass MemoryChannelState(Generic[T]):\n    max_buffer_size: int | float\n    data: deque[T] = attrs.Factory(deque)\n    # Counts of open endpoints using this state\n    open_send_channels: int = 0\n    open_receive_channels: int = 0\n    # {task: value}\n    send_tasks: OrderedDict[Task, T] = attrs.Factory(OrderedDict)\n    # {task: None}\n    receive_tasks: OrderedDict[Task, None] = attrs.Factory(OrderedDict)\n\n    def statistics(self) -> MemoryChannelStatistics:\n        return MemoryChannelStatistics(\n            current_buffer_used=len(self.data),\n            max_buffer_size=self.max_buffer_size,\n            open_send_channels=self.open_send_channels,\n            open_receive_channels=self.open_receive_channels,\n            tasks_waiting_send=len(self.send_tasks),\n            tasks_waiting_receive=len(self.receive_tasks),\n        )\n\n\n@final\n@attrs.define(eq=False, repr=False, slots=False)\nclass MemorySendChannel(SendChannel[SendType], metaclass=NoPublicConstructor):\n    _state: MemoryChannelState[SendType]\n    _closed: bool = False\n    # This is just the tasks waiting on *this* object. As compared to\n    # self._state.send_tasks, which includes tasks from this object and\n    # all clones.\n    _tasks: set[Task] = attrs.Factory(set)\n\n    def __attrs_post_init__(self) -> None:\n        self._state.open_send_channels += 1\n\n    def __repr__(self) -> str:\n        return f\"<send channel at {id(self):#x}, using buffer at {id(self._state):#x}>\"\n\n    def statistics(self) -> MemoryChannelStatistics:\n        \"\"\"Returns a `MemoryChannelStatistics` for the memory channel this is\n        associated with.\"\"\"\n        # XX should we also report statistics specific to this object?\n        return self._state.statistics()\n\n    @enable_ki_protection\n    def send_nowait(self, value: SendType) -> None:\n        \"\"\"Like `~trio.abc.SendChannel.send`, but if the channel's buffer is\n        full, raises `WouldBlock` instead of blocking.\n\n        \"\"\"\n        if self._closed:\n            raise trio.ClosedResourceError\n        if self._state.open_receive_channels == 0:\n            raise trio.BrokenResourceError\n        if self._state.receive_tasks:\n            assert not self._state.data\n            task, _ = self._state.receive_tasks.popitem(last=False)\n            task.custom_sleep_data._tasks.remove(task)\n            trio.lowlevel.reschedule(task, Value(value))\n        elif len(self._state.data) < self._state.max_buffer_size:\n            self._state.data.append(value)\n        else:\n            raise trio.WouldBlock\n\n    @enable_ki_protection\n    async def send(self, value: SendType) -> None:\n        \"\"\"See `SendChannel.send <trio.abc.SendChannel.send>`.\n\n        Memory channels allow multiple tasks to call `send` at the same time.\n\n        \"\"\"\n        await trio.lowlevel.checkpoint_if_cancelled()\n        try:\n            self.send_nowait(value)\n        except trio.WouldBlock:\n            pass\n        else:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n            return\n\n        task = trio.lowlevel.current_task()\n        self._tasks.add(task)\n        self._state.send_tasks[task] = value\n        task.custom_sleep_data = self\n\n        def abort_fn(_: RaiseCancelT) -> Abort:\n            self._tasks.remove(task)\n            del self._state.send_tasks[task]\n            return trio.lowlevel.Abort.SUCCEEDED\n\n        await trio.lowlevel.wait_task_rescheduled(abort_fn)\n\n    # Return type must be stringified or use a TypeVar\n    @enable_ki_protection\n    def clone(self) -> MemorySendChannel[SendType]:\n        \"\"\"Clone this send channel object.\n\n        This returns a new `MemorySendChannel` object, which acts as a\n        duplicate of the original: sending on the new object does exactly the\n        same thing as sending on the old object. (If you're familiar with\n        `os.dup`, then this is a similar idea.)\n\n        However, closing one of the objects does not close the other, and\n        receivers don't get `EndOfChannel` until *all* clones have been\n        closed.\n\n        This is useful for communication patterns that involve multiple\n        producers all sending objects to the same destination. If you give\n        each producer its own clone of the `MemorySendChannel`, and then make\n        sure to close each `MemorySendChannel` when it's finished, receivers\n        will automatically get notified when all producers are finished. See\n        :ref:`channel-mpmc` for examples.\n\n        Raises:\n          trio.ClosedResourceError: if you already closed this\n              `MemorySendChannel` object.\n\n        \"\"\"\n        if self._closed:\n            raise trio.ClosedResourceError\n        return MemorySendChannel._create(self._state)\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self.close()\n\n    @enable_ki_protection\n    def close(self) -> None:\n        \"\"\"Close this send channel object synchronously.\n\n        All channel objects have an asynchronous `~.AsyncResource.aclose` method.\n        Memory channels can also be closed synchronously. This has the same\n        effect on the channel and other tasks using it, but `close` is not a\n        trio checkpoint. This simplifies cleaning up in cancelled tasks.\n\n        Using ``with send_channel:`` will close the channel object on leaving\n        the with block.\n\n        \"\"\"\n        if self._closed:\n            return\n        self._closed = True\n        for task in self._tasks:\n            trio.lowlevel.reschedule(task, Error(trio.ClosedResourceError()))\n            del self._state.send_tasks[task]\n        self._tasks.clear()\n        self._state.open_send_channels -= 1\n        if self._state.open_send_channels == 0:\n            assert not self._state.send_tasks\n            for task in self._state.receive_tasks:\n                task.custom_sleep_data._tasks.remove(task)\n                trio.lowlevel.reschedule(task, Error(trio.EndOfChannel()))\n            self._state.receive_tasks.clear()\n\n    @enable_ki_protection\n    async def aclose(self) -> None:\n        \"\"\"Close this send channel object asynchronously.\n\n        See `MemorySendChannel.close`.\"\"\"\n        self.close()\n        await trio.lowlevel.checkpoint()\n\n\n@final\n@attrs.define(eq=False, repr=False, slots=False)\nclass MemoryReceiveChannel(ReceiveChannel[ReceiveType], metaclass=NoPublicConstructor):\n    _state: MemoryChannelState[ReceiveType]\n    _closed: bool = False\n    _tasks: set[trio._core._run.Task] = attrs.Factory(set)\n\n    def __attrs_post_init__(self) -> None:\n        self._state.open_receive_channels += 1\n\n    def statistics(self) -> MemoryChannelStatistics:\n        \"\"\"Returns a `MemoryChannelStatistics` for the memory channel this is\n        associated with.\"\"\"\n        return self._state.statistics()\n\n    def __repr__(self) -> str:\n        return (\n            f\"<receive channel at {id(self):#x}, using buffer at {id(self._state):#x}>\"\n        )\n\n    @enable_ki_protection\n    def receive_nowait(self) -> ReceiveType:\n        \"\"\"Like `~trio.abc.ReceiveChannel.receive`, but if there's nothing\n        ready to receive, raises `WouldBlock` instead of blocking.\n\n        \"\"\"\n        if self._closed:\n            raise trio.ClosedResourceError\n        if self._state.send_tasks:\n            task, value = self._state.send_tasks.popitem(last=False)\n            task.custom_sleep_data._tasks.remove(task)\n            trio.lowlevel.reschedule(task)\n            self._state.data.append(value)\n            # Fall through\n        if self._state.data:\n            return self._state.data.popleft()\n        if not self._state.open_send_channels:\n            raise trio.EndOfChannel\n        raise trio.WouldBlock\n\n    @enable_ki_protection\n    async def receive(self) -> ReceiveType:\n        \"\"\"See `ReceiveChannel.receive <trio.abc.ReceiveChannel.receive>`.\n\n        Memory channels allow multiple tasks to call `receive` at the same\n        time. The first task will get the first item sent, the second task\n        will get the second item sent, and so on.\n\n        \"\"\"\n        await trio.lowlevel.checkpoint_if_cancelled()\n        try:\n            value = self.receive_nowait()\n        except trio.WouldBlock:\n            pass\n        else:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n            return value\n\n        task = trio.lowlevel.current_task()\n        self._tasks.add(task)\n        self._state.receive_tasks[task] = None\n        task.custom_sleep_data = self\n\n        def abort_fn(_: RaiseCancelT) -> Abort:\n            self._tasks.remove(task)\n            del self._state.receive_tasks[task]\n            return trio.lowlevel.Abort.SUCCEEDED\n\n        # Not strictly guaranteed to return ReceiveType, but will do so unless\n        # you intentionally reschedule with a bad value.\n        return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]\n\n    @enable_ki_protection\n    def clone(self) -> MemoryReceiveChannel[ReceiveType]:\n        \"\"\"Clone this receive channel object.\n\n        This returns a new `MemoryReceiveChannel` object, which acts as a\n        duplicate of the original: receiving on the new object does exactly\n        the same thing as receiving on the old object.\n\n        However, closing one of the objects does not close the other, and the\n        underlying channel is not closed until all clones are closed. (If\n        you're familiar with `os.dup`, then this is a similar idea.)\n\n        This is useful for communication patterns that involve multiple\n        consumers all receiving objects from the same underlying channel. See\n        :ref:`channel-mpmc` for examples.\n\n        .. warning:: The clones all share the same underlying channel.\n           Whenever a clone :meth:`receive`\\\\s a value, it is removed from the\n           channel and the other clones do *not* receive that value. If you\n           want to send multiple copies of the same stream of values to\n           multiple destinations, like :func:`itertools.tee`, then you need to\n           find some other solution; this method does *not* do that.\n\n        Raises:\n          trio.ClosedResourceError: if you already closed this\n              `MemoryReceiveChannel` object.\n\n        \"\"\"\n        if self._closed:\n            raise trio.ClosedResourceError\n        return MemoryReceiveChannel._create(self._state)\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self.close()\n\n    @enable_ki_protection\n    def close(self) -> None:\n        \"\"\"Close this receive channel object synchronously.\n\n        All channel objects have an asynchronous `~.AsyncResource.aclose` method.\n        Memory channels can also be closed synchronously. This has the same\n        effect on the channel and other tasks using it, but `close` is not a\n        trio checkpoint. This simplifies cleaning up in cancelled tasks.\n\n        Using ``with receive_channel:`` will close the channel object on\n        leaving the with block.\n\n        \"\"\"\n        if self._closed:\n            return\n        self._closed = True\n        for task in self._tasks:\n            trio.lowlevel.reschedule(task, Error(trio.ClosedResourceError()))\n            del self._state.receive_tasks[task]\n        self._tasks.clear()\n        self._state.open_receive_channels -= 1\n        if self._state.open_receive_channels == 0:\n            assert not self._state.receive_tasks\n            for task in self._state.send_tasks:\n                task.custom_sleep_data._tasks.remove(task)\n                trio.lowlevel.reschedule(task, Error(trio.BrokenResourceError()))\n            self._state.send_tasks.clear()\n            self._state.data.clear()\n\n    @enable_ki_protection\n    async def aclose(self) -> None:\n        \"\"\"Close this receive channel object asynchronously.\n\n        See `MemoryReceiveChannel.close`.\"\"\"\n        self.close()\n        await trio.lowlevel.checkpoint()\n\n\nclass RecvChanWrapper(ReceiveChannel[T]):\n    def __init__(\n        self, recv_chan: MemoryReceiveChannel[T], send_semaphore: trio.Semaphore\n    ) -> None:\n        self._recv_chan = recv_chan\n        self._send_semaphore = send_semaphore\n\n    async def receive(self) -> T:\n        self._send_semaphore.release()\n        return await self._recv_chan.receive()\n\n    async def aclose(self) -> None:\n        await self._recv_chan.aclose()\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self._recv_chan.close()\n\n\ndef as_safe_channel(\n    fn: Callable[P, AsyncGenerator[T, None]],\n) -> Callable[P, AbstractAsyncContextManager[ReceiveChannel[T]]]:\n    \"\"\"Decorate an async generator function to make it cancellation-safe.\n\n    The ``yield`` keyword offers a very convenient way to write iterators...\n    which makes it really unfortunate that async generators are so difficult\n    to call correctly.  Yielding from the inside of a cancel scope or a nursery\n    to the outside `violates structured concurrency <https://xkcd.com/292/>`_\n    with consequences explained in :pep:`789`.  Even then, resource cleanup\n    errors remain common (:pep:`533`) unless you wrap every call in\n    :func:`~contextlib.aclosing`.\n\n    This decorator gives you the best of both worlds: with careful exception\n    handling and a background task we preserve structured concurrency by\n    offering only the safe interface, and you can still write your iterables\n    with the convenience of ``yield``.  For example::\n\n        @as_safe_channel\n        async def my_async_iterable(arg, *, kwarg=True):\n            while ...:\n                item = await ...\n                yield item\n\n        async with my_async_iterable(...) as recv_chan:\n            async for item in recv_chan:\n                ...\n\n    While the combined async-with-async-for can be inconvenient at first,\n    the context manager is indispensable for both correctness and for prompt\n    cleanup of resources.\n    \"\"\"\n    # Perhaps a future PEP will adopt `async with for` syntax, like\n    # https://coconut.readthedocs.io/en/master/DOCS.html#async-with-for\n\n    @asynccontextmanager\n    @wraps(fn)\n    async def context_manager(\n        *args: P.args, **kwargs: P.kwargs\n    ) -> AsyncGenerator[trio._channel.RecvChanWrapper[T], None]:\n        send_chan, recv_chan = trio.open_memory_channel[T](0)\n        try:\n            async with trio.open_nursery(strict_exception_groups=True) as nursery:\n                agen = fn(*args, **kwargs)\n                send_semaphore = trio.Semaphore(0)\n                # `nursery.start` to make sure that we will clean up send_chan & agen\n                # If this errors we don't close `recv_chan`, but the caller\n                # never gets access to it, so that's not a problem.\n                await nursery.start(\n                    _move_elems_to_channel, agen, send_chan, send_semaphore\n                )\n                # `async with recv_chan` could eat exceptions, so use sync cm\n                with RecvChanWrapper(recv_chan, send_semaphore) as wrapped_recv_chan:\n                    yield wrapped_recv_chan\n                # User has exited context manager, cancel to immediately close the\n                # abandoned generator if it's still alive.\n                nursery.cancel_scope.cancel(\n                    \"exited trio.as_safe_channel context manager\"\n                )\n        except BaseExceptionGroup as eg:\n            try:\n                raise_single_exception_from_group(eg)\n            except MultipleExceptionError:\n                # In case user has except* we make it possible for them to handle the\n                # exceptions.\n                if sys.version_info >= (3, 11):\n                    eg.add_note(\n                        \"Encountered exception during cleanup of generator object, as \"\n                        \"well as exception in the contextmanager body - unable to unwrap.\"\n                    )\n\n                raise eg from None\n\n    async def _move_elems_to_channel(\n        agen: AsyncGenerator[T, None],\n        send_chan: trio.MemorySendChannel[T],\n        send_semaphore: trio.Semaphore,\n        task_status: trio.TaskStatus,\n    ) -> None:\n        # `async with send_chan` will eat exceptions,\n        # see https://github.com/python-trio/trio/issues/1559\n        with send_chan:\n            # replace try-finally with contextlib.aclosing once python39 is\n            # dropped:\n            try:\n                task_status.started()\n                while True:\n                    # wait for receiver to call next on the aiter\n                    await send_semaphore.acquire()\n                    if not send_chan._state.open_receive_channels:\n                        # skip the possibly-expensive computation in the generator,\n                        # if we know it will be impossible to send the result.\n                        break\n                    try:\n                        value = await agen.__anext__()\n                    except StopAsyncIteration:\n                        return\n                    # Send the value to the channel\n                    try:\n                        await send_chan.send(value)\n                    except BrokenResourceError:\n                        break  # closed since we checked above\n            finally:\n                # work around `.aclose()` not suppressing GeneratorExit in an\n                # ExceptionGroup:\n                # TODO: make an issue on CPython about this\n                try:\n                    await agen.aclose()\n                except BaseExceptionGroup as exceptions:\n                    removed, narrowed_exceptions = exceptions.split(GeneratorExit)\n\n                    # TODO: extract a helper to flatten exception groups\n                    removed_exceptions: list[BaseException | None] = [removed]\n                    genexits_seen = 0\n                    for e in removed_exceptions:\n                        if isinstance(e, BaseExceptionGroup):\n                            removed_exceptions.extend(e.exceptions)  # noqa: B909\n                        else:\n                            genexits_seen += 1\n\n                    if genexits_seen > 1:\n                        exc = AssertionError(\"More than one GeneratorExit found.\")\n                        if narrowed_exceptions is None:\n                            narrowed_exceptions = exceptions.derive([exc])\n                        else:\n                            narrowed_exceptions = narrowed_exceptions.derive(\n                                [*narrowed_exceptions.exceptions, exc]\n                            )\n                    if narrowed_exceptions is not None:\n                        raise narrowed_exceptions from None\n\n    return context_manager\n"
  },
  {
    "path": "src/trio/_core/__init__.py",
    "content": "\"\"\"\nThis namespace represents the core functionality that has to be built-in\nand deal with private internal data structures. Things in this namespace\nare publicly available in either trio, trio.lowlevel, or trio.testing.\n\"\"\"\n\nimport sys\nimport typing as _t\n\nfrom ._entry_queue import TrioToken\nfrom ._exceptions import (\n    BrokenResourceError,\n    BusyResourceError,\n    Cancelled,\n    ClosedResourceError,\n    EndOfChannel,\n    RunFinishedError,\n    TrioInternalError,\n    WouldBlock,\n)\nfrom ._ki import currently_ki_protected, disable_ki_protection, enable_ki_protection\nfrom ._local import RunVar, RunVarToken\nfrom ._mock_clock import MockClock\nfrom ._parking_lot import (\n    ParkingLot,\n    ParkingLotStatistics,\n    add_parking_lot_breaker,\n    remove_parking_lot_breaker,\n)\n\n# Imports that always exist\nfrom ._run import (\n    TASK_STATUS_IGNORED,\n    CancelScope,\n    Nursery,\n    RunStatistics,\n    Task,\n    TaskStatus,\n    add_instrument,\n    checkpoint,\n    checkpoint_if_cancelled,\n    current_clock,\n    current_effective_deadline,\n    current_root_task,\n    current_statistics,\n    current_task,\n    current_time,\n    current_trio_token,\n    in_trio_run,\n    in_trio_task,\n    notify_closing,\n    open_nursery,\n    remove_instrument,\n    reschedule,\n    run,\n    spawn_system_task,\n    start_guest_run,\n    wait_all_tasks_blocked,\n    wait_readable,\n    wait_writable,\n)\nfrom ._thread_cache import start_thread_soon\n\n# Has to come after _run to resolve a circular import\nfrom ._traps import (\n    Abort,\n    RaiseCancelT,\n    cancel_shielded_checkpoint,\n    permanently_detach_coroutine_object,\n    reattach_detached_coroutine_object,\n    temporarily_detach_coroutine_object,\n    wait_task_rescheduled,\n)\nfrom ._unbounded_queue import UnboundedQueue, UnboundedQueueStatistics\n\n# Windows imports\nif sys.platform == \"win32\" or (\n    not _t.TYPE_CHECKING and \"sphinx.ext.autodoc\" in sys.modules\n):\n    from ._run import (\n        current_iocp,\n        monitor_completion_key,\n        readinto_overlapped,\n        register_with_iocp,\n        wait_overlapped,\n        write_overlapped,\n    )\n# Kqueue imports\nif (\n    sys.platform != \"linux\" and sys.platform != \"win32\" and sys.platform != \"android\"\n) or (not _t.TYPE_CHECKING and \"sphinx.ext.autodoc\" in sys.modules):\n    from ._run import current_kqueue, monitor_kevent, wait_kevent\n\ndel sys  # It would be better to import sys as _sys, but mypy does not understand it\n"
  },
  {
    "path": "src/trio/_core/_asyncgens.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport sys\nimport warnings\nimport weakref\nfrom typing import TYPE_CHECKING, NoReturn, TypeVar\n\nimport attrs\n\nfrom .. import _core\nfrom .._util import name_asyncgen\nfrom . import _run\n\n# Used to log exceptions in async generator finalizers\nASYNCGEN_LOGGER = logging.getLogger(\"trio.async_generator_errors\")\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from types import AsyncGeneratorType\n\n    from typing_extensions import ParamSpec\n\n    _P = ParamSpec(\"_P\")\n\n    _WEAK_ASYNC_GEN_SET = weakref.WeakSet[AsyncGeneratorType[object, NoReturn]]\n    _ASYNC_GEN_SET = set[AsyncGeneratorType[object, NoReturn]]\nelse:\n    _WEAK_ASYNC_GEN_SET = weakref.WeakSet\n    _ASYNC_GEN_SET = set\n\n_R = TypeVar(\"_R\")\n\n\n@_core.disable_ki_protection\ndef _call_without_ki_protection(\n    f: Callable[_P, _R],\n    /,\n    *args: _P.args,\n    **kwargs: _P.kwargs,\n) -> _R:\n    return f(*args, **kwargs)\n\n\n@attrs.define(eq=False)\nclass AsyncGenerators:\n    # Async generators are added to this set when first iterated. Any\n    # left after the main task exits will be closed before trio.run()\n    # returns.  During most of the run, this is a WeakSet so GC works.\n    # During shutdown, when we're finalizing all the remaining\n    # asyncgens after the system nursery has been closed, it's a\n    # regular set so we don't have to deal with GC firing at\n    # unexpected times.\n    alive: _WEAK_ASYNC_GEN_SET | _ASYNC_GEN_SET = attrs.Factory(_WEAK_ASYNC_GEN_SET)\n    # The ids of foreign async generators are added to this set when first\n    # iterated. Usually it is not safe to refer to ids like this, but because\n    # we're using a finalizer we can ensure ids in this set do not outlive\n    # their async generator.\n    foreign: set[int] = attrs.Factory(set)\n\n    # This collects async generators that get garbage collected during\n    # the one-tick window between the system nursery closing and the\n    # init task starting end-of-run asyncgen finalization.\n    trailing_needs_finalize: _ASYNC_GEN_SET = attrs.Factory(_ASYNC_GEN_SET)\n\n    prev_hooks: sys._asyncgen_hooks = attrs.field(init=False)\n\n    def install_hooks(self, runner: _run.Runner) -> None:\n        def firstiter(agen: AsyncGeneratorType[object, NoReturn]) -> None:\n            if hasattr(_run.GLOBAL_RUN_CONTEXT, \"task\"):\n                self.alive.add(agen)\n            else:\n                # An async generator first iterated outside of a Trio\n                # task doesn't belong to Trio. Probably we're in guest\n                # mode and the async generator belongs to our host.\n                # A strong set of ids is one of the only good places to\n                # remember this fact, at least until\n                # https://github.com/python/cpython/issues/85093 is implemented.\n                self.foreign.add(id(agen))\n                if self.prev_hooks.firstiter is not None:\n                    self.prev_hooks.firstiter(agen)\n\n        def finalize_in_trio_context(\n            agen: AsyncGeneratorType[object, NoReturn],\n            agen_name: str,\n        ) -> None:\n            try:\n                runner.spawn_system_task(\n                    self._finalize_one,\n                    agen,\n                    agen_name,\n                    name=f\"close asyncgen {agen_name} (abandoned)\",\n                )\n            except RuntimeError:\n                # There is a one-tick window where the system nursery\n                # is closed but the init task hasn't yet made\n                # self.asyncgens a strong set to disable GC. We seem to\n                # have hit it.\n                self.trailing_needs_finalize.add(agen)\n\n        @_core.enable_ki_protection\n        def finalizer(agen: AsyncGeneratorType[object, NoReturn]) -> None:\n            try:\n                self.foreign.remove(id(agen))\n            except KeyError:\n                is_ours = True\n            else:\n                is_ours = False\n\n            agen_name = name_asyncgen(agen)\n            if is_ours:\n                runner.entry_queue.run_sync_soon(\n                    finalize_in_trio_context,\n                    agen,\n                    agen_name,\n                )\n\n                # Do this last, because it might raise an exception\n                # depending on the user's warnings filter. (That\n                # exception will be printed to the terminal and\n                # ignored, since we're running in GC context.)\n                warnings.warn(\n                    f\"Async generator {agen_name!r} was garbage collected before it \"\n                    \"had been exhausted. Surround its use in 'async with \"\n                    \"aclosing(...):' to ensure that it gets cleaned up as soon as \"\n                    \"you're done using it.\",\n                    ResourceWarning,\n                    stacklevel=2,\n                    source=agen,\n                )\n            else:\n                # Not ours -> forward to the host loop's async generator finalizer\n                finalizer = self.prev_hooks.finalizer\n                if finalizer is not None:\n                    _call_without_ki_protection(finalizer, agen)\n                else:\n                    # Host has no finalizer.  Reimplement the default\n                    # Python behavior with no hooks installed: throw in\n                    # GeneratorExit, step once, raise RuntimeError if\n                    # it doesn't exit.\n                    closer = agen.aclose()\n                    try:\n                        # If the next thing is a yield, this will raise RuntimeError\n                        # which we allow to propagate\n                        _call_without_ki_protection(closer.send, None)\n                    except StopIteration:\n                        pass\n                    else:\n                        # If the next thing is an await, we get here. Give a nicer\n                        # error than the default \"async generator ignored GeneratorExit\"\n                        raise RuntimeError(\n                            f\"Non-Trio async generator {agen_name!r} awaited something \"\n                            \"during finalization; install a finalization hook to \"\n                            \"support this, or wrap it in 'async with aclosing(...):'\",\n                        )\n\n        self.prev_hooks = sys.get_asyncgen_hooks()\n        sys.set_asyncgen_hooks(firstiter=firstiter, finalizer=finalizer)  # type: ignore[arg-type]  # Finalizer doesn't use AsyncGeneratorType\n\n    async def finalize_remaining(self, runner: _run.Runner) -> None:\n        # This is called from init after shutting down the system nursery.\n        # The only tasks running at this point are init and\n        # the run_sync_soon task, and since the system nursery is closed,\n        # there's no way for user code to spawn more.\n        assert _core.current_task() is runner.init_task\n        assert len(runner.tasks) == 2\n\n        # To make async generator finalization easier to reason\n        # about, we'll shut down asyncgen garbage collection by turning\n        # the alive WeakSet into a regular set.\n        self.alive = set(self.alive)\n\n        # Process all pending run_sync_soon callbacks, in case one of\n        # them was an asyncgen finalizer that snuck in under the wire.\n        runner.entry_queue.run_sync_soon(runner.reschedule, runner.init_task)\n        await _core.wait_task_rescheduled(\n            lambda _: _core.Abort.FAILED,  # pragma: no cover\n        )\n        self.alive.update(self.trailing_needs_finalize)\n        self.trailing_needs_finalize.clear()\n\n        # None of the still-living tasks use async generators, so\n        # every async generator must be suspended at a yield point --\n        # there's no one to be doing the iteration. That's good,\n        # because aclose() only works on an asyncgen that's suspended\n        # at a yield point.  (If it's suspended at an event loop trap,\n        # because someone is in the middle of iterating it, then you\n        # get a RuntimeError on 3.8+, and a nasty surprise on earlier\n        # versions due to https://bugs.python.org/issue32526.)\n        #\n        # However, once we start aclose() of one async generator, it\n        # might start fetching the next value from another, thus\n        # preventing us from closing that other (at least until\n        # aclose() of the first one is complete).  This constraint\n        # effectively requires us to finalize the remaining asyncgens\n        # in arbitrary order, rather than doing all of them at the\n        # same time. On 3.8+ we could defer any generator with\n        # ag_running=True to a later batch, but that only catches\n        # the case where our aclose() starts after the user's\n        # asend()/etc. If our aclose() starts first, then the\n        # user's asend()/etc will raise RuntimeError, since they're\n        # probably not checking ag_running.\n        #\n        # It might be possible to allow some parallelized cleanup if\n        # we can determine that a certain set of asyncgens have no\n        # interdependencies, using gc.get_referents() and such.\n        # But just doing one at a time will typically work well enough\n        # (since each aclose() executes in a cancelled scope) and\n        # is much easier to reason about.\n\n        # It's possible that that cleanup code will itself create\n        # more async generators, so we iterate repeatedly until\n        # all are gone.\n        while self.alive:\n            batch = self.alive\n            self.alive = _ASYNC_GEN_SET()\n            for agen in batch:\n                await self._finalize_one(agen, name_asyncgen(agen))\n\n    def close(self) -> None:\n        sys.set_asyncgen_hooks(*self.prev_hooks)\n\n    async def _finalize_one(\n        self,\n        agen: AsyncGeneratorType[object, NoReturn],\n        name: object,\n    ) -> None:\n        try:\n            # This shield ensures that finalize_asyncgen never exits\n            # with an exception, not even a Cancelled. The inside\n            # is cancelled so there's no deadlock risk.\n            with _core.CancelScope(shield=True) as cancel_scope:\n                cancel_scope.cancel(\n                    reason=\"disallow async work when closing async generators during trio shutdown\"\n                )\n                await agen.aclose()\n        except BaseException:\n            ASYNCGEN_LOGGER.exception(\n                \"Exception ignored during finalization of async generator %r -- \"\n                \"surround your use of the generator in 'async with aclosing(...):' \"\n                \"to raise exceptions like this in the context where they're generated\",\n                name,\n            )\n"
  },
  {
    "path": "src/trio/_core/_concat_tb.py",
    "content": "from __future__ import annotations\n\nfrom types import TracebackType\n\n\n# this is used for collapsing single-exception ExceptionGroups when using\n# `strict_exception_groups=False`. Once that is retired this function can\n# be removed as well.\ndef concat_tb(\n    head: TracebackType | None,\n    tail: TracebackType | None,\n) -> TracebackType | None:\n    # We have to use an iterative algorithm here, because in the worst case\n    # this might be a RecursionError stack that is by definition too deep to\n    # process by recursion!\n    head_tbs = []\n    pointer = head\n    while pointer is not None:\n        head_tbs.append(pointer)\n        pointer = pointer.tb_next\n    current_head = tail\n    for head_tb in reversed(head_tbs):\n        current_head = TracebackType(\n            current_head, head_tb.tb_frame, head_tb.tb_lasti, head_tb.tb_lineno\n        )\n    return current_head\n"
  },
  {
    "path": "src/trio/_core/_entry_queue.py",
    "content": "from __future__ import annotations\n\nimport threading\nfrom collections import deque\nfrom collections.abc import Callable\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport attrs\n\nfrom .. import _core\nfrom .._util import NoPublicConstructor, final\nfrom ._wakeup_socketpair import WakeupSocketpair\n\nif TYPE_CHECKING:\n    from typing_extensions import TypeVarTuple, Unpack\n\n    PosArgsT = TypeVarTuple(\"PosArgsT\")\n\nFunction = Callable[..., object]  # type: ignore[explicit-any]\nJob = tuple[Function, tuple[object, ...]]\n\n\n@attrs.define\nclass EntryQueue:\n    # This used to use a queue.Queue. but that was broken, because Queues are\n    # implemented in Python, and not reentrant -- so it was thread-safe, but\n    # not signal-safe. deque is implemented in C, so each operation is atomic\n    # WRT threads (and this is guaranteed in the docs), AND each operation is\n    # atomic WRT signal delivery (signal handlers can run on either side, but\n    # not *during* a deque operation). dict makes similar guarantees - and\n    # it's even ordered!\n    queue: deque[Job] = attrs.Factory(deque)\n    idempotent_queue: dict[Job, None] = attrs.Factory(dict)\n\n    wakeup: WakeupSocketpair = attrs.Factory(WakeupSocketpair)\n    done: bool = False\n    # Must be a reentrant lock, because it's acquired from signal handlers.\n    # RLock is signal-safe as of cpython 3.2. NB that this does mean that the\n    # lock is effectively *disabled* when we enter from signal context. The\n    # way we use the lock this is OK though, because when\n    # run_sync_soon is called from a signal it's atomic WRT the\n    # main thread -- it just might happen at some inconvenient place. But if\n    # you look at the one place where the main thread holds the lock, it's\n    # just to make 1 assignment, so that's atomic WRT a signal anyway.\n    lock: threading.RLock = attrs.Factory(threading.RLock)\n\n    async def task(self) -> None:\n        assert _core.currently_ki_protected()\n        # RLock has two implementations: a signal-safe version in _thread, and\n        # and signal-UNsafe version in threading. We need the signal safe\n        # version. Python 3.2 and later should always use this anyway, but,\n        # since the symptoms if this goes wrong are just \"weird rare\n        # deadlocks\", then let's make a little check.\n        # See:\n        #     https://bugs.python.org/issue13697#msg237140\n        assert self.lock.__class__.__module__ == \"_thread\"\n\n        def run_cb(job: Job) -> None:\n            # We run this with KI protection enabled; it's the callback's\n            # job to disable it if it wants it disabled. Exceptions are\n            # treated like system task exceptions (i.e., converted into\n            # TrioInternalError and cause everything to shut down).\n            sync_fn, args = job\n            try:\n                sync_fn(*args)\n            except BaseException as exc:\n\n                async def kill_everything(  # noqa: RUF029  # await not used\n                    exc: BaseException,\n                ) -> NoReturn:\n                    raise exc\n\n                try:\n                    _core.spawn_system_task(kill_everything, exc)\n                except RuntimeError:\n                    # We're quite late in the shutdown process and the\n                    # system nursery is already closed.\n                    # TODO(2020-06): this is a gross hack and should\n                    # be fixed soon when we address #1607.\n                    parent_nursery = _core.current_task().parent_nursery\n                    if parent_nursery is None:\n                        raise AssertionError(\n                            \"Internal error: `parent_nursery` should never be `None`\",\n                        ) from exc  # pragma: no cover\n                    parent_nursery.start_soon(kill_everything, exc)\n\n        # This has to be carefully written to be safe in the face of new items\n        # being queued while we iterate, and to do a bounded amount of work on\n        # each pass:\n        def run_all_bounded() -> None:\n            for _ in range(len(self.queue)):\n                run_cb(self.queue.popleft())\n            for job in list(self.idempotent_queue):\n                del self.idempotent_queue[job]\n                run_cb(job)\n\n        try:\n            while True:\n                run_all_bounded()\n                if not self.queue and not self.idempotent_queue:\n                    await self.wakeup.wait_woken()\n                else:\n                    await _core.checkpoint()\n        except _core.Cancelled:\n            # Keep the work done with this lock held as minimal as possible,\n            # because it doesn't protect us against concurrent signal delivery\n            # (see the comment above). Notice that this code would still be\n            # correct if written like:\n            #   self.done = True\n            #   with self.lock:\n            #       pass\n            # because all we want is to force run_sync_soon\n            # to either be completely before or completely after the write to\n            # done. That's why we don't need the lock to protect\n            # against signal handlers.\n            with self.lock:\n                self.done = True\n            # No more jobs will be submitted, so just clear out any residual\n            # ones:\n            run_all_bounded()\n            assert not self.queue\n            assert not self.idempotent_queue\n\n    def close(self) -> None:\n        self.wakeup.close()\n\n    def size(self) -> int:\n        return len(self.queue) + len(self.idempotent_queue)\n\n    def run_sync_soon(\n        self,\n        sync_fn: Callable[[Unpack[PosArgsT]], object],\n        *args: Unpack[PosArgsT],\n        idempotent: bool = False,\n    ) -> None:\n        with self.lock:\n            if self.done:\n                raise _core.RunFinishedError(\"run() has exited\")\n            # We have to hold the lock all the way through here, because\n            # otherwise the main thread might exit *while* we're doing these\n            # calls, and then our queue item might not be processed, or the\n            # wakeup call might trigger an OSError b/c the IO manager has\n            # already been shut down.\n            if idempotent:\n                self.idempotent_queue[sync_fn, args] = None\n            else:\n                self.queue.append((sync_fn, args))\n            self.wakeup.wakeup_thread_and_signal_safe()\n\n\n@final\n@attrs.define(eq=False)\nclass TrioToken(metaclass=NoPublicConstructor):\n    \"\"\"An opaque object representing a single call to :func:`trio.run`.\n\n    It has no public constructor; instead, see :func:`current_trio_token`.\n\n    This object has two uses:\n\n    1. It lets you re-enter the Trio run loop from external threads or signal\n       handlers. This is the low-level primitive that :func:`trio.to_thread`\n       and `trio.from_thread` use to communicate with worker threads, that\n       `trio.open_signal_receiver` uses to receive notifications about\n       signals, and so forth.\n\n    2. Each call to :func:`trio.run` has exactly one associated\n       :class:`TrioToken` object, so you can use it to identify a particular\n       call.\n\n    \"\"\"\n\n    _reentry_queue: EntryQueue\n\n    def run_sync_soon(\n        self,\n        sync_fn: Callable[[Unpack[PosArgsT]], object],\n        *args: Unpack[PosArgsT],\n        idempotent: bool = False,\n    ) -> None:\n        \"\"\"Schedule a call to ``sync_fn(*args)`` to occur in the context of a\n        Trio task.\n\n        This is safe to call from the main thread, from other threads, and\n        from signal handlers. This is the fundamental primitive used to\n        re-enter the Trio run loop from outside of it.\n\n        The call will happen \"soon\", but there's no guarantee about exactly\n        when, and no mechanism provided for finding out when it's happened.\n        If you need this, you'll have to build your own.\n\n        The call is effectively run as part of a system task (see\n        :func:`~trio.lowlevel.spawn_system_task`). In particular this means\n        that:\n\n        * :exc:`KeyboardInterrupt` protection is *enabled* by default; if\n          you want ``sync_fn`` to be interruptible by control-C, then you\n          need to use :func:`~trio.lowlevel.disable_ki_protection`\n          explicitly.\n\n        * If ``sync_fn`` raises an exception, then it's converted into a\n          :exc:`~trio.TrioInternalError` and *all* tasks are cancelled. You\n          should be careful that ``sync_fn`` doesn't crash.\n\n        All calls with ``idempotent=False`` are processed in strict\n        first-in first-out order.\n\n        If ``idempotent=True``, then ``sync_fn`` and ``args`` must be\n        hashable, and Trio will make a best-effort attempt to discard any\n        call submission which is equal to an already-pending call. Trio\n        will process these in first-in first-out order.\n\n        Any ordering guarantees apply separately to ``idempotent=False``\n        and ``idempotent=True`` calls; there's no rule for how calls in the\n        different categories are ordered with respect to each other.\n\n        :raises trio.RunFinishedError:\n              if the associated call to :func:`trio.run`\n              has already exited. (Any call that *doesn't* raise this error\n              is guaranteed to be fully processed before :func:`trio.run`\n              exits.)\n\n        \"\"\"\n        self._reentry_queue.run_sync_soon(sync_fn, *args, idempotent=idempotent)\n"
  },
  {
    "path": "src/trio/_core/_exceptions.py",
    "content": "from __future__ import annotations\n\nfrom functools import partial\nfrom typing import TYPE_CHECKING, Literal, TypeAlias\n\nimport attrs\n\nfrom trio._util import NoPublicConstructor, final\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from typing_extensions import Self\n\nCancelReasonLiteral: TypeAlias = Literal[\n    \"KeyboardInterrupt\",\n    \"deadline\",\n    \"explicit\",\n    \"nursery\",\n    \"shutdown\",\n    \"unknown\",\n]\n\n\nclass TrioInternalError(Exception):\n    \"\"\"Raised by :func:`run` if we encounter a bug in Trio, or (possibly) a\n    misuse of one of the low-level :mod:`trio.lowlevel` APIs.\n\n    This should never happen! If you get this error, please file a bug.\n\n    Unfortunately, if you get this error it also means that all bets are off –\n    Trio doesn't know what is going on and its normal invariants may be void.\n    (For example, we might have \"lost track\" of a task. Or lost track of all\n    tasks.) Again, though, this shouldn't happen.\n\n    \"\"\"\n\n\nclass RunFinishedError(RuntimeError):\n    \"\"\"Raised by `trio.from_thread.run` and similar functions if the\n    corresponding call to :func:`trio.run` has already finished.\n\n    \"\"\"\n\n\nclass WouldBlock(Exception):\n    \"\"\"Raised by ``X_nowait`` functions if ``X`` would block.\"\"\"\n\n\n@final\n@attrs.define(eq=False, kw_only=True)\nclass Cancelled(BaseException, metaclass=NoPublicConstructor):\n    \"\"\"Raised by blocking calls if the surrounding scope has been cancelled.\n\n    You should let this exception propagate, to be caught by the relevant\n    cancel scope. To remind you of this, it inherits from :exc:`BaseException`\n    instead of :exc:`Exception`, just like :exc:`KeyboardInterrupt` and\n    :exc:`SystemExit` do. This means that if you write something like::\n\n       try:\n           ...\n       except Exception:\n           ...\n\n    then this *won't* catch a :exc:`Cancelled` exception.\n\n    You cannot raise :exc:`Cancelled` yourself. Attempting to do so\n    will produce a :exc:`TypeError`. Use :meth:`cancel_scope.cancel()\n    <trio.CancelScope.cancel>` instead.\n\n    .. note::\n\n       In the US it's also common to see this word spelled \"canceled\", with\n       only one \"l\". This is a `recent\n       <https://books.google.com/ngrams/graph?content=canceled%2Ccancelled&year_start=1800&year_end=2000&corpus=5&smoothing=3&direct_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0>`__\n       and `US-specific\n       <https://books.google.com/ngrams/graph?content=canceled%2Ccancelled&year_start=1800&year_end=2000&corpus=18&smoothing=3&share=&direct_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0>`__\n       innovation, and even in the US both forms are still commonly used. So\n       for consistency with the rest of the world and with \"cancellation\"\n       (which always has two \"l\"s), Trio uses the two \"l\" spelling\n       everywhere.\n\n    \"\"\"\n\n    source: CancelReasonLiteral = \"unknown\"\n    # repr(Task), so as to avoid gc troubles from holding a reference\n    source_task: str | None = None\n    reason: str | None = None\n\n    def __str__(self) -> str:\n        return (\n            f\"cancelled due to {self.source}\"\n            + (\"\" if self.reason is None else f\" with reason {self.reason!r}\")\n            + (\"\" if self.source_task is None else f\" from task {self.source_task}\")\n        )\n\n    def __reduce__(self) -> tuple[Callable[[], Cancelled], tuple[()]]:\n        # The `__reduce__` tuple does not support directly passing kwargs, and the\n        # kwargs are required so we can't use the third item for adding to __dict__,\n        # so we use partial.\n        return (\n            partial(\n                Cancelled._create,\n                source=self.source,\n                source_task=self.source_task,\n                reason=self.reason,\n            ),\n            (),\n        )\n\n    if TYPE_CHECKING:\n        # for type checking on internal code\n        @classmethod\n        def _create(\n            cls,\n            *,\n            source: CancelReasonLiteral = \"unknown\",\n            source_task: str | None = None,\n            reason: str | None = None,\n        ) -> Self: ...\n\n\nclass BusyResourceError(Exception):\n    \"\"\"Raised when a task attempts to use a resource that some other task is\n    already using, and this would lead to bugs and nonsense.\n\n    For example, if two tasks try to send data through the same socket at the\n    same time, Trio will raise :class:`BusyResourceError` instead of letting\n    the data get scrambled.\n\n    \"\"\"\n\n\nclass ClosedResourceError(Exception):\n    \"\"\"Raised when attempting to use a resource after it has been closed.\n\n    Note that \"closed\" here means that *your* code closed the resource,\n    generally by calling a method with a name like ``close`` or ``aclose``, or\n    by exiting a context manager. If a problem arises elsewhere – for example,\n    because of a network failure, or because a remote peer closed their end of\n    a connection – then that should be indicated by a different exception\n    class, like :exc:`BrokenResourceError` or an :exc:`OSError` subclass.\n\n    \"\"\"\n\n\nclass BrokenResourceError(Exception):\n    \"\"\"Raised when an attempt to use a resource fails due to external\n    circumstances.\n\n    For example, you might get this if you try to send data on a stream where\n    the remote side has already closed the connection.\n\n    You *don't* get this error if *you* closed the resource – in that case you\n    get :class:`ClosedResourceError`.\n\n    This exception's ``__cause__`` attribute will often contain more\n    information about the underlying error.\n\n    \"\"\"\n\n\nclass EndOfChannel(Exception):\n    \"\"\"Raised when trying to receive from a :class:`trio.abc.ReceiveChannel`\n    that has no more data to receive.\n\n    This is analogous to an \"end-of-file\" condition, but for channels.\n\n    \"\"\"\n"
  },
  {
    "path": "src/trio/_core/_generated_instrumentation.py",
    "content": "# ***********************************************************\n# ******* WARNING: AUTOGENERATED! ALL EDITS WILL BE LOST ******\n# *************************************************************\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom ._ki import enable_ki_protection\nfrom ._run import GLOBAL_RUN_CONTEXT\n\nif TYPE_CHECKING:\n    from ._instrumentation import Instrument\n\n__all__ = [\"add_instrument\", \"remove_instrument\"]\n\n\n@enable_ki_protection\ndef add_instrument(instrument: Instrument) -> None:\n    \"\"\"Start instrumenting the current run loop with the given instrument.\n\n    Args:\n      instrument (trio.abc.Instrument): The instrument to activate.\n\n    If ``instrument`` is already active, does nothing.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.instruments.add_instrument(instrument)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef remove_instrument(instrument: Instrument) -> None:\n    \"\"\"Stop instrumenting the current run loop with the given instrument.\n\n    Args:\n      instrument (trio.abc.Instrument): The instrument to de-activate.\n\n    Raises:\n      KeyError: if the instrument is not currently active. This could\n          occur either because you never added it, or because you added it\n          and then it raised an unhandled exception and was automatically\n          deactivated.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.instruments.remove_instrument(instrument)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n"
  },
  {
    "path": "src/trio/_core/_generated_io_epoll.py",
    "content": "# ***********************************************************\n# ******* WARNING: AUTOGENERATED! ALL EDITS WILL BE LOST ******\n# *************************************************************\nfrom __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING\n\nfrom ._ki import enable_ki_protection\nfrom ._run import GLOBAL_RUN_CONTEXT\n\nif TYPE_CHECKING:\n    from .._file_io import _HasFileNo\n\nassert not TYPE_CHECKING or sys.platform == \"linux\"\n\n\n__all__ = [\"notify_closing\", \"wait_readable\", \"wait_writable\"]\n\n\n@enable_ki_protection\nasync def wait_readable(fd: int | _HasFileNo) -> None:\n    \"\"\"Block until the kernel reports that the given object is readable.\n\n    On Unix systems, ``fd`` must either be an integer file descriptor,\n    or else an object with a ``.fileno()`` method which returns an\n    integer file descriptor. Any kind of file descriptor can be passed,\n    though the exact semantics will depend on your kernel. For example,\n    this probably won't do anything useful for on-disk files.\n\n    On Windows systems, ``fd`` must either be an integer ``SOCKET``\n    handle, or else an object with a ``.fileno()`` method which returns\n    an integer ``SOCKET`` handle. File descriptors aren't supported,\n    and neither are handles that refer to anything besides a\n    ``SOCKET``.\n\n    :raises trio.BusyResourceError:\n        if another task is already waiting for the given socket to\n        become readable.\n    :raises trio.ClosedResourceError:\n        if another task calls :func:`notify_closing` while this\n        function is still working.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_writable(fd: int | _HasFileNo) -> None:\n    \"\"\"Block until the kernel reports that the given object is writable.\n\n    See `wait_readable` for the definition of ``fd``.\n\n    :raises trio.BusyResourceError:\n        if another task is already waiting for the given socket to\n        become writable.\n    :raises trio.ClosedResourceError:\n        if another task calls :func:`notify_closing` while this\n        function is still working.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef notify_closing(fd: int | _HasFileNo) -> None:\n    \"\"\"Notify waiters of the given object that it will be closed.\n\n    Call this before closing a file descriptor (on Unix) or socket (on\n    Windows). This will cause any `wait_readable` or `wait_writable`\n    calls on the given object to immediately wake up and raise\n    `~trio.ClosedResourceError`.\n\n    This doesn't actually close the object – you still have to do that\n    yourself afterwards. Also, you want to be careful to make sure no\n    new tasks start waiting on the object in between when you call this\n    and when it's actually closed. So to close something properly, you\n    usually want to do these steps in order:\n\n    1. Explicitly mark the object as closed, so that any new attempts\n       to use it will abort before they start.\n    2. Call `notify_closing` to wake up any already-existing users.\n    3. Actually close the object.\n\n    It's also possible to do them in a different order if that's more\n    convenient, *but only if* you make sure not to have any checkpoints in\n    between the steps. This way they all happen in a single atomic\n    step, so other tasks won't be able to tell what order they happened\n    in anyway.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n"
  },
  {
    "path": "src/trio/_core/_generated_io_kqueue.py",
    "content": "# ***********************************************************\n# ******* WARNING: AUTOGENERATED! ALL EDITS WILL BE LOST ******\n# *************************************************************\nfrom __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING\n\nfrom ._ki import enable_ki_protection\nfrom ._run import GLOBAL_RUN_CONTEXT\n\nif TYPE_CHECKING:\n    import select\n    from collections.abc import Callable\n    from contextlib import AbstractContextManager\n\n    from .. import _core\n    from .._file_io import _HasFileNo\n    from ._traps import Abort, RaiseCancelT\n\nassert not TYPE_CHECKING or sys.platform == \"darwin\"\n\n\n__all__ = [\n    \"current_kqueue\",\n    \"monitor_kevent\",\n    \"notify_closing\",\n    \"wait_kevent\",\n    \"wait_readable\",\n    \"wait_writable\",\n]\n\n\n@enable_ki_protection\ndef current_kqueue() -> select.kqueue:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.current_kqueue()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef monitor_kevent(\n    ident: int, filter: int\n) -> AbstractContextManager[_core.UnboundedQueue[select.kevent]]:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_kevent(ident, filter)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_kevent(\n    ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort]\n) -> Abort:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_kevent(\n            ident, filter, abort_func\n        )\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_readable(fd: int | _HasFileNo) -> None:\n    \"\"\"Block until the kernel reports that the given object is readable.\n\n    On Unix systems, ``fd`` must either be an integer file descriptor,\n    or else an object with a ``.fileno()`` method which returns an\n    integer file descriptor. Any kind of file descriptor can be passed,\n    though the exact semantics will depend on your kernel. For example,\n    this probably won't do anything useful for on-disk files.\n\n    On Windows systems, ``fd`` must either be an integer ``SOCKET``\n    handle, or else an object with a ``.fileno()`` method which returns\n    an integer ``SOCKET`` handle. File descriptors aren't supported,\n    and neither are handles that refer to anything besides a\n    ``SOCKET``.\n\n    :raises trio.BusyResourceError:\n        if another task is already waiting for the given socket to\n        become readable.\n    :raises trio.ClosedResourceError:\n        if another task calls :func:`notify_closing` while this\n        function is still working.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_writable(fd: int | _HasFileNo) -> None:\n    \"\"\"Block until the kernel reports that the given object is writable.\n\n    See `wait_readable` for the definition of ``fd``.\n\n    :raises trio.BusyResourceError:\n        if another task is already waiting for the given socket to\n        become writable.\n    :raises trio.ClosedResourceError:\n        if another task calls :func:`notify_closing` while this\n        function is still working.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef notify_closing(fd: int | _HasFileNo) -> None:\n    \"\"\"Notify waiters of the given object that it will be closed.\n\n    Call this before closing a file descriptor (on Unix) or socket (on\n    Windows). This will cause any `wait_readable` or `wait_writable`\n    calls on the given object to immediately wake up and raise\n    `~trio.ClosedResourceError`.\n\n    This doesn't actually close the object – you still have to do that\n    yourself afterwards. Also, you want to be careful to make sure no\n    new tasks start waiting on the object in between when you call this\n    and when it's actually closed. So to close something properly, you\n    usually want to do these steps in order:\n\n    1. Explicitly mark the object as closed, so that any new attempts\n       to use it will abort before they start.\n    2. Call `notify_closing` to wake up any already-existing users.\n    3. Actually close the object.\n\n    It's also possible to do them in a different order if that's more\n    convenient, *but only if* you make sure not to have any checkpoints in\n    between the steps. This way they all happen in a single atomic\n    step, so other tasks won't be able to tell what order they happened\n    in anyway.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n"
  },
  {
    "path": "src/trio/_core/_generated_io_windows.py",
    "content": "# ***********************************************************\n# ******* WARNING: AUTOGENERATED! ALL EDITS WILL BE LOST ******\n# *************************************************************\nfrom __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING\n\nfrom ._ki import enable_ki_protection\nfrom ._run import GLOBAL_RUN_CONTEXT\n\nif TYPE_CHECKING:\n    from contextlib import AbstractContextManager\n\n    from typing_extensions import Buffer\n\n    from .._file_io import _HasFileNo\n    from ._unbounded_queue import UnboundedQueue\n    from ._windows_cffi import CData, Handle\n\nassert not TYPE_CHECKING or sys.platform == \"win32\"\n\n\n__all__ = [\n    \"current_iocp\",\n    \"monitor_completion_key\",\n    \"notify_closing\",\n    \"readinto_overlapped\",\n    \"register_with_iocp\",\n    \"wait_overlapped\",\n    \"wait_readable\",\n    \"wait_writable\",\n    \"write_overlapped\",\n]\n\n\n@enable_ki_protection\nasync def wait_readable(sock: _HasFileNo | int) -> None:\n    \"\"\"Block until the kernel reports that the given object is readable.\n\n    On Unix systems, ``sock`` must either be an integer file descriptor,\n    or else an object with a ``.fileno()`` method which returns an\n    integer file descriptor. Any kind of file descriptor can be passed,\n    though the exact semantics will depend on your kernel. For example,\n    this probably won't do anything useful for on-disk files.\n\n    On Windows systems, ``sock`` must either be an integer ``SOCKET``\n    handle, or else an object with a ``.fileno()`` method which returns\n    an integer ``SOCKET`` handle. File descriptors aren't supported,\n    and neither are handles that refer to anything besides a\n    ``SOCKET``.\n\n    :raises trio.BusyResourceError:\n        if another task is already waiting for the given socket to\n        become readable.\n    :raises trio.ClosedResourceError:\n        if another task calls :func:`notify_closing` while this\n        function is still working.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(sock)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_writable(sock: _HasFileNo | int) -> None:\n    \"\"\"Block until the kernel reports that the given object is writable.\n\n    See `wait_readable` for the definition of ``sock``.\n\n    :raises trio.BusyResourceError:\n        if another task is already waiting for the given socket to\n        become writable.\n    :raises trio.ClosedResourceError:\n        if another task calls :func:`notify_closing` while this\n        function is still working.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(sock)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef notify_closing(handle: Handle | int | _HasFileNo) -> None:\n    \"\"\"Notify waiters of the given object that it will be closed.\n\n    Call this before closing a file descriptor (on Unix) or socket (on\n    Windows). This will cause any `wait_readable` or `wait_writable`\n    calls on the given object to immediately wake up and raise\n    `~trio.ClosedResourceError`.\n\n    This doesn't actually close the object – you still have to do that\n    yourself afterwards. Also, you want to be careful to make sure no\n    new tasks start waiting on the object in between when you call this\n    and when it's actually closed. So to close something properly, you\n    usually want to do these steps in order:\n\n    1. Explicitly mark the object as closed, so that any new attempts\n       to use it will abort before they start.\n    2. Call `notify_closing` to wake up any already-existing users.\n    3. Actually close the object.\n\n    It's also possible to do them in a different order if that's more\n    convenient, *but only if* you make sure not to have any checkpoints in\n    between the steps. This way they all happen in a single atomic\n    step, so other tasks won't be able to tell what order they happened\n    in anyway.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(handle)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef register_with_iocp(handle: int | CData) -> None:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__ and `#52\n    <https://github.com/python-trio/trio/issues/52>`__.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.register_with_iocp(handle)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_overlapped(handle_: int | CData, lpOverlapped: CData | int) -> object:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__ and `#52\n    <https://github.com/python-trio/trio/issues/52>`__.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_overlapped(\n            handle_, lpOverlapped\n        )\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def write_overlapped(\n    handle: int | CData, data: Buffer, file_offset: int = 0\n) -> int:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__ and `#52\n    <https://github.com/python-trio/trio/issues/52>`__.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.write_overlapped(\n            handle, data, file_offset\n        )\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def readinto_overlapped(\n    handle: int | CData, buffer: Buffer, file_offset: int = 0\n) -> int:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__ and `#52\n    <https://github.com/python-trio/trio/issues/52>`__.\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.io_manager.readinto_overlapped(\n            handle, buffer, file_offset\n        )\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef current_iocp() -> int:\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__ and `#52\n    <https://github.com/python-trio/trio/issues/52>`__.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.current_iocp()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef monitor_completion_key() -> (\n    AbstractContextManager[tuple[int, UnboundedQueue[object]]]\n):\n    \"\"\"TODO: these are implemented, but are currently more of a sketch than\n    anything real. See `#26\n    <https://github.com/python-trio/trio/issues/26>`__ and `#52\n    <https://github.com/python-trio/trio/issues/52>`__.\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_completion_key()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n"
  },
  {
    "path": "src/trio/_core/_generated_run.py",
    "content": "# ***********************************************************\n# ******* WARNING: AUTOGENERATED! ALL EDITS WILL BE LOST ******\n# *************************************************************\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom ._ki import enable_ki_protection\nfrom ._run import _NO_SEND, GLOBAL_RUN_CONTEXT, RunStatistics, Task\n\nif TYPE_CHECKING:\n    import contextvars\n    from collections.abc import Awaitable, Callable\n\n    import outcome\n    from typing_extensions import Unpack\n\n    from .._abc import Clock\n    from ._entry_queue import TrioToken\n    from ._run import PosArgT\n\n\n__all__ = [\n    \"current_clock\",\n    \"current_root_task\",\n    \"current_statistics\",\n    \"current_time\",\n    \"current_trio_token\",\n    \"reschedule\",\n    \"spawn_system_task\",\n    \"wait_all_tasks_blocked\",\n]\n\n\n@enable_ki_protection\ndef current_statistics() -> RunStatistics:\n    \"\"\"Returns ``RunStatistics``, which contains run-loop-level debugging information.\n\n    Currently, the following fields are defined:\n\n    * ``tasks_living`` (int): The number of tasks that have been spawned\n      and not yet exited.\n    * ``tasks_runnable`` (int): The number of tasks that are currently\n      queued on the run queue (as opposed to blocked waiting for something\n      to happen).\n    * ``seconds_to_next_deadline`` (float): The time until the next\n      pending cancel scope deadline. May be negative if the deadline has\n      expired but we haven't yet processed cancellations. May be\n      :data:`~math.inf` if there are no pending deadlines.\n    * ``run_sync_soon_queue_size`` (int): The number of\n      unprocessed callbacks queued via\n      :meth:`trio.lowlevel.TrioToken.run_sync_soon`.\n    * ``io_statistics`` (object): Some statistics from Trio's I/O\n      backend. This always has an attribute ``backend`` which is a string\n      naming which operating-system-specific I/O backend is in use; the\n      other attributes vary between backends.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.current_statistics()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef current_time() -> float:\n    \"\"\"Returns the current time according to Trio's internal clock.\n\n    Returns:\n        float: The current time.\n\n    Raises:\n        RuntimeError: if not inside a call to :func:`trio.run`.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.current_time()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef current_clock() -> Clock:\n    \"\"\"Returns the current :class:`~trio.abc.Clock`.\"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.current_clock()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef current_root_task() -> Task | None:\n    \"\"\"Returns the current root :class:`Task`.\n\n    This is the task that is the ultimate parent of all other tasks.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.current_root_task()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef reschedule(task: Task, next_send: outcome.Outcome[object] = _NO_SEND) -> None:\n    \"\"\"Reschedule the given task with the given\n    :class:`outcome.Outcome`.\n\n    See :func:`wait_task_rescheduled` for the gory details.\n\n    There must be exactly one call to :func:`reschedule` for every call to\n    :func:`wait_task_rescheduled`. (And when counting, keep in mind that\n    returning :data:`Abort.SUCCEEDED` from an abort callback is equivalent\n    to calling :func:`reschedule` once.)\n\n    Args:\n      task (trio.lowlevel.Task): the task to be rescheduled. Must be blocked\n          in a call to :func:`wait_task_rescheduled`.\n      next_send (outcome.Outcome): the value (or error) to return (or\n          raise) from :func:`wait_task_rescheduled`.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.reschedule(task, next_send)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef spawn_system_task(\n    async_fn: Callable[[Unpack[PosArgT]], Awaitable[object]],\n    *args: Unpack[PosArgT],\n    name: object = None,\n    context: contextvars.Context | None = None,\n) -> Task:\n    \"\"\"Spawn a \"system\" task.\n\n    System tasks have a few differences from regular tasks:\n\n    * They don't need an explicit nursery; instead they go into the\n      internal \"system nursery\".\n\n    * If a system task raises an exception, then it's converted into a\n      :exc:`~trio.TrioInternalError` and *all* tasks are cancelled. If you\n      write a system task, you should be careful to make sure it doesn't\n      crash.\n\n    * System tasks are automatically cancelled when the main task exits.\n\n    * By default, system tasks have :exc:`KeyboardInterrupt` protection\n      *enabled*. If you want your task to be interruptible by control-C,\n      then you need to use :func:`disable_ki_protection` explicitly (and\n      come up with some plan for what to do with a\n      :exc:`KeyboardInterrupt`, given that system tasks aren't allowed to\n      raise exceptions).\n\n    * System tasks do not inherit context variables from their creator.\n\n    Towards the end of a call to :meth:`trio.run`, after the main\n    task and all system tasks have exited, the system nursery\n    becomes closed. At this point, new calls to\n    :func:`spawn_system_task` will raise ``RuntimeError(\"Nursery\n    is closed to new arrivals\")`` instead of creating a system\n    task. It's possible to encounter this state either in\n    a ``finally`` block in an async generator, or in a callback\n    passed to :meth:`TrioToken.run_sync_soon` at the right moment.\n\n    Args:\n      async_fn: An async callable.\n      args: Positional arguments for ``async_fn``. If you want to pass\n          keyword arguments, use :func:`functools.partial`.\n      name: The name for this task. Only used for debugging/introspection\n          (e.g. ``repr(task_obj)``). If this isn't a string,\n          :func:`spawn_system_task` will try to make it one. A common use\n          case is if you're wrapping a function before spawning a new\n          task, you might pass the original function as the ``name=`` to\n          make debugging easier.\n      context: An optional ``contextvars.Context`` object with context variables\n          to use for this task. You would normally get a copy of the current\n          context with ``context = contextvars.copy_context()`` and then you would\n          pass that ``context`` object here.\n\n    Returns:\n      Task: the newly spawned task\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.spawn_system_task(\n            async_fn, *args, name=name, context=context\n        )\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\ndef current_trio_token() -> TrioToken:\n    \"\"\"Retrieve the :class:`TrioToken` for the current call to\n    :func:`trio.run`.\n\n    \"\"\"\n    try:\n        return GLOBAL_RUN_CONTEXT.runner.current_trio_token()\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\n@enable_ki_protection\nasync def wait_all_tasks_blocked(cushion: float = 0.0) -> None:\n    \"\"\"Block until there are no runnable tasks.\n\n    This is useful in testing code when you want to give other tasks a\n    chance to \"settle down\". The calling task is blocked, and doesn't wake\n    up until all other tasks are also blocked for at least ``cushion``\n    seconds. (Setting a non-zero ``cushion`` is intended to handle cases\n    like two tasks talking to each other over a local socket, where we\n    want to ignore the potential brief moment between a send and receive\n    when all tasks are blocked.)\n\n    Note that ``cushion`` is measured in *real* time, not the Trio clock\n    time.\n\n    If there are multiple tasks blocked in :func:`wait_all_tasks_blocked`,\n    then the one with the shortest ``cushion`` is the one woken (and\n    this task becoming unblocked resets the timers for the remaining\n    tasks). If there are multiple tasks that have exactly the same\n    ``cushion``, then all are woken.\n\n    You should also consider :class:`trio.testing.Sequencer`, which\n    provides a more explicit way to control execution ordering within a\n    test, and will often produce more readable tests.\n\n    Example:\n      Here's an example of one way to test that Trio's locks are fair: we\n      take the lock in the parent, start a child, wait for the child to be\n      blocked waiting for the lock (!), and then check that we can't\n      release and immediately re-acquire the lock::\n\n         async def lock_taker(lock):\n             await lock.acquire()\n             lock.release()\n\n         async def test_lock_fairness():\n             lock = trio.Lock()\n             await lock.acquire()\n             async with trio.open_nursery() as nursery:\n                 nursery.start_soon(lock_taker, lock)\n                 # child hasn't run yet, we have the lock\n                 assert lock.locked()\n                 assert lock._owner is trio.lowlevel.current_task()\n                 await trio.testing.wait_all_tasks_blocked()\n                 # now the child has run and is blocked on lock.acquire(), we\n                 # still have the lock\n                 assert lock.locked()\n                 assert lock._owner is trio.lowlevel.current_task()\n                 lock.release()\n                 try:\n                     # The child has a prior claim, so we can't have it\n                     lock.acquire_nowait()\n                 except trio.WouldBlock:\n                     assert lock._owner is not trio.lowlevel.current_task()\n                     print(\"PASS\")\n                 else:\n                     print(\"FAIL\")\n\n    \"\"\"\n    try:\n        return await GLOBAL_RUN_CONTEXT.runner.wait_all_tasks_blocked(cushion)\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n"
  },
  {
    "path": "src/trio/_core/_generated_windows_ffi.py",
    "content": "# auto-generated file\nimport _cffi_backend\n\nffi = _cffi_backend.FFI('trio._core._generated_windows_ffi',\n    _version = 0x2601,\n    _types = b'\\x00\\x00\\x39\\x0D\\x00\\x00\\x1A\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x72\\x03\\x00\\x00\\x0A\\x01\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x02\\x03\\x00\\x00\\x6D\\x03\\x00\\x00\\x03\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x04\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x07\\x11\\x00\\x00\\x08\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x07\\x11\\x00\\x00\\x08\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x72\\x03\\x00\\x00\\x0A\\x01\\x00\\x00\\x07\\x11\\x00\\x00\\x08\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x02\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x08\\x11\\x00\\x00\\x02\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x6E\\x03\\x00\\x00\\x0A\\x01\\x00\\x00\\x07\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x07\\x01\\x00\\x00\\x02\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x07\\x01\\x00\\x00\\x02\\x0F\\x00\\x00\\x39\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x1A\\x01\\x00\\x00\\x08\\x11\\x00\\x00\\x02\\x0F\\x00\\x00\\x02\\x0D\\x00\\x00\\x08\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x02\\x0D\\x00\\x00\\x0A\\x01\\x00\\x00\\x03\\x03\\x00\\x00\\x07\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x02\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x03\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x07\\x01\\x00\\x00\\x07\\x01\\x00\\x00\\x03\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x03\\x0D\\x00\\x00\\x73\\x03\\x00\\x00\\x0A\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x03\\x11\\x00\\x00\\x0A\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x03\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x03\\x0D\\x00\\x00\\x03\\x11\\x00\\x00\\x03\\x11\\x00\\x00\\x1A\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x02\\x0F\\x00\\x00\\x68\\x03\\x00\\x00\\x02\\x09\\x00\\x00\\x68\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x6C\\x03\\x00\\x00\\x03\\x09\\x00\\x00\\x04\\x09\\x00\\x00\\x05\\x09\\x00\\x00\\x17\\x01\\x00\\x00\\x01\\x09\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x01\\x00\\x00\\x10\\x01',\n    _globals = (b'\\x00\\x00\\x2F\\x23CancelIoEx',0,b'\\x00\\x00\\x2C\\x23CloseHandle',0,b'\\x00\\x00\\x52\\x23CreateEventA',0,b'\\x00\\x00\\x58\\x23CreateFileW',0,b'\\x00\\x00\\x61\\x23CreateIoCompletionPort',0,b'\\x00\\x00\\x12\\x23DeviceIoControl',0,b'\\x00\\x00\\x33\\x23GetQueuedCompletionStatusEx',0,b'\\x00\\x00\\x3F\\x23PostQueuedCompletionStatus',0,b'\\x00\\x00\\x1C\\x23ReadFile',0,b'\\x00\\x00\\x0B\\x23ResetEvent',0,b'\\x00\\x00\\x45\\x23RtlNtStatusToDosError',0,b'\\x00\\x00\\x3B\\x23SetConsoleCtrlHandler',0,b'\\x00\\x00\\x0B\\x23SetEvent',0,b'\\x00\\x00\\x0E\\x23SetFileCompletionNotificationModes',0,b'\\x00\\x00\\x2A\\x23WSAGetLastError',0,b'\\x00\\x00\\x00\\x23WSAIoctl',0,b'\\x00\\x00\\x48\\x23WaitForMultipleObjects',0,b'\\x00\\x00\\x4E\\x23WaitForSingleObject',0,b'\\x00\\x00\\x23\\x23WriteFile',0),\n    _struct_unions = ((b'\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x03$1',b'\\x00\\x00\\x70\\x11DUMMYSTRUCTNAME',b'\\x00\\x00\\x03\\x11Pointer'),(b'\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x02$2',b'\\x00\\x00\\x02\\x11Offset',b'\\x00\\x00\\x02\\x11OffsetHigh'),(b'\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x02_AFD_POLL_HANDLE_INFO',b'\\x00\\x00\\x03\\x11Handle',b'\\x00\\x00\\x02\\x11Events',b'\\x00\\x00\\x46\\x11Status'),(b'\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x02_AFD_POLL_INFO',b'\\x00\\x00\\x6F\\x11Timeout',b'\\x00\\x00\\x02\\x11NumberOfHandles',b'\\x00\\x00\\x02\\x11Exclusive',b'\\x00\\x00\\x69\\x11Handles'),(b'\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x02_OVERLAPPED',b'\\x00\\x00\\x01\\x11Internal',b'\\x00\\x00\\x01\\x11InternalHigh',b'\\x00\\x00\\x71\\x11DUMMYUNIONNAME',b'\\x00\\x00\\x03\\x11hEvent'),(b'\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x02_OVERLAPPED_ENTRY',b'\\x00\\x00\\x01\\x11lpCompletionKey',b'\\x00\\x00\\x08\\x11lpOverlapped',b'\\x00\\x00\\x01\\x11Internal',b'\\x00\\x00\\x02\\x11dwNumberOfBytesTransferred')),\n    _typenames = (b'\\x00\\x00\\x00\\x68AFD_POLL_HANDLE_INFO',b'\\x00\\x00\\x00\\x6CAFD_POLL_INFO',b'\\x00\\x00\\x00\\x39BOOL',b'\\x00\\x00\\x00\\x10BOOLEAN',b'\\x00\\x00\\x00\\x10BYTE',b'\\x00\\x00\\x00\\x02DWORD',b'\\x00\\x00\\x00\\x03HANDLE',b'\\x00\\x00\\x00\\x6FLARGE_INTEGER',b'\\x00\\x00\\x00\\x03LPCSTR',b'\\x00\\x00\\x00\\x25LPCVOID',b'\\x00\\x00\\x00\\x59LPCWSTR',b'\\x00\\x00\\x00\\x07LPDWORD',b'\\x00\\x00\\x00\\x08LPOVERLAPPED',b'\\x00\\x00\\x00\\x35LPOVERLAPPED_ENTRY',b'\\x00\\x00\\x00\\x03LPSECURITY_ATTRIBUTES',b'\\x00\\x00\\x00\\x03LPVOID',b'\\x00\\x00\\x00\\x08LPWSAOVERLAPPED',b'\\x00\\x00\\x00\\x46NTSTATUS',b'\\x00\\x00\\x00\\x6DOVERLAPPED',b'\\x00\\x00\\x00\\x6EOVERLAPPED_ENTRY',b'\\x00\\x00\\x00\\x67PAFD_POLL_HANDLE_INFO',b'\\x00\\x00\\x00\\x6BPAFD_POLL_INFO',b'\\x00\\x00\\x00\\x07PULONG',b'\\x00\\x00\\x00\\x03PVOID',b'\\x00\\x00\\x00\\x01SOCKET',b'\\x00\\x00\\x00\\x10UCHAR',b'\\x00\\x00\\x00\\x01UINT_PTR',b'\\x00\\x00\\x00\\x02ULONG',b'\\x00\\x00\\x00\\x01ULONG_PTR',b'\\x00\\x00\\x00\\x6DWSAOVERLAPPED',b'\\x00\\x00\\x00\\x02u_long'),\n)\n"
  },
  {
    "path": "src/trio/_core/_instrumentation.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport types\nfrom collections import UserDict\nfrom typing import TYPE_CHECKING, TypeVar\n\nfrom .._abc import Instrument\n\n# Used to log exceptions in instruments\nINSTRUMENT_LOGGER = logging.getLogger(\"trio.abc.Instrument\")\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n    T = TypeVar(\"T\")\n\n\n# Decorator to mark methods public. This does nothing by itself, but\n# trio/_tools/gen_exports.py looks for it.\ndef _public(fn: T) -> T:\n    return fn\n\n\nclass Instruments(UserDict[str, dict[Instrument, None]]):\n    \"\"\"A collection of `trio.abc.Instrument` organized by hook.\n\n    Instrumentation calls are rather expensive, and we don't want a\n    rarely-used instrument (like before_run()) to slow down hot\n    operations (like before_task_step()). Thus, we cache the set of\n    instruments to be called for each hook, and skip the instrumentation\n    call if there's nothing currently installed for that hook.\n    \"\"\"\n\n    __slots__ = ()\n\n    def __init__(self, incoming: Sequence[Instrument]) -> None:\n        super().__init__({\"_all\": {}})\n        for instrument in incoming:\n            self.add_instrument(instrument)\n\n    @_public\n    def add_instrument(self, instrument: Instrument) -> None:\n        \"\"\"Start instrumenting the current run loop with the given instrument.\n\n        Args:\n          instrument (trio.abc.Instrument): The instrument to activate.\n\n        If ``instrument`` is already active, does nothing.\n\n        \"\"\"\n        if instrument in self.data[\"_all\"]:\n            return\n        self.data[\"_all\"][instrument] = None\n        try:\n            for name in dir(instrument):\n                if name.startswith(\"_\"):\n                    continue\n                try:\n                    prototype = getattr(Instrument, name)\n                except AttributeError:\n                    continue\n                impl = getattr(instrument, name)\n                if isinstance(impl, types.MethodType) and impl.__func__ is prototype:\n                    # Inherited unchanged from _abc.Instrument\n                    continue\n                self.data.setdefault(name, {})[instrument] = None\n        except:\n            self.remove_instrument(instrument)\n            raise\n\n    @_public\n    def remove_instrument(self, instrument: Instrument) -> None:\n        \"\"\"Stop instrumenting the current run loop with the given instrument.\n\n        Args:\n          instrument (trio.abc.Instrument): The instrument to de-activate.\n\n        Raises:\n          KeyError: if the instrument is not currently active. This could\n              occur either because you never added it, or because you added it\n              and then it raised an unhandled exception and was automatically\n              deactivated.\n\n        \"\"\"\n        # If instrument isn't present, the KeyError propagates out\n        self.data[\"_all\"].pop(instrument)\n        for hookname, instruments in list(self.data.items()):\n            if instrument in instruments:\n                del instruments[instrument]\n                if not instruments:\n                    del self.data[hookname]\n\n    def call(\n        self,\n        hookname: str,\n        *args: object,\n    ) -> None:\n        \"\"\"Call hookname(*args) on each applicable instrument.\n\n        You must first check whether there are any instruments installed for\n        that hook, e.g.::\n\n            if \"before_task_step\" in instruments:\n                instruments.call(\"before_task_step\", task)\n        \"\"\"\n        for instrument in list(self.data[hookname]):\n            try:\n                getattr(instrument, hookname)(*args)\n            except BaseException:\n                self.remove_instrument(instrument)\n                INSTRUMENT_LOGGER.exception(\n                    \"Exception raised when calling %r on instrument %r. \"\n                    \"Instrument has been disabled.\",\n                    hookname,\n                    instrument,\n                )\n"
  },
  {
    "path": "src/trio/_core/_io_common.py",
    "content": "from __future__ import annotations\n\nimport copy\nfrom typing import TYPE_CHECKING\n\nimport outcome\n\nfrom .. import _core\n\nif TYPE_CHECKING:\n    from ._io_epoll import EpollWaiters\n    from ._io_windows import AFDWaiters\n\n\n# Utility function shared between _io_epoll and _io_windows\ndef wake_all(waiters: EpollWaiters | AFDWaiters, exc: BaseException) -> None:\n    try:\n        current_task = _core.current_task()\n    except RuntimeError:\n        current_task = None\n    raise_at_end = False\n    for attr_name in [\"read_task\", \"write_task\"]:\n        task = getattr(waiters, attr_name)\n        if task is not None:\n            if task is current_task:\n                raise_at_end = True\n            else:\n                _core.reschedule(task, outcome.Error(copy.copy(exc)))\n            setattr(waiters, attr_name, None)\n    if raise_at_end:\n        raise exc\n"
  },
  {
    "path": "src/trio/_core/_io_epoll.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport select\nimport sys\nfrom collections import defaultdict\nfrom typing import TYPE_CHECKING, Literal, TypeAlias\n\nimport attrs\n\nfrom .. import _core\nfrom ._io_common import wake_all\nfrom ._run import Task, _public\nfrom ._wakeup_socketpair import WakeupSocketpair\n\nif TYPE_CHECKING:\n    from .._core import Abort, RaiseCancelT\n    from .._file_io import _HasFileNo\n\n\n@attrs.define(eq=False)\nclass EpollWaiters:\n    read_task: Task | None = None\n    write_task: Task | None = None\n    current_flags: int = 0\n\n\nassert not TYPE_CHECKING or sys.platform == \"linux\"\n\n\nEventResult: TypeAlias = \"list[tuple[int, int]]\"\n\n\n@attrs.frozen(eq=False)\nclass _EpollStatistics:\n    tasks_waiting_read: int\n    tasks_waiting_write: int\n    backend: Literal[\"epoll\"] = attrs.field(init=False, default=\"epoll\")\n\n\n# Some facts about epoll\n# ----------------------\n#\n# Internally, an epoll object is sort of like a WeakKeyDictionary where the\n# keys are tuples of (fd number, file object). When you call epoll_ctl, you\n# pass in an fd; that gets converted to an (fd number, file object) tuple by\n# looking up the fd in the process's fd table at the time of the call. When an\n# event happens on the file object, epoll_wait drops the file object part, and\n# just returns the fd number in its event. So from the outside it looks like\n# it's keeping a table of fds, but really it's a bit more complicated. This\n# has some subtle consequences.\n#\n# In general, file objects inside the kernel are reference counted. Each entry\n# in a process's fd table holds a strong reference to the corresponding file\n# object, and most operations that use file objects take a temporary strong\n# reference while they're working. So when you call close() on an fd, that\n# might or might not cause the file object to be deallocated -- it depends on\n# whether there are any other references to that file object. Some common ways\n# this can happen:\n#\n# - after calling dup(), you have two fds in the same process referring to the\n#   same file object. Even if you close one fd (= remove that entry from the\n#   fd table), the file object will be kept alive by the other fd.\n# - when calling fork(), the child inherits a copy of the parent's fd table,\n#   so all the file objects get another reference. (But if the fork() is\n#   followed by exec(), then all of the child's fds that have the CLOEXEC flag\n#   set will be closed at that point.)\n# - most syscalls that work on fds take a strong reference to the underlying\n#   file object while they're using it. So there's one thread blocked in\n#   read(fd), and then another thread calls close() on the last fd referring\n#   to that object, the underlying file won't actually be closed until\n#   after read() returns.\n#\n# However, epoll does *not* take a reference to any of the file objects in its\n# interest set (that's what makes it similar to a WeakKeyDictionary). File\n# objects inside an epoll interest set will be deallocated if all *other*\n# references to them are closed. And when that happens, the epoll object will\n# automatically deregister that file object and stop reporting events on it.\n# So that's quite handy.\n#\n# But, what happens if we do this?\n#\n#   fd1 = open(...)\n#   epoll_ctl(EPOLL_CTL_ADD, fd1, ...)\n#   fd2 = dup(fd1)\n#   close(fd1)\n#\n# In this case, the dup() keeps the underlying file object alive, so it\n# remains registered in the epoll object's interest set, as the tuple (fd1,\n# file object). But, fd1 no longer refers to this file object! You might think\n# there was some magic to handle this, but unfortunately no; the consequences\n# are totally predictable from what I said above:\n#\n# If any events occur on the file object, then epoll will report them as\n# happening on fd1, even though that doesn't make sense.\n#\n# Perhaps we would like to deregister fd1 to stop getting nonsensical events.\n# But how? When we call epoll_ctl, we have to pass an fd number, which will\n# get expanded to an (fd number, file object) tuple. We can't pass fd1,\n# because when epoll_ctl tries to look it up, it won't find our file object.\n# And we can't pass fd2, because that will get expanded to (fd2, file object),\n# which is a different lookup key. In fact, it's *impossible* to de-register\n# this fd!\n#\n# We could even have fd1 get assigned to another file object, and then we can\n# have multiple keys registered simultaneously using the same fd number, like:\n# (fd1, file object 1), (fd1, file object 2). And if events happen on either\n# file object, then epoll will happily report that something happened to\n# \"fd1\".\n#\n# Now here's what makes this especially nasty: suppose the old file object\n# becomes, say, readable. That means that every time we call epoll_wait, it\n# will return immediately to tell us that \"fd1\" is readable. Normally, we\n# would handle this by de-registering fd1, waking up the corresponding call to\n# wait_readable, then the user will call read() or recv() or something, and\n# we're fine. But if this happens on a stale fd where we can't remove the\n# registration, then we might get stuck in a state where epoll_wait *always*\n# returns immediately, so our event loop becomes unable to sleep, and now our\n# program is burning 100% of the CPU doing nothing, with no way out.\n#\n#\n# What does this mean for Trio?\n# -----------------------------\n#\n# Since we don't control the user's code, we have no way to guarantee that we\n# don't get stuck with stale fd's in our epoll interest set. For example, a\n# user could call wait_readable(fd) in one task, and then while that's\n# running, they might close(fd) from another task. In this situation, they're\n# *supposed* to call notify_closing(fd) to let us know what's happening, so we\n# can interrupt the wait_readable() call and avoid getting into this mess. And\n# that's the only thing that can possibly work correctly in all cases. But\n# sometimes user code has bugs. So if this does happen, we'd like to degrade\n# gracefully, and survive without corrupting Trio's internal state or\n# otherwise causing the whole program to explode messily.\n#\n# Our solution: we always use EPOLLONESHOT. This way, we might get *one*\n# spurious event on a stale fd, but then epoll will automatically silence it\n# until we explicitly say that we want more events... and if we have a stale\n# fd, then we actually can't re-enable it! So we can't get stuck in an\n# infinite busy-loop. If there's a stale fd hanging around, then it might\n# cause a spurious `BusyResourceError`, or cause one wait_* call to return\n# before it should have... but in general, the wait_* functions are allowed to\n# have some spurious wakeups; the user code will just attempt the operation,\n# get EWOULDBLOCK, and call wait_* again. And the program as a whole will\n# survive, any exceptions will propagate, etc.\n#\n# As a bonus, EPOLLONESHOT also saves us having to explicitly deregister fds\n# on the normal wakeup path, so it's a bit more efficient in general.\n#\n# However, EPOLLONESHOT has a few trade-offs to consider:\n#\n# First, you can't combine EPOLLONESHOT with EPOLLEXCLUSIVE. This is a bit sad\n# in one somewhat rare case: if you have a multi-process server where a group\n# of processes all share the same listening socket, then EPOLLEXCLUSIVE can be\n# used to avoid \"thundering herd\" problems when a new connection comes in. But\n# this isn't too bad. It's not clear if EPOLLEXCLUSIVE even works for us\n# anyway:\n#\n#   https://stackoverflow.com/questions/41582560/how-does-epolls-epollexclusive-mode-interact-with-level-triggering\n#\n# And it's not clear that EPOLLEXCLUSIVE is a great approach either:\n#\n#   https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/\n#\n# And if we do need to support this, we could always add support through some\n# more-specialized API in the future. So this isn't a blocker to using\n# EPOLLONESHOT.\n#\n# Second, EPOLLONESHOT does not actually *deregister* the fd after delivering\n# an event (EPOLL_CTL_DEL). Instead, it keeps the fd registered, but\n# effectively does an EPOLL_CTL_MOD to set the fd's interest flags to\n# all-zeros. So we could still end up with an fd hanging around in the\n# interest set for a long time, even if we're not using it.\n#\n# Fortunately, this isn't a problem, because it's only a weak reference – if\n# we have a stale fd that's been silenced by EPOLLONESHOT, then it wastes a\n# tiny bit of kernel memory remembering this fd that can never be revived, but\n# when the underlying file object is eventually closed, that memory will be\n# reclaimed. So that's OK.\n#\n# The other issue is that when someone calls wait_*, using EPOLLONESHOT means\n# that if we have ever waited for this fd before, we have to use EPOLL_CTL_MOD\n# to re-enable it; but if it's a new fd, we have to use EPOLL_CTL_ADD. How do\n# we know which one to use? There's no reasonable way to track which fds are\n# currently registered -- remember, we're assuming the user might have gone\n# and rearranged their fds without telling us!\n#\n# Fortunately, this also has a simple solution: if we wait on a socket or\n# other fd once, then we'll probably wait on it lots of times. And the epoll\n# object itself knows which fds it already has registered. So when an fd comes\n# in, we optimistically assume that it's been waited on before, and try doing\n# EPOLL_CTL_MOD. And if that fails with an ENOENT error, then we try again\n# with EPOLL_CTL_ADD.\n#\n# So that's why this code is the way it is. And now you know more than you\n# wanted to about how epoll works.\n\n\n@attrs.define(eq=False)\nclass EpollIOManager:\n    # Using lambda here because otherwise crash on import with gevent monkey patching\n    # See https://github.com/python-trio/trio/issues/2848\n    _epoll: select.epoll = attrs.Factory(lambda: select.epoll())\n    # {fd: EpollWaiters}\n    _registered: defaultdict[int, EpollWaiters] = attrs.Factory(\n        lambda: defaultdict(EpollWaiters),\n    )\n    _force_wakeup: WakeupSocketpair = attrs.Factory(WakeupSocketpair)\n    _force_wakeup_fd: int | None = None\n\n    def __attrs_post_init__(self) -> None:\n        self._epoll.register(self._force_wakeup.wakeup_sock, select.EPOLLIN)\n        self._force_wakeup_fd = self._force_wakeup.wakeup_sock.fileno()\n\n    def statistics(self) -> _EpollStatistics:\n        tasks_waiting_read = 0\n        tasks_waiting_write = 0\n        for waiter in self._registered.values():\n            if waiter.read_task is not None:\n                tasks_waiting_read += 1\n            if waiter.write_task is not None:\n                tasks_waiting_write += 1\n        return _EpollStatistics(\n            tasks_waiting_read=tasks_waiting_read,\n            tasks_waiting_write=tasks_waiting_write,\n        )\n\n    def close(self) -> None:\n        self._epoll.close()\n        self._force_wakeup.close()\n\n    def force_wakeup(self) -> None:\n        self._force_wakeup.wakeup_thread_and_signal_safe()\n\n    # Return value must be False-y IFF the timeout expired, NOT if any I/O\n    # happened or force_wakeup was called. Otherwise it can be anything; gets\n    # passed straight through to process_events.\n    def get_events(self, timeout: float) -> EventResult:\n        # max_events must be > 0 or epoll gets cranky\n        # accessing self._registered from a thread looks dangerous, but it's\n        # OK because it doesn't matter if our value is a little bit off.\n        max_events = max(1, len(self._registered))\n        return self._epoll.poll(timeout, max_events)\n\n    def process_events(self, events: EventResult) -> None:\n        for fd, flags in events:\n            if fd == self._force_wakeup_fd:\n                self._force_wakeup.drain()\n                continue\n            waiters = self._registered[fd]\n            # EPOLLONESHOT always clears the flags when an event is delivered\n            waiters.current_flags = 0\n            # Clever hack stolen from selectors.EpollSelector: an event\n            # with EPOLLHUP or EPOLLERR flags wakes both readers and\n            # writers.\n            if flags & ~select.EPOLLIN and waiters.write_task is not None:\n                _core.reschedule(waiters.write_task)\n                waiters.write_task = None\n            if flags & ~select.EPOLLOUT and waiters.read_task is not None:\n                _core.reschedule(waiters.read_task)\n                waiters.read_task = None\n            self._update_registrations(fd)\n\n    def _update_registrations(self, fd: int) -> None:\n        waiters = self._registered[fd]\n        wanted_flags = 0\n        if waiters.read_task is not None:\n            wanted_flags |= select.EPOLLIN\n        if waiters.write_task is not None:\n            wanted_flags |= select.EPOLLOUT\n        if wanted_flags != waiters.current_flags:\n            try:\n                try:\n                    # First try EPOLL_CTL_MOD\n                    self._epoll.modify(fd, wanted_flags | select.EPOLLONESHOT)\n                except OSError:\n                    # If that fails, it might be a new fd; try EPOLL_CTL_ADD\n                    self._epoll.register(fd, wanted_flags | select.EPOLLONESHOT)\n                waiters.current_flags = wanted_flags\n            except OSError as exc:\n                # If everything fails, probably it's a bad fd, e.g. because\n                # the fd was closed behind our back. In this case we don't\n                # want to try to unregister the fd, because that will probably\n                # fail too. Just clear our state and wake everyone up.\n                del self._registered[fd]\n                # This could raise (in case we're calling this inside one of\n                # the to-be-woken tasks), so we have to do it last.\n                wake_all(waiters, exc)\n                return\n        if not wanted_flags:\n            del self._registered[fd]\n\n    async def _epoll_wait(self, fd: int | _HasFileNo, attr_name: str) -> None:\n        if not isinstance(fd, int):\n            fd = fd.fileno()\n        waiters = self._registered[fd]\n        if getattr(waiters, attr_name) is not None:\n            raise _core.BusyResourceError(\n                \"another task is already reading / writing this fd\",\n            )\n        setattr(waiters, attr_name, _core.current_task())\n        self._update_registrations(fd)\n\n        def abort(_: RaiseCancelT) -> Abort:\n            setattr(waiters, attr_name, None)\n            self._update_registrations(fd)\n            return _core.Abort.SUCCEEDED\n\n        await _core.wait_task_rescheduled(abort)\n\n    @_public\n    async def wait_readable(self, fd: int | _HasFileNo) -> None:\n        \"\"\"Block until the kernel reports that the given object is readable.\n\n        On Unix systems, ``fd`` must either be an integer file descriptor,\n        or else an object with a ``.fileno()`` method which returns an\n        integer file descriptor. Any kind of file descriptor can be passed,\n        though the exact semantics will depend on your kernel. For example,\n        this probably won't do anything useful for on-disk files.\n\n        On Windows systems, ``fd`` must either be an integer ``SOCKET``\n        handle, or else an object with a ``.fileno()`` method which returns\n        an integer ``SOCKET`` handle. File descriptors aren't supported,\n        and neither are handles that refer to anything besides a\n        ``SOCKET``.\n\n        :raises trio.BusyResourceError:\n            if another task is already waiting for the given socket to\n            become readable.\n        :raises trio.ClosedResourceError:\n            if another task calls :func:`notify_closing` while this\n            function is still working.\n        \"\"\"\n        await self._epoll_wait(fd, \"read_task\")\n\n    @_public\n    async def wait_writable(self, fd: int | _HasFileNo) -> None:\n        \"\"\"Block until the kernel reports that the given object is writable.\n\n        See `wait_readable` for the definition of ``fd``.\n\n        :raises trio.BusyResourceError:\n            if another task is already waiting for the given socket to\n            become writable.\n        :raises trio.ClosedResourceError:\n            if another task calls :func:`notify_closing` while this\n            function is still working.\n        \"\"\"\n        await self._epoll_wait(fd, \"write_task\")\n\n    @_public\n    def notify_closing(self, fd: int | _HasFileNo) -> None:\n        \"\"\"Notify waiters of the given object that it will be closed.\n\n        Call this before closing a file descriptor (on Unix) or socket (on\n        Windows). This will cause any `wait_readable` or `wait_writable`\n        calls on the given object to immediately wake up and raise\n        `~trio.ClosedResourceError`.\n\n        This doesn't actually close the object – you still have to do that\n        yourself afterwards. Also, you want to be careful to make sure no\n        new tasks start waiting on the object in between when you call this\n        and when it's actually closed. So to close something properly, you\n        usually want to do these steps in order:\n\n        1. Explicitly mark the object as closed, so that any new attempts\n           to use it will abort before they start.\n        2. Call `notify_closing` to wake up any already-existing users.\n        3. Actually close the object.\n\n        It's also possible to do them in a different order if that's more\n        convenient, *but only if* you make sure not to have any checkpoints in\n        between the steps. This way they all happen in a single atomic\n        step, so other tasks won't be able to tell what order they happened\n        in anyway.\n        \"\"\"\n        if not isinstance(fd, int):\n            fd = fd.fileno()\n        wake_all(\n            self._registered[fd],\n            _core.ClosedResourceError(\"another task closed this fd\"),\n        )\n        del self._registered[fd]\n        with contextlib.suppress(OSError, ValueError):\n            self._epoll.unregister(fd)\n"
  },
  {
    "path": "src/trio/_core/_io_kqueue.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport select\nimport sys\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING, Literal, TypeAlias\n\nimport attrs\nimport outcome\n\nfrom .. import _core\nfrom ._run import _public\nfrom ._wakeup_socketpair import WakeupSocketpair\n\nif TYPE_CHECKING:\n    from collections.abc import Callable, Iterator\n\n    from .._core import Abort, RaiseCancelT, Task, UnboundedQueue\n    from .._file_io import _HasFileNo\n\nassert not TYPE_CHECKING or (sys.platform != \"linux\" and sys.platform != \"win32\")\n\nEventResult: TypeAlias = \"list[select.kevent]\"\n\n\n@attrs.frozen(eq=False)\nclass _KqueueStatistics:\n    tasks_waiting: int\n    monitors: int\n    backend: Literal[\"kqueue\"] = attrs.field(init=False, default=\"kqueue\")\n\n\n@attrs.define(eq=False)\nclass KqueueIOManager:\n    _kqueue: select.kqueue = attrs.Factory(select.kqueue)\n    # {(ident, filter): Task or UnboundedQueue}\n    _registered: dict[tuple[int, int], Task | UnboundedQueue[select.kevent]] = (\n        attrs.Factory(dict)\n    )\n    _force_wakeup: WakeupSocketpair = attrs.Factory(WakeupSocketpair)\n    _force_wakeup_fd: int | None = None\n\n    def __attrs_post_init__(self) -> None:\n        force_wakeup_event = select.kevent(\n            self._force_wakeup.wakeup_sock,\n            select.KQ_FILTER_READ,\n            select.KQ_EV_ADD,\n        )\n        self._kqueue.control([force_wakeup_event], 0)\n        self._force_wakeup_fd = self._force_wakeup.wakeup_sock.fileno()\n\n    def statistics(self) -> _KqueueStatistics:\n        tasks_waiting = 0\n        monitors = 0\n        for receiver in self._registered.values():\n            if type(receiver) is _core.Task:\n                tasks_waiting += 1\n            else:\n                monitors += 1\n        return _KqueueStatistics(tasks_waiting=tasks_waiting, monitors=monitors)\n\n    def close(self) -> None:\n        self._kqueue.close()\n        self._force_wakeup.close()\n\n    def force_wakeup(self) -> None:\n        self._force_wakeup.wakeup_thread_and_signal_safe()\n\n    def get_events(self, timeout: float) -> EventResult:\n        # max_events must be > 0 or kqueue gets cranky\n        # and we generally want this to be strictly larger than the actual\n        # number of events we get, so that we can tell that we've gotten\n        # all the events in just 1 call.\n        max_events = len(self._registered) + 1\n        events = []\n        while True:\n            batch = self._kqueue.control([], max_events, timeout)\n            events += batch\n            if len(batch) < max_events:\n                break\n            else:  # TODO: test this line\n                timeout = 0\n                # and loop back to the start\n        return events\n\n    def process_events(self, events: EventResult) -> None:\n        for event in events:\n            key = (event.ident, event.filter)\n            if event.ident == self._force_wakeup_fd:\n                self._force_wakeup.drain()\n                continue\n            receiver = self._registered[key]\n            if event.flags & select.KQ_EV_ONESHOT:  # TODO: test this branch\n                del self._registered[key]\n            if isinstance(receiver, _core.Task):\n                _core.reschedule(receiver, outcome.Value(event))\n            else:\n                receiver.put_nowait(event)  # TODO: test this line\n\n    # kevent registration is complicated -- e.g. aio submission can\n    # implicitly perform a EV_ADD, and EVFILT_PROC with NOTE_TRACK will\n    # automatically register filters for child processes. So our lowlevel\n    # API is *very* low-level: we expose the kqueue itself for adding\n    # events or sticking into AIO submission structs, and split waiting\n    # off into separate methods. It's your responsibility to make sure\n    # that handle_io never receives an event without a corresponding\n    # registration! This may be challenging if you want to be careful\n    # about e.g. KeyboardInterrupt. Possibly this API could be improved to\n    # be more ergonomic...\n\n    @_public\n    def current_kqueue(self) -> select.kqueue:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__.\n        \"\"\"\n        return self._kqueue\n\n    @contextmanager\n    @_public\n    def monitor_kevent(\n        self,\n        ident: int,\n        filter: int,\n    ) -> Iterator[_core.UnboundedQueue[select.kevent]]:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__.\n        \"\"\"\n        key = (ident, filter)\n        if key in self._registered:\n            raise _core.BusyResourceError(\n                \"attempt to register multiple listeners for same ident/filter pair\",\n            )\n        q = _core.UnboundedQueue[select.kevent]()\n        self._registered[key] = q\n        try:\n            yield q\n        finally:\n            del self._registered[key]\n\n    @_public\n    async def wait_kevent(\n        self,\n        ident: int,\n        filter: int,\n        abort_func: Callable[[RaiseCancelT], Abort],\n    ) -> Abort:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__.\n        \"\"\"\n        key = (ident, filter)\n        if key in self._registered:\n            raise _core.BusyResourceError(\n                \"attempt to register multiple listeners for same ident/filter pair\",\n            )\n        self._registered[key] = _core.current_task()\n\n        def abort(raise_cancel: RaiseCancelT) -> Abort:\n            r = abort_func(raise_cancel)\n            if r is _core.Abort.SUCCEEDED:  # TODO: test this branch\n                del self._registered[key]\n            return r\n\n        # wait_task_rescheduled does not have its return type typed\n        return await _core.wait_task_rescheduled(abort)  # type: ignore[no-any-return]\n\n    async def _wait_common(\n        self,\n        fd: int | _HasFileNo,\n        filter: int,\n    ) -> None:\n        if not isinstance(fd, int):\n            fd = fd.fileno()\n        flags = select.KQ_EV_ADD | select.KQ_EV_ONESHOT\n        event = select.kevent(fd, filter, flags)\n        self._kqueue.control([event], 0)\n\n        def abort(_: RaiseCancelT) -> Abort:\n            event = select.kevent(fd, filter, select.KQ_EV_DELETE)\n            try:\n                self._kqueue.control([event], 0)\n            except OSError as exc:\n                # kqueue tracks individual fds (*not* the underlying file\n                # object, see _io_epoll.py for a long discussion of why this\n                # distinction matters), and automatically deregisters an event\n                # if the fd is closed. So if kqueue.control says that it\n                # doesn't know about this event, then probably it's because\n                # the fd was closed behind our backs. (Too bad we can't ask it\n                # to wake us up when this happens, versus discovering it after\n                # the fact... oh well, you can't have everything.)\n                #\n                # FreeBSD reports this using EBADF. macOS uses ENOENT.\n                if exc.errno in (errno.EBADF, errno.ENOENT):  # pragma: no branch\n                    pass\n                else:  # pragma: no cover\n                    # As far as we know, this branch can't happen.\n                    raise\n            return _core.Abort.SUCCEEDED\n\n        await self.wait_kevent(fd, filter, abort)\n\n    @_public\n    async def wait_readable(self, fd: int | _HasFileNo) -> None:\n        \"\"\"Block until the kernel reports that the given object is readable.\n\n        On Unix systems, ``fd`` must either be an integer file descriptor,\n        or else an object with a ``.fileno()`` method which returns an\n        integer file descriptor. Any kind of file descriptor can be passed,\n        though the exact semantics will depend on your kernel. For example,\n        this probably won't do anything useful for on-disk files.\n\n        On Windows systems, ``fd`` must either be an integer ``SOCKET``\n        handle, or else an object with a ``.fileno()`` method which returns\n        an integer ``SOCKET`` handle. File descriptors aren't supported,\n        and neither are handles that refer to anything besides a\n        ``SOCKET``.\n\n        :raises trio.BusyResourceError:\n            if another task is already waiting for the given socket to\n            become readable.\n        :raises trio.ClosedResourceError:\n            if another task calls :func:`notify_closing` while this\n            function is still working.\n        \"\"\"\n        await self._wait_common(fd, select.KQ_FILTER_READ)\n\n    @_public\n    async def wait_writable(self, fd: int | _HasFileNo) -> None:\n        \"\"\"Block until the kernel reports that the given object is writable.\n\n        See `wait_readable` for the definition of ``fd``.\n\n        :raises trio.BusyResourceError:\n            if another task is already waiting for the given socket to\n            become writable.\n        :raises trio.ClosedResourceError:\n            if another task calls :func:`notify_closing` while this\n            function is still working.\n        \"\"\"\n        await self._wait_common(fd, select.KQ_FILTER_WRITE)\n\n    @_public\n    def notify_closing(self, fd: int | _HasFileNo) -> None:\n        \"\"\"Notify waiters of the given object that it will be closed.\n\n        Call this before closing a file descriptor (on Unix) or socket (on\n        Windows). This will cause any `wait_readable` or `wait_writable`\n        calls on the given object to immediately wake up and raise\n        `~trio.ClosedResourceError`.\n\n        This doesn't actually close the object – you still have to do that\n        yourself afterwards. Also, you want to be careful to make sure no\n        new tasks start waiting on the object in between when you call this\n        and when it's actually closed. So to close something properly, you\n        usually want to do these steps in order:\n\n        1. Explicitly mark the object as closed, so that any new attempts\n           to use it will abort before they start.\n        2. Call `notify_closing` to wake up any already-existing users.\n        3. Actually close the object.\n\n        It's also possible to do them in a different order if that's more\n        convenient, *but only if* you make sure not to have any checkpoints in\n        between the steps. This way they all happen in a single atomic\n        step, so other tasks won't be able to tell what order they happened\n        in anyway.\n        \"\"\"\n        if not isinstance(fd, int):\n            fd = fd.fileno()\n\n        for filter_ in [select.KQ_FILTER_READ, select.KQ_FILTER_WRITE]:\n            key = (fd, filter_)\n            receiver = self._registered.get(key)\n\n            if receiver is None:\n                continue\n\n            if type(receiver) is _core.Task:\n                event = select.kevent(fd, filter_, select.KQ_EV_DELETE)\n                self._kqueue.control([event], 0)\n                exc = _core.ClosedResourceError(\"another task closed this fd\")\n                _core.reschedule(receiver, outcome.Error(exc))\n                del self._registered[key]\n            else:\n                # XX this is an interesting example of a case where being able\n                # to close a queue would be useful...\n                raise NotImplementedError(\n                    \"can't close an fd that monitor_kevent is using\",\n                )\n"
  },
  {
    "path": "src/trio/_core/_io_windows.py",
    "content": "from __future__ import annotations\n\nimport enum\nimport itertools\nimport socket\nimport sys\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING, Literal, Protocol, TypeAlias, TypeVar, cast\n\nimport attrs\nfrom outcome import Value\n\nfrom .. import _core\nfrom ._io_common import wake_all\nfrom ._run import _public\nfrom ._windows_cffi import (\n    INVALID_HANDLE_VALUE,\n    AFDPollFlags,\n    CData,\n    CompletionModes,\n    CType,\n    ErrorCodes,\n    FileFlags,\n    Handle,\n    IoControlCodes,\n    WSAIoctls,\n    _handle,\n    _Overlapped,\n    ffi,\n    kernel32,\n    ntdll,\n    raise_winerror,\n    ws2_32,\n)\n\nif TYPE_CHECKING:\n    from collections.abc import Callable, Iterator\n\n    from typing_extensions import Buffer\n\n    from .._file_io import _HasFileNo\n    from ._traps import Abort, RaiseCancelT\n    from ._unbounded_queue import UnboundedQueue\n\nEventResult: TypeAlias = int\nT = TypeVar(\"T\")\n\n# There's a lot to be said about the overall design of a Windows event\n# loop. See\n#\n#    https://github.com/python-trio/trio/issues/52\n#\n# for discussion. This now just has some lower-level notes:\n#\n# How IOCP fits together:\n#\n# The general model is that you call some function like ReadFile or WriteFile\n# to tell the kernel that you want it to perform some operation, and the\n# kernel goes off and does that in the background, then at some point later it\n# sends you a notification that the operation is complete. There are some more\n# exotic APIs that don't quite fit this pattern, but most APIs do.\n#\n# Each background operation is tracked using an OVERLAPPED struct, that\n# uniquely identifies that particular operation.\n#\n# An \"IOCP\" (or \"I/O completion port\") is an object that lets the kernel send\n# us these notifications -- basically it's just a kernel->userspace queue.\n#\n# Each IOCP notification is represented by an OVERLAPPED_ENTRY struct, which\n# contains 3 fields:\n# - The \"completion key\". This is an opaque integer that we pick, and use\n#   however is convenient.\n# - pointer to the OVERLAPPED struct for the completed operation.\n# - dwNumberOfBytesTransferred (an integer).\n#\n# And in addition, for regular I/O, the OVERLAPPED structure gets filled in\n# with:\n# - result code (named \"Internal\")\n# - number of bytes transferred (named \"InternalHigh\"); usually redundant\n#   with dwNumberOfBytesTransferred.\n#\n# There are also some other entries in OVERLAPPED which only matter on input:\n# - Offset and OffsetHigh which are inputs to {Read,Write}File and\n#   otherwise always zero\n# - hEvent which is for if you aren't using IOCP; we always set it to zero.\n#\n# That describes the usual pattern for operations and the usual meaning of\n# these struct fields, but really these are just some arbitrary chunks of\n# bytes that get passed back and forth, so some operations like to overload\n# them to mean something else.\n#\n# You can also directly queue an OVERLAPPED_ENTRY object to an IOCP by calling\n# PostQueuedCompletionStatus. When you use this you get to set all the\n# OVERLAPPED_ENTRY fields to arbitrary values.\n#\n# You can request to cancel any operation if you know which handle it was\n# issued on + the OVERLAPPED struct that identifies it (via CancelIoEx). This\n# request might fail because the operation has already completed, or it might\n# be queued to happen in the background, so you only find out whether it\n# succeeded or failed later, when we get back the notification for the\n# operation being complete.\n#\n# There are three types of operations that we support:\n#\n# == Regular I/O operations on handles (e.g. files or named pipes) ==\n#\n# Implemented by: register_with_iocp, wait_overlapped\n#\n# To use these, you have to register the handle with your IOCP first. Once\n# it's registered, any operations on that handle will automatically send\n# completion events to that IOCP, with a completion key that you specify *when\n# the handle is registered* (so you can't use different completion keys for\n# different operations).\n#\n# We give these two dedicated completion keys: CKeys.WAIT_OVERLAPPED for\n# regular operations, and CKeys.LATE_CANCEL that's used to make\n# wait_overlapped cancellable even if the user forgot to call\n# register_with_iocp. The problem here is that after we request the cancel,\n# wait_overlapped keeps blocking until it sees the completion notification...\n# but if the user forgot to register_with_iocp, then the completion will never\n# come, so the cancellation will never resolve. To avoid this, whenever we try\n# to cancel an I/O operation and the cancellation fails, we use\n# PostQueuedCompletionStatus to send a CKeys.LATE_CANCEL notification. If this\n# arrives before the real completion, we assume the user forgot to call\n# register_with_iocp on their handle, and raise an error accordingly.\n#\n# == Socket state notifications ==\n#\n# Implemented by: wait_readable, wait_writable\n#\n# The public APIs that windows provides for this are all really awkward and\n# don't integrate with IOCP. So we drop down to a lower level, and talk\n# directly to the socket device driver in the kernel, which is called \"AFD\".\n# Unfortunately, this is a totally undocumented internal API. Fortunately\n# libuv also does this, so we can be pretty confident that MS won't break it\n# on us, and there is a *little* bit of information out there if you go\n# digging.\n#\n# Basically: we open a magic file that refers to the AFD driver, register the\n# magic file with our IOCP, and then we can issue regular overlapped I/O\n# operations on that handle. Specifically, the operation we use is called\n# IOCTL_AFD_POLL, which lets us pass in a buffer describing which events we're\n# interested in on a given socket (readable, writable, etc.). Later, when the\n# operation completes, the kernel rewrites the buffer we passed in to record\n# which events happened, and uses IOCP as normal to notify us that this\n# operation has completed.\n#\n# Unfortunately, the Windows kernel seems to have bugs if you try to issue\n# multiple simultaneous IOCTL_AFD_POLL operations on the same socket (see\n# https://github.com/python-trio/trio/wiki/notes-to-self#afd-labpy).\n# So if a user calls wait_readable and\n# wait_writable at the same time, we have to combine those into a single\n# IOCTL_AFD_POLL. This means we can't just use the wait_overlapped machinery.\n# Instead we have some dedicated code to handle these operations, and a\n# dedicated completion key CKeys.AFD_POLL.\n#\n# Sources of information:\n# - https://github.com/python-trio/trio/issues/52\n# - Wepoll: https://github.com/piscisaureus/wepoll/\n# - libuv: https://github.com/libuv/libuv/\n# - ReactOS: https://github.com/reactos/reactos/\n# - Ancient leaked copies of the Windows NT and Winsock source code:\n#   https://github.com/pustladi/Windows-2000/blob/661d000d50637ed6fab2329d30e31775046588a9/private/net/sockets/winsock2/wsp/msafd/select.c#L59-L655\n#   https://github.com/metoo10987/WinNT4/blob/f5c14e6b42c8f45c20fe88d14c61f9d6e0386b8e/private/ntos/afd/poll.c#L68-L707\n# - The WSAEventSelect docs (this exposes a finer-grained set of events than\n#   select(), so if you squint you can treat it as a source of information on\n#   the fine-grained AFD poll types)\n#\n#\n# == Everything else ==\n#\n# There are also some weirder APIs for interacting with IOCP. For example, the\n# \"Job\" API lets you specify an IOCP handle and \"completion key\", and then in\n# the future whenever certain events happen it sends uses IOCP to send a\n# notification. These notifications don't correspond to any particular\n# operation; they're just spontaneous messages you get. The\n# \"dwNumberOfBytesTransferred\" field gets repurposed to carry an identifier\n# for the message type (e.g. JOB_OBJECT_MSG_EXIT_PROCESS), and the\n# \"lpOverlapped\" field gets repurposed to carry some arbitrary data that\n# depends on the message type (e.g. the pid of the process that exited).\n#\n# To handle these, we have monitor_completion_key, where we hand out an\n# unassigned completion key, let users set it up however they want, and then\n# get any events that arrive on that key.\n#\n# (Note: monitor_completion_key is not documented or fully baked; expect it to\n# change in the future.)\n\n\n# Our completion keys\nclass CKeys(enum.IntEnum):\n    AFD_POLL = 0\n    WAIT_OVERLAPPED = 1\n    LATE_CANCEL = 2\n    FORCE_WAKEUP = 3\n    USER_DEFINED = 4  # and above\n\n\n# AFD_POLL has a finer-grained set of events than other APIs. We collapse them\n# down into Unix-style \"readable\" and \"writable\".\n#\n# Note: AFD_POLL_LOCAL_CLOSE isn't a reliable substitute for notify_closing(),\n# because even if the user closes the socket *handle*, the socket *object*\n# could still remain open, e.g. if the socket was dup'ed (possibly into\n# another process). Explicitly calling notify_closing() guarantees that\n# everyone waiting on the *handle* wakes up, which is what you'd expect.\n#\n# However, we can't avoid getting LOCAL_CLOSE notifications -- the kernel\n# delivers them whether we ask for them or not -- so better to include them\n# here for documentation, and so that when we check (delivered & requested) we\n# get a match.\n\nREADABLE_FLAGS = (\n    AFDPollFlags.AFD_POLL_RECEIVE\n    | AFDPollFlags.AFD_POLL_ACCEPT\n    | AFDPollFlags.AFD_POLL_DISCONNECT  # other side sent an EOF\n    | AFDPollFlags.AFD_POLL_ABORT\n    | AFDPollFlags.AFD_POLL_LOCAL_CLOSE\n)\n\nWRITABLE_FLAGS = (\n    AFDPollFlags.AFD_POLL_SEND\n    | AFDPollFlags.AFD_POLL_CONNECT_FAIL\n    | AFDPollFlags.AFD_POLL_ABORT\n    | AFDPollFlags.AFD_POLL_LOCAL_CLOSE\n)\n\n\n# Annoyingly, while the API makes it *seem* like you can happily issue as many\n# independent AFD_POLL operations as you want without them interfering with\n# each other, in fact if you issue two AFD_POLL operations for the same socket\n# at the same time with notification going to the same IOCP port, then Windows\n# gets super confused. For example, if we issue one operation from\n# wait_readable, and another independent operation from wait_writable, then\n# Windows may complete the wait_writable operation when the socket becomes\n# readable.\n#\n# To avoid this, we have to coalesce all the operations on a single socket\n# into one, and when the set of waiters changes we have to throw away the old\n# operation and start a new one.\n@attrs.define(eq=False)\nclass AFDWaiters:\n    read_task: _core.Task | None = None\n    write_task: _core.Task | None = None\n    current_op: AFDPollOp | None = None\n\n\n# Just used for internal type checking.\nclass _AFDHandle(Protocol):\n    Handle: Handle\n    Status: int\n    Events: int\n\n\n# Just used for internal type checking.\nclass _AFDPollInfo(Protocol):\n    Timeout: int\n    NumberOfHandles: int\n    Exclusive: int\n    Handles: list[_AFDHandle]\n\n\n# We also need to bundle up all the info for a single op into a standalone\n# object, because we need to keep all these objects alive until the operation\n# finishes, even if we're throwing it away.\n@attrs.frozen(eq=False)\nclass AFDPollOp:\n    lpOverlapped: CData\n    poll_info: _AFDPollInfo\n    waiters: AFDWaiters\n    afd_group: AFDGroup\n\n\n# The Windows kernel has a weird issue when using AFD handles. If you have N\n# instances of wait_readable/wait_writable registered with a single AFD handle,\n# then cancelling any one of them takes something like O(N**2) time. So if we\n# used just a single AFD handle, then cancellation would quickly become very\n# expensive, e.g. a program with N active sockets would take something like\n# O(N**3) time to unwind after control-C. The solution is to spread our sockets\n# out over multiple AFD handles, so that N doesn't grow too large for any\n# individual handle.\nMAX_AFD_GROUP_SIZE = 500  # at 1000, the cubic scaling is just starting to bite\n\n\n@attrs.define(eq=False)\nclass AFDGroup:\n    size: int\n    handle: Handle\n\n\nassert not TYPE_CHECKING or sys.platform == \"win32\"\n\n\n@attrs.frozen(eq=False)\nclass _WindowsStatistics:\n    tasks_waiting_read: int\n    tasks_waiting_write: int\n    tasks_waiting_overlapped: int\n    completion_key_monitors: int\n    backend: Literal[\"windows\"] = attrs.field(init=False, default=\"windows\")\n\n\n# Maximum number of events to dequeue from the completion port on each pass\n# through the run loop. Somewhat arbitrary. Should be large enough to collect\n# a good set of tasks on each loop, but not so large to waste tons of memory.\n# (Each WindowsIOManager holds a buffer whose size is ~32x this number.)\nMAX_EVENTS = 1000\n\n\ndef _check(success: T) -> T:\n    if not success:\n        raise_winerror()\n    return success\n\n\ndef _get_underlying_socket(\n    sock: _HasFileNo | int | Handle,\n    *,\n    which: WSAIoctls = WSAIoctls.SIO_BASE_HANDLE,\n) -> Handle:\n    if hasattr(sock, \"fileno\"):\n        sock = sock.fileno()\n    base_ptr = ffi.new(\"HANDLE *\")\n    out_size = ffi.new(\"DWORD *\")\n    failed = ws2_32.WSAIoctl(\n        ffi.cast(\"SOCKET\", sock),\n        which,\n        ffi.NULL,\n        0,\n        base_ptr,\n        ffi.sizeof(\"HANDLE\"),\n        out_size,\n        ffi.NULL,\n        ffi.NULL,\n    )\n    if failed:\n        code = ws2_32.WSAGetLastError()\n        raise_winerror(code)\n    return Handle(base_ptr[0])\n\n\ndef _get_base_socket(sock: _HasFileNo | int | Handle) -> Handle:\n    # There is a development kit for LSPs called Komodia Redirector.\n    # It does some unusual (some might say evil) things like intercepting\n    # SIO_BASE_HANDLE (fails) and SIO_BSP_HANDLE_SELECT (returns the same\n    # socket) in a misguided attempt to prevent bypassing it. It's been used\n    # in malware including the infamous Lenovo Superfish incident from 2015,\n    # but unfortunately is also used in some legitimate products such as\n    # parental control tools and Astrill VPN. Komodia happens to not\n    # block SIO_BSP_HANDLE_POLL, so we'll try SIO_BASE_HANDLE and fall back\n    # to SIO_BSP_HANDLE_POLL if it doesn't work.\n    # References:\n    # - https://github.com/piscisaureus/wepoll/blob/0598a791bf9cbbf480793d778930fc635b044980/wepoll.c#L2223\n    # - https://github.com/tokio-rs/mio/issues/1314\n\n    while True:\n        try:\n            # If this is not a Komodia-intercepted socket, we can just use\n            # SIO_BASE_HANDLE.\n            return _get_underlying_socket(sock)\n        except OSError as ex:\n            if ex.winerror == ErrorCodes.ERROR_NOT_SOCKET:\n                # SIO_BASE_HANDLE might fail even without LSP intervention,\n                # if we get something that's not a socket.\n                raise\n            if hasattr(sock, \"fileno\"):\n                sock = sock.fileno()\n            sock = _handle(sock)\n            next_sock = _get_underlying_socket(\n                sock,\n                which=WSAIoctls.SIO_BSP_HANDLE_POLL,\n            )\n            if next_sock == sock:\n                # If BSP_HANDLE_POLL returns the same socket we already had,\n                # then there's no layering going on and we need to fail\n                # to prevent an infinite loop.\n                raise RuntimeError(\n                    \"Unexpected network configuration detected: \"\n                    \"SIO_BASE_HANDLE failed and SIO_BSP_HANDLE_POLL didn't \"\n                    \"return a different socket. Please file a bug at \"\n                    \"https://github.com/python-trio/trio/issues/new, \"\n                    \"and include the output of running: \"\n                    \"netsh winsock show catalog\",\n                ) from ex\n            # Otherwise we've gotten at least one layer deeper, so\n            # loop back around to keep digging.\n            sock = next_sock\n\n\ndef _afd_helper_handle() -> Handle:\n    # The \"AFD\" driver is exposed at the NT path \"\\Device\\Afd\". We're using\n    # the Win32 CreateFile, though, so we have to pass a Win32 path. \\\\.\\ is\n    # how Win32 refers to the NT \\GLOBAL??\\ directory, and GLOBALROOT is a\n    # symlink inside that directory that points to the root of the NT path\n    # system. So by sticking that in front of the NT path, we get a Win32\n    # path. Alternatively, we could use NtCreateFile directly, since it takes\n    # an NT path. But we already wrap CreateFileW so this was easier.\n    # References:\n    #   https://blogs.msdn.microsoft.com/jeremykuhne/2016/05/02/dos-to-nt-a-paths-journey/\n    #   https://stackoverflow.com/a/21704022\n    #\n    # I'm actually not sure what the \\Trio part at the end of the path does.\n    # Wepoll uses \\Device\\Afd\\Wepoll, so I just copied them. (I'm guessing it\n    # might be visible in some debug tools, and is otherwise arbitrary?)\n    rawname = r\"\\\\.\\GLOBALROOT\\Device\\Afd\\Trio\".encode(\"utf-16le\") + b\"\\0\\0\"\n    rawname_buf = ffi.from_buffer(rawname)\n\n    handle = kernel32.CreateFileW(\n        ffi.cast(\"LPCWSTR\", rawname_buf),\n        FileFlags.SYNCHRONIZE,\n        FileFlags.FILE_SHARE_READ | FileFlags.FILE_SHARE_WRITE,\n        ffi.NULL,  # no security attributes\n        FileFlags.OPEN_EXISTING,\n        FileFlags.FILE_FLAG_OVERLAPPED,\n        ffi.NULL,  # no template file\n    )\n    if handle == INVALID_HANDLE_VALUE:  # pragma: no cover\n        raise_winerror()\n    return handle\n\n\n@attrs.frozen(slots=False)\nclass CompletionKeyEventInfo:\n    lpOverlapped: CData | int\n    dwNumberOfBytesTransferred: int\n\n\nclass WindowsIOManager:\n    def __init__(self) -> None:\n        # If this method raises an exception, then __del__ could run on a\n        # half-initialized object. So we initialize everything that __del__\n        # touches to safe values up front, before we do anything that can\n        # fail.\n        self._iocp = None\n        self._all_afd_handles: list[Handle] = []\n\n        self._iocp = _check(\n            kernel32.CreateIoCompletionPort(INVALID_HANDLE_VALUE, ffi.NULL, 0, 0),\n        )\n        self._events = ffi.new(\"OVERLAPPED_ENTRY[]\", MAX_EVENTS)\n\n        self._vacant_afd_groups: set[AFDGroup] = set()\n        # {lpOverlapped: AFDPollOp}\n        self._afd_ops: dict[CData, AFDPollOp] = {}\n        # {socket handle: AFDWaiters}\n        self._afd_waiters: dict[Handle, AFDWaiters] = {}\n\n        # {lpOverlapped: task}\n        self._overlapped_waiters: dict[CData, _core.Task] = {}\n        self._posted_too_late_to_cancel: set[CData] = set()\n\n        self._completion_key_queues: dict[int, UnboundedQueue[object]] = {}\n        self._completion_key_counter = itertools.count(CKeys.USER_DEFINED)\n\n        with socket.socket() as s:\n            # We assume we're not working with any LSP that changes\n            # how select() is supposed to work. Validate this by\n            # ensuring that the result of SIO_BSP_HANDLE_SELECT (the\n            # LSP-hookable mechanism for \"what should I use for\n            # select()?\") matches that of SIO_BASE_HANDLE (\"what is\n            # the real non-hooked underlying socket here?\").\n            #\n            # This doesn't work for Komodia-based LSPs; see the comments\n            # in _get_base_socket() for details. But we have special\n            # logic for those, so we just skip this check if\n            # SIO_BASE_HANDLE fails.\n\n            # LSPs can in theory override this, but we believe that it never\n            # actually happens in the wild (except Komodia)\n            select_handle = _get_underlying_socket(\n                s,\n                which=WSAIoctls.SIO_BSP_HANDLE_SELECT,\n            )\n            try:\n                # LSPs shouldn't override this...\n                base_handle = _get_underlying_socket(s, which=WSAIoctls.SIO_BASE_HANDLE)\n            except OSError:\n                # But Komodia-based LSPs do anyway, in a way that causes\n                # a failure with WSAEFAULT. We have special handling for\n                # them in _get_base_socket(). Make sure it works.\n                _get_base_socket(s)\n            else:\n                if base_handle != select_handle:\n                    raise RuntimeError(\n                        \"Unexpected network configuration detected: \"\n                        \"SIO_BASE_HANDLE and SIO_BSP_HANDLE_SELECT differ. \"\n                        \"Please file a bug at \"\n                        \"https://github.com/python-trio/trio/issues/new, \"\n                        \"and include the output of running: \"\n                        \"netsh winsock show catalog\",\n                    )\n\n    def close(self) -> None:\n        try:\n            if self._iocp is not None:\n                iocp = self._iocp\n                self._iocp = None\n                _check(kernel32.CloseHandle(iocp))\n        finally:\n            while self._all_afd_handles:\n                afd_handle = self._all_afd_handles.pop()\n                _check(kernel32.CloseHandle(afd_handle))\n\n    def __del__(self) -> None:\n        self.close()\n\n    def statistics(self) -> _WindowsStatistics:\n        tasks_waiting_read = 0\n        tasks_waiting_write = 0\n        for waiter in self._afd_waiters.values():\n            if waiter.read_task is not None:\n                tasks_waiting_read += 1\n            if waiter.write_task is not None:\n                tasks_waiting_write += 1\n        return _WindowsStatistics(\n            tasks_waiting_read=tasks_waiting_read,\n            tasks_waiting_write=tasks_waiting_write,\n            tasks_waiting_overlapped=len(self._overlapped_waiters),\n            completion_key_monitors=len(self._completion_key_queues),\n        )\n\n    def force_wakeup(self) -> None:\n        assert self._iocp is not None\n        _check(\n            kernel32.PostQueuedCompletionStatus(\n                self._iocp,\n                0,\n                CKeys.FORCE_WAKEUP,\n                ffi.NULL,\n            ),\n        )\n\n    def get_events(self, timeout: float) -> EventResult:\n        received = ffi.new(\"PULONG\")\n        milliseconds = round(1000 * timeout)\n        if timeout > 0 and milliseconds == 0:\n            milliseconds = 1\n        try:\n            assert self._iocp is not None\n            _check(\n                kernel32.GetQueuedCompletionStatusEx(\n                    self._iocp,\n                    self._events,\n                    MAX_EVENTS,\n                    received,\n                    milliseconds,\n                    0,\n                ),\n            )\n        except OSError as exc:\n            if exc.winerror != ErrorCodes.WAIT_TIMEOUT:  # pragma: no cover\n                raise\n            return 0\n        result = received[0]\n        assert isinstance(result, int)\n        return result\n\n    def process_events(self, received: EventResult) -> None:\n        for i in range(received):\n            entry = self._events[i]\n            if entry.lpCompletionKey == CKeys.AFD_POLL:\n                lpo = entry.lpOverlapped\n                op = self._afd_ops.pop(lpo)\n                waiters = op.waiters\n                if waiters.current_op is not op:\n                    # Stale op, nothing to do\n                    pass\n                else:\n                    waiters.current_op = None\n                    # I don't think this can happen, so if it does let's crash\n                    # and get a debug trace.\n                    if lpo.Internal != 0:  # pragma: no cover\n                        code = ntdll.RtlNtStatusToDosError(lpo.Internal)\n                        raise_winerror(code)\n                    flags = op.poll_info.Handles[0].Events\n                    if waiters.read_task and flags & READABLE_FLAGS:\n                        _core.reschedule(waiters.read_task)\n                        waiters.read_task = None\n                    if waiters.write_task and flags & WRITABLE_FLAGS:\n                        _core.reschedule(waiters.write_task)\n                        waiters.write_task = None\n                    self._refresh_afd(op.poll_info.Handles[0].Handle)\n            elif entry.lpCompletionKey == CKeys.WAIT_OVERLAPPED:\n                # Regular I/O event, dispatch on lpOverlapped\n                waiter = self._overlapped_waiters.pop(entry.lpOverlapped)\n                overlapped = entry.lpOverlapped\n                transferred = entry.dwNumberOfBytesTransferred\n                info = CompletionKeyEventInfo(\n                    lpOverlapped=overlapped,\n                    dwNumberOfBytesTransferred=transferred,\n                )\n                _core.reschedule(waiter, Value(info))\n            elif entry.lpCompletionKey == CKeys.LATE_CANCEL:\n                # Post made by a regular I/O event's abort_fn\n                # after it failed to cancel the I/O. If we still\n                # have a waiter with this lpOverlapped, we didn't\n                # get the regular I/O completion and almost\n                # certainly the user forgot to call\n                # register_with_iocp.\n                self._posted_too_late_to_cancel.remove(entry.lpOverlapped)\n                try:\n                    waiter = self._overlapped_waiters.pop(entry.lpOverlapped)\n                except KeyError:\n                    # Looks like the actual completion got here before this\n                    # fallback post did -- we're in the \"expected\" case of\n                    # too-late-to-cancel, where the user did nothing wrong.\n                    # Nothing more to do.\n                    pass\n                else:\n                    exc = _core.TrioInternalError(\n                        f\"Failed to cancel overlapped I/O in {waiter.name} and didn't \"\n                        \"receive the completion either. Did you forget to \"\n                        \"call register_with_iocp()?\",\n                    )\n                    # Raising this out of handle_io ensures that\n                    # the user will see our message even if some\n                    # other task is in an uncancellable wait due\n                    # to the same underlying forgot-to-register\n                    # issue (if their CancelIoEx succeeds, we\n                    # have no way of noticing that their completion\n                    # won't arrive). Unfortunately it loses the\n                    # task traceback. If you're debugging this\n                    # error and can't tell where it's coming from,\n                    # try changing this line to\n                    # _core.reschedule(waiter, outcome.Error(exc))\n                    raise exc\n            elif entry.lpCompletionKey == CKeys.FORCE_WAKEUP:\n                pass\n            else:\n                # dispatch on lpCompletionKey\n                queue = self._completion_key_queues[entry.lpCompletionKey]\n                overlapped = int(ffi.cast(\"uintptr_t\", entry.lpOverlapped))\n                transferred = entry.dwNumberOfBytesTransferred\n                info = CompletionKeyEventInfo(\n                    lpOverlapped=overlapped,\n                    dwNumberOfBytesTransferred=transferred,\n                )\n                queue.put_nowait(info)\n\n    def _register_with_iocp(self, handle_: int | CData, completion_key: int) -> None:\n        handle = _handle(handle_)\n        assert self._iocp is not None\n        _check(kernel32.CreateIoCompletionPort(handle, self._iocp, completion_key, 0))\n        # Supposedly this makes things slightly faster, by disabling the\n        # ability to do WaitForSingleObject(handle). We would never want to do\n        # that anyway, so might as well get the extra speed (if any).\n        # Ref: http://www.lenholgate.com/blog/2009/09/interesting-blog-posts-on-high-performance-servers.html\n        _check(\n            kernel32.SetFileCompletionNotificationModes(\n                handle,\n                CompletionModes.FILE_SKIP_SET_EVENT_ON_HANDLE,\n            ),\n        )\n\n    ################################################################\n    # AFD stuff\n    ################################################################\n\n    def _refresh_afd(self, base_handle: Handle) -> None:\n        waiters = self._afd_waiters[base_handle]\n        if waiters.current_op is not None:\n            afd_group = waiters.current_op.afd_group\n            try:\n                _check(\n                    kernel32.CancelIoEx(\n                        afd_group.handle,\n                        waiters.current_op.lpOverlapped,\n                    ),\n                )\n            except OSError as exc:\n                if exc.winerror != ErrorCodes.ERROR_NOT_FOUND:\n                    # I don't think this is possible, so if it happens let's\n                    # crash noisily.\n                    raise  # pragma: no cover\n            waiters.current_op = None\n            afd_group.size -= 1\n            self._vacant_afd_groups.add(afd_group)\n\n        flags = 0\n        if waiters.read_task is not None:\n            flags |= READABLE_FLAGS\n        if waiters.write_task is not None:\n            flags |= WRITABLE_FLAGS\n\n        if not flags:\n            del self._afd_waiters[base_handle]\n        else:\n            try:\n                afd_group = self._vacant_afd_groups.pop()\n            except KeyError:\n                afd_group = AFDGroup(0, _afd_helper_handle())\n                self._register_with_iocp(afd_group.handle, CKeys.AFD_POLL)\n                self._all_afd_handles.append(afd_group.handle)\n            self._vacant_afd_groups.add(afd_group)\n\n            lpOverlapped = ffi.new(\"LPOVERLAPPED\")\n\n            poll_info = cast(\"_AFDPollInfo\", ffi.new(\"AFD_POLL_INFO *\"))\n            poll_info.Timeout = 2**63 - 1  # INT64_MAX\n            poll_info.NumberOfHandles = 1\n            poll_info.Exclusive = 0\n            poll_info.Handles[0].Handle = base_handle\n            poll_info.Handles[0].Status = 0\n            poll_info.Handles[0].Events = flags\n\n            try:\n                _check(\n                    kernel32.DeviceIoControl(\n                        afd_group.handle,\n                        IoControlCodes.IOCTL_AFD_POLL,\n                        cast(\"CType\", poll_info),  # type: ignore[arg-type]\n                        ffi.sizeof(\"AFD_POLL_INFO\"),\n                        cast(\"CType\", poll_info),  # type: ignore[arg-type]\n                        ffi.sizeof(\"AFD_POLL_INFO\"),\n                        ffi.NULL,\n                        lpOverlapped,\n                    ),\n                )\n            except OSError as exc:\n                if exc.winerror != ErrorCodes.ERROR_IO_PENDING:\n                    # This could happen if the socket handle got closed behind\n                    # our back while a wait_* call was pending, and we tried\n                    # to re-issue the call. Clear our state and wake up any\n                    # pending calls.\n                    del self._afd_waiters[base_handle]\n                    # Do this last, because it could raise.\n                    wake_all(waiters, exc)\n                    return\n            op = AFDPollOp(lpOverlapped, poll_info, waiters, afd_group)\n            waiters.current_op = op\n            self._afd_ops[lpOverlapped] = op\n            afd_group.size += 1\n            if afd_group.size >= MAX_AFD_GROUP_SIZE:\n                self._vacant_afd_groups.remove(afd_group)\n\n    async def _afd_poll(self, sock: _HasFileNo | int, mode: str) -> None:\n        base_handle = _get_base_socket(sock)\n        waiters = self._afd_waiters.get(base_handle)\n        if waiters is None:\n            waiters = AFDWaiters()\n            self._afd_waiters[base_handle] = waiters\n        if getattr(waiters, mode) is not None:\n            raise _core.BusyResourceError\n        setattr(waiters, mode, _core.current_task())\n        # Could potentially raise if the handle is somehow invalid; that's OK,\n        # we let it escape.\n        self._refresh_afd(base_handle)\n\n        def abort_fn(_: RaiseCancelT) -> Abort:\n            setattr(waiters, mode, None)\n            self._refresh_afd(base_handle)\n            return _core.Abort.SUCCEEDED\n\n        await _core.wait_task_rescheduled(abort_fn)\n\n    @_public\n    async def wait_readable(self, sock: _HasFileNo | int) -> None:\n        \"\"\"Block until the kernel reports that the given object is readable.\n\n        On Unix systems, ``sock`` must either be an integer file descriptor,\n        or else an object with a ``.fileno()`` method which returns an\n        integer file descriptor. Any kind of file descriptor can be passed,\n        though the exact semantics will depend on your kernel. For example,\n        this probably won't do anything useful for on-disk files.\n\n        On Windows systems, ``sock`` must either be an integer ``SOCKET``\n        handle, or else an object with a ``.fileno()`` method which returns\n        an integer ``SOCKET`` handle. File descriptors aren't supported,\n        and neither are handles that refer to anything besides a\n        ``SOCKET``.\n\n        :raises trio.BusyResourceError:\n            if another task is already waiting for the given socket to\n            become readable.\n        :raises trio.ClosedResourceError:\n            if another task calls :func:`notify_closing` while this\n            function is still working.\n        \"\"\"\n        await self._afd_poll(sock, \"read_task\")\n\n    @_public\n    async def wait_writable(self, sock: _HasFileNo | int) -> None:\n        \"\"\"Block until the kernel reports that the given object is writable.\n\n        See `wait_readable` for the definition of ``sock``.\n\n        :raises trio.BusyResourceError:\n            if another task is already waiting for the given socket to\n            become writable.\n        :raises trio.ClosedResourceError:\n            if another task calls :func:`notify_closing` while this\n            function is still working.\n        \"\"\"\n        await self._afd_poll(sock, \"write_task\")\n\n    @_public\n    def notify_closing(self, handle: Handle | int | _HasFileNo) -> None:\n        \"\"\"Notify waiters of the given object that it will be closed.\n\n        Call this before closing a file descriptor (on Unix) or socket (on\n        Windows). This will cause any `wait_readable` or `wait_writable`\n        calls on the given object to immediately wake up and raise\n        `~trio.ClosedResourceError`.\n\n        This doesn't actually close the object – you still have to do that\n        yourself afterwards. Also, you want to be careful to make sure no\n        new tasks start waiting on the object in between when you call this\n        and when it's actually closed. So to close something properly, you\n        usually want to do these steps in order:\n\n        1. Explicitly mark the object as closed, so that any new attempts\n           to use it will abort before they start.\n        2. Call `notify_closing` to wake up any already-existing users.\n        3. Actually close the object.\n\n        It's also possible to do them in a different order if that's more\n        convenient, *but only if* you make sure not to have any checkpoints in\n        between the steps. This way they all happen in a single atomic\n        step, so other tasks won't be able to tell what order they happened\n        in anyway.\n        \"\"\"\n        handle = _get_base_socket(handle)\n        waiters = self._afd_waiters.get(handle)\n        if waiters is not None:\n            wake_all(waiters, _core.ClosedResourceError())\n            self._refresh_afd(handle)\n\n    ################################################################\n    # Regular overlapped operations\n    ################################################################\n\n    @_public\n    def register_with_iocp(self, handle: int | CData) -> None:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__ and `#52\n        <https://github.com/python-trio/trio/issues/52>`__.\n        \"\"\"\n        self._register_with_iocp(handle, CKeys.WAIT_OVERLAPPED)\n\n    @_public\n    async def wait_overlapped(\n        self,\n        handle_: int | CData,\n        lpOverlapped: CData | int,\n    ) -> object:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__ and `#52\n        <https://github.com/python-trio/trio/issues/52>`__.\n        \"\"\"\n        handle = _handle(handle_)\n        if isinstance(lpOverlapped, int):  # TODO: test this line\n            lpOverlapped = ffi.cast(\"LPOVERLAPPED\", lpOverlapped)\n        if lpOverlapped in self._overlapped_waiters:  # TODO: test this line\n            raise _core.BusyResourceError(\n                \"another task is already waiting on that lpOverlapped\",\n            )\n        task = _core.current_task()\n        self._overlapped_waiters[lpOverlapped] = task\n        raise_cancel = None\n\n        def abort(raise_cancel_: RaiseCancelT) -> Abort:\n            nonlocal raise_cancel\n            raise_cancel = raise_cancel_\n            try:\n                _check(kernel32.CancelIoEx(handle, lpOverlapped))\n            except OSError as exc:\n                if exc.winerror == ErrorCodes.ERROR_NOT_FOUND:\n                    assert self._iocp is not None\n                    # Too late to cancel. If this happens because the\n                    # operation is already completed, we don't need to do\n                    # anything; we'll get a notification of that completion\n                    # soon. But another possibility is that the operation was\n                    # performed on a handle that wasn't registered with our\n                    # IOCP (ie, the user forgot to call register_with_iocp),\n                    # in which case we're just never going to see the\n                    # completion. To avoid an uncancellable infinite sleep in\n                    # the latter case, we'll PostQueuedCompletionStatus here,\n                    # and if our post arrives before the original completion\n                    # does, we'll assume the handle wasn't registered.\n                    _check(\n                        kernel32.PostQueuedCompletionStatus(\n                            self._iocp,\n                            0,\n                            CKeys.LATE_CANCEL,\n                            lpOverlapped,\n                        ),\n                    )\n                    # Keep the lpOverlapped referenced so its address\n                    # doesn't get reused until our posted completion\n                    # status has been processed. Otherwise, we can\n                    # get confused about which completion goes with\n                    # which I/O.\n                    self._posted_too_late_to_cancel.add(lpOverlapped)\n                else:  # pragma: no cover\n                    raise _core.TrioInternalError(\n                        \"CancelIoEx failed with unexpected error\",\n                    ) from exc\n            return _core.Abort.FAILED\n\n        # TODO: what type does this return?\n        info = await _core.wait_task_rescheduled(abort)\n        lpOverlappedTyped = cast(\"_Overlapped\", lpOverlapped)\n        if lpOverlappedTyped.Internal != 0:\n            # the lpOverlapped reports the error as an NT status code,\n            # which we must convert back to a Win32 error code before\n            # it will produce the right sorts of exceptions\n            code = ntdll.RtlNtStatusToDosError(lpOverlappedTyped.Internal)\n            if code == ErrorCodes.ERROR_OPERATION_ABORTED:\n                if raise_cancel is not None:\n                    raise_cancel()\n                else:\n                    # We didn't request this cancellation, so assume\n                    # it happened due to the underlying handle being\n                    # closed before the operation could complete.\n                    raise _core.ClosedResourceError(\"another task closed this resource\")\n            else:\n                raise_winerror(code)\n        return info\n\n    async def _perform_overlapped(\n        self,\n        handle: int | CData,\n        submit_fn: Callable[[_Overlapped], None],\n    ) -> _Overlapped:\n        # submit_fn(lpOverlapped) submits some I/O\n        # it may raise an OSError with ERROR_IO_PENDING\n        # the handle must already be registered using\n        # register_with_iocp(handle)\n        # This always does a schedule point, but it's possible that the\n        # operation will not be cancellable, depending on how Windows is\n        # feeling today. So we need to check for cancellation manually.\n        await _core.checkpoint_if_cancelled()\n        lpOverlapped = cast(\"_Overlapped\", ffi.new(\"LPOVERLAPPED\"))\n        try:\n            submit_fn(lpOverlapped)\n        except OSError as exc:\n            if exc.winerror != ErrorCodes.ERROR_IO_PENDING:\n                raise\n        await self.wait_overlapped(handle, cast(\"CData\", lpOverlapped))\n        return lpOverlapped\n\n    @_public\n    async def write_overlapped(\n        self,\n        handle: int | CData,\n        data: Buffer,\n        file_offset: int = 0,\n    ) -> int:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__ and `#52\n        <https://github.com/python-trio/trio/issues/52>`__.\n        \"\"\"\n        with ffi.from_buffer(data) as cbuf:\n\n            def submit_write(lpOverlapped: _Overlapped) -> None:\n                # yes, these are the real documented names\n                offset_fields = lpOverlapped.DUMMYUNIONNAME.DUMMYSTRUCTNAME\n                offset_fields.Offset = file_offset & 0xFFFFFFFF\n                offset_fields.OffsetHigh = file_offset >> 32\n                _check(\n                    kernel32.WriteFile(\n                        _handle(handle),\n                        ffi.cast(\"LPCVOID\", cbuf),\n                        len(cbuf),\n                        ffi.NULL,\n                        lpOverlapped,\n                    ),\n                )\n\n            lpOverlapped = await self._perform_overlapped(handle, submit_write)\n            # this is \"number of bytes transferred\"\n            return lpOverlapped.InternalHigh\n\n    @_public\n    async def readinto_overlapped(\n        self,\n        handle: int | CData,\n        buffer: Buffer,\n        file_offset: int = 0,\n    ) -> int:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__ and `#52\n        <https://github.com/python-trio/trio/issues/52>`__.\n        \"\"\"\n        with ffi.from_buffer(buffer, require_writable=True) as cbuf:\n\n            def submit_read(lpOverlapped: _Overlapped) -> None:\n                offset_fields = lpOverlapped.DUMMYUNIONNAME.DUMMYSTRUCTNAME\n                offset_fields.Offset = file_offset & 0xFFFFFFFF\n                offset_fields.OffsetHigh = file_offset >> 32\n                _check(\n                    kernel32.ReadFile(\n                        _handle(handle),\n                        ffi.cast(\"LPVOID\", cbuf),\n                        len(cbuf),\n                        ffi.NULL,\n                        lpOverlapped,\n                    ),\n                )\n\n            lpOverlapped = await self._perform_overlapped(handle, submit_read)\n            return lpOverlapped.InternalHigh\n\n    ################################################################\n    # Raw IOCP operations\n    ################################################################\n\n    @_public\n    def current_iocp(self) -> int:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__ and `#52\n        <https://github.com/python-trio/trio/issues/52>`__.\n        \"\"\"\n        assert self._iocp is not None\n        return int(ffi.cast(\"uintptr_t\", self._iocp))\n\n    @contextmanager\n    @_public\n    def monitor_completion_key(self) -> Iterator[tuple[int, UnboundedQueue[object]]]:\n        \"\"\"TODO: these are implemented, but are currently more of a sketch than\n        anything real. See `#26\n        <https://github.com/python-trio/trio/issues/26>`__ and `#52\n        <https://github.com/python-trio/trio/issues/52>`__.\n        \"\"\"\n        key = next(self._completion_key_counter)\n        queue = _core.UnboundedQueue[object]()\n        self._completion_key_queues[key] = queue\n        try:\n            yield (key, queue)\n        finally:\n            del self._completion_key_queues[key]\n"
  },
  {
    "path": "src/trio/_core/_ki.py",
    "content": "from __future__ import annotations\n\nimport signal\nimport sys\nimport weakref\nfrom typing import TYPE_CHECKING, Generic, Protocol, TypeGuard, TypeVar\n\nimport attrs\n\nfrom .._util import is_main_thread\nfrom ._run_context import GLOBAL_RUN_CONTEXT\n\nif TYPE_CHECKING:\n    import types\n    from collections.abc import Callable\n\n    from typing_extensions import Self\n# In ordinary single-threaded Python code, when you hit control-C, it raises\n# an exception and automatically does all the regular unwinding stuff.\n#\n# In Trio code, we would like hitting control-C to raise an exception and\n# automatically do all the regular unwinding stuff. In particular, we would\n# like to maintain our invariant that all tasks always run to completion (one\n# way or another), by unwinding all of them.\n#\n# But it's basically impossible to write the core task running code in such a\n# way that it can maintain this invariant in the face of KeyboardInterrupt\n# exceptions arising at arbitrary bytecode positions. Similarly, if a\n# KeyboardInterrupt happened at the wrong moment inside pretty much any of our\n# inter-task synchronization or I/O primitives, then the system state could\n# get corrupted and prevent our being able to clean up properly.\n#\n# So, we need a way to defer KeyboardInterrupt processing from these critical\n# sections.\n#\n# Things that don't work:\n#\n# - Listen for SIGINT and process it in a system task: works fine for\n#   well-behaved programs that regularly pass through the event loop, but if\n#   user-code goes into an infinite loop then it can't be interrupted. Which\n#   is unfortunate, since dealing with infinite loops is what\n#   KeyboardInterrupt is for!\n#\n# - Use pthread_sigmask to disable signal delivery during critical section:\n#   (a) windows has no pthread_sigmask, (b) python threads start with all\n#   signals unblocked, so if there are any threads around they'll receive the\n#   signal and then tell the main thread to run the handler, even if the main\n#   thread has that signal blocked.\n#\n# - Install a signal handler which checks a global variable to decide whether\n#   to raise the exception immediately (if we're in a non-critical section),\n#   or to schedule it on the event loop (if we're in a critical section). The\n#   problem here is that it's impossible to transition safely out of user code:\n#\n#     with keyboard_interrupt_enabled:\n#         msg = coro.send(value)\n#\n#   If this raises a KeyboardInterrupt, it might be because the coroutine got\n#   interrupted and has unwound... or it might be the KeyboardInterrupt\n#   arrived just *after* 'send' returned, so the coroutine is still running,\n#   but we just lost the message it sent. (And worse, in our actual task\n#   runner, the send is hidden inside a utility function etc.)\n#\n# Solution:\n#\n# Mark *stack frames* as being interrupt-safe or interrupt-unsafe, and from\n# the signal handler check which kind of frame we're currently in when\n# deciding whether to raise or schedule the exception.\n#\n# There are still some cases where this can fail, like if someone hits\n# control-C while the process is in the event loop, and then it immediately\n# enters an infinite loop in user code. In this case the user has to hit\n# control-C a second time. And of course if the user code is written so that\n# it doesn't actually exit after a task crashes and everything gets cancelled,\n# then there's not much to be done. (Hitting control-C repeatedly might help,\n# but in general the solution is to kill the process some other way, just like\n# for any Python program that's written to catch and ignore\n# KeyboardInterrupt.)\n\n_T = TypeVar(\"_T\")\n\n\nclass _IdRef(weakref.ref[_T]):\n    __slots__ = (\"_hash\",)\n    _hash: int\n\n    def __new__(\n        cls,\n        ob: _T,\n        callback: Callable[[Self], object] | None = None,\n        /,\n    ) -> Self:\n        self: Self = weakref.ref.__new__(cls, ob, callback)\n        self._hash = object.__hash__(ob)\n        return self\n\n    def __eq__(self, other: object) -> bool:\n        if self is other:\n            return True\n\n        if not isinstance(other, _IdRef):\n            return NotImplemented\n\n        my_obj = None\n        try:\n            my_obj = self()\n            return my_obj is not None and my_obj is other()\n        finally:\n            del my_obj\n\n    # we're overriding a builtin so we do need this\n    def __ne__(self, other: object) -> bool:\n        return not self == other\n\n    def __hash__(self) -> int:\n        return self._hash\n\n\n_KT = TypeVar(\"_KT\")\n_VT = TypeVar(\"_VT\")\n\n\n# see also: https://github.com/python/cpython/issues/88306\nclass WeakKeyIdentityDictionary(Generic[_KT, _VT]):\n    def __init__(self) -> None:\n        self._data: dict[_IdRef[_KT], _VT] = {}\n\n        def remove(\n            k: _IdRef[_KT],\n            selfref: weakref.ref[\n                WeakKeyIdentityDictionary[_KT, _VT]\n            ] = weakref.ref(  # noqa: B008  # function-call-in-default-argument\n                self,\n            ),\n        ) -> None:\n            self = selfref()\n            if self is not None:\n                try:  # noqa: SIM105  # suppressible-exception\n                    del self._data[k]\n                except KeyError:\n                    pass\n\n        self._remove = remove\n\n    def __getitem__(self, k: _KT) -> _VT:\n        return self._data[_IdRef(k)]\n\n    def __setitem__(self, k: _KT, v: _VT) -> None:\n        self._data[_IdRef(k, self._remove)] = v\n\n\n_CODE_KI_PROTECTION_STATUS_WMAP: WeakKeyIdentityDictionary[\n    types.CodeType,\n    bool,\n] = WeakKeyIdentityDictionary()\n\n\n# This is to support the async_generator package necessary for aclosing on <3.10\n# functions decorated @async_generator are given this magic property that's a\n# reference to the object itself\n# see python-trio/async_generator/async_generator/_impl.py\ndef legacy_isasyncgenfunction(\n    obj: object,\n) -> TypeGuard[Callable[..., types.AsyncGeneratorType[object, object]]]:\n    return getattr(obj, \"_async_gen_function\", None) == id(obj)\n\n\n# NB: according to the signal.signal docs, 'frame' can be None on entry to\n# this function:\ndef ki_protection_enabled(frame: types.FrameType | None) -> bool:\n    try:\n        task = GLOBAL_RUN_CONTEXT.task\n    except AttributeError:\n        task_ki_protected = False\n        task_frame = None\n    else:\n        task_ki_protected = task._ki_protected\n        task_frame = task.coro.cr_frame\n\n    while frame is not None:\n        try:\n            v = _CODE_KI_PROTECTION_STATUS_WMAP[frame.f_code]\n        except KeyError:\n            pass\n        else:\n            return bool(v)\n        if frame.f_code.co_name == \"__del__\":\n            return True\n        if frame is task_frame:\n            return task_ki_protected\n        frame = frame.f_back\n    return True\n\n\ndef currently_ki_protected() -> bool:\n    r\"\"\"Check whether the calling code has :exc:`KeyboardInterrupt` protection\n    enabled.\n\n    It's surprisingly easy to think that one's :exc:`KeyboardInterrupt`\n    protection is enabled when it isn't, or vice-versa. This function tells\n    you what Trio thinks of the matter, which makes it useful for ``assert``\\s\n    and unit tests.\n\n    Returns:\n      bool: True if protection is enabled, and False otherwise.\n\n    \"\"\"\n    return ki_protection_enabled(sys._getframe())\n\n\nclass _SupportsCode(Protocol):\n    __code__: types.CodeType\n\n\n_T_supports_code = TypeVar(\"_T_supports_code\", bound=_SupportsCode)\n\n\ndef enable_ki_protection(f: _T_supports_code, /) -> _T_supports_code:\n    \"\"\"Decorator to enable KI protection.\"\"\"\n    orig = f\n\n    if legacy_isasyncgenfunction(f):\n        f = f.__wrapped__  # type: ignore\n\n    _CODE_KI_PROTECTION_STATUS_WMAP[f.__code__] = True\n    return orig\n\n\ndef disable_ki_protection(f: _T_supports_code, /) -> _T_supports_code:\n    \"\"\"Decorator to disable KI protection.\"\"\"\n    orig = f\n\n    if legacy_isasyncgenfunction(f):\n        f = f.__wrapped__  # type: ignore\n\n    _CODE_KI_PROTECTION_STATUS_WMAP[f.__code__] = False\n    return orig\n\n\n@attrs.define(slots=False)\nclass KIManager:\n    handler: Callable[[int, types.FrameType | None], None] | None = None\n\n    def install(\n        self,\n        deliver_cb: Callable[[], object],\n        restrict_keyboard_interrupt_to_checkpoints: bool,\n    ) -> None:\n        assert self.handler is None\n        if (\n            not is_main_thread()\n            or signal.getsignal(signal.SIGINT) != signal.default_int_handler\n        ):\n            return\n\n        def handler(signum: int, frame: types.FrameType | None) -> None:\n            assert signum == signal.SIGINT\n            protection_enabled = ki_protection_enabled(frame)\n            if protection_enabled or restrict_keyboard_interrupt_to_checkpoints:\n                deliver_cb()\n            else:\n                raise KeyboardInterrupt\n\n        self.handler = handler\n        signal.signal(signal.SIGINT, handler)\n\n    def close(self) -> None:\n        if self.handler is not None:\n            if signal.getsignal(signal.SIGINT) is self.handler:\n                signal.signal(signal.SIGINT, signal.default_int_handler)\n            self.handler = None\n"
  },
  {
    "path": "src/trio/_core/_local.py",
    "content": "from __future__ import annotations\n\nfrom typing import Generic, TypeVar, cast\n\n# Runvar implementations\nimport attrs\n\nfrom .._util import NoPublicConstructor, final\nfrom . import _run\n\nT = TypeVar(\"T\")\n\n\n@final\nclass _NoValue: ...\n\n\n@final\n@attrs.define(eq=False)\nclass RunVarToken(Generic[T], metaclass=NoPublicConstructor):\n    _var: RunVar[T]\n    previous_value: T | type[_NoValue] = _NoValue\n    redeemed: bool = attrs.field(default=False, init=False)\n\n    @classmethod\n    def _empty(cls, var: RunVar[T]) -> RunVarToken[T]:\n        return cls._create(var)\n\n\n@final\n@attrs.define(eq=False, repr=False)\nclass RunVar(Generic[T]):\n    \"\"\"The run-local variant of a context variable.\n\n    :class:`RunVar` objects are similar to context variable objects,\n    except that they are shared across a single call to :func:`trio.run`\n    rather than a single task.\n\n    \"\"\"\n\n    _name: str = attrs.field(alias=\"name\")\n    _default: T | type[_NoValue] = attrs.field(default=_NoValue, alias=\"default\")\n\n    def get(self, default: T | type[_NoValue] = _NoValue) -> T:\n        \"\"\"Gets the value of this :class:`RunVar` for the current run call.\"\"\"\n        try:\n            return cast(\"T\", _run.GLOBAL_RUN_CONTEXT.runner._locals[self])\n        except AttributeError:\n            raise RuntimeError(\"Cannot be used outside of a run context\") from None\n        except KeyError:\n            # contextvars consistency\n            # `type: ignore` awaiting https://github.com/python/mypy/issues/15553 to be fixed & released\n            if default is not _NoValue:\n                return default  # type: ignore[return-value]\n\n            if self._default is not _NoValue:\n                return self._default  # type: ignore[return-value]\n\n            raise LookupError(self) from None\n\n    def set(self, value: T) -> RunVarToken[T]:\n        \"\"\"Sets the value of this :class:`RunVar` for this current run\n        call.\n\n        \"\"\"\n        try:\n            old_value = self.get()\n        except LookupError:\n            token = RunVarToken._empty(self)\n        else:\n            token = RunVarToken[T]._create(self, old_value)\n\n        # This can't fail, because if we weren't in Trio context then the\n        # get() above would have failed.\n        _run.GLOBAL_RUN_CONTEXT.runner._locals[self] = value\n        return token\n\n    def reset(self, token: RunVarToken[T]) -> None:\n        \"\"\"Resets the value of this :class:`RunVar` to what it was\n        previously specified by the token.\n\n        \"\"\"\n        if token is None:\n            raise TypeError(\"token must not be none\")\n\n        if token.redeemed:\n            raise ValueError(\"token has already been used\")\n\n        if token._var is not self:\n            raise ValueError(\"token is not for us\")\n\n        previous = token.previous_value\n        try:\n            if previous is _NoValue:\n                _run.GLOBAL_RUN_CONTEXT.runner._locals.pop(self)\n            else:\n                _run.GLOBAL_RUN_CONTEXT.runner._locals[self] = previous\n        except AttributeError:\n            raise RuntimeError(\"Cannot be used outside of a run context\") from None\n\n        token.redeemed = True\n\n    def __repr__(self) -> str:\n        return f\"<RunVar name={self._name!r}>\"\n"
  },
  {
    "path": "src/trio/_core/_mock_clock.py",
    "content": "import time\nfrom math import inf\n\nfrom .. import _core\nfrom .._abc import Clock\nfrom .._util import final\nfrom ._run import GLOBAL_RUN_CONTEXT\n\n################################################################\n# The glorious MockClock\n################################################################\n\n\n# Prior art:\n#   https://twistedmatrix.com/documents/current/api/twisted.internet.task.Clock.html\n#   https://github.com/ztellman/manifold/issues/57\n@final\nclass MockClock(Clock):\n    \"\"\"A user-controllable clock suitable for writing tests.\n\n    Args:\n      rate (float): the initial :attr:`rate`.\n      autojump_threshold (float): the initial :attr:`autojump_threshold`.\n\n    .. attribute:: rate\n\n       How many seconds of clock time pass per second of real time. Default is\n       0.0, i.e. the clock only advances through manuals calls to :meth:`jump`\n       or when the :attr:`autojump_threshold` is triggered. You can assign to\n       this attribute to change it.\n\n    .. attribute:: autojump_threshold\n\n       The clock keeps an eye on the run loop, and if at any point it detects\n       that all tasks have been blocked for this many real seconds (i.e.,\n       according to the actual clock, not this clock), then the clock\n       automatically jumps ahead to the run loop's next scheduled\n       timeout. Default is :data:`math.inf`, i.e., to never autojump. You can\n       assign to this attribute to change it.\n\n       Basically the idea is that if you have code or tests that use sleeps\n       and timeouts, you can use this to make it run much faster, totally\n       automatically. (At least, as long as those sleeps/timeouts are\n       happening inside Trio; if your test involves talking to external\n       service and waiting for it to timeout then obviously we can't help you\n       there.)\n\n       You should set this to the smallest value that lets you reliably avoid\n       \"false alarms\" where some I/O is in flight (e.g. between two halves of\n       a socketpair) but the threshold gets triggered and time gets advanced\n       anyway. This will depend on the details of your tests and test\n       environment. If you aren't doing any I/O (like in our sleeping example\n       above) then just set it to zero, and the clock will jump whenever all\n       tasks are blocked.\n\n       .. note:: If you use ``autojump_threshold`` and\n          `wait_all_tasks_blocked` at the same time, then you might wonder how\n          they interact, since they both cause things to happen after the run\n          loop goes idle for some time. The answer is:\n          `wait_all_tasks_blocked` takes priority. If there's a task blocked\n          in `wait_all_tasks_blocked`, then the autojump feature treats that\n          as active task and does *not* jump the clock.\n\n    \"\"\"\n\n    def __init__(self, rate: float = 0.0, autojump_threshold: float = inf) -> None:\n        # when the real clock said 'real_base', the virtual time was\n        # 'virtual_base', and since then it's advanced at 'rate' virtual\n        # seconds per real second.\n        self._real_base = 0.0\n        self._virtual_base = 0.0\n        self._rate = 0.0\n\n        # kept as an attribute so that our tests can monkeypatch it\n        self._real_clock = time.perf_counter\n\n        # use the property update logic to set initial values\n        self.rate = rate\n        self.autojump_threshold = autojump_threshold\n\n    def __repr__(self) -> str:\n        return f\"<MockClock, time={self.current_time():.7f}, rate={self._rate} @ {id(self):#x}>\"\n\n    @property\n    def rate(self) -> float:\n        return self._rate\n\n    @rate.setter\n    def rate(self, new_rate: float) -> None:\n        if new_rate < 0:\n            raise ValueError(\"rate must be >= 0\")\n        else:\n            real = self._real_clock()\n            virtual = self._real_to_virtual(real)\n            self._virtual_base = virtual\n            self._real_base = real\n            self._rate = float(new_rate)\n\n    @property\n    def autojump_threshold(self) -> float:\n        return self._autojump_threshold\n\n    @autojump_threshold.setter\n    def autojump_threshold(self, new_autojump_threshold: float) -> None:\n        self._autojump_threshold = float(new_autojump_threshold)\n        self._try_resync_autojump_threshold()\n\n    # runner.clock_autojump_threshold is an internal API that isn't easily\n    # usable by custom third-party Clock objects. If you need access to this\n    # functionality, let us know, and we'll figure out how to make a public\n    # API. Discussion:\n    #\n    #     https://github.com/python-trio/trio/issues/1587\n    def _try_resync_autojump_threshold(self) -> None:\n        try:\n            runner = GLOBAL_RUN_CONTEXT.runner\n            if runner.is_guest:\n                runner.force_guest_tick_asap()\n        except AttributeError:\n            pass\n        else:\n            if runner.clock is self:\n                runner.clock_autojump_threshold = self._autojump_threshold\n\n    # Invoked by the run loop when runner.clock_autojump_threshold is\n    # exceeded.\n    def _autojump(self) -> None:\n        statistics = _core.current_statistics()\n        jump = statistics.seconds_to_next_deadline\n        if 0 < jump < inf:\n            self.jump(jump)\n\n    def _real_to_virtual(self, real: float) -> float:\n        real_offset = real - self._real_base\n        virtual_offset = self._rate * real_offset\n        return self._virtual_base + virtual_offset\n\n    def start_clock(self) -> None:\n        self._try_resync_autojump_threshold()\n\n    def current_time(self) -> float:\n        return self._real_to_virtual(self._real_clock())\n\n    def deadline_to_sleep_time(self, deadline: float) -> float:\n        virtual_timeout = deadline - self.current_time()\n        if virtual_timeout <= 0:\n            return 0\n        elif self._rate > 0:\n            return virtual_timeout / self._rate\n        else:\n            return 999999999\n\n    def jump(self, seconds: float) -> None:\n        \"\"\"Manually advance the clock by the given number of seconds.\n\n        Args:\n          seconds (float): the number of seconds to jump the clock forward.\n\n        Raises:\n          ValueError: if you try to pass a negative value for ``seconds``.\n\n        \"\"\"\n        if seconds < 0:\n            raise ValueError(\"time can't go backwards\")\n        self._virtual_base += seconds\n"
  },
  {
    "path": "src/trio/_core/_parking_lot.py",
    "content": "# ParkingLot provides an abstraction for a fair waitqueue with cancellation\n# and requeuing support. Inspiration:\n#\n#    https://webkit.org/blog/6161/locking-in-webkit/\n#    https://amanieu.github.io/parking_lot/\n#\n# which were in turn heavily influenced by\n#\n#    http://gee.cs.oswego.edu/dl/papers/aqs.pdf\n#\n# Compared to these, our use of cooperative scheduling allows some\n# simplifications (no need for internal locking). On the other hand, the need\n# to support Trio's strong cancellation semantics adds some complications\n# (tasks need to know where they're queued so they can cancel). Also, in the\n# above work, the ParkingLot is a global structure that holds a collection of\n# waitqueues keyed by lock address, and which are opportunistically allocated\n# and destroyed as contention arises; this allows the worst-case memory usage\n# for all waitqueues to be O(#tasks). Here we allocate a separate wait queue\n# for each synchronization object, so we're O(#objects + #tasks). This isn't\n# *so* bad since compared to our synchronization objects are heavier than\n# theirs and our tasks are lighter, so for us #objects is smaller and #tasks\n# is larger.\n#\n# This is in the core because for two reasons. First, it's used by\n# UnboundedQueue, and UnboundedQueue is used for a number of things in the\n# core. And second, it's responsible for providing fairness to all of our\n# high-level synchronization primitives (locks, queues, etc.). For now with\n# our FIFO scheduler this is relatively trivial (it's just a FIFO waitqueue),\n# but in the future we ever start support task priorities or fair scheduling\n#\n#    https://github.com/python-trio/trio/issues/32\n#\n# then all we'll have to do is update this. (Well, full-fledged task\n# priorities might also require priority inheritance, which would require more\n# work.)\n#\n# For discussion of data structures to use here, see:\n#\n#     https://github.com/dabeaz/curio/issues/136\n#\n# (and also the articles above). Currently we use a SortedDict ordered by a\n# global monotonic counter that ensures FIFO ordering. The main advantage of\n# this is that it's easy to implement :-). An intrusive doubly-linked list\n# would also be a natural approach, so long as we only handle FIFO ordering.\n#\n# XX: should we switch to the shared global ParkingLot approach?\n#\n# XX: we should probably add support for \"parking tokens\" to allow for\n# task-fair RWlock (basically: when parking a task needs to be able to mark\n# itself as a reader or a writer, and then a task-fair wakeup policy is, wake\n# the next task, and if it's a reader than keep waking tasks so long as they\n# are readers). Without this I think you can implement write-biased or\n# read-biased RWlocks (by using two parking lots and drawing from whichever is\n# preferred), but not task-fair -- and task-fair plays much more nicely with\n# WFQ. (Consider what happens in the two-lot implementation if you're\n# write-biased but all the pending writers are blocked at the scheduler level\n# by the WFQ logic...)\n# ...alternatively, \"phase-fair\" RWlocks are pretty interesting:\n#    http://www.cs.unc.edu/~anderson/papers/ecrts09b.pdf\n# Useful summary:\n# https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html\n#\n# XX: if we do add WFQ, then we might have to drop the current feature where\n# unpark returns the tasks that were unparked. Rationale: suppose that at the\n# time we call unpark, the next task is deprioritized... and then, before it\n# becomes runnable, a new task parks which *is* runnable. Ideally we should\n# immediately wake the new task, and leave the old task on the queue for\n# later. But this means we can't commit to which task we are unparking when\n# unpark is called.\n#\n# See: https://github.com/python-trio/trio/issues/53\nfrom __future__ import annotations\n\nimport inspect\nimport math\nfrom collections import OrderedDict\nfrom typing import TYPE_CHECKING\n\nimport attrs\nimport outcome\n\nfrom .. import _core\nfrom .._util import final\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from ._run import Task\n\n\nGLOBAL_PARKING_LOT_BREAKER: dict[Task, list[ParkingLot]] = {}\n\n\ndef add_parking_lot_breaker(task: Task, lot: ParkingLot) -> None:\n    \"\"\"Register a task as a breaker for a lot. See :meth:`ParkingLot.break_lot`.\n\n    raises:\n      trio.BrokenResourceError: if the task has already exited.\n    \"\"\"\n    if inspect.getcoroutinestate(task.coro) == inspect.CORO_CLOSED:\n        raise _core._exceptions.BrokenResourceError(\n            \"Attempted to add already exited task as lot breaker.\",\n        )\n    if task not in GLOBAL_PARKING_LOT_BREAKER:\n        GLOBAL_PARKING_LOT_BREAKER[task] = [lot]\n    else:\n        GLOBAL_PARKING_LOT_BREAKER[task].append(lot)\n\n\ndef remove_parking_lot_breaker(task: Task, lot: ParkingLot) -> None:\n    \"\"\"Deregister a task as a breaker for a lot. See :meth:`ParkingLot.break_lot`\"\"\"\n    try:\n        GLOBAL_PARKING_LOT_BREAKER[task].remove(lot)\n    except (KeyError, ValueError):\n        raise RuntimeError(\n            \"Attempted to remove task as breaker for a lot it is not registered for\",\n        ) from None\n    if not GLOBAL_PARKING_LOT_BREAKER[task]:\n        del GLOBAL_PARKING_LOT_BREAKER[task]\n\n\n@attrs.frozen\nclass ParkingLotStatistics:\n    \"\"\"An object containing debugging information for a ParkingLot.\n\n    Currently, the following fields are defined:\n\n    * ``tasks_waiting`` (int): The number of tasks blocked on this lot's\n      :meth:`trio.lowlevel.ParkingLot.park` method.\n\n    \"\"\"\n\n    tasks_waiting: int\n\n\n@final\n@attrs.define(eq=False)\nclass ParkingLot:\n    \"\"\"A fair wait queue with cancellation and requeuing.\n\n    This class encapsulates the tricky parts of implementing a wait\n    queue. It's useful for implementing higher-level synchronization\n    primitives like queues and locks.\n\n    In addition to the methods below, you can use ``len(parking_lot)`` to get\n    the number of parked tasks, and ``if parking_lot: ...`` to check whether\n    there are any parked tasks.\n\n    \"\"\"\n\n    # {task: None}, we just want a deque where we can quickly delete random\n    # items\n    _parked: OrderedDict[Task, None] = attrs.field(factory=OrderedDict, init=False)\n    broken_by: list[Task] = attrs.field(factory=list, init=False)\n\n    def __len__(self) -> int:\n        \"\"\"Returns the number of parked tasks.\"\"\"\n        return len(self._parked)\n\n    def __bool__(self) -> bool:\n        \"\"\"True if there are parked tasks, False otherwise.\"\"\"\n        return bool(self._parked)\n\n    # XX this currently returns None\n    # if we ever add the ability to repark while one's resuming place in\n    # line (for false wakeups), then we could have it return a ticket that\n    # abstracts the \"place in line\" concept.\n    @_core.enable_ki_protection\n    async def park(self) -> None:\n        \"\"\"Park the current task until woken by a call to :meth:`unpark` or\n        :meth:`unpark_all`.\n\n        Raises:\n          BrokenResourceError: if attempting to park in a broken lot, or the lot\n            breaks before we get to unpark.\n\n        \"\"\"\n        if self.broken_by:\n            raise _core.BrokenResourceError(\n                f\"Attempted to park in parking lot broken by {self.broken_by}\",\n            )\n        task = _core.current_task()\n        self._parked[task] = None\n        task.custom_sleep_data = self\n\n        def abort_fn(_: _core.RaiseCancelT) -> _core.Abort:\n            del task.custom_sleep_data._parked[task]\n            return _core.Abort.SUCCEEDED\n\n        await _core.wait_task_rescheduled(abort_fn)\n\n    def _pop_several(self, count: int | float) -> Iterator[Task]:  # noqa: PYI041\n        if isinstance(count, float):\n            if math.isinf(count):\n                count = len(self._parked)\n            else:\n                raise ValueError(\"Cannot pop a non-integer number of tasks.\")\n        else:\n            count = min(count, len(self._parked))\n        for _ in range(count):\n            task, _ = self._parked.popitem(last=False)\n            yield task\n\n    @_core.enable_ki_protection\n    def unpark(self, *, count: int | float = 1) -> list[Task]:  # noqa: PYI041\n        \"\"\"Unpark one or more tasks.\n\n        This wakes up ``count`` tasks that are blocked in :meth:`park`. If\n        there are fewer than ``count`` tasks parked, then wakes as many tasks\n        are available and then returns successfully.\n\n        Args:\n          count (int | math.inf): the number of tasks to unpark.\n\n        \"\"\"\n        tasks = list(self._pop_several(count))\n        for task in tasks:\n            _core.reschedule(task)\n        return tasks\n\n    def unpark_all(self) -> list[Task]:\n        \"\"\"Unpark all parked tasks.\"\"\"\n        return self.unpark(count=len(self))\n\n    @_core.enable_ki_protection\n    def repark(\n        self,\n        new_lot: ParkingLot,\n        *,\n        count: int | float = 1,  # noqa: PYI041\n    ) -> None:\n        \"\"\"Move parked tasks from one :class:`ParkingLot` object to another.\n\n        This dequeues ``count`` tasks from one lot, and requeues them on\n        another, preserving order. For example::\n\n           async def parker(lot):\n               print(\"sleeping\")\n               await lot.park()\n               print(\"woken\")\n\n           async def main():\n               lot1 = trio.lowlevel.ParkingLot()\n               lot2 = trio.lowlevel.ParkingLot()\n               async with trio.open_nursery() as nursery:\n                   nursery.start_soon(parker, lot1)\n                   await trio.testing.wait_all_tasks_blocked()\n                   assert len(lot1) == 1\n                   assert len(lot2) == 0\n                   lot1.repark(lot2)\n                   assert len(lot1) == 0\n                   assert len(lot2) == 1\n                   # This wakes up the task that was originally parked in lot1\n                   lot2.unpark()\n\n        If there are fewer than ``count`` tasks parked, then reparks as many\n        tasks as are available and then returns successfully.\n\n        Args:\n          new_lot (ParkingLot): the parking lot to move tasks to.\n          count (int|math.inf): the number of tasks to move.\n\n        \"\"\"\n        if not isinstance(new_lot, ParkingLot):\n            raise TypeError(\"new_lot must be a ParkingLot\")\n        for task in self._pop_several(count):\n            new_lot._parked[task] = None\n            task.custom_sleep_data = new_lot\n\n    def repark_all(self, new_lot: ParkingLot) -> None:\n        \"\"\"Move all parked tasks from one :class:`ParkingLot` object to\n        another.\n\n        See :meth:`repark` for details.\n\n        \"\"\"\n        return self.repark(new_lot, count=len(self))\n\n    def break_lot(self, task: Task | None = None) -> None:\n        \"\"\"Break this lot, with ``task`` noted as the task that broke it.\n\n        This causes all parked tasks to raise an error, and any\n        future tasks attempting to park to error. Unpark & repark become no-ops as the\n        parking lot is empty.\n\n        The error raised contains a reference to the task sent as a parameter. The task\n        is also saved in the parking lot in the ``broken_by`` attribute.\n        \"\"\"\n        if task is None:\n            task = _core.current_task()\n\n        # if lot is already broken, just mark this as another breaker and return\n        if self.broken_by:\n            self.broken_by.append(task)\n            return\n\n        self.broken_by.append(task)\n\n        for parked_task in self._parked:\n            _core.reschedule(\n                parked_task,\n                outcome.Error(\n                    _core.BrokenResourceError(f\"Parking lot broken by {task}\"),\n                ),\n            )\n        self._parked.clear()\n\n    def statistics(self) -> ParkingLotStatistics:\n        \"\"\"Return an object containing debugging information.\n\n        Currently the following fields are defined:\n\n        * ``tasks_waiting``: The number of tasks blocked on this lot's\n          :meth:`park` method.\n\n        \"\"\"\n        return ParkingLotStatistics(tasks_waiting=len(self._parked))\n"
  },
  {
    "path": "src/trio/_core/_run.py",
    "content": "from __future__ import annotations\n\nimport enum\nimport functools\nimport gc\nimport itertools\nimport random\nimport select\nimport sys\nimport warnings\nfrom collections import deque\nfrom contextlib import AbstractAsyncContextManager, contextmanager, suppress\nfrom contextvars import copy_context\nfrom heapq import heapify, heappop, heappush\nfrom math import inf, isnan\nfrom time import perf_counter\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Final,\n    NoReturn,\n    Protocol,\n    cast,\n    overload,\n)\n\nimport attrs\nimport outcome\nfrom outcome import Error, Outcome, Value, capture\nfrom sniffio import thread_local as sniffio_library\nfrom sortedcontainers import SortedDict\n\nfrom .. import _core\nfrom .._abc import Clock, Instrument\nfrom .._deprecate import warn_deprecated\nfrom .._util import NoPublicConstructor, coroutine_or_error, final, raise_saving_context\nfrom ._asyncgens import AsyncGenerators\nfrom ._concat_tb import concat_tb\nfrom ._entry_queue import EntryQueue, TrioToken\nfrom ._exceptions import (\n    Cancelled,\n    CancelReasonLiteral,\n    RunFinishedError,\n    TrioInternalError,\n)\nfrom ._instrumentation import Instruments\nfrom ._ki import KIManager, enable_ki_protection\nfrom ._parking_lot import GLOBAL_PARKING_LOT_BREAKER\nfrom ._run_context import GLOBAL_RUN_CONTEXT as GLOBAL_RUN_CONTEXT\nfrom ._thread_cache import start_thread_soon\nfrom ._traps import (\n    Abort,\n    CancelShieldedCheckpoint,\n    PermanentlyDetachCoroutineObject,\n    WaitTaskRescheduled,\n    cancel_shielded_checkpoint,\n    wait_task_rescheduled,\n)\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup\n\n\nif TYPE_CHECKING:\n    import contextvars\n    import types\n    from collections.abc import (\n        Awaitable,\n        Callable,\n        Generator,\n        Iterator,\n        Sequence,\n    )\n    from types import TracebackType\n\n    # for some strange reason Sphinx works with outcome.Outcome, but not Outcome, in\n    # start_guest_run. Same with types.FrameType in iter_await_frames\n    import outcome\n    from typing_extensions import Self, TypeVar, TypeVarTuple, Unpack\n\n    PosArgT = TypeVarTuple(\"PosArgT\")\n    StatusT = TypeVar(\"StatusT\", default=None)\n    StatusT_contra = TypeVar(\"StatusT_contra\", contravariant=True, default=None)\n    BaseExcT = TypeVar(\"BaseExcT\", bound=BaseException)\nelse:\n    from typing import TypeVar\n\n    StatusT = TypeVar(\"StatusT\")\n    StatusT_contra = TypeVar(\"StatusT_contra\", contravariant=True)\n\nRetT = TypeVar(\"RetT\")\n\n\nDEADLINE_HEAP_MIN_PRUNE_THRESHOLD: Final = 1000\n\n# Passed as a sentinel\n_NO_SEND: Final[Outcome[object]] = cast(\"Outcome[object]\", object())\n\n# Used to track if an exceptiongroup can be collapsed\nNONSTRICT_EXCEPTIONGROUP_NOTE = 'This is a \"loose\" ExceptionGroup, and may be collapsed by Trio if it only contains one exception - typically after `Cancelled` has been stripped from it. Note this has consequences for exception handling, and strict_exception_groups=True is recommended.'\n\n\n@final\nclass _NoStatus(metaclass=NoPublicConstructor):\n    \"\"\"Sentinel for unset TaskStatus._value.\"\"\"\n\n\n# Decorator to mark methods public. This does nothing by itself, but\n# trio/_tools/gen_exports.py looks for it.\ndef _public(fn: RetT) -> RetT:\n    return fn\n\n\n# When running under Hypothesis, we want examples to be reproducible and\n# shrinkable.  We therefore register `_hypothesis_plugin_setup()` as a\n# plugin, so that importing *Hypothesis* will make Trio's task\n# scheduling loop deterministic.  We have a test for that, of course.\n# Before Hypothesis supported entry-point plugins this integration was\n# handled by pytest-trio, but we want it to work in e.g. unittest too.\n_ALLOW_DETERMINISTIC_SCHEDULING: Final = False\n_r = random.Random()\n\n\n# no cover because we don't check the hypothesis plugin works with hypothesis\ndef _hypothesis_plugin_setup() -> None:  # pragma: no cover\n    from hypothesis import register_random\n\n    global _ALLOW_DETERMINISTIC_SCHEDULING\n    _ALLOW_DETERMINISTIC_SCHEDULING = True  # type: ignore\n    register_random(_r)\n\n    # monkeypatch repr_callable to make repr's way better\n    # requires importing hypothesis (in the test file or in conftest.py)\n    try:\n        from hypothesis.internal.reflection import get_pretty_function_description\n\n        import trio.testing._raises_group\n\n        def repr_callable(fun: Callable[[BaseExcT], bool]) -> str:\n            # add quotes around the signature\n            return repr(get_pretty_function_description(fun))\n\n        trio.testing._raises_group.repr_callable = repr_callable\n    except ImportError:\n        pass\n\n\ndef _count_context_run_tb_frames() -> int:\n    \"\"\"Count implementation dependent traceback frames from Context.run()\n\n    On CPython, Context.run() is implemented in C and doesn't show up in\n    tracebacks. On PyPy, it is implemented in Python and adds 1 frame to\n    tracebacks.\n\n    Returns:\n        int: Traceback frame count\n\n    \"\"\"\n\n    def function_with_unique_name_xyzzy() -> NoReturn:\n        try:\n            1 / 0  # noqa: B018  # We need a ZeroDivisionError to fire\n        except ZeroDivisionError:\n            raise\n        else:  # pragma: no cover\n            raise TrioInternalError(\n                \"A ZeroDivisionError should have been raised, but it wasn't.\",\n            )\n\n    ctx = copy_context()\n    try:\n        ctx.run(function_with_unique_name_xyzzy)\n    except ZeroDivisionError as exc:\n        tb = exc.__traceback__\n        # Skip the frame where we caught it\n        tb = tb.tb_next  # type: ignore[union-attr]\n        count = 0\n        while tb.tb_frame.f_code.co_name != \"function_with_unique_name_xyzzy\":  # type: ignore[union-attr]\n            tb = tb.tb_next  # type: ignore[union-attr]\n            count += 1\n        return count\n    else:  # pragma: no cover\n        raise TrioInternalError(\n            f\"The purpose of {function_with_unique_name_xyzzy.__name__} is \"\n            \"to raise a ZeroDivisionError, but it didn't.\",\n        )\n\n\nCONTEXT_RUN_TB_FRAMES: Final = _count_context_run_tb_frames()\n\n\n@attrs.frozen\nclass SystemClock(Clock):\n    # Add a large random offset to our clock to ensure that if people\n    # accidentally call time.perf_counter() directly or start comparing clocks\n    # between different runs, then they'll notice the bug quickly:\n    offset: float = attrs.Factory(lambda: _r.uniform(10000, 200000))\n\n    def start_clock(self) -> None:\n        pass\n\n    # In cPython 3, on every platform except Windows, perf_counter is\n    # exactly the same as time.monotonic; and on Windows, it uses\n    # QueryPerformanceCounter instead of GetTickCount64.\n    def current_time(self) -> float:\n        return self.offset + perf_counter()\n\n    def deadline_to_sleep_time(self, deadline: float) -> float:\n        return deadline - self.current_time()\n\n\nclass IdlePrimedTypes(enum.Enum):\n    WAITING_FOR_IDLE = 1\n    AUTOJUMP_CLOCK = 2\n\n\n################################################################\n# CancelScope and friends\n################################################################\n\n\ndef collapse_exception_group(\n    excgroup: BaseExceptionGroup[BaseException],\n) -> BaseException:\n    \"\"\"Recursively collapse any single-exception groups into that single contained\n    exception.\n\n    \"\"\"\n    exceptions = list(excgroup.exceptions)\n    modified = False\n    for i, exc in enumerate(exceptions):\n        if isinstance(exc, BaseExceptionGroup):\n            new_exc = collapse_exception_group(exc)\n            if new_exc is not exc:\n                modified = True\n                exceptions[i] = new_exc\n\n    if (\n        len(exceptions) == 1\n        and isinstance(excgroup, BaseExceptionGroup)\n        and NONSTRICT_EXCEPTIONGROUP_NOTE in getattr(excgroup, \"__notes__\", ())\n    ):\n        exceptions[0].__traceback__ = concat_tb(\n            excgroup.__traceback__,\n            exceptions[0].__traceback__,\n        )\n        return exceptions[0]\n    elif modified:\n        return excgroup.derive(exceptions)\n    else:\n        return excgroup\n\n\n@attrs.define(eq=False)\nclass Deadlines:\n    \"\"\"A container of deadlined cancel scopes.\n\n    Only contains scopes with non-infinite deadlines that are currently\n    attached to at least one task.\n\n    \"\"\"\n\n    # Heap of (deadline, id(CancelScope), CancelScope)\n    _heap: list[tuple[float, int, CancelScope]] = attrs.Factory(list)\n    # Count of active deadlines (those that haven't been changed)\n    _active: int = 0\n\n    def add(self, deadline: float, cancel_scope: CancelScope) -> None:\n        heappush(self._heap, (deadline, id(cancel_scope), cancel_scope))\n        self._active += 1\n\n    def remove(self, deadline: float, cancel_scope: CancelScope) -> None:\n        self._active -= 1\n\n    def next_deadline(self) -> float:\n        while self._heap:\n            deadline, _, cancel_scope = self._heap[0]\n            if deadline == cancel_scope._registered_deadline:\n                return deadline\n            else:\n                # This entry is stale; discard it and try again\n                heappop(self._heap)\n        return inf\n\n    def _prune(self) -> None:\n        # In principle, it's possible for a cancel scope to toggle back and\n        # forth repeatedly between the same two deadlines, and end up with\n        # lots of stale entries that *look* like they're still active, because\n        # their deadline is correct, but in fact are redundant. So when\n        # pruning we have to eliminate entries with the wrong deadline, *and*\n        # eliminate duplicates.\n        seen = set()\n        pruned_heap = []\n        for deadline, tiebreaker, cancel_scope in self._heap:\n            if deadline == cancel_scope._registered_deadline:\n                if cancel_scope in seen:\n                    continue\n                seen.add(cancel_scope)\n                pruned_heap.append((deadline, tiebreaker, cancel_scope))\n        # See test_cancel_scope_deadline_duplicates for a test that exercises\n        # this assert:\n        assert len(pruned_heap) == self._active\n        heapify(pruned_heap)\n        self._heap = pruned_heap\n\n    def expire(self, now: float) -> bool:\n        did_something = False\n        while self._heap and self._heap[0][0] <= now:\n            deadline, _, cancel_scope = heappop(self._heap)\n            if deadline == cancel_scope._registered_deadline:\n                did_something = True\n                # This implicitly calls self.remove(), so we don't need to\n                # decrement _active here\n                cancel_scope._cancel(CancelReason(source=\"deadline\"))\n        # If we've accumulated too many stale entries, then prune the heap to\n        # keep it under control. (We only do this occasionally in a batch, to\n        # keep the amortized cost down)\n        if len(self._heap) > self._active * 2 + DEADLINE_HEAP_MIN_PRUNE_THRESHOLD:\n            self._prune()\n        return did_something\n\n\n@attrs.define\nclass CancelReason:\n    \"\"\"Attached to a :class:`CancelScope` upon cancellation with details of the source of the\n    cancellation, which is then used to construct the string in a :exc:`Cancelled`.\n    Users can pass a ``reason`` str to :meth:`CancelScope.cancel` to set it.\n\n    Not publicly exported or documented.\n    \"\"\"\n\n    source: CancelReasonLiteral\n    source_task: str | None = None\n    reason: str | None = None\n\n\n@attrs.define(eq=False)\nclass CancelStatus:\n    \"\"\"Tracks the cancellation status for a contiguous extent\n    of code that will become cancelled, or not, as a unit.\n\n    Each task has at all times a single \"active\" CancelStatus whose\n    cancellation state determines whether checkpoints executed in that\n    task raise Cancelled. Each 'with CancelScope(...)' context is\n    associated with a particular CancelStatus.  When a task enters\n    such a context, a CancelStatus is created which becomes the active\n    CancelStatus for that task; when the 'with' block is exited, the\n    active CancelStatus for that task goes back to whatever it was\n    before.\n\n    CancelStatus objects are arranged in a tree whose structure\n    mirrors the lexical nesting of the cancel scope contexts.  When a\n    CancelStatus becomes cancelled, it notifies all of its direct\n    children, who become cancelled in turn (and continue propagating\n    the cancellation down the tree) unless they are shielded. (There\n    will be at most one such child except in the case of a\n    CancelStatus that immediately encloses a nursery.) At the leaves\n    of this tree are the tasks themselves, which get woken up to deliver\n    an abort when their direct parent CancelStatus becomes cancelled.\n\n    You can think of CancelStatus as being responsible for the\n    \"plumbing\" of cancellations as opposed to CancelScope which is\n    responsible for the origination of them.\n\n    \"\"\"\n\n    # Our associated cancel scope. Can be any object with attributes\n    # `deadline`, `shield`, and `cancel_called`, but in current usage\n    # is always a CancelScope object. Must not be None.\n    _scope: CancelScope = attrs.field(alias=\"scope\")\n\n    # True iff the tasks in self._tasks should receive cancellations\n    # when they checkpoint. Always True when scope.cancel_called is True;\n    # may also be True due to a cancellation propagated from our\n    # parent.  Unlike scope.cancel_called, this does not necessarily stay\n    # true once it becomes true. For example, we might become\n    # effectively cancelled due to the cancel scope two levels out\n    # becoming cancelled, but then the cancel scope one level out\n    # becomes shielded so we're not effectively cancelled anymore.\n    effectively_cancelled: bool = False\n\n    # The CancelStatus whose cancellations can propagate to us; we\n    # become effectively cancelled when they do, unless scope.shield\n    # is True.  May be None (for the outermost CancelStatus in a call\n    # to trio.run(), briefly during TaskStatus.started(), or during\n    # recovery from misnesting of cancel scopes).\n    _parent: CancelStatus | None = attrs.field(default=None, repr=False, alias=\"parent\")\n\n    # All of the CancelStatuses that have this CancelStatus as their parent.\n    _children: set[CancelStatus] = attrs.field(factory=set, init=False, repr=False)\n\n    # Tasks whose cancellation state is currently tied directly to\n    # the cancellation state of this CancelStatus object. Don't modify\n    # this directly; instead, use Task._activate_cancel_status().\n    # Invariant: all(task._cancel_status is self for task in self._tasks)\n    _tasks: set[Task] = attrs.field(factory=set, init=False, repr=False)\n\n    # Set to True on still-active cancel statuses that are children\n    # of a cancel status that's been closed. This is used to permit\n    # recovery from misnested cancel scopes (well, at least enough\n    # recovery to show a useful traceback).\n    abandoned_by_misnesting: bool = attrs.field(default=False, init=False, repr=False)\n\n    def __attrs_post_init__(self) -> None:\n        if self._parent is not None:\n            self._parent._children.add(self)\n            self.recalculate()\n\n    # parent/children/tasks accessors are used by TaskStatus.started()\n\n    @property\n    def parent(self) -> CancelStatus | None:\n        return self._parent\n\n    @parent.setter\n    def parent(self, parent: CancelStatus | None) -> None:\n        if self._parent is not None:\n            self._parent._children.remove(self)\n        self._parent = parent\n        if self._parent is not None:\n            self._parent._children.add(self)\n            self.recalculate()\n\n    @property\n    def children(self) -> frozenset[CancelStatus]:\n        return frozenset(self._children)\n\n    @property\n    def tasks(self) -> frozenset[Task]:\n        return frozenset(self._tasks)\n\n    def encloses(self, other: CancelStatus | None) -> bool:\n        \"\"\"Returns true if this cancel status is a direct or indirect\n        parent of cancel status *other*, or if *other* is *self*.\n        \"\"\"\n        while other is not None:\n            if other is self:\n                return True\n            other = other.parent\n        return False\n\n    def close(self) -> None:\n        self.parent = None  # now we're not a child of self.parent anymore\n        if self._tasks or self._children:\n            # Cancel scopes weren't exited in opposite order of being\n            # entered. CancelScope._close() deals with raising an error\n            # if appropriate; our job is to leave things in a reasonable\n            # state for unwinding our dangling children. We choose to leave\n            # this part of the CancelStatus tree unlinked from everyone\n            # else, cancelled, and marked so that exiting a CancelScope\n            # within the abandoned subtree doesn't affect the active\n            # CancelStatus. Note that it's possible for us to get here\n            # without CancelScope._close() raising an error, if a\n            # nursery's cancel scope is closed within the nursery's\n            # nested child and no other cancel scopes are involved,\n            # but in that case task_exited() will deal with raising\n            # the error.\n            self._mark_abandoned()\n\n            # Since our CancelScope is about to forget about us, and we\n            # have no parent anymore, there's nothing left to call\n            # recalculate(). So, we can stay cancelled by setting\n            # effectively_cancelled and updating our children.\n            self.effectively_cancelled = True\n            for task in self._tasks:\n                task._attempt_delivery_of_any_pending_cancel()\n            for child in self._children:\n                child.recalculate()\n\n    @property\n    def parent_cancellation_is_visible_to_us(self) -> bool:\n        return (\n            self._parent is not None\n            and not self._scope.shield\n            and self._parent.effectively_cancelled\n        )\n\n    def recalculate(self) -> None:\n        # This does a depth-first traversal over this and descendent cancel\n        # statuses, to ensure their state is up-to-date. It's basically a\n        # recursive algorithm, but we use an explicit stack to avoid any\n        # issues with stack overflow.\n        todo = [self]\n        while todo:\n            current = todo.pop()\n            new_state = (\n                current._scope.cancel_called\n                or current.parent_cancellation_is_visible_to_us\n            )\n            if new_state != current.effectively_cancelled:\n                if (\n                    current._scope._cancel_reason is None\n                    and current.parent_cancellation_is_visible_to_us\n                ):\n                    assert current._parent is not None\n                    current._scope._cancel_reason = (\n                        current._parent._scope._cancel_reason\n                    )\n                current.effectively_cancelled = new_state\n                if new_state:\n                    for task in current._tasks:\n                        task._attempt_delivery_of_any_pending_cancel()\n                todo.extend(current._children)\n\n    def _mark_abandoned(self) -> None:\n        self.abandoned_by_misnesting = True\n        for child in self._children:\n            child._mark_abandoned()\n\n    def effective_deadline(self) -> float:\n        if self.effectively_cancelled:\n            return -inf\n        if self._parent is None or self._scope.shield:\n            return self._scope.deadline\n        return min(self._scope.deadline, self._parent.effective_deadline())\n\n\nMISNESTING_ADVICE = \"\"\"\nThis is probably a bug in your code, that has caused Trio's internal state to\nbecome corrupted. We'll do our best to recover, but from now on there are\nno guarantees.\n\nTypically this is caused by one of the following:\n  - yielding within a generator or async generator that's opened a cancel\n    scope or nursery (unless the generator is a @contextmanager or\n    @asynccontextmanager); see https://github.com/python-trio/trio/issues/638\n  - manually calling __enter__ or __exit__ on a trio.CancelScope, or\n    __aenter__ or __aexit__ on the object returned by trio.open_nursery();\n    doing so correctly is difficult and you should use @[async]contextmanager\n    instead, or maybe [Async]ExitStack\n  - using [Async]ExitStack to interleave the entries/exits of cancel scopes\n    and/or nurseries in a way that couldn't be achieved by some nesting of\n    'with' and 'async with' blocks\n  - using the low-level coroutine object protocol to execute some parts of\n    an async function in a different cancel scope/nursery context than\n    other parts\nIf you don't believe you're doing any of these things, please file a bug:\nhttps://github.com/python-trio/trio/issues/new\n\"\"\"\n\n\n@final\n@attrs.define(eq=False, repr=False)\nclass CancelScope:\n    \"\"\"A *cancellation scope*: the link between a unit of cancellable\n    work and Trio's cancellation system.\n\n    A :class:`CancelScope` becomes associated with some cancellable work\n    when it is used as a context manager surrounding that work::\n\n        cancel_scope = trio.CancelScope()\n        ...\n        with cancel_scope:\n            await long_running_operation()\n\n    Inside the ``with`` block, a cancellation of ``cancel_scope`` (via\n    a call to its :meth:`cancel` method or via the expiry of its\n    :attr:`deadline`) will immediately interrupt the\n    ``long_running_operation()`` by raising :exc:`Cancelled` at its\n    next :ref:`checkpoint <checkpoints>`.\n\n    The context manager ``__enter__`` returns the :class:`CancelScope`\n    object itself, so you can also write ``with trio.CancelScope() as\n    cancel_scope:``.\n\n    If a cancel scope becomes cancelled before entering its ``with`` block,\n    the :exc:`Cancelled` exception will be raised at the first\n    checkpoint inside the ``with`` block. This allows a\n    :class:`CancelScope` to be created in one :ref:`task <tasks>` and\n    passed to another, so that the first task can later cancel some work\n    inside the second.\n\n    Cancel scopes are not reusable or reentrant; that is, each cancel\n    scope can be used for at most one ``with`` block.  (You'll get a\n    :exc:`RuntimeError` if you violate this rule.)\n\n    The :class:`CancelScope` constructor takes initial values for the\n    cancel scope's :attr:`deadline` and :attr:`shield` attributes; these\n    may be freely modified after construction, whether or not the scope\n    has been entered yet, and changes take immediate effect.\n    \"\"\"\n\n    _cancel_status: CancelStatus | None = attrs.field(default=None, init=False)\n    _has_been_entered: bool = attrs.field(default=False, init=False)\n    _registered_deadline: float = attrs.field(default=inf, init=False)\n    _cancel_called: bool = attrs.field(default=False, init=False)\n    cancelled_caught: bool = attrs.field(default=False, init=False)\n\n    _cancel_reason: CancelReason | None = attrs.field(default=None, init=False)\n\n    # Constructor arguments:\n    _relative_deadline: float = attrs.field(\n        default=inf,\n        kw_only=True,\n        alias=\"relative_deadline\",\n    )\n    _deadline: float = attrs.field(default=inf, kw_only=True, alias=\"deadline\")\n    _shield: bool = attrs.field(default=False, kw_only=True, alias=\"shield\")\n\n    def __attrs_post_init__(self) -> None:\n        if isnan(self._deadline):\n            raise ValueError(\"deadline must not be NaN\")\n        if isnan(self._relative_deadline):\n            raise ValueError(\"relative deadline must not be NaN\")\n        if self._relative_deadline < 0:\n            raise ValueError(\"timeout must be non-negative\")\n        if self._relative_deadline != inf and self._deadline != inf:\n            raise ValueError(\n                \"Cannot specify both a deadline and a relative deadline\",\n            )\n\n    @enable_ki_protection\n    def __enter__(self) -> Self:\n        task = _core.current_task()\n        if self._has_been_entered:\n            raise RuntimeError(\n                \"Each CancelScope may only be used for a single 'with' block\",\n            )\n        self._has_been_entered = True\n\n        if self._relative_deadline != inf:\n            assert self._deadline == inf\n            self._deadline = current_time() + self._relative_deadline\n            self._relative_deadline = inf\n\n        if current_time() >= self._deadline:\n            self._cancel(CancelReason(source=\"deadline\"))\n        with self._might_change_registered_deadline():\n            self._cancel_status = CancelStatus(scope=self, parent=task._cancel_status)\n            task._activate_cancel_status(self._cancel_status)\n        return self\n\n    def _close(self, exc: BaseException | None) -> BaseException | None:\n        if self._cancel_status is None:\n            new_exc = RuntimeError(\n                f\"Cancel scope stack corrupted: attempted to exit {self!r} \"\n                \"which had already been exited\",\n            )\n            new_exc.__context__ = exc\n            return new_exc\n        scope_task = current_task()\n        if scope_task._cancel_status is not self._cancel_status:\n            # Cancel scope misnesting: this cancel scope isn't the most\n            # recently opened by this task (that's still open). That is,\n            # our assumptions about context managers forming a stack\n            # have been violated. Try and make the best of it.\n            if self._cancel_status.abandoned_by_misnesting:\n                # We are an inner cancel scope that was still active when\n                # some outer scope was closed. The closure of that outer\n                # scope threw an error, so we don't need to throw another\n                # one; it would just confuse the traceback.\n                pass\n            elif not self._cancel_status.encloses(scope_task._cancel_status):\n                # This task isn't even indirectly contained within the\n                # cancel scope it's trying to close. Raise an error\n                # without changing any state.\n                new_exc = RuntimeError(\n                    f\"Cancel scope stack corrupted: attempted to exit {self!r} \"\n                    f\"from unrelated {scope_task!r}\\n{MISNESTING_ADVICE}\",\n                )\n                new_exc.__context__ = exc\n                return new_exc\n            else:\n                # Otherwise, there's some inner cancel scope(s) that\n                # we're abandoning by closing this outer one.\n                # CancelStatus.close() will take care of the plumbing;\n                # we just need to make sure we don't let the error\n                # pass silently.\n                new_exc = RuntimeError(\n                    f\"Cancel scope stack corrupted: attempted to exit {self!r} \"\n                    f\"in {scope_task!r} that's still within its child {scope_task._cancel_status._scope!r}\\n{MISNESTING_ADVICE}\",\n                )\n                new_exc.__context__ = exc\n                exc = new_exc\n                scope_task._activate_cancel_status(self._cancel_status.parent)\n        else:\n            scope_task._activate_cancel_status(self._cancel_status.parent)\n        if (\n            exc is not None\n            and self._cancel_status.effectively_cancelled\n            and not self._cancel_status.parent_cancellation_is_visible_to_us\n        ) or (\n            scope_task._cancel_status is not self._cancel_status\n            and self._cancel_status.abandoned_by_misnesting\n        ):\n            if isinstance(exc, Cancelled):\n                self.cancelled_caught = True\n                exc = None\n            elif isinstance(exc, BaseExceptionGroup):\n                matched, exc = exc.split(Cancelled)\n                if matched:\n                    self.cancelled_caught = True\n\n                if exc:\n                    exc = collapse_exception_group(exc)\n\n        self._cancel_status.close()\n        with self._might_change_registered_deadline():\n            self._cancel_status = None\n        return exc\n\n    @enable_ki_protection\n    def __exit__(\n        self,\n        etype: type[BaseException] | None,\n        exc: BaseException | None,\n        tb: TracebackType | None,\n    ) -> bool:\n        # NB: NurseryManager calls _close() directly rather than __exit__(),\n        # so __exit__() must be just _close() plus this logic for adapting\n        # the exception-filtering result to the context manager API.\n\n        # Tracebacks show the 'raise' line below out of context, so let's give\n        # this variable a name that makes sense out of context.\n        remaining_error_after_cancel_scope = self._close(exc)\n        if remaining_error_after_cancel_scope is None:\n            return True\n        elif remaining_error_after_cancel_scope is exc:\n            return False\n        else:\n            # Copied verbatim from the old MultiErrorCatcher.  Python doesn't\n            # allow us to encapsulate this __context__ fixup.\n            old_context = remaining_error_after_cancel_scope.__context__\n            try:\n                raise remaining_error_after_cancel_scope\n            finally:\n                _, value, _ = sys.exc_info()\n                assert value is remaining_error_after_cancel_scope\n                value.__context__ = old_context\n                # delete references from locals to avoid creating cycles\n                # see test_cancel_scope_exit_doesnt_create_cyclic_garbage\n                # Note: still relevant\n                del remaining_error_after_cancel_scope, value, _, exc\n\n    def __repr__(self) -> str:\n        if self._cancel_status is not None:\n            binding = \"active\"\n        elif self._has_been_entered:\n            binding = \"exited\"\n        else:\n            binding = \"unbound\"\n\n        if self._cancel_called:\n            state = \", cancelled\"\n        elif self._deadline == inf:\n            state = \"\"\n        else:\n            try:\n                now = current_time()\n            except RuntimeError:  # must be called from async context\n                state = \"\"\n            else:\n                state = \", deadline is {:.2f} seconds {}\".format(\n                    abs(self._deadline - now),\n                    \"from now\" if self._deadline >= now else \"ago\",\n                )\n\n        return f\"<trio.CancelScope at {id(self):#x}, {binding}{state}>\"\n\n    @contextmanager\n    @enable_ki_protection\n    def _might_change_registered_deadline(self) -> Iterator[None]:\n        try:\n            yield\n        finally:\n            old = self._registered_deadline\n            if self._cancel_status is None or self._cancel_called:\n                new = inf\n            else:\n                new = self._deadline\n            if old != new:\n                self._registered_deadline = new\n                runner = GLOBAL_RUN_CONTEXT.runner\n                if runner.is_guest:\n                    old_next_deadline = runner.deadlines.next_deadline()\n                if old != inf:\n                    runner.deadlines.remove(old, self)\n                if new != inf:\n                    runner.deadlines.add(new, self)\n                if runner.is_guest:\n                    new_next_deadline = runner.deadlines.next_deadline()\n                    if old_next_deadline != new_next_deadline:\n                        runner.force_guest_tick_asap()\n\n    @property\n    def deadline(self) -> float:\n        \"\"\"Read-write, :class:`float`. An absolute time on the current\n        run's clock at which this scope will automatically become\n        cancelled. You can adjust the deadline by modifying this\n        attribute, e.g.::\n\n           # I need a little more time!\n           cancel_scope.deadline += 30\n\n        Note that for efficiency, the core run loop only checks for\n        expired deadlines every once in a while. This means that in\n        certain cases there may be a short delay between when the clock\n        says the deadline should have expired, and when checkpoints\n        start raising :exc:`~trio.Cancelled`. This is a very obscure\n        corner case that you're unlikely to notice, but we document it\n        for completeness. (If this *does* cause problems for you, of\n        course, then `we want to know!\n        <https://github.com/python-trio/trio/issues>`__)\n\n        Defaults to :data:`math.inf`, which means \"no deadline\", though\n        this can be overridden by the ``deadline=`` argument to\n        the :class:`~trio.CancelScope` constructor.\n        \"\"\"\n        if self._relative_deadline != inf:\n            assert self._deadline == inf\n            warnings.warn(\n                DeprecationWarning(\n                    \"unentered relative cancel scope does not have an absolute deadline. Use `.relative_deadline`\",\n                ),\n                stacklevel=2,\n            )\n            return current_time() + self._relative_deadline\n        return self._deadline\n\n    @deadline.setter\n    def deadline(self, new_deadline: float) -> None:\n        if isnan(new_deadline):\n            raise ValueError(\"deadline must not be NaN\")\n        if self._relative_deadline != inf:\n            assert self._deadline == inf\n            warnings.warn(\n                DeprecationWarning(\n                    \"unentered relative cancel scope does not have an absolute deadline. Transforming into an absolute cancel scope. First set `.relative_deadline = math.inf` if you do want an absolute cancel scope.\",\n                ),\n                stacklevel=2,\n            )\n            self._relative_deadline = inf\n        with self._might_change_registered_deadline():\n            self._deadline = float(new_deadline)\n\n    @property\n    def relative_deadline(self) -> float:\n        \"\"\"Read-write, :class:`float`. The number of seconds remaining until this\n        scope's deadline, relative to the current time.\n\n        Defaults to :data:`math.inf` (\"no deadline\"). Must be non-negative.\n\n        When modified\n        Before entering: sets the deadline relative to when the scope enters.\n        After entering: sets a new deadline relative to the current time.\n\n        Raises:\n          RuntimeError: if trying to read or modify an unentered scope with an absolute deadline, i.e. when :attr:`is_relative` is ``False``.\n        \"\"\"\n        if self._has_been_entered:\n            return self._deadline - current_time()\n        elif self._deadline != inf:\n            assert self._relative_deadline == inf\n            raise RuntimeError(\n                \"unentered non-relative cancel scope does not have a relative deadline\",\n            )\n        return self._relative_deadline\n\n    @relative_deadline.setter\n    def relative_deadline(self, new_relative_deadline: float) -> None:\n        if isnan(new_relative_deadline):\n            raise ValueError(\"relative deadline must not be NaN\")\n        if new_relative_deadline < 0:\n            raise ValueError(\"relative deadline must be non-negative\")\n        if self._has_been_entered:\n            with self._might_change_registered_deadline():\n                self._deadline = current_time() + float(new_relative_deadline)\n        elif self._deadline != inf:\n            assert self._relative_deadline == inf\n            raise RuntimeError(\n                \"unentered non-relative cancel scope does not have a relative deadline\",\n            )\n        else:\n            self._relative_deadline = new_relative_deadline\n\n    @property\n    def is_relative(self) -> bool | None:\n        \"\"\"Returns None after entering. Returns False if both deadline and\n        relative_deadline are inf.\"\"\"\n        assert not (self._deadline != inf and self._relative_deadline != inf)\n        if self._has_been_entered:\n            return None\n        return self._relative_deadline != inf\n\n    @property\n    def shield(self) -> bool:\n        \"\"\"Read-write, :class:`bool`, default :data:`False`. So long as\n        this is set to :data:`True`, then the code inside this scope\n        will not receive :exc:`~trio.Cancelled` exceptions from scopes\n        that are outside this scope. They can still receive\n        :exc:`~trio.Cancelled` exceptions from (1) this scope, or (2)\n        scopes inside this scope. You can modify this attribute::\n\n           with trio.CancelScope() as cancel_scope:\n               cancel_scope.shield = True\n               # This cannot be interrupted by any means short of\n               # killing the process:\n               await sleep(10)\n\n               cancel_scope.shield = False\n               # Now this can be cancelled normally:\n               await sleep(10)\n\n        Defaults to :data:`False`, though this can be overridden by the\n        ``shield=`` argument to the :class:`~trio.CancelScope` constructor.\n        \"\"\"\n        return self._shield\n\n    @shield.setter\n    @enable_ki_protection\n    def shield(self, new_value: bool) -> None:\n        if not isinstance(new_value, bool):\n            raise TypeError(\"shield must be a bool\")\n        self._shield = new_value\n        if self._cancel_status is not None:\n            self._cancel_status.recalculate()\n\n    @enable_ki_protection\n    def _cancel(self, cancel_reason: CancelReason | None) -> None:\n        \"\"\"Internal sources of cancellation should use this instead of :meth:`cancel`\n        in order to set a more detailed :class:`CancelReason`\n        Helper or high-level functions can use `cancel`.\n        \"\"\"\n        if self._cancel_called:\n            return\n\n        if self._cancel_reason is None:\n            self._cancel_reason = cancel_reason\n\n        with self._might_change_registered_deadline():\n            self._cancel_called = True\n\n        if self._cancel_status is not None:\n            self._cancel_status.recalculate()\n\n    @enable_ki_protection\n    def cancel(self, reason: str | None = None) -> None:\n        \"\"\"Cancels this scope immediately.\n\n        The optional ``reason`` argument accepts a string, which will be attached to\n        any resulting :exc:`Cancelled` exception to help you understand where that\n        cancellation is coming from and why it happened.\n\n        This method is idempotent, i.e., if the scope was already\n        cancelled then this method silently does nothing.\n        \"\"\"\n        try:\n            current_task = repr(_core.current_task())\n        except RuntimeError:\n            current_task = None\n        self._cancel(\n            CancelReason(reason=reason, source=\"explicit\", source_task=current_task)\n        )\n\n    @property\n    def cancel_called(self) -> bool:\n        \"\"\"Readonly :class:`bool`. Records whether cancellation has been\n        requested for this scope, either by an explicit call to\n        :meth:`cancel` or by the deadline expiring.\n\n        This attribute being True does *not* necessarily mean that the\n        code within the scope has been, or will be, affected by the\n        cancellation. For example, if :meth:`cancel` was called after\n        the last checkpoint in the ``with`` block, when it's too late to\n        deliver a :exc:`~trio.Cancelled` exception, then this attribute\n        will still be True.\n\n        This attribute is mostly useful for debugging and introspection.\n        If you want to know whether or not a chunk of code was actually\n        cancelled, then :attr:`cancelled_caught` is usually more\n        appropriate.\n        \"\"\"\n        if (  # noqa: SIM102  # collapsible-if but this way is nicer\n            self._cancel_status is not None or not self._has_been_entered\n        ):\n            # Scope is active or not yet entered: make sure cancel_called\n            # is true if the deadline has passed. This shouldn't\n            # be able to actually change behavior, since we check for\n            # deadline expiry on scope entry and at every checkpoint,\n            # but it makes the value returned by cancel_called more\n            # closely match expectations.\n            if not self._cancel_called and current_time() >= self._deadline:\n                self._cancel(CancelReason(source=\"deadline\"))\n        return self._cancel_called\n\n\n################################################################\n# Nursery and friends\n################################################################\n\n\nclass TaskStatus(Protocol[StatusT_contra]):\n    \"\"\"The interface provided by :meth:`Nursery.start()` to the spawned task.\n\n    This is provided via the ``task_status`` keyword-only parameter.\n    \"\"\"\n\n    @overload\n    def started(self: TaskStatus[None]) -> None: ...\n\n    @overload\n    def started(self, value: StatusT_contra) -> None: ...\n\n    def started(self, value: StatusT_contra | None = None) -> None:\n        \"\"\"Tasks call this method to indicate that they have initialized.\n\n        See `nursery.start() <trio.Nursery.start>` for more information.\n        \"\"\"\n\n\n# This code needs to be read alongside the code from Nursery.start to make\n# sense.\n@attrs.define(eq=False, repr=False, slots=False)\nclass _TaskStatus(TaskStatus[StatusT]):\n    _old_nursery: Nursery\n    _new_nursery: Nursery\n    # NoStatus is a sentinel.\n    _value: StatusT | type[_NoStatus] = _NoStatus\n\n    def __repr__(self) -> str:\n        return f\"<Task status object at {id(self):#x}>\"\n\n    @overload\n    def started(self: _TaskStatus[None]) -> None: ...\n\n    @overload\n    def started(self: _TaskStatus[StatusT], value: StatusT) -> None: ...\n\n    def started(self, value: StatusT | None = None) -> None:\n        if self._value is not _NoStatus:\n            raise RuntimeError(\"called 'started' twice on the same task status\")\n        self._value = cast(\"StatusT\", value)  # If None, StatusT == None\n\n        # If the old nursery is cancelled, then quietly quit now; the child\n        # will eventually exit on its own, and we don't want to risk moving\n        # children that might have propagating Cancelled exceptions into\n        # a place with no cancelled cancel scopes to catch them.\n        assert self._old_nursery._cancel_status is not None\n        if self._old_nursery._cancel_status.effectively_cancelled:\n            return\n\n        # Can't be closed, b/c we checked in start() and then _pending_starts\n        # should keep it open.\n        assert not self._new_nursery._closed\n\n        # Move tasks from the old nursery to the new\n        tasks = self._old_nursery._children\n        self._old_nursery._children = set()\n        for task in tasks:\n            task._parent_nursery = self._new_nursery\n            task._eventual_parent_nursery = None\n            self._new_nursery._children.add(task)\n\n        # Move all children of the old nursery's cancel status object\n        # to be underneath the new nursery instead. This includes both\n        # tasks and child cancel status objects.\n        # NB: If the new nursery is cancelled, reparenting a cancel\n        # status to be underneath it can invoke an abort_fn, which might\n        # do something evil like cancel the old nursery. We thus break\n        # everything off from the old nursery before we start attaching\n        # anything to the new.\n        cancel_status_children = self._old_nursery._cancel_status.children\n        cancel_status_tasks = set(self._old_nursery._cancel_status.tasks)\n        cancel_status_tasks.discard(self._old_nursery._parent_task)\n        for cancel_status in cancel_status_children:\n            cancel_status.parent = None\n        for task in cancel_status_tasks:\n            task._activate_cancel_status(None)\n        for cancel_status in cancel_status_children:\n            cancel_status.parent = self._new_nursery._cancel_status\n        for task in cancel_status_tasks:\n            task._activate_cancel_status(self._new_nursery._cancel_status)\n\n        # That should have removed all the children from the old nursery\n        assert not self._old_nursery._children\n\n        # And finally, poke the old nursery so it notices that all its\n        # children have disappeared and can exit.\n        self._old_nursery._check_nursery_closed()\n\n\n@attrs.define(slots=False)\nclass NurseryManager:\n    \"\"\"Nursery context manager.\n\n    Note we explicitly avoid @asynccontextmanager and @async_generator\n    since they add a lot of extraneous stack frames to exceptions, as\n    well as cause problematic behavior with handling of StopIteration\n    and StopAsyncIteration.\n\n    \"\"\"\n\n    strict_exception_groups: bool = True\n\n    @enable_ki_protection\n    async def __aenter__(self) -> Nursery:\n        self._scope = CancelScope()\n        self._scope.__enter__()\n        self._nursery = Nursery._create(\n            current_task(),\n            self._scope,\n            self.strict_exception_groups,\n        )\n        return self._nursery\n\n    @enable_ki_protection\n    async def __aexit__(\n        self,\n        etype: type[BaseException] | None,\n        exc: BaseException | None,\n        tb: TracebackType | None,\n    ) -> bool:\n        new_exc = await self._nursery._nested_child_finished(exc)\n        # Tracebacks show the 'raise' line below out of context, so let's give\n        # this variable a name that makes sense out of context.\n        combined_error_from_nursery = self._scope._close(new_exc)\n        if combined_error_from_nursery is None:\n            return True\n        elif combined_error_from_nursery is exc:\n            return False\n        else:\n            # Copied verbatim from the old MultiErrorCatcher.  Python doesn't\n            # allow us to encapsulate this __context__ fixup.\n            old_context = combined_error_from_nursery.__context__\n            try:\n                raise combined_error_from_nursery\n            finally:\n                _, value, _ = sys.exc_info()\n                assert value is combined_error_from_nursery\n                value.__context__ = old_context\n                # delete references from locals to avoid creating cycles\n                # see test_cancel_scope_exit_doesnt_create_cyclic_garbage\n                del _, combined_error_from_nursery, value, new_exc\n\n    # make sure these raise errors in static analysis if called\n    if not TYPE_CHECKING:\n\n        def __enter__(self) -> NoReturn:\n            raise RuntimeError(\n                \"use 'async with open_nursery(...)', not 'with open_nursery(...)'\",\n            )\n\n        def __exit__(\n            self,\n            exc_type: type[BaseException] | None,\n            exc_value: BaseException | None,\n            traceback: TracebackType | None,\n        ) -> NoReturn:  # pragma: no cover\n            raise AssertionError(\"Never called, but should be defined\")\n\n\ndef open_nursery(\n    strict_exception_groups: bool | None = None,\n) -> AbstractAsyncContextManager[Nursery]:\n    \"\"\"Returns an async context manager which must be used to create a\n    new `Nursery`.\n\n    It does not block on entry; on exit it blocks until all child tasks\n    have exited. If no child tasks are running on exit, it will insert a\n    schedule point (but no cancellation point) - equivalent to\n    :func:`trio.lowlevel.cancel_shielded_checkpoint`. This means a nursery\n    is never the source of a cancellation exception, it only propagates it\n    from sub-tasks.\n\n    Args:\n      strict_exception_groups (bool): Unless set to False, even a single raised exception\n          will be wrapped in an exception group. If not specified, uses the value passed\n          to :func:`run`, which defaults to true. Setting it to False will be deprecated\n          and ultimately removed in a future version of Trio.\n\n    \"\"\"\n    # only warn if explicitly set to falsy, not if we get it from the global context.\n    if strict_exception_groups is not None and not strict_exception_groups:\n        warn_deprecated(\n            \"open_nursery(strict_exception_groups=False)\",\n            version=\"0.25.0\",\n            issue=2929,\n            instead=(\n                \"the default value of True and rewrite exception handlers to handle ExceptionGroups. \"\n                \"See https://trio.readthedocs.io/en/stable/reference-core.html#designing-for-multiple-errors\"\n            ),\n            use_triodeprecationwarning=True,\n        )\n\n    if strict_exception_groups is None:\n        strict_exception_groups = GLOBAL_RUN_CONTEXT.runner.strict_exception_groups\n\n    return NurseryManager(strict_exception_groups=strict_exception_groups)\n\n\n@final\nclass Nursery(metaclass=NoPublicConstructor):\n    \"\"\"A context which may be used to spawn (or cancel) child tasks.\n\n    Not constructed directly, use `open_nursery` instead.\n\n    The nursery will remain open until all child tasks have completed,\n    or until it is cancelled, at which point it will cancel all its\n    remaining child tasks and close.\n\n    Nurseries ensure the absence of orphaned Tasks, since all running\n    tasks will belong to an open Nursery.\n\n    Attributes:\n        cancel_scope:\n            Creating a nursery also implicitly creates a cancellation scope,\n            which is exposed as the :attr:`cancel_scope` attribute. This is\n            used internally to implement the logic where if an error occurs\n            then ``__aexit__`` cancels all children, but you can use it for\n            other things, e.g. if you want to explicitly cancel all children\n            in response to some external event.\n    \"\"\"\n\n    def __init__(\n        self,\n        parent_task: Task,\n        cancel_scope: CancelScope,\n        strict_exception_groups: bool,\n    ) -> None:\n        self._parent_task = parent_task\n        self._strict_exception_groups = strict_exception_groups\n        parent_task._child_nurseries.append(self)\n        # the cancel status that children inherit - we take a snapshot, so it\n        # won't be affected by any changes in the parent.\n        self._cancel_status = parent_task._cancel_status\n        # the cancel scope that directly surrounds us; used for cancelling all\n        # children.\n        self.cancel_scope = cancel_scope\n        assert self.cancel_scope._cancel_status is self._cancel_status\n        self._children: set[Task] = set()\n        self._pending_excs: list[BaseException] = []\n        # The \"nested child\" is how this code refers to the contents of the\n        # nursery's 'async with' block, which acts like a child Task in all\n        # the ways we can make it.\n        self._nested_child_running = True\n        self._parent_waiting_in_aexit = False\n        self._pending_starts = 0\n        self._closed = False\n\n    @property\n    def child_tasks(self) -> frozenset[Task]:\n        \"\"\"(`frozenset`): Contains all the child :class:`~trio.lowlevel.Task`\n        objects which are still running.\"\"\"\n        return frozenset(self._children)\n\n    @property\n    def parent_task(self) -> Task:\n        \"(`~trio.lowlevel.Task`):  The Task that opened this nursery.\"\n        return self._parent_task\n\n    def _add_exc(self, exc: BaseException, reason: CancelReason | None) -> None:\n        self._pending_excs.append(exc)\n        self.cancel_scope._cancel(reason)\n\n    def _check_nursery_closed(self) -> None:\n        if not any([self._nested_child_running, self._children, self._pending_starts]):\n            self._closed = True\n            if self._parent_waiting_in_aexit:\n                self._parent_waiting_in_aexit = False\n                GLOBAL_RUN_CONTEXT.runner.reschedule(self._parent_task)\n\n    def _child_finished(\n        self,\n        task: Task,\n        outcome: Outcome[object],\n    ) -> None:\n        self._children.remove(task)\n        if self._closed and not hasattr(self, \"_pending_excs\"):\n            # We're abandoned by misnested nurseries, the result of the task is lost.\n            return\n        if isinstance(outcome, Error):\n            self._add_exc(\n                outcome.error,\n                CancelReason(\n                    source=\"nursery\",\n                    source_task=repr(task),\n                    reason=f\"child task raised exception {outcome.error!r}\",\n                ),\n            )\n        self._check_nursery_closed()\n\n    async def _nested_child_finished(\n        self,\n        nested_child_exc: BaseException | None,\n    ) -> BaseException | None:\n        # Returns ExceptionGroup instance (or any exception if the nursery is in loose mode\n        # and there is just one contained exception) if there are pending exceptions\n        if nested_child_exc is not None:\n            self._add_exc(\n                nested_child_exc,\n                reason=CancelReason(\n                    source=\"nursery\",\n                    source_task=repr(self._parent_task),\n                    reason=f\"Code block inside nursery contextmanager raised exception {nested_child_exc!r}\",\n                ),\n            )\n        self._nested_child_running = False\n        self._check_nursery_closed()\n\n        if not self._closed:\n            # If we have a KeyboardInterrupt injected, we want to save it in\n            # the nursery's final exceptions list. But if it's just a\n            # Cancelled, then we don't -- see gh-1457.\n            def aborted(raise_cancel: _core.RaiseCancelT) -> Abort:\n                exn = capture(raise_cancel).error\n                if not isinstance(exn, Cancelled):\n                    self._add_exc(\n                        exn,\n                        CancelReason(\n                            source=\"KeyboardInterrupt\",\n                            source_task=repr(self._parent_task),\n                        ),\n                    )\n                # see test_cancel_scope_exit_doesnt_create_cyclic_garbage\n                del exn  # prevent cyclic garbage creation\n                return Abort.FAILED\n\n            self._parent_waiting_in_aexit = True\n            await wait_task_rescheduled(aborted)\n        else:\n            # Nothing to wait for, so execute a schedule point, but don't\n            # allow us to be cancelled, just like the other branch.  We\n            # still need to catch and store non-Cancelled exceptions.\n            try:\n                await cancel_shielded_checkpoint()\n            except BaseException as exc:\n                # there's no children to cancel, so don't need to supply cancel reason\n                self._add_exc(exc, reason=None)\n\n        popped = self._parent_task._child_nurseries.pop()\n        assert popped is self, \"Nursery misnesting detected!\"\n        if self._pending_excs:\n            try:\n                if not self._strict_exception_groups and len(self._pending_excs) == 1:\n                    return self._pending_excs[0]\n                exception = BaseExceptionGroup(\n                    \"Exceptions from Trio nursery\",\n                    self._pending_excs,\n                )\n                if not self._strict_exception_groups:\n                    exception.add_note(NONSTRICT_EXCEPTIONGROUP_NOTE)\n                return exception\n            finally:\n                # avoid a garbage cycle\n                # (see test_locals_destroyed_promptly_on_cancel)\n                del self._pending_excs\n        return None\n\n    def start_soon(\n        self,\n        async_fn: Callable[[Unpack[PosArgT]], Awaitable[object]],\n        *args: Unpack[PosArgT],\n        name: object = None,\n    ) -> None:\n        \"\"\"Creates a child task, scheduling ``await async_fn(*args)``.\n\n        If you want to run a function and immediately wait for its result,\n        then you don't need a nursery; just use ``await async_fn(*args)``.\n        If you want to wait for the task to initialize itself before\n        continuing, see :meth:`start`, the other fundamental method for\n        creating concurrent tasks in Trio.\n\n        Note that this is *not* an async function and you don't use await\n        when calling it. It sets up the new task, but then returns\n        immediately, *before* the new task has a chance to do anything.\n        New tasks may start running in any order, and at any checkpoint the\n        scheduler chooses - at latest when the nursery is waiting to exit.\n\n        It's possible to pass a nursery object into another task, which\n        allows that task to start new child tasks in the first task's\n        nursery.\n\n        The child task inherits its parent nursery's cancel scopes.\n\n        Args:\n            async_fn: An async callable.\n            args: Positional arguments for ``async_fn``. If you want\n                  to pass keyword arguments, use\n                  :func:`functools.partial`.\n            name: The name for this task. Only used for\n                  debugging/introspection\n                  (e.g. ``repr(task_obj)``). If this isn't a string,\n                  :meth:`start_soon` will try to make it one. A\n                  common use case is if you're wrapping a function\n                  before spawning a new task, you might pass the\n                  original function as the ``name=`` to make\n                  debugging easier.\n\n        Raises:\n            RuntimeError: If this nursery is no longer open\n                          (i.e. its ``async with`` block has\n                          exited).\n        \"\"\"\n        GLOBAL_RUN_CONTEXT.runner.spawn_impl(async_fn, args, self, name)\n\n    # Typing changes blocked by https://github.com/python/mypy/pull/17512\n    async def start(  # type: ignore[explicit-any]\n        self,\n        async_fn: Callable[..., Awaitable[object]],\n        *args: object,\n        name: object = None,\n    ) -> Any:\n        r\"\"\"Creates and initializes a child task.\n\n        Like :meth:`start_soon`, but blocks until the new task has\n        finished initializing itself, and optionally returns some\n        information from it.\n\n        The ``async_fn`` must accept a ``task_status`` keyword argument,\n        and it must make sure that it (or someone) eventually calls\n        :meth:`task_status.started() <TaskStatus.started>`.\n\n        The conventional way to define ``async_fn`` is like::\n\n            async def async_fn(arg1, arg2, *, task_status=trio.TASK_STATUS_IGNORED):\n                ...  # Caller is blocked waiting for this code to run\n                task_status.started()\n                ...  # This async code can be interleaved with the caller\n\n        :attr:`trio.TASK_STATUS_IGNORED` is a special global object with\n        a do-nothing ``started`` method. This way your function supports\n        being called either like ``await nursery.start(async_fn, arg1,\n        arg2)`` or directly like ``await async_fn(arg1, arg2)``, and\n        either way it can call :meth:`task_status.started() <TaskStatus.started>`\n        without worrying about which mode it's in. Defining your function like\n        this will make it obvious to readers that it supports being used\n        in both modes.\n\n        Before the child calls :meth:`task_status.started() <TaskStatus.started>`,\n        it's effectively run underneath the call to :meth:`start`: if it\n        raises an exception then that exception is reported by\n        :meth:`start`, and does *not* propagate out of the nursery. If\n        :meth:`start` is cancelled, then the child task is also\n        cancelled.\n\n        When the child calls :meth:`task_status.started() <TaskStatus.started>`,\n        it's moved out from underneath :meth:`start` and into the given nursery.\n\n        If the child task passes a value to :meth:`task_status.started(value) <TaskStatus.started>`,\n        then :meth:`start` returns this value. Otherwise, it returns ``None``.\n        \"\"\"\n        if self._closed:\n            raise RuntimeError(\"Nursery is closed to new arrivals\")\n        try:\n            self._pending_starts += 1\n            # wrap internal nursery in try-except to unroll any exceptiongroups\n            # to avoid wrapping pre-started() exceptions in an extra ExceptionGroup.\n            # See #2611.\n            try:\n                # set strict_exception_groups = True to make sure we always unwrap\n                # *this* nursery's exceptiongroup\n                async with open_nursery(strict_exception_groups=True) as old_nursery:\n                    task_status: _TaskStatus[object | None] = _TaskStatus(\n                        old_nursery,\n                        self,\n                    )\n                    thunk = functools.partial(async_fn, task_status=task_status)\n                    task = GLOBAL_RUN_CONTEXT.runner.spawn_impl(\n                        thunk,\n                        args,\n                        old_nursery,\n                        name,\n                    )\n                    task._eventual_parent_nursery = self\n                    # Wait for either TaskStatus.started or an exception to\n                    # cancel this nursery:\n            except BaseExceptionGroup as exc:\n                if len(exc.exceptions) == 1:\n                    raise_saving_context(exc.exceptions[0])\n                raise TrioInternalError(\n                    \"Internal nursery should not have multiple tasks. This can be \"\n                    'caused by the user managing to access the \"old\" nursery in '\n                    \"`task_status` and spawning tasks in it.\",\n                ) from exc\n\n            # If we get here, then the child either got reparented or exited\n            # normally. The complicated logic is all in TaskStatus.started().\n            # (Any exceptions propagate directly out of the above.)\n            if task_status._value is _NoStatus:\n                raise RuntimeError(\"child exited without calling task_status.started()\")\n            return task_status._value\n        finally:\n            self._pending_starts -= 1\n            self._check_nursery_closed()\n\n    def __del__(self) -> None:\n        assert not self._children\n\n\n################################################################\n# Task and friends\n################################################################\n\n\n@final\n@attrs.define(eq=False, repr=False)\nclass Task(metaclass=NoPublicConstructor):  # type: ignore[explicit-any]\n    _parent_nursery: Nursery | None\n    coro: types.CoroutineType[Any, Outcome[object], Any]  # type: ignore[explicit-any]\n    _runner: Runner\n    name: str\n    context: contextvars.Context\n    _counter: int = attrs.field(init=False, factory=itertools.count().__next__)\n    _ki_protected: bool\n\n    # Invariant:\n    # - for unscheduled tasks, _next_send_fn and _next_send are both None\n    # - for scheduled tasks, _next_send_fn(_next_send) resumes the task;\n    #   usually _next_send_fn is self.coro.send and _next_send is an\n    #   Outcome. When recovering from a foreign await, _next_send_fn is\n    #   self.coro.throw and _next_send is an exception. _next_send_fn\n    #   will effectively be at the top of every task's call stack, so\n    #   it should be written in C if you don't want to pollute Trio\n    #   tracebacks with extraneous frames.\n    # - for scheduled tasks, custom_sleep_data is None\n    # Tasks start out unscheduled.\n    _next_send_fn: Callable[[Any], object] | None = None  # type: ignore[explicit-any]\n    _next_send: Outcome[Any] | BaseException | None = None  # type: ignore[explicit-any]\n    _abort_func: Callable[[_core.RaiseCancelT], Abort] | None = None\n    custom_sleep_data: Any = None  # type: ignore[explicit-any]\n\n    # For introspection and nursery.start()\n    _child_nurseries: list[Nursery] = attrs.Factory(list)\n    _eventual_parent_nursery: Nursery | None = None\n\n    # these are counts of how many cancel/schedule points this task has\n    # executed, for assert{_no,}_checkpoints\n    # XX maybe these should be exposed as part of a statistics() method?\n    _cancel_points: int = 0\n    _schedule_points: int = 0\n\n    def __repr__(self) -> str:\n        return f\"<Task {self.name!r} at {id(self):#x}>\"\n\n    @property\n    def parent_nursery(self) -> Nursery | None:\n        \"\"\"The nursery this task is inside (or None if this is the \"init\"\n        task).\n\n        Example use case: drawing a visualization of the task tree in a\n        debugger.\n\n        \"\"\"\n        return self._parent_nursery\n\n    @property\n    def eventual_parent_nursery(self) -> Nursery | None:\n        \"\"\"The nursery this task will be inside after it calls\n        ``task_status.started()``.\n\n        If this task has already called ``started()``, or if it was not\n        spawned using `nursery.start() <trio.Nursery.start>`, then\n        its `eventual_parent_nursery` is ``None``.\n\n        \"\"\"\n        return self._eventual_parent_nursery\n\n    @property\n    def child_nurseries(self) -> list[Nursery]:\n        \"\"\"The nurseries this task contains.\n\n        This is a list, with outer nurseries before inner nurseries.\n\n        \"\"\"\n        return list(self._child_nurseries)\n\n    def iter_await_frames(self) -> Iterator[tuple[types.FrameType, int]]:\n        \"\"\"Iterates recursively over the coroutine-like objects this\n        task is waiting on, yielding the frame and line number at each\n        frame.\n\n        This is similar to `traceback.walk_stack` in a synchronous\n        context. Note that `traceback.walk_stack` returns frames from\n        the bottom of the call stack to the top, while this function\n        starts from `Task.coro <trio.lowlevel.Task.coro>` and works it\n        way down.\n\n        Example usage: extracting a stack trace::\n\n            import traceback\n\n            def print_stack_for_task(task):\n                ss = traceback.StackSummary.extract(task.iter_await_frames())\n                print(\"\".join(ss.format()))\n\n        \"\"\"\n        # Ignore static typing as we're doing lots of dynamic introspection\n        coro: Any = self.coro  # type: ignore[explicit-any]\n        while coro is not None:\n            if hasattr(coro, \"cr_frame\"):\n                # A real coroutine\n                if cr_frame := coro.cr_frame:  # None if the task has finished\n                    yield cr_frame, cr_frame.f_lineno\n                coro = coro.cr_await\n            elif hasattr(coro, \"gi_frame\"):\n                # A generator decorated with @types.coroutine\n                if gi_frame := coro.gi_frame:  # pragma: no branch\n                    yield gi_frame, gi_frame.f_lineno  # pragma: no cover\n                coro = coro.gi_yieldfrom\n            elif coro.__class__.__name__ in [\n                \"async_generator_athrow\",\n                \"async_generator_asend\",\n            ]:\n                # cannot extract the generator directly, see https://github.com/python/cpython/issues/76991\n                # we can however use the gc to look through the object\n                for referent in gc.get_referents(coro):\n                    if hasattr(referent, \"ag_frame\"):  # pragma: no branch\n                        yield referent.ag_frame, referent.ag_frame.f_lineno\n                        coro = referent.ag_await\n                        break\n                else:  # pragma: no cover\n                    # either cpython changed or we are running on an alternative python implementation\n                    return\n            else:  # pragma: no cover\n                return\n\n    ################\n    # Cancellation\n    ################\n\n    # The CancelStatus object that is currently active for this task.\n    # Don't change this directly; instead, use _activate_cancel_status().\n    # This can be None, but only in the init task.\n    _cancel_status: CancelStatus = attrs.field(default=None, repr=False)\n\n    def _activate_cancel_status(self, cancel_status: CancelStatus | None) -> None:\n        if self._cancel_status is not None:\n            self._cancel_status._tasks.remove(self)\n        self._cancel_status = cancel_status  # type: ignore[assignment]\n        if self._cancel_status is not None:\n            self._cancel_status._tasks.add(self)\n            if self._cancel_status.effectively_cancelled:\n                self._attempt_delivery_of_any_pending_cancel()\n\n    def _attempt_abort(self, raise_cancel: _core.RaiseCancelT) -> None:\n        # Either the abort succeeds, in which case we will reschedule the\n        # task, or else it fails, in which case it will worry about\n        # rescheduling itself (hopefully eventually calling reraise to raise\n        # the given exception, but not necessarily).\n\n        # This is only called by the functions immediately below, which both check\n        # `self.abort_func is not None`.\n        assert self._abort_func is not None, \"FATAL INTERNAL ERROR\"\n\n        success = self._abort_func(raise_cancel)\n        if type(success) is not Abort:\n            raise TrioInternalError(\"abort function must return Abort enum\")\n        # We only attempt to abort once per blocking call, regardless of\n        # whether we succeeded or failed.\n        self._abort_func = None\n        if success is Abort.SUCCEEDED:\n            self._runner.reschedule(self, capture(raise_cancel))\n\n    def _attempt_delivery_of_any_pending_cancel(self) -> None:\n        if self._abort_func is None:\n            return\n        if not self._cancel_status.effectively_cancelled:\n            return\n\n        reason = self._cancel_status._scope._cancel_reason\n\n        def raise_cancel() -> NoReturn:\n            if reason is None:\n                raise Cancelled._create(source=\"unknown\", reason=\"misnesting\")\n            else:\n                raise Cancelled._create(\n                    source=reason.source,\n                    reason=reason.reason,\n                    source_task=reason.source_task,\n                )\n\n        self._attempt_abort(raise_cancel)\n\n    def _attempt_delivery_of_pending_ki(self) -> None:\n        assert self._runner.ki_pending\n        if self._abort_func is None:\n            return\n\n        def raise_cancel() -> NoReturn:\n            self._runner.ki_pending = False\n            raise KeyboardInterrupt\n\n        self._attempt_abort(raise_cancel)\n\n\n################################################################\n# The central Runner object\n################################################################\n\n\n@attrs.frozen\nclass RunStatistics:\n    \"\"\"An object containing run-loop-level debugging information.\n\n    Currently, the following fields are defined:\n\n    * ``tasks_living`` (int): The number of tasks that have been spawned\n      and not yet exited.\n    * ``tasks_runnable`` (int): The number of tasks that are currently\n      queued on the run queue (as opposed to blocked waiting for something\n      to happen).\n    * ``seconds_to_next_deadline`` (float): The time until the next\n      pending cancel scope deadline. May be negative if the deadline has\n      expired but we haven't yet processed cancellations. May be\n      :data:`~math.inf` if there are no pending deadlines.\n    * ``run_sync_soon_queue_size`` (int): The number of\n      unprocessed callbacks queued via\n      :meth:`trio.lowlevel.TrioToken.run_sync_soon`.\n    * ``io_statistics`` (object): Some statistics from Trio's I/O\n      backend. This always has an attribute ``backend`` which is a string\n      naming which operating-system-specific I/O backend is in use; the\n      other attributes vary between backends.\n    \"\"\"\n\n    tasks_living: int\n    tasks_runnable: int\n    seconds_to_next_deadline: float\n    io_statistics: IOStatistics\n    run_sync_soon_queue_size: int\n\n\n# This holds all the state that gets trampolined back and forth between\n# callbacks when we're running in guest mode.\n#\n# It has to be a separate object from Runner, and Runner *cannot* hold\n# references to it (directly or indirectly)!\n#\n# The idea is that we want a chance to detect if our host loop quits and stops\n# driving us forward. We detect that by unrolled_run_gen being garbage\n# collected, and hitting its 'except GeneratorExit:' block. So this only\n# happens if unrolled_run_gen is GCed.\n#\n# The Runner state is referenced from the global GLOBAL_RUN_CONTEXT. The only\n# way it gets *un*referenced is by unrolled_run_gen completing, e.g. by being\n# GCed. But if Runner has a direct or indirect reference to it, and the host\n# loop has abandoned it, then this will never happen!\n#\n# So this object can reference Runner, but Runner can't reference it. The only\n# references to it are the \"in flight\" callback chain on the host loop /\n# worker thread.\n\n\n@attrs.define(eq=False)\nclass GuestState:  # type: ignore[explicit-any]\n    runner: Runner\n    run_sync_soon_threadsafe: Callable[[Callable[[], object]], object]\n    run_sync_soon_not_threadsafe: Callable[[Callable[[], object]], object]\n    done_callback: Callable[[Outcome[Any]], object]  # type: ignore[explicit-any]\n    unrolled_run_gen: Generator[float, EventResult, None]\n    unrolled_run_next_send: Outcome[Any] = attrs.Factory(lambda: Value(None))  # type: ignore[explicit-any]\n\n    def guest_tick(self) -> None:\n        prev_library, sniffio_library.name = sniffio_library.name, \"trio\"\n        try:\n            timeout = self.unrolled_run_next_send.send(self.unrolled_run_gen)\n        except StopIteration:\n            assert self.runner.main_task_outcome is not None\n            self.done_callback(self.runner.main_task_outcome)\n            return\n        except TrioInternalError as exc:\n            self.done_callback(Error(exc))\n            return\n        finally:\n            sniffio_library.name = prev_library\n\n        # Optimization: try to skip going into the thread if we can avoid it\n        events_outcome: Value[EventResult] | Error = capture(\n            self.runner.io_manager.get_events,\n            0,\n        )\n        if timeout <= 0 or isinstance(events_outcome, Error) or events_outcome.value:\n            # No need to go into the thread\n            self.unrolled_run_next_send = events_outcome\n            self.runner.guest_tick_scheduled = True\n            self.run_sync_soon_not_threadsafe(self.guest_tick)\n        else:\n            # Need to go into the thread and call get_events() there\n            self.runner.guest_tick_scheduled = False\n\n            def get_events() -> EventResult:\n                return self.runner.io_manager.get_events(timeout)\n\n            def deliver(events_outcome: Outcome[EventResult]) -> None:\n                def in_main_thread() -> None:\n                    self.unrolled_run_next_send = events_outcome\n                    self.runner.guest_tick_scheduled = True\n                    self.guest_tick()\n\n                self.run_sync_soon_threadsafe(in_main_thread)\n\n            start_thread_soon(get_events, deliver)\n\n\n@attrs.define(eq=False)\nclass Runner:  # type: ignore[explicit-any]\n    clock: Clock\n    instruments: Instruments\n    io_manager: TheIOManager\n    ki_manager: KIManager\n    strict_exception_groups: bool\n\n    # Run-local values, see _local.py\n    _locals: dict[_core.RunVar[Any], object] = attrs.Factory(dict)  # type: ignore[explicit-any]\n\n    runq: deque[Task] = attrs.Factory(deque)\n    tasks: set[Task] = attrs.Factory(set)\n\n    deadlines: Deadlines = attrs.Factory(Deadlines)\n\n    init_task: Task | None = None\n    system_nursery: Nursery | None = None\n    system_context: contextvars.Context = attrs.field(kw_only=True)\n    main_task: Task | None = None\n    main_task_outcome: Outcome[object] | None = None\n\n    entry_queue: EntryQueue = attrs.Factory(EntryQueue)\n    trio_token: TrioToken | None = None\n    asyncgens: AsyncGenerators = attrs.Factory(AsyncGenerators)\n\n    # If everything goes idle for this long, we call clock._autojump()\n    clock_autojump_threshold: float = inf\n\n    # Guest mode stuff\n    is_guest: bool = False\n    guest_tick_scheduled: bool = False\n\n    def force_guest_tick_asap(self) -> None:\n        if self.guest_tick_scheduled:\n            return\n        self.guest_tick_scheduled = True\n        self.io_manager.force_wakeup()\n\n    def close(self) -> None:\n        self.io_manager.close()\n        self.entry_queue.close()\n        self.asyncgens.close()\n        if \"after_run\" in self.instruments:\n            self.instruments.call(\"after_run\")\n        # This is where KI protection gets disabled, so we do it last\n        self.ki_manager.close()\n\n    @_public\n    def current_statistics(self) -> RunStatistics:\n        \"\"\"Returns ``RunStatistics``, which contains run-loop-level debugging information.\n\n        Currently, the following fields are defined:\n\n        * ``tasks_living`` (int): The number of tasks that have been spawned\n          and not yet exited.\n        * ``tasks_runnable`` (int): The number of tasks that are currently\n          queued on the run queue (as opposed to blocked waiting for something\n          to happen).\n        * ``seconds_to_next_deadline`` (float): The time until the next\n          pending cancel scope deadline. May be negative if the deadline has\n          expired but we haven't yet processed cancellations. May be\n          :data:`~math.inf` if there are no pending deadlines.\n        * ``run_sync_soon_queue_size`` (int): The number of\n          unprocessed callbacks queued via\n          :meth:`trio.lowlevel.TrioToken.run_sync_soon`.\n        * ``io_statistics`` (object): Some statistics from Trio's I/O\n          backend. This always has an attribute ``backend`` which is a string\n          naming which operating-system-specific I/O backend is in use; the\n          other attributes vary between backends.\n\n        \"\"\"\n        seconds_to_next_deadline = self.deadlines.next_deadline() - self.current_time()\n        return RunStatistics(\n            tasks_living=len(self.tasks),\n            tasks_runnable=len(self.runq),\n            seconds_to_next_deadline=seconds_to_next_deadline,\n            io_statistics=self.io_manager.statistics(),\n            run_sync_soon_queue_size=self.entry_queue.size(),\n        )\n\n    @_public\n    def current_time(self) -> float:\n        \"\"\"Returns the current time according to Trio's internal clock.\n\n        Returns:\n            float: The current time.\n\n        Raises:\n            RuntimeError: if not inside a call to :func:`trio.run`.\n\n        \"\"\"\n        return self.clock.current_time()\n\n    @_public\n    def current_clock(self) -> Clock:\n        \"\"\"Returns the current :class:`~trio.abc.Clock`.\"\"\"\n        return self.clock\n\n    @_public\n    def current_root_task(self) -> Task | None:\n        \"\"\"Returns the current root :class:`Task`.\n\n        This is the task that is the ultimate parent of all other tasks.\n\n        \"\"\"\n        return self.init_task\n\n    ################\n    # Core task handling primitives\n    ################\n\n    @_public\n    def reschedule(\n        self, task: Task, next_send: outcome.Outcome[object] = _NO_SEND\n    ) -> None:\n        \"\"\"Reschedule the given task with the given\n        :class:`outcome.Outcome`.\n\n        See :func:`wait_task_rescheduled` for the gory details.\n\n        There must be exactly one call to :func:`reschedule` for every call to\n        :func:`wait_task_rescheduled`. (And when counting, keep in mind that\n        returning :data:`Abort.SUCCEEDED` from an abort callback is equivalent\n        to calling :func:`reschedule` once.)\n\n        Args:\n          task (trio.lowlevel.Task): the task to be rescheduled. Must be blocked\n              in a call to :func:`wait_task_rescheduled`.\n          next_send (outcome.Outcome): the value (or error) to return (or\n              raise) from :func:`wait_task_rescheduled`.\n\n        \"\"\"\n        if next_send is _NO_SEND:\n            next_send = Value(None)\n\n        assert task._runner is self\n        assert task._next_send_fn is None\n        task._next_send_fn = task.coro.send\n        task._next_send = next_send\n        task._abort_func = None\n        task.custom_sleep_data = None\n        if not self.runq and self.is_guest:\n            self.force_guest_tick_asap()\n        self.runq.append(task)\n        if \"task_scheduled\" in self.instruments:\n            self.instruments.call(\"task_scheduled\", task)\n\n    def spawn_impl(\n        self,\n        async_fn: Callable[[Unpack[PosArgT]], Awaitable[object]],\n        args: tuple[Unpack[PosArgT]],\n        nursery: Nursery | None,\n        name: object,\n        *,\n        system_task: bool = False,\n        context: contextvars.Context | None = None,\n    ) -> Task:\n        ######\n        # Make sure the nursery is in working order\n        ######\n\n        # This sorta feels like it should be a method on nursery, except it\n        # has to handle nursery=None for init. And it touches the internals of\n        # all kinds of objects.\n        if nursery is not None and nursery._closed:\n            raise RuntimeError(\"Nursery is closed to new arrivals\")\n        if nursery is None:\n            assert self.init_task is None\n\n        ######\n        # Propagate contextvars\n        ######\n        if context is None:\n            context = self.system_context.copy() if system_task else copy_context()\n\n        ######\n        # Call the function and get the coroutine object, while giving helpful\n        # errors for common mistakes.\n        ######\n        # TypeVarTuple passed into ParamSpec function confuses Mypy.\n        coro = context.run(coroutine_or_error, async_fn, *args)  # type: ignore[arg-type]\n\n        if name is None:\n            name = async_fn\n        if isinstance(name, functools.partial):\n            name = name.func\n        if not isinstance(name, str):\n            try:\n                name = f\"{name.__module__}.{name.__qualname__}\"  # type: ignore[attr-defined]\n            except AttributeError:\n                name = repr(name)\n\n        # very old Cython versions (<0.29.24) has the attribute, but with a value of None\n        if getattr(coro, \"cr_frame\", None) is None:\n            # This async function is implemented in C or Cython\n            async def python_wrapper(orig_coro: Awaitable[RetT]) -> RetT:\n                return await orig_coro\n\n            coro = python_wrapper(coro)\n        assert coro.cr_frame is not None, \"Coroutine frame should exist\"  # type: ignore[attr-defined]\n\n        ######\n        # Set up the Task object\n        ######\n        task = Task._create(\n            coro=coro,\n            parent_nursery=nursery,\n            runner=self,\n            name=name,\n            context=context,\n            ki_protected=system_task,\n        )\n\n        self.tasks.add(task)\n        if nursery is not None:\n            nursery._children.add(task)\n            task._activate_cancel_status(nursery._cancel_status)\n\n        if \"task_spawned\" in self.instruments:\n            self.instruments.call(\"task_spawned\", task)\n        # Special case: normally next_send should be an Outcome, but for the\n        # very first send we have to send a literal unboxed None.\n        self.reschedule(task, None)  # type: ignore[arg-type]\n        return task\n\n    def task_exited(self, task: Task, outcome: Outcome[object]) -> None:\n        if task._child_nurseries:\n            for nursery in task._child_nurseries:\n                nursery.cancel_scope._cancel(\n                    CancelReason(\n                        source=\"nursery\",\n                        reason=\"Parent Task exited prematurely, abandoning this nursery without exiting it properly.\",\n                        source_task=repr(task),\n                    )\n                )\n                nursery._closed = True\n\n        # break parking lots associated with the exiting task\n        if task in GLOBAL_PARKING_LOT_BREAKER:\n            for lot in GLOBAL_PARKING_LOT_BREAKER[task]:\n                lot.break_lot(task)\n            del GLOBAL_PARKING_LOT_BREAKER[task]\n\n        if (\n            task._cancel_status is not None\n            and task._cancel_status.abandoned_by_misnesting\n            and task._cancel_status.parent is None\n        ) or task._child_nurseries:\n            reason = \"Nursery\" if task._child_nurseries else \"Cancel scope\"\n            # The cancel scope surrounding this task's nursery was closed\n            # before the task exited. Force the task to exit with an error,\n            # since the error might not have been caught elsewhere. See the\n            # comments in CancelStatus.close().\n            try:\n                # Raise this, rather than just constructing it, to get a\n                # traceback frame included\n                raise RuntimeError(\n                    f\"{reason} stack corrupted: {reason} surrounding \"\n                    f\"{task!r} was closed before the task exited\\n{MISNESTING_ADVICE}\",\n                )\n            except RuntimeError as new_exc:\n                if isinstance(outcome, Error):\n                    new_exc.__context__ = outcome.error\n                outcome = Error(new_exc)\n\n        task._activate_cancel_status(None)\n        self.tasks.remove(task)\n        if task is self.init_task:\n            # If the init task crashed, then something is very wrong and we\n            # let the error propagate. (It'll eventually be wrapped in a\n            # TrioInternalError.)\n            outcome.unwrap()\n            # the init task should be the last task to exit. If not, then\n            # something is very wrong.\n            if self.tasks:  # pragma: no cover\n                raise TrioInternalError\n        else:\n            if task is self.main_task:\n                self.main_task_outcome = outcome\n                outcome = Value(None)\n            assert task._parent_nursery is not None, task\n            task._parent_nursery._child_finished(task, outcome)\n\n        if \"task_exited\" in self.instruments:\n            self.instruments.call(\"task_exited\", task)\n\n    ################\n    # System tasks and init\n    ################\n\n    @_public\n    def spawn_system_task(\n        self,\n        async_fn: Callable[[Unpack[PosArgT]], Awaitable[object]],\n        *args: Unpack[PosArgT],\n        name: object = None,\n        context: contextvars.Context | None = None,\n    ) -> Task:\n        \"\"\"Spawn a \"system\" task.\n\n        System tasks have a few differences from regular tasks:\n\n        * They don't need an explicit nursery; instead they go into the\n          internal \"system nursery\".\n\n        * If a system task raises an exception, then it's converted into a\n          :exc:`~trio.TrioInternalError` and *all* tasks are cancelled. If you\n          write a system task, you should be careful to make sure it doesn't\n          crash.\n\n        * System tasks are automatically cancelled when the main task exits.\n\n        * By default, system tasks have :exc:`KeyboardInterrupt` protection\n          *enabled*. If you want your task to be interruptible by control-C,\n          then you need to use :func:`disable_ki_protection` explicitly (and\n          come up with some plan for what to do with a\n          :exc:`KeyboardInterrupt`, given that system tasks aren't allowed to\n          raise exceptions).\n\n        * System tasks do not inherit context variables from their creator.\n\n        Towards the end of a call to :meth:`trio.run`, after the main\n        task and all system tasks have exited, the system nursery\n        becomes closed. At this point, new calls to\n        :func:`spawn_system_task` will raise ``RuntimeError(\"Nursery\n        is closed to new arrivals\")`` instead of creating a system\n        task. It's possible to encounter this state either in\n        a ``finally`` block in an async generator, or in a callback\n        passed to :meth:`TrioToken.run_sync_soon` at the right moment.\n\n        Args:\n          async_fn: An async callable.\n          args: Positional arguments for ``async_fn``. If you want to pass\n              keyword arguments, use :func:`functools.partial`.\n          name: The name for this task. Only used for debugging/introspection\n              (e.g. ``repr(task_obj)``). If this isn't a string,\n              :func:`spawn_system_task` will try to make it one. A common use\n              case is if you're wrapping a function before spawning a new\n              task, you might pass the original function as the ``name=`` to\n              make debugging easier.\n          context: An optional ``contextvars.Context`` object with context variables\n              to use for this task. You would normally get a copy of the current\n              context with ``context = contextvars.copy_context()`` and then you would\n              pass that ``context`` object here.\n\n        Returns:\n          Task: the newly spawned task\n\n        \"\"\"\n        return self.spawn_impl(\n            async_fn,\n            args,\n            self.system_nursery,\n            name,\n            system_task=True,\n            context=context,\n        )\n\n    async def init(\n        self,\n        async_fn: Callable[[Unpack[PosArgT]], Awaitable[object]],\n        args: tuple[Unpack[PosArgT]],\n    ) -> None:\n        # run_sync_soon task runs here:\n        async with open_nursery() as run_sync_soon_nursery:\n            # All other system tasks run here:\n            async with open_nursery() as self.system_nursery:\n                # Only the main task runs here:\n                async with open_nursery() as main_task_nursery:\n                    try:\n                        self.main_task = self.spawn_impl(\n                            async_fn,\n                            args,\n                            main_task_nursery,\n                            None,\n                        )\n                    except BaseException as exc:\n                        self.main_task_outcome = Error(exc)\n                        return\n                    self.spawn_impl(\n                        self.entry_queue.task,\n                        (),\n                        run_sync_soon_nursery,\n                        \"<TrioToken.run_sync_soon task>\",\n                        system_task=True,\n                    )\n\n                # Main task is done; start shutting down system tasks\n                self.system_nursery.cancel_scope._cancel(\n                    CancelReason(\n                        source=\"shutdown\",\n                        reason=\"main task done, shutting down system tasks\",\n                        source_task=repr(self.init_task),\n                    )\n                )\n\n            # System nursery is closed; finalize remaining async generators\n            await self.asyncgens.finalize_remaining(self)\n\n            # There are no more asyncgens, which means no more user-provided\n            # code except possibly run_sync_soon callbacks. It's finally safe\n            # to stop the run_sync_soon task and exit run().\n            run_sync_soon_nursery.cancel_scope._cancel(\n                CancelReason(\n                    source=\"shutdown\",\n                    reason=\"main task done, shutting down run_sync_soon callbacks\",\n                    source_task=repr(self.init_task),\n                )\n            )\n\n    ################\n    # Outside context problems\n    ################\n\n    @_public\n    def current_trio_token(self) -> TrioToken:\n        \"\"\"Retrieve the :class:`TrioToken` for the current call to\n        :func:`trio.run`.\n\n        \"\"\"\n        if self.trio_token is None:\n            self.trio_token = TrioToken._create(self.entry_queue)\n        return self.trio_token\n\n    ################\n    # KI handling\n    ################\n\n    ki_pending: bool = False\n\n    # deliver_ki is broke. Maybe move all the actual logic and state into\n    # RunToken, and we'll only have one instance per runner? But then we can't\n    # have a public constructor. Eh, but current_run_token() returning a\n    # unique object per run feels pretty nice. Maybe let's just go for it. And\n    # keep the class public so people can isinstance() it if they want.\n\n    # This gets called from signal context\n    def deliver_ki(self) -> None:\n        self.ki_pending = True\n        with suppress(RunFinishedError):\n            self.entry_queue.run_sync_soon(self._deliver_ki_cb)\n\n    def _deliver_ki_cb(self) -> None:\n        if not self.ki_pending:\n            return\n        # Can't happen because main_task and run_sync_soon_task are created at\n        # the same time -- so even if KI arrives before main_task is created,\n        # we won't get here until afterwards.\n        assert self.main_task is not None\n        if self.main_task_outcome is not None:\n            # We're already in the process of exiting -- leave ki_pending set\n            # and we'll check it again on our way out of run().\n            return\n        self.main_task._attempt_delivery_of_pending_ki()\n\n    ################\n    # Quiescing\n    ################\n\n    # sortedcontainers doesn't have types, and is reportedly very hard to type:\n    # https://github.com/grantjenks/python-sortedcontainers/issues/68\n    waiting_for_idle: Any = attrs.Factory(SortedDict)  # type: ignore[explicit-any]\n\n    @_public\n    async def wait_all_tasks_blocked(self, cushion: float = 0.0) -> None:\n        \"\"\"Block until there are no runnable tasks.\n\n        This is useful in testing code when you want to give other tasks a\n        chance to \"settle down\". The calling task is blocked, and doesn't wake\n        up until all other tasks are also blocked for at least ``cushion``\n        seconds. (Setting a non-zero ``cushion`` is intended to handle cases\n        like two tasks talking to each other over a local socket, where we\n        want to ignore the potential brief moment between a send and receive\n        when all tasks are blocked.)\n\n        Note that ``cushion`` is measured in *real* time, not the Trio clock\n        time.\n\n        If there are multiple tasks blocked in :func:`wait_all_tasks_blocked`,\n        then the one with the shortest ``cushion`` is the one woken (and\n        this task becoming unblocked resets the timers for the remaining\n        tasks). If there are multiple tasks that have exactly the same\n        ``cushion``, then all are woken.\n\n        You should also consider :class:`trio.testing.Sequencer`, which\n        provides a more explicit way to control execution ordering within a\n        test, and will often produce more readable tests.\n\n        Example:\n          Here's an example of one way to test that Trio's locks are fair: we\n          take the lock in the parent, start a child, wait for the child to be\n          blocked waiting for the lock (!), and then check that we can't\n          release and immediately re-acquire the lock::\n\n             async def lock_taker(lock):\n                 await lock.acquire()\n                 lock.release()\n\n             async def test_lock_fairness():\n                 lock = trio.Lock()\n                 await lock.acquire()\n                 async with trio.open_nursery() as nursery:\n                     nursery.start_soon(lock_taker, lock)\n                     # child hasn't run yet, we have the lock\n                     assert lock.locked()\n                     assert lock._owner is trio.lowlevel.current_task()\n                     await trio.testing.wait_all_tasks_blocked()\n                     # now the child has run and is blocked on lock.acquire(), we\n                     # still have the lock\n                     assert lock.locked()\n                     assert lock._owner is trio.lowlevel.current_task()\n                     lock.release()\n                     try:\n                         # The child has a prior claim, so we can't have it\n                         lock.acquire_nowait()\n                     except trio.WouldBlock:\n                         assert lock._owner is not trio.lowlevel.current_task()\n                         print(\"PASS\")\n                     else:\n                         print(\"FAIL\")\n\n        \"\"\"\n        task = current_task()\n        key = (cushion, id(task))\n        self.waiting_for_idle[key] = task\n\n        def abort(_: _core.RaiseCancelT) -> Abort:\n            del self.waiting_for_idle[key]\n            return Abort.SUCCEEDED\n\n        await wait_task_rescheduled(abort)\n\n\n################################################################\n# run\n################################################################\n#\n# Trio's core task scheduler and coroutine runner is in 'unrolled_run'. It's\n# called that because it has an unusual feature: it's actually a generator.\n# Whenever it needs to fetch IO events from the OS, it yields, and waits for\n# its caller to send the IO events back in. So the loop is \"unrolled\" into a\n# sequence of generator send() calls.\n#\n# The reason for this unusual design is to support two different modes of\n# operation, where the IO is handled differently.\n#\n# In normal mode using trio.run, the scheduler and IO run in the same thread:\n#\n# Main thread:\n#\n# +---------------------------+\n# | Run tasks                 |\n# | (unrolled_run)            |\n# +---------------------------+\n# | Block waiting for I/O     |\n# | (io_manager.get_events)   |\n# +---------------------------+\n# | Run tasks                 |\n# | (unrolled_run)            |\n# +---------------------------+\n# | Block waiting for I/O     |\n# | (io_manager.get_events)   |\n# +---------------------------+\n# :\n#\n#\n# In guest mode using trio.lowlevel.start_guest_run, the scheduler runs on the\n# main thread as a host loop callback, but blocking for IO gets pushed into a\n# worker thread:\n#\n# Main thread executing host loop:           Trio I/O thread:\n#\n# +---------------------------+\n# | Run Trio tasks            |\n# | (unrolled_run)            |\n# +---------------------------+ --------------+\n#                                             v\n# +---------------------------+              +----------------------------+\n# | Host loop does whatever   |              | Block waiting for Trio I/O |\n# | it wants                  |              | (io_manager.get_events)    |\n# +---------------------------+              +----------------------------+\n#                                             |\n# +---------------------------+ <-------------+\n# | Run Trio tasks            |\n# | (unrolled_run)            |\n# +---------------------------+ --------------+\n#                                             v\n# +---------------------------+              +----------------------------+\n# | Host loop does whatever   |              | Block waiting for Trio I/O |\n# | it wants                  |              | (io_manager.get_events)    |\n# +---------------------------+              +----------------------------+\n# :                                            :\n#\n# Most of Trio's internals don't need to care about this difference. The main\n# complication it creates is that in guest mode, we might need to wake up not\n# just due to OS-reported IO events, but also because of code running on the\n# host loop calling reschedule() or changing task deadlines. Search for\n# 'is_guest' to see the special cases we need to handle this.\n\n\ndef setup_runner(\n    clock: Clock | None,\n    instruments: Sequence[Instrument],\n    restrict_keyboard_interrupt_to_checkpoints: bool,\n    strict_exception_groups: bool,\n) -> Runner:\n    \"\"\"Create a Runner object and install it as the GLOBAL_RUN_CONTEXT.\"\"\"\n    # It wouldn't be *hard* to support nested calls to run(), but I can't\n    # think of a single good reason for it, so let's be conservative for\n    # now:\n    if in_trio_run():\n        raise RuntimeError(\"Attempted to call run() from inside a run()\")\n\n    if clock is None:\n        clock = SystemClock()\n    instrument_group = Instruments(instruments)\n    io_manager = TheIOManager()\n    system_context = copy_context()\n    ki_manager = KIManager()\n\n    runner = Runner(\n        clock=clock,\n        instruments=instrument_group,\n        io_manager=io_manager,\n        system_context=system_context,\n        ki_manager=ki_manager,\n        strict_exception_groups=strict_exception_groups,\n    )\n    runner.asyncgens.install_hooks(runner)\n\n    # This is where KI protection gets enabled, so we want to do it early - in\n    # particular before we start modifying global state like GLOBAL_RUN_CONTEXT\n    ki_manager.install(runner.deliver_ki, restrict_keyboard_interrupt_to_checkpoints)\n\n    GLOBAL_RUN_CONTEXT.runner = runner\n    return runner\n\n\ndef run(\n    async_fn: Callable[[Unpack[PosArgT]], Awaitable[RetT]],\n    *args: Unpack[PosArgT],\n    clock: Clock | None = None,\n    instruments: Sequence[Instrument] = (),\n    restrict_keyboard_interrupt_to_checkpoints: bool = False,\n    strict_exception_groups: bool = True,\n) -> RetT:\n    \"\"\"Run a Trio-flavored async function, and return the result.\n\n    Calling::\n\n       run(async_fn, *args)\n\n    is the equivalent of::\n\n       await async_fn(*args)\n\n    except that :func:`run` can (and must) be called from a synchronous\n    context.\n\n    This is Trio's main entry point. Almost every other function in Trio\n    requires that you be inside a call to :func:`run`.\n\n    Args:\n      async_fn: An async function.\n\n      args: Positional arguments to be passed to *async_fn*. If you need to\n          pass keyword arguments, then use :func:`functools.partial`.\n\n      clock: ``None`` to use the default system-specific monotonic clock;\n          otherwise, an object implementing the :class:`trio.abc.Clock`\n          interface, like (for example) a :class:`trio.testing.MockClock`\n          instance.\n\n      instruments (list of :class:`trio.abc.Instrument` objects): Any\n          instrumentation you want to apply to this run. This can also be\n          modified during the run; see :ref:`instrumentation`.\n\n      restrict_keyboard_interrupt_to_checkpoints (bool): What happens if the\n          user hits control-C while :func:`run` is running? If this argument\n          is False (the default), then you get the standard Python behavior: a\n          :exc:`KeyboardInterrupt` exception will immediately interrupt\n          whatever task is running (or if no task is running, then Trio will\n          wake up a task to be interrupted). Alternatively, if you set this\n          argument to True, then :exc:`KeyboardInterrupt` delivery will be\n          delayed: it will be *only* be raised at :ref:`checkpoints\n          <checkpoints>`, like a :exc:`Cancelled` exception.\n\n          The default behavior is nice because it means that even if you\n          accidentally write an infinite loop that never executes any\n          checkpoints, then you can still break out of it using control-C.\n          The alternative behavior is nice if you're paranoid about a\n          :exc:`KeyboardInterrupt` at just the wrong place leaving your\n          program in an inconsistent state, because it means that you only\n          have to worry about :exc:`KeyboardInterrupt` at the exact same\n          places where you already have to worry about :exc:`Cancelled`.\n\n          This setting has no effect if your program has registered a custom\n          SIGINT handler, or if :func:`run` is called from anywhere but the\n          main thread (this is a Python limitation), or if you use\n          :func:`open_signal_receiver` to catch SIGINT.\n\n      strict_exception_groups (bool): Unless set to False, nurseries will always wrap\n          even a single raised exception in an exception group. This can be overridden\n          on the level of individual nurseries. Setting it to False will be deprecated\n          and ultimately removed in a future version of Trio.\n\n    Returns:\n      Whatever ``async_fn`` returns.\n\n    Raises:\n      TrioInternalError: if an unexpected error is encountered inside Trio's\n          internal machinery. This is a bug and you should `let us know\n          <https://github.com/python-trio/trio/issues>`__.\n\n      Anything else: if ``async_fn`` raises an exception, then :func:`run`\n          propagates it.\n\n    \"\"\"\n    if strict_exception_groups is not None and not strict_exception_groups:\n        warn_deprecated(\n            \"trio.run(..., strict_exception_groups=False)\",\n            version=\"0.25.0\",\n            issue=2929,\n            instead=(\n                \"the default value of True and rewrite exception handlers to handle ExceptionGroups. \"\n                \"See https://trio.readthedocs.io/en/stable/reference-core.html#designing-for-multiple-errors\"\n            ),\n            use_triodeprecationwarning=True,\n        )\n\n    __tracebackhide__ = True\n\n    runner = setup_runner(\n        clock,\n        instruments,\n        restrict_keyboard_interrupt_to_checkpoints,\n        strict_exception_groups,\n    )\n\n    prev_library, sniffio_library.name = sniffio_library.name, \"trio\"\n    try:\n        gen = unrolled_run(runner, async_fn, args)\n        # Need to send None in the first time.\n        next_send: EventResult = None  # type: ignore[assignment]\n        while True:\n            try:\n                timeout = gen.send(next_send)\n            except StopIteration:\n                break\n            next_send = runner.io_manager.get_events(timeout)\n    finally:\n        sniffio_library.name = prev_library\n    # Inlined copy of runner.main_task_outcome.unwrap() to avoid\n    # cluttering every single Trio traceback with an extra frame.\n    if isinstance(runner.main_task_outcome, Value):\n        return cast(\"RetT\", runner.main_task_outcome.value)\n    elif isinstance(runner.main_task_outcome, Error):\n        raise runner.main_task_outcome.error\n    else:  # pragma: no cover\n        raise AssertionError(runner.main_task_outcome)\n\n\ndef start_guest_run(  # type: ignore[explicit-any]\n    async_fn: Callable[..., Awaitable[RetT]],\n    *args: object,\n    run_sync_soon_threadsafe: Callable[[Callable[[], object]], object],\n    done_callback: Callable[[outcome.Outcome[RetT]], object],\n    run_sync_soon_not_threadsafe: (\n        Callable[[Callable[[], object]], object] | None\n    ) = None,\n    host_uses_signal_set_wakeup_fd: bool = False,\n    clock: Clock | None = None,\n    instruments: Sequence[Instrument] = (),\n    restrict_keyboard_interrupt_to_checkpoints: bool = False,\n    strict_exception_groups: bool = True,\n) -> None:\n    \"\"\"Start a \"guest\" run of Trio on top of some other \"host\" event loop.\n\n    Each host loop can only have one guest run at a time.\n\n    You should always let the Trio run finish before stopping the host loop;\n    if not, it may leave Trio's internal data structures in an inconsistent\n    state. You might be able to get away with it if you immediately exit the\n    program, but it's safest not to go there in the first place.\n\n    Generally, the best way to do this is wrap this in a function that starts\n    the host loop and then immediately starts the guest run, and then shuts\n    down the host when the guest run completes.\n\n    Once :func:`start_guest_run` returns successfully, the guest run\n    has been set up enough that you can invoke sync-colored Trio\n    functions such as :func:`~trio.current_time`, :func:`spawn_system_task`,\n    and :func:`current_trio_token`. If a `~trio.TrioInternalError` occurs\n    during this early setup of the guest run, it will be raised out of\n    :func:`start_guest_run`.  All other errors, including all errors\n    raised by the *async_fn*, will be delivered to your\n    *done_callback* at some point after :func:`start_guest_run` returns\n    successfully.\n\n    Args:\n\n      run_sync_soon_threadsafe: An arbitrary callable, which will be passed a\n         function as its sole argument::\n\n            def my_run_sync_soon_threadsafe(fn):\n                ...\n\n         This callable should schedule ``fn()`` to be run by the host on its\n         next pass through its loop. **Must support being called from\n         arbitrary threads.**\n\n      done_callback: An arbitrary callable::\n\n            def my_done_callback(run_outcome):\n                ...\n\n         When the Trio run has finished, Trio will invoke this callback to let\n         you know. The argument is an `outcome.Outcome`, reporting what would\n         have been returned or raised by `trio.run`. This function can do\n         anything you want, but commonly you'll want it to shut down the\n         host loop, unwrap the outcome, etc.\n\n      run_sync_soon_not_threadsafe: Like ``run_sync_soon_threadsafe``, but\n         will only be called from inside the host loop's main thread.\n         Optional, but if your host loop allows you to implement this more\n         efficiently than ``run_sync_soon_threadsafe`` then passing it will\n         make things a bit faster.\n\n      host_uses_signal_set_wakeup_fd (bool): Pass `True` if your host loop\n         uses `signal.set_wakeup_fd`, and `False` otherwise. For more details,\n         see :ref:`guest-run-implementation`.\n\n    For the meaning of other arguments, see `trio.run`.\n\n    \"\"\"\n    if strict_exception_groups is not None and not strict_exception_groups:\n        warn_deprecated(\n            \"trio.start_guest_run(..., strict_exception_groups=False)\",\n            version=\"0.25.0\",\n            issue=2929,\n            instead=(\n                \"the default value of True and rewrite exception handlers to handle ExceptionGroups. \"\n                \"See https://trio.readthedocs.io/en/stable/reference-core.html#designing-for-multiple-errors\"\n            ),\n            use_triodeprecationwarning=True,\n        )\n\n    runner = setup_runner(\n        clock,\n        instruments,\n        restrict_keyboard_interrupt_to_checkpoints,\n        strict_exception_groups,\n    )\n    runner.is_guest = True\n    runner.guest_tick_scheduled = True\n\n    if run_sync_soon_not_threadsafe is None:\n        run_sync_soon_not_threadsafe = run_sync_soon_threadsafe\n\n    guest_state = GuestState(\n        runner=runner,\n        run_sync_soon_threadsafe=run_sync_soon_threadsafe,\n        run_sync_soon_not_threadsafe=run_sync_soon_not_threadsafe,\n        done_callback=done_callback,\n        unrolled_run_gen=unrolled_run(\n            runner,\n            async_fn,\n            args,\n            host_uses_signal_set_wakeup_fd=host_uses_signal_set_wakeup_fd,\n        ),\n    )\n\n    # Run a few ticks of the guest run synchronously, so that by the\n    # time we return, the system nursery exists and callers can use\n    # spawn_system_task. We don't actually run any user code during\n    # this time, so it shouldn't be possible to get an exception here,\n    # except for a TrioInternalError.\n    next_send = cast(\n        \"EventResult\",\n        None,\n    )  # First iteration must be `None`, every iteration after that is EventResult\n    for _tick in range(5):  # expected need is 2 iterations + leave some wiggle room\n        if runner.system_nursery is not None:\n            # We're initialized enough to switch to async guest ticks\n            break\n        try:\n            timeout = guest_state.unrolled_run_gen.send(next_send)\n        except StopIteration:  # pragma: no cover\n            raise TrioInternalError(\n                \"Guest runner exited before system nursery was initialized\",\n            ) from None\n        if timeout != 0:  # pragma: no cover\n            guest_state.unrolled_run_gen.throw(\n                TrioInternalError(\n                    \"Guest runner blocked before system nursery was initialized\",\n                ),\n            )\n        # next_send should be the return value of\n        # IOManager.get_events() if no I/O was waiting, which is\n        # platform-dependent. We don't actually check for I/O during\n        # this init phase because no one should be expecting any yet.\n        if sys.platform == \"win32\":\n            next_send = 0\n        else:\n            next_send = []\n    else:  # pragma: no cover\n        guest_state.unrolled_run_gen.throw(\n            TrioInternalError(\n                \"Guest runner yielded too many times before \"\n                \"system nursery was initialized\",\n            ),\n        )\n\n    guest_state.unrolled_run_next_send = Value(next_send)\n    run_sync_soon_not_threadsafe(guest_state.guest_tick)\n\n\n# 24 hours is arbitrary, but it avoids issues like people setting timeouts of\n# 10**20 and then getting integer overflows in the underlying system calls.\n_MAX_TIMEOUT: Final = 24 * 60 * 60\n\n\n# Weird quirk: this is written as a generator in order to support \"guest\n# mode\", where our core event loop gets unrolled into a series of callbacks on\n# the host loop. If you're doing a regular trio.run then this gets run\n# straight through.\n@enable_ki_protection\ndef unrolled_run(\n    runner: Runner,\n    async_fn: Callable[[Unpack[PosArgT]], Awaitable[object]],\n    args: tuple[Unpack[PosArgT]],\n    host_uses_signal_set_wakeup_fd: bool = False,\n) -> Generator[float, EventResult, None]:\n    __tracebackhide__ = True\n\n    try:\n        if not host_uses_signal_set_wakeup_fd:\n            runner.entry_queue.wakeup.wakeup_on_signals()\n\n        if \"before_run\" in runner.instruments:\n            runner.instruments.call(\"before_run\")\n        runner.clock.start_clock()\n        runner.init_task = runner.spawn_impl(\n            runner.init,\n            (async_fn, args),\n            None,\n            \"<init>\",\n            system_task=True,\n        )\n\n        # You know how people talk about \"event loops\"? This 'while' loop right\n        # here is our event loop:\n        while runner.tasks:\n            if runner.runq:\n                timeout: float = 0\n            else:\n                deadline = runner.deadlines.next_deadline()\n                timeout = runner.clock.deadline_to_sleep_time(deadline)\n            timeout = min(max(0, timeout), _MAX_TIMEOUT)\n\n            idle_primed = None\n            if runner.waiting_for_idle:\n                cushion, _ = runner.waiting_for_idle.keys()[0]\n                if cushion < timeout:\n                    timeout = cushion\n                    idle_primed = IdlePrimedTypes.WAITING_FOR_IDLE\n            # We use 'elif' here because if there are tasks in\n            # wait_all_tasks_blocked, then those tasks will wake up without\n            # jumping the clock, so we don't need to autojump.\n            elif runner.clock_autojump_threshold < timeout:\n                timeout = runner.clock_autojump_threshold\n                idle_primed = IdlePrimedTypes.AUTOJUMP_CLOCK\n\n            if \"before_io_wait\" in runner.instruments:\n                runner.instruments.call(\"before_io_wait\", timeout)\n\n            # Driver will call io_manager.get_events(timeout) and pass it back\n            # in through the yield\n            events = yield timeout\n            runner.io_manager.process_events(events)\n\n            if \"after_io_wait\" in runner.instruments:\n                runner.instruments.call(\"after_io_wait\", timeout)\n\n            # Process cancellations due to deadline expiry\n            now = runner.clock.current_time()\n            if runner.deadlines.expire(now):\n                idle_primed = None\n\n            # idle_primed != None means: if the IO wait hit the timeout, and\n            # still nothing is happening, then we should start waking up\n            # wait_all_tasks_blocked tasks or autojump the clock. But there\n            # are some subtleties in defining \"nothing is happening\".\n            #\n            # 'not runner.runq' means that no tasks are currently runnable.\n            # 'not events' means that the last IO wait call hit its full\n            # timeout. These are very similar, and if idle_primed != None and\n            # we're running in regular mode then they always go together. But,\n            # in *guest* mode, they can happen independently, even when\n            # idle_primed=True:\n            #\n            # - runner.runq=empty and events=True: the host loop adjusted a\n            #   deadline and that forced an IO wakeup before the timeout expired,\n            #   even though no actual tasks were scheduled.\n            #\n            # - runner.runq=nonempty and events=False: the IO wait hit its\n            #   timeout, but then some code in the host thread rescheduled a task\n            #   before we got here.\n            #\n            # So we need to check both.\n            if idle_primed is not None and not runner.runq and not events:\n                if idle_primed is IdlePrimedTypes.WAITING_FOR_IDLE:\n                    while runner.waiting_for_idle:\n                        key, task = runner.waiting_for_idle.peekitem(0)\n                        if key[0] == cushion:\n                            del runner.waiting_for_idle[key]\n                            runner.reschedule(task)\n                        else:\n                            break\n                else:\n                    assert idle_primed is IdlePrimedTypes.AUTOJUMP_CLOCK\n                    assert isinstance(runner.clock, _core.MockClock)\n                    runner.clock._autojump()\n\n            # Process all runnable tasks, but only the ones that are already\n            # runnable now. Anything that becomes runnable during this cycle\n            # needs to wait until the next pass. This avoids various\n            # starvation issues by ensuring that there's never an unbounded\n            # delay between successive checks for I/O.\n            #\n            # Also, we randomize the order of each batch to avoid assumptions\n            # about scheduling order sneaking in. In the long run, I suspect\n            # we'll either (a) use strict FIFO ordering and document that for\n            # predictability/determinism, or (b) implement a more\n            # sophisticated scheduler (e.g. some variant of fair queueing),\n            # for better behavior under load. For now, this is the worst of\n            # both worlds - but it keeps our options open. (If we do decide to\n            # go all in on deterministic scheduling, then there are other\n            # things that will probably need to change too, like the deadlines\n            # tie-breaker and the non-deterministic ordering of\n            # task._notify_queues.)\n            batch = list(runner.runq)\n            runner.runq.clear()\n            if _ALLOW_DETERMINISTIC_SCHEDULING:\n                # We're running under Hypothesis, and pytest-trio has patched\n                # this in to make the scheduler deterministic and avoid flaky\n                # tests. It's not worth the (small) performance cost in normal\n                # operation, since we'll shuffle the list and _r is only\n                # seeded for tests.\n                batch.sort(key=lambda t: t._counter)\n                _r.shuffle(batch)\n            else:\n                # 50% chance of reversing the batch, this way each task\n                # can appear before/after any other task.\n                if _r.random() < 0.5:\n                    batch.reverse()\n            while batch:\n                task = batch.pop()\n                GLOBAL_RUN_CONTEXT.task = task\n\n                if \"before_task_step\" in runner.instruments:\n                    runner.instruments.call(\"before_task_step\", task)\n\n                next_send_fn = task._next_send_fn\n                next_send = task._next_send\n                task._next_send_fn = task._next_send = None\n                final_outcome: Outcome[object] | None = None\n\n                assert next_send_fn is not None\n\n                try:\n                    # We used to unwrap the Outcome object here and send/throw\n                    # its contents in directly, but it turns out that .throw()\n                    # is buggy on CPython (all versions at time of writing):\n                    #   https://bugs.python.org/issue29587\n                    #   https://bugs.python.org/issue29590\n                    #   https://bugs.python.org/issue40694\n                    #   https://github.com/python/cpython/issues/108668\n                    # So now we send in the Outcome object and unwrap it on the\n                    # other side.\n                    msg = task.context.run(next_send_fn, next_send)\n                except StopIteration as stop_iteration:\n                    final_outcome = Value(stop_iteration.value)\n                except BaseException as task_exc:\n                    # Store for later, removing uninteresting top frames: 1\n                    # frame we always remove, because it's this function\n                    # catching it, and then in addition we remove however many\n                    # more Context.run adds.\n                    tb = task_exc.__traceback__\n                    for _ in range(1 + CONTEXT_RUN_TB_FRAMES):\n                        if tb is not None:  # pragma: no branch\n                            tb = tb.tb_next\n                    final_outcome = Error(task_exc.with_traceback(tb))\n                    # Remove local refs so that e.g. cancelled coroutine locals\n                    # are not kept alive by this frame until another exception\n                    # comes along.\n                    del tb\n\n                if final_outcome is not None:\n                    # We can't call this directly inside the except: blocks\n                    # above, because then the exceptions end up attaching\n                    # themselves to other exceptions as __context__ in\n                    # unwanted ways.\n                    runner.task_exited(task, final_outcome)\n                    # final_outcome may contain a traceback ref. It's not as\n                    # crucial compared to the above, but this will allow more\n                    # prompt release of resources in coroutine locals.\n                    final_outcome = None\n                else:\n                    task._schedule_points += 1\n                    if msg is CancelShieldedCheckpoint:\n                        runner.reschedule(task)\n                    elif type(msg) is WaitTaskRescheduled:\n                        task._cancel_points += 1\n                        task._abort_func = msg.abort_func\n                        # KI is \"outside\" all cancel scopes, so check for it\n                        # before checking for regular cancellation:\n                        if runner.ki_pending and task is runner.main_task:\n                            task._attempt_delivery_of_pending_ki()\n                        task._attempt_delivery_of_any_pending_cancel()\n                    elif type(msg) is PermanentlyDetachCoroutineObject:\n                        # Pretend the task just exited with the given outcome\n                        runner.task_exited(task, msg.final_outcome)\n                    else:\n                        exc = TypeError(\n                            f\"trio.run received unrecognized yield message {msg!r}. \"\n                            \"Are you trying to use a library written for some \"\n                            \"other framework like asyncio? That won't work \"\n                            \"without some kind of compatibility shim.\",\n                        )\n                        # The foreign library probably doesn't adhere to our\n                        # protocol of unwrapping whatever outcome gets sent in.\n                        # Instead, we'll arrange to throw `exc` in directly,\n                        # which works for at least asyncio and curio.\n                        runner.reschedule(task, exc)  # type: ignore[arg-type]\n                        task._next_send_fn = task.coro.throw\n                    # prevent long-lived reference\n                    # TODO: develop test for this deletion\n                    del msg\n\n                if \"after_task_step\" in runner.instruments:\n                    runner.instruments.call(\"after_task_step\", task)\n                del GLOBAL_RUN_CONTEXT.task\n                # prevent long-lived references\n                # TODO: develop test for this deletion\n                del task, next_send, next_send_fn\n\n    except GeneratorExit:\n        # The run-loop generator has been garbage collected without finishing\n        warnings.warn(\n            RuntimeWarning(\n                \"Trio guest run got abandoned without properly finishing... \"\n                \"weird stuff might happen\",\n            ),\n            stacklevel=1,\n        )\n    except TrioInternalError:\n        raise\n    except BaseException as exc:\n        raise TrioInternalError(\"internal error in Trio - please file a bug!\") from exc\n    finally:\n        runner.close()\n        GLOBAL_RUN_CONTEXT.__dict__.clear()\n\n        # Have to do this after runner.close() has disabled KI protection,\n        # because otherwise there's a race where ki_pending could get set\n        # after we check it.\n        if runner.ki_pending:\n            ki = KeyboardInterrupt()\n            if isinstance(runner.main_task_outcome, Error):\n                ki.__context__ = runner.main_task_outcome.error\n            runner.main_task_outcome = Error(ki)\n\n\n################################################################\n# Other public API functions\n################################################################\n\n\nclass _TaskStatusIgnored(TaskStatus[object]):\n    def __repr__(self) -> str:\n        return \"TASK_STATUS_IGNORED\"\n\n    def started(self, value: object = None) -> None:\n        pass\n\n\nTASK_STATUS_IGNORED: Final[TaskStatus[object]] = _TaskStatusIgnored()\n\n\ndef current_task() -> Task:\n    \"\"\"Return the :class:`Task` object representing the current task.\n\n    Returns:\n      Task: the :class:`Task` that called :func:`current_task`.\n\n    \"\"\"\n\n    try:\n        return GLOBAL_RUN_CONTEXT.task\n    except AttributeError:\n        raise RuntimeError(\"must be called from async context\") from None\n\n\ndef current_effective_deadline() -> float:\n    \"\"\"Returns the current effective deadline for the current task.\n\n    This function examines all the cancellation scopes that are currently in\n    effect (taking into account shielding), and returns the deadline that will\n    expire first.\n\n    One example of where this might be is useful is if your code is trying to\n    decide whether to begin an expensive operation like an RPC call, but wants\n    to skip it if it knows that it can't possibly complete in the available\n    time. Another example would be if you're using a protocol like gRPC that\n    `propagates timeout information to the remote peer\n    <http://www.grpc.io/docs/guides/concepts.html#deadlines>`__; this function\n    gives a way to fetch that information so you can send it along.\n\n    If this is called in a context where a cancellation is currently active\n    (i.e., a blocking call will immediately raise :exc:`Cancelled`), then\n    returned deadline is ``-inf``. If it is called in a context where no\n    scopes have a deadline set, it returns ``inf``.\n\n    Returns:\n        float: the effective deadline, as an absolute time.\n\n    \"\"\"\n    return current_task()._cancel_status.effective_deadline()\n\n\nasync def checkpoint() -> None:\n    \"\"\"A pure :ref:`checkpoint <checkpoints>`.\n\n    This checks for cancellation and allows other tasks to be scheduled,\n    without otherwise blocking.\n\n    Note that the scheduler has the option of ignoring this and continuing to\n    run the current task if it decides this is appropriate (e.g. for increased\n    efficiency).\n\n    Equivalent to ``await trio.sleep(0)`` (which is implemented by calling\n    :func:`checkpoint`.)\n\n    \"\"\"\n    # The scheduler is what checks timeouts and converts them into\n    # cancellations. So by doing the schedule point first, we ensure that the\n    # cancel point has the most up-to-date info.\n    await cancel_shielded_checkpoint()\n    task = current_task()\n    task._cancel_points += 1\n    if task._cancel_status.effectively_cancelled or (\n        task is task._runner.main_task and task._runner.ki_pending\n    ):\n        cs = CancelScope(deadline=-inf)\n        if (\n            task._cancel_status._scope._cancel_reason is None\n            and task is task._runner.main_task\n            and task._runner.ki_pending\n        ):\n            task._cancel_status._scope._cancel_reason = CancelReason(\n                source=\"KeyboardInterrupt\"\n            )\n        assert task._cancel_status._scope._cancel_reason is not None\n        cs._cancel_reason = task._cancel_status._scope._cancel_reason\n        with cs:\n            await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)\n\n\nasync def checkpoint_if_cancelled() -> None:\n    \"\"\"Issue a :ref:`checkpoint <checkpoints>` if the calling context has been\n    cancelled.\n\n    Equivalent to (but potentially more efficient than)::\n\n        if trio.current_effective_deadline() == -inf:\n            await trio.lowlevel.checkpoint()\n\n    This is either a no-op, or else it allow other tasks to be scheduled and\n    then raises :exc:`trio.Cancelled`.\n\n    Typically used together with :func:`cancel_shielded_checkpoint`.\n\n    \"\"\"\n    task = current_task()\n    if task._cancel_status.effectively_cancelled or (\n        task is task._runner.main_task and task._runner.ki_pending\n    ):\n        await _core.checkpoint()\n        raise AssertionError(\"this should never happen\")  # pragma: no cover\n    task._cancel_points += 1\n\n\ndef in_trio_run() -> bool:\n    \"\"\"Check whether we are in a Trio run.\n    This returns `True` if and only if :func:`~trio.current_time` will succeed.\n\n    See also the discussion of differing ways of :ref:`detecting Trio <trio_contexts>`.\n    \"\"\"\n    return hasattr(GLOBAL_RUN_CONTEXT, \"runner\")\n\n\ndef in_trio_task() -> bool:\n    \"\"\"Check whether we are in a Trio task.\n    This returns `True` if and only if :func:`~trio.lowlevel.current_task` will succeed.\n\n    See also the discussion of differing ways of :ref:`detecting Trio <trio_contexts>`.\n    \"\"\"\n    return hasattr(GLOBAL_RUN_CONTEXT, \"task\")\n\n\n# export everything for the documentation\nif \"sphinx.ext.autodoc\" in sys.modules:\n    from ._generated_io_epoll import *\n    from ._generated_io_kqueue import *\n    from ._generated_io_windows import *\n\nif sys.platform == \"win32\":\n    from ._generated_io_windows import *\n    from ._io_windows import (\n        EventResult as EventResult,\n        WindowsIOManager as TheIOManager,\n        _WindowsStatistics as IOStatistics,\n    )\nelif (\n    sys.platform == \"linux\"\n    or sys.platform == \"android\"\n    or (not TYPE_CHECKING and hasattr(select, \"epoll\"))\n):\n    from ._generated_io_epoll import *\n    from ._io_epoll import (\n        EpollIOManager as TheIOManager,\n        EventResult as EventResult,\n        _EpollStatistics as IOStatistics,\n    )\nelif TYPE_CHECKING or hasattr(select, \"kqueue\"):\n    from ._generated_io_kqueue import *\n    from ._io_kqueue import (\n        EventResult as EventResult,\n        KqueueIOManager as TheIOManager,\n        _KqueueStatistics as IOStatistics,\n    )\nelse:  # pragma: no cover\n    _patchers = sorted({\"eventlet\", \"gevent\"}.intersection(sys.modules))\n    if _patchers:\n        raise NotImplementedError(\n            \"unsupported platform or primitives Trio depends on are monkey-patched out by \"\n            + \", \".join(_patchers),\n        )\n\n    raise NotImplementedError(\"unsupported platform\")\n\nfrom ._generated_instrumentation import *\nfrom ._generated_run import *\n"
  },
  {
    "path": "src/trio/_core/_run_context.py",
    "content": "from __future__ import annotations\n\nimport threading\nfrom typing import TYPE_CHECKING, Final\n\nif TYPE_CHECKING:\n    from ._run import Runner, Task\n\n\nclass RunContext(threading.local):\n    runner: Runner\n    task: Task\n\n\nGLOBAL_RUN_CONTEXT: Final = RunContext()\n"
  },
  {
    "path": "src/trio/_core/_tests/__init__.py",
    "content": ""
  },
  {
    "path": "src/trio/_core/_tests/test_asyncgen.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport sys\nimport weakref\nfrom math import inf\nfrom types import AsyncGeneratorType\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport pytest\n\nfrom ... import _core\nfrom .tutil import gc_collect_harder, restore_unraisablehook\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator\n\n\ndef test_asyncgen_basics() -> None:\n    collected = []\n\n    async def example(cause: str) -> AsyncGenerator[int, None]:\n        try:\n            with contextlib.suppress(GeneratorExit):\n                yield 42\n            await _core.checkpoint()\n        except _core.Cancelled:\n            assert \"exhausted\" not in cause\n            task_name = _core.current_task().name\n            assert cause in task_name or task_name == \"<init>\"\n            assert _core.current_effective_deadline() == -inf\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n            collected.append(cause)\n        else:\n            assert \"async_main\" in _core.current_task().name\n            assert \"exhausted\" in cause\n            assert _core.current_effective_deadline() == inf\n            await _core.checkpoint()\n            collected.append(cause)\n\n    saved = []\n\n    async def async_main() -> None:\n        # GC'ed before exhausted\n        with pytest.warns(  # noqa: PT031\n            ResourceWarning,\n            match=\"Async generator.*collected before.*exhausted\",\n        ):\n            assert await example(\"abandoned\").asend(None) == 42\n            gc_collect_harder()\n        await _core.wait_all_tasks_blocked()\n        assert collected.pop() == \"abandoned\"\n\n        aiter_ = example(\"exhausted 1\")\n        try:\n            assert await aiter_.asend(None) == 42\n        finally:\n            await aiter_.aclose()\n        assert collected.pop() == \"exhausted 1\"\n\n        # Also fine if you exhaust it at point of use\n        async for val in example(\"exhausted 2\"):\n            assert val == 42\n        assert collected.pop() == \"exhausted 2\"\n\n        gc_collect_harder()\n\n        # No problems saving the geniter when using either of these patterns\n        aiter_ = example(\"exhausted 3\")\n        try:\n            saved.append(aiter_)\n            assert await aiter_.asend(None) == 42\n        finally:\n            await aiter_.aclose()\n        assert collected.pop() == \"exhausted 3\"\n\n        # Also fine if you exhaust it at point of use\n        saved.append(example(\"exhausted 4\"))\n        async for val in saved[-1]:\n            assert val == 42\n        assert collected.pop() == \"exhausted 4\"\n\n        # Leave one referenced-but-unexhausted and make sure it gets cleaned up\n        saved.append(example(\"outlived run\"))\n        assert await saved[-1].asend(None) == 42\n        assert collected == []\n\n    _core.run(async_main)\n    assert collected.pop() == \"outlived run\"\n    for agen in saved:\n        assert isinstance(agen, AsyncGeneratorType)\n        assert agen.ag_frame is None  # all should now be exhausted\n\n\nasync def test_asyncgen_throws_during_finalization(\n    caplog: pytest.LogCaptureFixture,\n) -> None:\n    record = []\n\n    async def agen() -> AsyncGenerator[int, None]:\n        try:\n            yield 1\n        finally:\n            await _core.cancel_shielded_checkpoint()\n            record.append(\"crashing\")\n            raise ValueError(\"oops\")\n\n    with restore_unraisablehook():\n        await agen().asend(None)\n        gc_collect_harder()\n    await _core.wait_all_tasks_blocked()\n    assert record == [\"crashing\"]\n    # Following type ignore is because typing for LogCaptureFixture is wrong\n    exc_type, exc_value, _exc_traceback = caplog.records[0].exc_info  # type: ignore[misc]\n    assert exc_type is ValueError\n    assert str(exc_value) == \"oops\"\n    assert \"during finalization of async generator\" in caplog.records[0].message\n\n\ndef test_firstiter_after_closing() -> None:\n    saved = []\n    record = []\n\n    async def funky_agen() -> AsyncGenerator[int, None]:\n        try:\n            yield 1\n        except GeneratorExit:\n            record.append(\"cleanup 1\")\n            raise\n        try:\n            yield 2\n        finally:\n            record.append(\"cleanup 2\")\n            await funky_agen().asend(None)\n\n    async def async_main() -> None:\n        aiter_ = funky_agen()\n        saved.append(aiter_)\n        assert await aiter_.asend(None) == 1\n        assert await aiter_.asend(None) == 2\n\n    _core.run(async_main)\n    assert record == [\"cleanup 2\", \"cleanup 1\"]\n\n\ndef test_interdependent_asyncgen_cleanup_order() -> None:\n    saved: list[AsyncGenerator[int, None]] = []\n    record: list[int | str] = []\n\n    async def innermost() -> AsyncGenerator[int, None]:\n        try:\n            yield 1\n        finally:\n            await _core.cancel_shielded_checkpoint()\n            record.append(\"innermost\")\n\n    async def agen(\n        label: int,\n        inner: AsyncGenerator[int, None],\n    ) -> AsyncGenerator[int, None]:\n        try:\n            yield await inner.asend(None)\n        finally:\n            # Either `inner` has already been cleaned up, or\n            # we're about to exhaust it. Either way, we wind\n            # up with `record` containing the labels in\n            # innermost-to-outermost order.\n            with pytest.raises(StopAsyncIteration):\n                await inner.asend(None)\n            record.append(label)\n\n    async def async_main() -> None:\n        # This makes a chain of 101 interdependent asyncgens:\n        # agen(99)'s cleanup will iterate agen(98)'s will iterate\n        # ... agen(0)'s will iterate innermost()'s\n        ag_chain = innermost()\n        for idx in range(100):\n            ag_chain = agen(idx, ag_chain)\n        saved.append(ag_chain)\n        assert await ag_chain.asend(None) == 1\n        assert record == []\n\n    _core.run(async_main)\n    assert record == [\"innermost\", *range(100)]\n\n\n@restore_unraisablehook()\ndef test_last_minute_gc_edge_case() -> None:\n    saved: list[AsyncGenerator[int, None]] = []\n    record = []\n    needs_retry = True\n\n    async def agen() -> AsyncGenerator[int, None]:\n        try:\n            yield 1\n        finally:\n            record.append(\"cleaned up\")\n\n    def collect_at_opportune_moment(token: _core._entry_queue.TrioToken) -> None:\n        runner = _core._run.GLOBAL_RUN_CONTEXT.runner\n        assert runner.system_nursery is not None\n        if runner.system_nursery._closed and isinstance(\n            runner.asyncgens.alive,\n            weakref.WeakSet,\n        ):\n            saved.clear()\n            record.append(\"final collection\")\n            gc_collect_harder()\n            record.append(\"done\")\n        else:\n            try:\n                token.run_sync_soon(collect_at_opportune_moment, token)\n            except _core.RunFinishedError:  # pragma: no cover\n                nonlocal needs_retry\n                needs_retry = True\n\n    async def async_main() -> None:\n        token = _core.current_trio_token()\n        token.run_sync_soon(collect_at_opportune_moment, token)\n        saved.append(agen())\n        await saved[-1].asend(None)\n\n    ATTEMPT_AMOUNT = 50\n\n    # Actually running into the edge case requires that the run_sync_soon task\n    # execute in between the system nursery's closure and the strong-ification\n    # of runner.asyncgens. There's about a 25% chance that it doesn't\n    # (if the run_sync_soon task runs before init on one tick and after init\n    # on the next tick); if we try enough times, we can make the chance of\n    # failure as small as we want.\n    for _ in range(ATTEMPT_AMOUNT):\n        needs_retry = False\n        record.clear()\n        saved.clear()\n        _core.run(async_main)\n        if needs_retry:  # pragma: no cover\n            assert record == [\"cleaned up\"]\n        else:\n            assert record == [\"final collection\", \"done\", \"cleaned up\"]\n            break\n    else:  # pragma: no cover\n        pytest.fail(\n            \"Didn't manage to hit the trailing_finalizer_asyncgens case \"\n            f\"despite trying {ATTEMPT_AMOUNT} times\",\n        )\n\n\nasync def step_outside_async_context(aiter_: AsyncGenerator[int, None]) -> None:\n    # abort_fns run outside of task context, at least if they're\n    # triggered by a deadline expiry rather than a direct\n    # cancellation.  Thus, an asyncgen first iterated inside one\n    # will appear non-Trio, and since no other hooks were installed,\n    # will use the last-ditch fallback handling (that tries to mimic\n    # CPython's behavior with no hooks).\n    #\n    # NB: the strangeness with aiter being an attribute of abort_fn is\n    # to make it as easy as possible to ensure we don't hang onto a\n    # reference to aiter inside the guts of the run loop.\n    def abort_fn(_: _core.RaiseCancelT) -> _core.Abort:\n        with pytest.raises(StopIteration, match=\"42\"):\n            abort_fn.aiter.asend(None).send(None)  # type: ignore[attr-defined]  # Callables don't have attribute \"aiter\"\n        del abort_fn.aiter  # type: ignore[attr-defined]\n        return _core.Abort.SUCCEEDED\n\n    abort_fn.aiter = aiter_  # type: ignore[attr-defined]\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(_core.wait_task_rescheduled, abort_fn)\n        await _core.wait_all_tasks_blocked()\n        nursery.cancel_scope.deadline = _core.current_time()\n\n\nasync def test_fallback_when_no_hook_claims_it(\n    capsys: pytest.CaptureFixture[str],\n) -> None:\n    async def well_behaved() -> AsyncGenerator[int, None]:\n        yield 42\n\n    async def yields_after_yield() -> AsyncGenerator[int, None]:\n        with pytest.raises(GeneratorExit):\n            yield 42\n        yield 100\n\n    async def awaits_after_yield() -> AsyncGenerator[int, None]:\n        with pytest.raises(GeneratorExit):\n            yield 42\n        await _core.cancel_shielded_checkpoint()\n\n    with restore_unraisablehook():\n        await step_outside_async_context(well_behaved())\n        gc_collect_harder()\n        assert capsys.readouterr().err == \"\"\n\n        await step_outside_async_context(yields_after_yield())\n        gc_collect_harder()\n        assert \"ignored GeneratorExit\" in capsys.readouterr().err\n\n        await step_outside_async_context(awaits_after_yield())\n        gc_collect_harder()\n        assert \"awaited something during finalization\" in capsys.readouterr().err\n\n\ndef test_delegation_to_existing_hooks() -> None:\n    record = []\n\n    def my_firstiter(agen: AsyncGenerator[object, NoReturn]) -> None:\n        assert isinstance(agen, AsyncGeneratorType)\n        record.append(\"firstiter \" + agen.ag_frame.f_locals[\"arg\"])\n\n    def my_finalizer(agen: AsyncGenerator[object, NoReturn]) -> None:\n        assert isinstance(agen, AsyncGeneratorType)\n        record.append(\"finalizer \" + agen.ag_frame.f_locals[\"arg\"])\n\n    async def example(arg: str) -> AsyncGenerator[int, None]:\n        try:\n            yield 42\n        finally:\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n            record.append(\"trio collected \" + arg)\n\n    async def async_main() -> None:\n        await step_outside_async_context(example(\"theirs\"))\n        assert await example(\"ours\").asend(None) == 42\n        gc_collect_harder()\n        assert record == [\"firstiter theirs\", \"finalizer theirs\"]\n        record[:] = []\n        await _core.wait_all_tasks_blocked()\n        assert record == [\"trio collected ours\"]\n\n    with restore_unraisablehook():\n        old_hooks = sys.get_asyncgen_hooks()\n        sys.set_asyncgen_hooks(my_firstiter, my_finalizer)\n        try:\n            _core.run(async_main)\n        finally:\n            assert sys.get_asyncgen_hooks() == (my_firstiter, my_finalizer)\n            sys.set_asyncgen_hooks(*old_hooks)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_cancelled.py",
    "content": "import pickle\nimport re\nfrom math import inf\n\nimport pytest\n\nimport trio\nfrom trio import Cancelled\nfrom trio.lowlevel import current_task\nfrom trio.testing import wait_all_tasks_blocked\n\nfrom .test_ki import ki_self\n\n\ndef test_Cancelled_init() -> None:\n    with pytest.raises(TypeError, match=r\"^trio.Cancelled has no public constructor$\"):\n        raise Cancelled\n\n    with pytest.raises(TypeError, match=r\"^trio.Cancelled has no public constructor$\"):\n        Cancelled(source=\"explicit\")\n\n    # private constructor should not raise\n    Cancelled._create(source=\"explicit\")\n\n\nasync def test_Cancelled_str() -> None:\n    cancelled = Cancelled._create(source=\"explicit\")\n    assert str(cancelled) == \"cancelled due to explicit\"\n    # note: repr(current_task()) is often fairly verbose\n    assert re.fullmatch(\n        r\"cancelled due to deadline from task \"\n        r\"<Task 'trio._core._tests.test_cancelled.test_Cancelled_str' at 0x\\w*>\",\n        str(\n            Cancelled._create(\n                source=\"deadline\",\n                source_task=repr(current_task()),\n            )\n        ),\n    )\n\n    assert re.fullmatch(\n        rf\"cancelled due to nursery with reason 'pigs flying' from task {current_task()!r}\",\n        str(\n            Cancelled._create(\n                source=\"nursery\",\n                source_task=repr(current_task()),\n                reason=\"pigs flying\",\n            )\n        ),\n    )\n\n\ndef test_Cancelled_subclass() -> None:\n    with pytest.raises(TypeError):\n        type(\"Subclass\", (Cancelled,), {})\n\n\n# https://github.com/python-trio/trio/issues/3248\ndef test_Cancelled_pickle() -> None:\n    cancelled = Cancelled._create(source=\"KeyboardInterrupt\")\n    pickled_cancelled = pickle.loads(pickle.dumps(cancelled))\n    assert isinstance(pickled_cancelled, Cancelled)\n    assert cancelled.source == pickled_cancelled.source\n    assert cancelled.source_task == pickled_cancelled.source_task\n    assert cancelled.reason == pickled_cancelled.reason\n\n\nasync def test_cancel_reason() -> None:\n    with trio.CancelScope() as cs:\n        cs.cancel(reason=\"hello\")\n        with pytest.raises(\n            Cancelled,\n            match=rf\"^cancelled due to explicit with reason 'hello' from task {current_task()!r}$\",\n        ) as excinfo:\n            await trio.lowlevel.checkpoint()\n    assert excinfo.value.source == \"explicit\"\n    assert excinfo.value.reason == \"hello\"\n    assert excinfo.value.source_task == repr(current_task())\n\n    with trio.CancelScope(deadline=-inf) as cs:\n        with pytest.raises(Cancelled, match=r\"^cancelled due to deadline\"):\n            await trio.lowlevel.checkpoint()\n\n    with trio.CancelScope() as cs:\n        cs.deadline = -inf\n        with pytest.raises(\n            Cancelled,\n            match=r\"^cancelled due to deadline\",\n        ):\n            await trio.lowlevel.checkpoint()\n\n\nmatch_str = r\"^cancelled due to nursery with reason 'child task raised exception ValueError\\(\\)' from task {0!r}$\"\n\n\nasync def cancelled_task(\n    fail_task: trio.lowlevel.Task, task_status: trio.TaskStatus\n) -> None:\n    task_status.started()\n    with pytest.raises(Cancelled, match=match_str.format(fail_task)):\n        await trio.sleep_forever()\n    raise TypeError\n\n\n# failing_task raises before cancelled_task is started\nasync def test_cancel_reason_nursery() -> None:\n    async def failing_task(task_status: trio.TaskStatus[trio.lowlevel.Task]) -> None:\n        task_status.started(current_task())\n        raise ValueError\n\n    with pytest.RaisesGroup(ValueError, TypeError):\n        async with trio.open_nursery() as nursery:\n            fail_task = await nursery.start(failing_task)\n            with pytest.raises(Cancelled, match=match_str.format(fail_task)):\n                await wait_all_tasks_blocked()\n            await nursery.start(cancelled_task, fail_task)\n\n\n# wait until both tasks are running before failing_task raises\nasync def test_cancel_reason_nursery2() -> None:\n    async def failing_task(task_status: trio.TaskStatus[trio.lowlevel.Task]) -> None:\n        task_status.started(current_task())\n        await wait_all_tasks_blocked()\n        raise ValueError\n\n    with pytest.RaisesGroup(ValueError, TypeError):\n        async with trio.open_nursery() as nursery:\n            fail_task = await nursery.start(failing_task)\n            await nursery.start(cancelled_task, fail_task)\n\n\n# failing_task raises before calling task_status.started()\nasync def test_cancel_reason_nursery3() -> None:\n    async def failing_task(task_status: trio.TaskStatus[None]) -> None:\n        raise ValueError\n\n    parent_task = current_task()\n\n    async def cancelled_task() -> None:\n        # We don't have a way of distinguishing that the nursery code block failed\n        # because it failed to `start()` a task.\n        with pytest.raises(\n            Cancelled,\n            match=re.escape(\n                rf\"cancelled due to nursery with reason 'Code block inside nursery contextmanager raised exception ValueError()' from task {parent_task!r}\"\n            ),\n        ):\n            await trio.sleep_forever()\n\n    with pytest.RaisesGroup(ValueError):\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(cancelled_task)\n            await wait_all_tasks_blocked()\n            await nursery.start(failing_task)\n\n\nasync def test_cancel_reason_not_overwritten() -> None:\n    with trio.CancelScope() as cs:\n        cs.cancel()\n        with pytest.raises(\n            Cancelled,\n            match=rf\"^cancelled due to explicit from task {current_task()!r}$\",\n        ):\n            await trio.lowlevel.checkpoint()\n        cs.deadline = -inf\n        with pytest.raises(\n            Cancelled,\n            match=rf\"^cancelled due to explicit from task {current_task()!r}$\",\n        ):\n            await trio.lowlevel.checkpoint()\n\n\nasync def test_cancel_reason_not_overwritten_2() -> None:\n    with trio.CancelScope() as cs:\n        cs.deadline = -inf\n        with pytest.raises(Cancelled, match=r\"^cancelled due to deadline$\"):\n            await trio.lowlevel.checkpoint()\n        cs.cancel()\n        with pytest.raises(Cancelled, match=r\"^cancelled due to deadline$\"):\n            await trio.lowlevel.checkpoint()\n\n\nasync def test_nested_child_source() -> None:\n    ev = trio.Event()\n    parent_task = current_task()\n\n    async def child() -> None:\n        ev.set()\n        with pytest.raises(\n            Cancelled,\n            match=rf\"^cancelled due to nursery with reason 'Code block inside nursery contextmanager raised exception ValueError\\(\\)' from task {parent_task!r}$\",\n        ):\n            await trio.sleep_forever()\n\n    with pytest.RaisesGroup(ValueError):\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(child)\n            await ev.wait()\n            raise ValueError\n\n\nasync def test_reason_delayed_ki() -> None:\n    # simplified version of test_ki.test_ki_protection_works check #2\n    parent_task = current_task()\n\n    async def sleeper(name: str) -> None:\n        with pytest.raises(\n            Cancelled,\n            match=rf\"^cancelled due to KeyboardInterrupt from task {parent_task!r}$\",\n        ):\n            while True:\n                await trio.lowlevel.checkpoint()\n\n    async def raiser(name: str) -> None:\n        ki_self()\n\n    with pytest.RaisesGroup(KeyboardInterrupt):\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(sleeper, \"s1\")\n            nursery.start_soon(sleeper, \"s2\")\n            nursery.start_soon(trio.lowlevel.enable_ki_protection(raiser), \"r1\")\n            # __aexit__ blocks, and then receives the KI\n"
  },
  {
    "path": "src/trio/_core/_tests/test_exceptiongroup_gc.py",
    "content": "from __future__ import annotations\n\nimport gc\nimport sys\nfrom traceback import extract_tb\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport pytest\n\nfrom .._concat_tb import concat_tb\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from types import TracebackType\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import ExceptionGroup\n\n\ndef raiser1() -> NoReturn:\n    raiser1_2()\n\n\ndef raiser1_2() -> NoReturn:\n    raiser1_3()\n\n\ndef raiser1_3() -> NoReturn:\n    raise ValueError(\"raiser1_string\")\n\n\ndef raiser2() -> NoReturn:\n    raiser2_2()\n\n\ndef raiser2_2() -> NoReturn:\n    raise KeyError(\"raiser2_string\")\n\n\ndef get_exc(raiser: Callable[[], NoReturn]) -> Exception:\n    try:\n        raiser()\n    except Exception as exc:\n        return exc\n    raise AssertionError(\"raiser should always raise\")  # pragma: no cover\n\n\ndef get_tb(raiser: Callable[[], NoReturn]) -> TracebackType | None:\n    return get_exc(raiser).__traceback__\n\n\ndef test_concat_tb() -> None:\n    tb1 = get_tb(raiser1)\n    tb2 = get_tb(raiser2)\n\n    # These return a list of (filename, lineno, fn name, text) tuples\n    # https://docs.python.org/3/library/traceback.html#traceback.extract_tb\n    entries1 = extract_tb(tb1)\n    entries2 = extract_tb(tb2)\n\n    tb12 = concat_tb(tb1, tb2)\n    assert extract_tb(tb12) == entries1 + entries2\n\n    tb21 = concat_tb(tb2, tb1)\n    assert extract_tb(tb21) == entries2 + entries1\n\n    # Check degenerate cases\n    assert extract_tb(concat_tb(None, tb1)) == entries1\n    assert extract_tb(concat_tb(tb1, None)) == entries1\n    assert concat_tb(None, None) is None\n\n    # Make sure the original tracebacks didn't get mutated by mistake\n    assert extract_tb(get_tb(raiser1)) == entries1\n    assert extract_tb(get_tb(raiser2)) == entries2\n\n\n# Unclear if this can still fail, removing the `del` from _concat_tb.copy_tb does not seem\n# to trigger it (on a platform where the `del` is executed)\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Only makes sense with refcounting GC\",\n)\ndef test_ExceptionGroup_catch_doesnt_create_cyclic_garbage() -> None:\n    # https://github.com/python-trio/trio/pull/2063\n    gc.collect()\n    old_flags = gc.get_debug()\n\n    def make_multi() -> NoReturn:\n        raise ExceptionGroup(\"\", [get_exc(raiser1), get_exc(raiser2)])\n\n    try:\n        gc.set_debug(gc.DEBUG_SAVEALL)\n        with pytest.raises(ExceptionGroup) as excinfo:\n            # covers ~~MultiErrorCatcher.__exit__ and~~ _concat_tb.copy_tb\n            # TODO: is the above comment true anymore? as this no longer uses MultiError.catch\n            raise make_multi()\n        for exc in excinfo.value.exceptions:\n            assert isinstance(exc, (ValueError, KeyError))\n        gc.collect()\n        assert not gc.garbage\n    finally:\n        gc.set_debug(old_flags)\n        gc.garbage.clear()\n"
  },
  {
    "path": "src/trio/_core/_tests/test_guest_mode.py",
    "content": "from __future__ import annotations\n\nimport asyncio\nimport contextlib\nimport queue\nimport signal\nimport socket\nimport sys\nimport threading\nimport time\nimport traceback\nimport warnings\nimport weakref\nfrom collections.abc import AsyncGenerator, Awaitable, Callable, Sequence\nfrom functools import partial\nfrom math import inf\nfrom typing import (\n    TYPE_CHECKING,\n    NoReturn,\n    TypeAlias,\n    TypeVar,\n    cast,\n)\n\nimport pytest\nimport sniffio\nfrom outcome import Outcome\n\nimport trio\nimport trio.testing\nfrom trio.abc import Clock, Instrument\n\nfrom .tutil import gc_collect_harder, restore_unraisablehook\n\nif TYPE_CHECKING:\n    from trio._channel import MemorySendChannel\n\nT = TypeVar(\"T\")\nInHost: TypeAlias = Callable[[Callable[[], object]], None]\n\n\n# The simplest possible \"host\" loop.\n# Nice features:\n# - we can run code \"outside\" of trio using the schedule function passed to\n#   our main\n# - final result is returned\n# - any unhandled exceptions cause an immediate crash\ndef trivial_guest_run(\n    trio_fn: Callable[[InHost], Awaitable[T]],\n    *,\n    in_host_after_start: Callable[[], None] | None = None,\n    host_uses_signal_set_wakeup_fd: bool = False,\n    clock: Clock | None = None,\n    instruments: Sequence[Instrument] = (),\n    restrict_keyboard_interrupt_to_checkpoints: bool = False,\n    strict_exception_groups: bool = True,\n) -> T:\n    todo: queue.Queue[tuple[str, Outcome[T] | Callable[[], object]]] = queue.Queue()\n\n    host_thread = threading.current_thread()\n\n    def run_sync_soon_threadsafe(fn: Callable[[], object]) -> None:\n        nonlocal todo\n        if host_thread is threading.current_thread():  # pragma: no cover\n            crash = partial(\n                pytest.fail,\n                \"run_sync_soon_threadsafe called from host thread\",\n            )\n            todo.put((\"run\", crash))\n        todo.put((\"run\", fn))\n\n    def run_sync_soon_not_threadsafe(fn: Callable[[], object]) -> None:\n        nonlocal todo\n        if host_thread is not threading.current_thread():  # pragma: no cover\n            crash = partial(\n                pytest.fail,\n                \"run_sync_soon_not_threadsafe called from worker thread\",\n            )\n            todo.put((\"run\", crash))\n        todo.put((\"run\", fn))\n\n    def done_callback(outcome: Outcome[T]) -> None:\n        nonlocal todo\n        todo.put((\"unwrap\", outcome))\n\n    trio.lowlevel.start_guest_run(\n        trio_fn,\n        run_sync_soon_not_threadsafe,\n        run_sync_soon_threadsafe=run_sync_soon_threadsafe,\n        run_sync_soon_not_threadsafe=run_sync_soon_not_threadsafe,\n        done_callback=done_callback,\n        host_uses_signal_set_wakeup_fd=host_uses_signal_set_wakeup_fd,\n        clock=clock,\n        instruments=instruments,\n        restrict_keyboard_interrupt_to_checkpoints=restrict_keyboard_interrupt_to_checkpoints,\n        strict_exception_groups=strict_exception_groups,\n    )\n    if in_host_after_start is not None:\n        in_host_after_start()\n\n    try:\n        while True:\n            op, obj = todo.get()\n            if op == \"run\":\n                assert not isinstance(obj, Outcome)\n                obj()\n            elif op == \"unwrap\":\n                assert isinstance(obj, Outcome)\n                return obj.unwrap()\n            else:  # pragma: no cover\n                raise NotImplementedError(f\"{op!r} not handled\")\n    finally:\n        # Make sure that exceptions raised here don't capture these, so that\n        # if an exception does cause us to abandon a run then the Trio state\n        # has a chance to be GC'ed and warn about it.\n        del todo, run_sync_soon_threadsafe, done_callback\n\n\ndef test_guest_trivial() -> None:\n    async def trio_return(in_host: InHost) -> str:\n        await trio.lowlevel.checkpoint()\n        return \"ok\"\n\n    assert trivial_guest_run(trio_return) == \"ok\"\n\n    async def trio_fail(in_host: InHost) -> NoReturn:\n        raise KeyError(\"whoopsiedaisy\")\n\n    with pytest.raises(KeyError, match=\"whoopsiedaisy\"):\n        trivial_guest_run(trio_fail)\n\n\ndef test_guest_can_do_io() -> None:\n    async def trio_main(in_host: InHost) -> None:\n        record = []\n        a, b = trio.socket.socketpair()\n        with a, b:\n            async with trio.open_nursery() as nursery:\n\n                async def do_receive() -> None:\n                    record.append(await a.recv(1))\n\n                nursery.start_soon(do_receive)\n                await trio.testing.wait_all_tasks_blocked()\n\n                await b.send(b\"x\")\n\n        assert record == [b\"x\"]\n\n    trivial_guest_run(trio_main)\n\n\ndef test_guest_is_initialized_when_start_returns() -> None:\n    trio_token = None\n    record = []\n\n    async def trio_main(in_host: InHost) -> str:\n        record.append(\"main task ran\")\n        await trio.lowlevel.checkpoint()\n        assert trio.lowlevel.current_trio_token() is trio_token\n        return \"ok\"\n\n    def after_start() -> None:\n        # We should get control back before the main task executes any code\n        assert record == []\n\n        nonlocal trio_token\n        trio_token = trio.lowlevel.current_trio_token()\n        trio_token.run_sync_soon(record.append, \"run_sync_soon cb ran\")\n\n        @trio.lowlevel.spawn_system_task\n        async def early_task() -> None:\n            record.append(\"system task ran\")\n            await trio.lowlevel.checkpoint()\n\n    res = trivial_guest_run(trio_main, in_host_after_start=after_start)\n    assert res == \"ok\"\n    assert set(record) == {\"system task ran\", \"main task ran\", \"run_sync_soon cb ran\"}\n\n    class BadClock(Clock):\n        def start_clock(self) -> NoReturn:\n            raise ValueError(\"whoops\")\n\n        def current_time(self) -> float:\n            raise NotImplementedError()\n\n        def deadline_to_sleep_time(self, deadline: float) -> float:\n            raise NotImplementedError()\n\n    def after_start_never_runs() -> None:  # pragma: no cover\n        pytest.fail(\"shouldn't get here\")\n\n    # Errors during initialization (which can only be TrioInternalErrors)\n    # are raised out of start_guest_run, not out of the done_callback\n    with pytest.raises(trio.TrioInternalError):\n        trivial_guest_run(\n            trio_main,\n            clock=BadClock(),\n            in_host_after_start=after_start_never_runs,\n        )\n\n\ndef test_host_can_directly_wake_trio_task() -> None:\n    async def trio_main(in_host: InHost) -> str:\n        ev = trio.Event()\n        in_host(ev.set)\n        await ev.wait()\n        return \"ok\"\n\n    assert trivial_guest_run(trio_main) == \"ok\"\n\n\ndef test_host_altering_deadlines_wakes_trio_up() -> None:\n    def set_deadline(cscope: trio.CancelScope, new_deadline: float) -> None:\n        cscope.deadline = new_deadline\n\n    async def trio_main(in_host: InHost) -> str:\n        with trio.CancelScope() as cscope:\n            in_host(lambda: set_deadline(cscope, -inf))\n            await trio.sleep_forever()\n        assert cscope.cancelled_caught\n\n        with trio.CancelScope() as cscope:\n            # also do a change that doesn't affect the next deadline, just to\n            # exercise that path\n            in_host(lambda: set_deadline(cscope, 1e6))\n            in_host(lambda: set_deadline(cscope, -inf))\n            await trio.sleep(999)\n        assert cscope.cancelled_caught\n\n        return \"ok\"\n\n    assert trivial_guest_run(trio_main) == \"ok\"\n\n\ndef test_guest_mode_sniffio_integration() -> None:\n    current_async_library = sniffio.current_async_library\n    sniffio_library = sniffio.thread_local\n\n    async def trio_main(in_host: InHost) -> str:\n        async def synchronize() -> None:\n            \"\"\"Wait for all in_host() calls issued so far to complete.\"\"\"\n            evt = trio.Event()\n            in_host(evt.set)\n            await evt.wait()\n\n        # Host and guest have separate sniffio_library contexts\n        in_host(partial(setattr, sniffio_library, \"name\", \"nullio\"))\n        await synchronize()\n        assert current_async_library() == \"trio\"\n\n        record = []\n        in_host(lambda: record.append(current_async_library()))\n        await synchronize()\n        assert record == [\"nullio\"]\n        assert current_async_library() == \"trio\"\n\n        return \"ok\"\n\n    try:\n        assert trivial_guest_run(trio_main) == \"ok\"\n    finally:\n        sniffio_library.name = None\n\n\ndef test_guest_mode_trio_context_detection() -> None:\n    def check(thing: bool) -> None:\n        assert thing\n\n    assert not trio.lowlevel.in_trio_run()\n    assert not trio.lowlevel.in_trio_task()\n\n    async def trio_main(in_host: InHost) -> None:\n        for _ in range(2):\n            assert trio.lowlevel.in_trio_run()\n            assert trio.lowlevel.in_trio_task()\n\n            in_host(lambda: check(trio.lowlevel.in_trio_run()))\n            in_host(lambda: check(not trio.lowlevel.in_trio_task()))\n\n    trivial_guest_run(trio_main)\n    assert not trio.lowlevel.in_trio_run()\n    assert not trio.lowlevel.in_trio_task()\n\n\ndef test_warn_set_wakeup_fd_overwrite() -> None:\n    assert signal.set_wakeup_fd(-1) == -1\n\n    async def trio_main(in_host: InHost) -> str:\n        return \"ok\"\n\n    a, b = socket.socketpair()\n    with a, b:\n        a.setblocking(False)\n\n        # Warn if there's already a wakeup fd\n        signal.set_wakeup_fd(a.fileno())\n        try:\n            with pytest.warns(RuntimeWarning, match=\"signal handling code.*collided\"):\n                assert trivial_guest_run(trio_main) == \"ok\"\n        finally:\n            assert signal.set_wakeup_fd(-1) == a.fileno()\n\n        signal.set_wakeup_fd(a.fileno())\n        try:\n            with pytest.warns(RuntimeWarning, match=\"signal handling code.*collided\"):\n                assert (\n                    trivial_guest_run(trio_main, host_uses_signal_set_wakeup_fd=False)\n                    == \"ok\"\n                )\n        finally:\n            assert signal.set_wakeup_fd(-1) == a.fileno()\n\n        # Don't warn if there isn't already a wakeup fd\n        with warnings.catch_warnings():\n            warnings.simplefilter(\"error\")\n            assert trivial_guest_run(trio_main) == \"ok\"\n\n        with warnings.catch_warnings():\n            warnings.simplefilter(\"error\")\n            assert (\n                trivial_guest_run(trio_main, host_uses_signal_set_wakeup_fd=True)\n                == \"ok\"\n            )\n\n        # If there's already a wakeup fd, but we've been told to trust it,\n        # then it's left alone and there's no warning\n        signal.set_wakeup_fd(a.fileno())\n        try:\n\n            async def trio_check_wakeup_fd_unaltered(in_host: InHost) -> str:\n                fd = signal.set_wakeup_fd(-1)\n                assert fd == a.fileno()\n                signal.set_wakeup_fd(fd)\n                return \"ok\"\n\n            with warnings.catch_warnings():\n                warnings.simplefilter(\"error\")\n                assert (\n                    trivial_guest_run(\n                        trio_check_wakeup_fd_unaltered,\n                        host_uses_signal_set_wakeup_fd=True,\n                    )\n                    == \"ok\"\n                )\n        finally:\n            assert signal.set_wakeup_fd(-1) == a.fileno()\n\n\ndef test_host_wakeup_doesnt_trigger_wait_all_tasks_blocked() -> None:\n    # This is designed to hit the branch in unrolled_run where:\n    #   idle_primed=True\n    #   runner.runq is empty\n    #   events is Truth-y\n    # ...and confirm that in this case, wait_all_tasks_blocked does not get\n    # triggered.\n    def set_deadline(cscope: trio.CancelScope, new_deadline: float) -> None:\n        print(f\"setting deadline {new_deadline}\")\n        cscope.deadline = new_deadline\n\n    async def trio_main(in_host: InHost) -> str:\n        async def sit_in_wait_all_tasks_blocked(watb_cscope: trio.CancelScope) -> None:\n            with watb_cscope:\n                # Overall point of this test is that this\n                # wait_all_tasks_blocked should *not* return normally, but\n                # only by cancellation.\n                await trio.testing.wait_all_tasks_blocked(cushion=9999)\n                raise AssertionError(  # pragma: no cover\n                    \"wait_all_tasks_blocked should *not* return normally, \"\n                    \"only by cancellation.\",\n                )\n            assert watb_cscope.cancelled_caught\n\n        async def get_woken_by_host_deadline(watb_cscope: trio.CancelScope) -> None:\n            with trio.CancelScope() as cscope:\n                print(\"scheduling stuff to happen\")\n\n                # Altering the deadline from the host, to something in the\n                # future, will cause the run loop to wake up, but then\n                # discover that there is nothing to do and go back to sleep.\n                # This should *not* trigger wait_all_tasks_blocked.\n                #\n                # So the 'before_io_wait' here will wait until we're blocking\n                # with the wait_all_tasks_blocked primed, and then schedule a\n                # deadline change. The critical test is that this should *not*\n                # wake up 'sit_in_wait_all_tasks_blocked'.\n                #\n                # The after we've had a chance to wake up\n                # 'sit_in_wait_all_tasks_blocked', we want the test to\n                # actually end. So in after_io_wait we schedule a second host\n                # call to tear things down.\n                class InstrumentHelper(Instrument):\n                    def __init__(self) -> None:\n                        self.primed = False\n\n                    def before_io_wait(self, timeout: float) -> None:\n                        print(f\"before_io_wait({timeout})\")\n                        if timeout == 9999:  # pragma: no branch\n                            assert not self.primed\n                            in_host(lambda: set_deadline(cscope, 1e9))\n                            self.primed = True\n\n                    def after_io_wait(self, timeout: float) -> None:\n                        if self.primed:  # pragma: no branch\n                            print(\"instrument triggered\")\n                            in_host(lambda: cscope.cancel())\n                            trio.lowlevel.remove_instrument(self)\n\n                trio.lowlevel.add_instrument(InstrumentHelper())\n                await trio.sleep_forever()\n            assert cscope.cancelled_caught\n            watb_cscope.cancel()\n\n        async with trio.open_nursery() as nursery:\n            watb_cscope = trio.CancelScope()\n            nursery.start_soon(sit_in_wait_all_tasks_blocked, watb_cscope)\n            await trio.testing.wait_all_tasks_blocked()\n            nursery.start_soon(get_woken_by_host_deadline, watb_cscope)\n\n        return \"ok\"\n\n    assert trivial_guest_run(trio_main) == \"ok\"\n\n\n@restore_unraisablehook()\ndef test_guest_warns_if_abandoned() -> None:\n    # This warning is emitted from the garbage collector. So we have to make\n    # sure that our abandoned run is garbage. The easiest way to do this is to\n    # put it into a function, so that we're sure all the local state,\n    # traceback frames, etc. are garbage once it returns.\n    def do_abandoned_guest_run() -> None:\n        async def abandoned_main(in_host: InHost) -> None:\n            in_host(lambda: 1 / 0)\n            while True:\n                await trio.lowlevel.checkpoint()\n\n        with pytest.raises(ZeroDivisionError):\n            trivial_guest_run(abandoned_main)\n\n    with pytest.warns(  # noqa: PT031\n        RuntimeWarning,\n        match=\"Trio guest run got abandoned\",\n    ):\n        do_abandoned_guest_run()\n        gc_collect_harder()\n\n    # If you have problems some day figuring out what's holding onto a\n    # reference to the unrolled_run generator and making this test fail,\n    # then this might be useful to help track it down. (It assumes you\n    # also hack start_guest_run so that it does 'global W; W =\n    # weakref(unrolled_run_gen)'.)\n    #\n    # import gc\n    # print(trio._core._run.W)\n    # targets = [trio._core._run.W()]\n    # for i in range(15):\n    #     new_targets = []\n    #     for target in targets:\n    #         new_targets += gc.get_referrers(target)\n    #         new_targets.remove(targets)\n    #     print(\"#####################\")\n    #     print(f\"depth {i}: {len(new_targets)}\")\n    #     print(new_targets)\n    #     targets = new_targets\n\n    with pytest.raises(RuntimeError):\n        trio.current_time()\n\n\ndef aiotrio_run(\n    trio_fn: Callable[[], Awaitable[T]],\n    *,\n    pass_not_threadsafe: bool = True,\n    run_sync_soon_not_threadsafe: InHost | None = None,\n    host_uses_signal_set_wakeup_fd: bool = False,\n    clock: Clock | None = None,\n    instruments: Sequence[Instrument] = (),\n    restrict_keyboard_interrupt_to_checkpoints: bool = False,\n    strict_exception_groups: bool = True,\n) -> T:\n    loop = asyncio.new_event_loop()\n\n    async def aio_main() -> T:\n        nonlocal run_sync_soon_not_threadsafe\n        trio_done_fut: asyncio.Future[Outcome[T]] = loop.create_future()\n\n        def trio_done_callback(main_outcome: Outcome[T]) -> None:\n            print(f\"trio_fn finished: {main_outcome!r}\")\n            trio_done_fut.set_result(main_outcome)\n\n        if pass_not_threadsafe:\n            run_sync_soon_not_threadsafe = cast(\"InHost\", loop.call_soon)\n\n        trio.lowlevel.start_guest_run(\n            trio_fn,\n            run_sync_soon_threadsafe=loop.call_soon_threadsafe,\n            done_callback=trio_done_callback,\n            run_sync_soon_not_threadsafe=run_sync_soon_not_threadsafe,\n            host_uses_signal_set_wakeup_fd=host_uses_signal_set_wakeup_fd,\n            clock=clock,\n            instruments=instruments,\n            restrict_keyboard_interrupt_to_checkpoints=restrict_keyboard_interrupt_to_checkpoints,\n            strict_exception_groups=strict_exception_groups,\n        )\n\n        return (await trio_done_fut).unwrap()\n\n    try:\n        # can't use asyncio.run because that fails on Windows (3.10, x64, with\n        # Komodia LSP)\n        return loop.run_until_complete(aio_main())\n    finally:\n        loop.close()\n\n\ndef test_guest_mode_on_asyncio() -> None:\n    async def trio_main() -> str:\n        print(\"trio_main!\")\n\n        to_trio, from_aio = trio.open_memory_channel[int](float(\"inf\"))\n        from_trio: asyncio.Queue[int] = asyncio.Queue()\n\n        aio_task = asyncio.ensure_future(aio_pingpong(from_trio, to_trio))\n\n        # Make sure we have at least one tick where we don't need to go into\n        # the thread\n        await trio.lowlevel.checkpoint()\n\n        from_trio.put_nowait(0)\n\n        async for n in from_aio:\n            print(f\"trio got: {n}\")\n            from_trio.put_nowait(n + 1)\n            if n >= 10:\n                aio_task.cancel()\n                return \"trio-main-done\"\n\n        raise AssertionError(\"should never be reached\")  # pragma: no cover\n\n    async def aio_pingpong(\n        from_trio: asyncio.Queue[int],\n        to_trio: MemorySendChannel[int],\n    ) -> None:\n        print(\"aio_pingpong!\")\n\n        try:\n            while True:\n                n = await from_trio.get()\n                print(f\"aio got: {n}\")\n                to_trio.send_nowait(n + 1)\n        except asyncio.CancelledError:\n            raise\n        except:  # pragma: no cover\n            traceback.print_exc()\n            raise\n\n    assert (\n        aiotrio_run(\n            trio_main,\n            # Not all versions of asyncio we test on can actually be trusted,\n            # but this test doesn't care about signal handling, and it's\n            # easier to just avoid the warnings.\n            host_uses_signal_set_wakeup_fd=True,\n        )\n        == \"trio-main-done\"\n    )\n\n    assert (\n        aiotrio_run(\n            trio_main,\n            # Also check that passing only call_soon_threadsafe works, via the\n            # fallback path where we use it for everything.\n            pass_not_threadsafe=False,\n            host_uses_signal_set_wakeup_fd=True,\n        )\n        == \"trio-main-done\"\n    )\n\n\ndef test_guest_mode_internal_errors(\n    monkeypatch: pytest.MonkeyPatch,\n    recwarn: pytest.WarningsRecorder,\n) -> None:\n    with monkeypatch.context() as m:\n\n        async def crash_in_run_loop(in_host: InHost) -> None:\n            m.setattr(\"trio._core._run.GLOBAL_RUN_CONTEXT.runner.runq\", \"HI\")\n            await trio.sleep(1)\n\n        with pytest.raises(trio.TrioInternalError):\n            trivial_guest_run(crash_in_run_loop)\n\n    with monkeypatch.context() as m:\n\n        async def crash_in_io(in_host: InHost) -> None:\n            m.setattr(\"trio._core._run.TheIOManager.get_events\", None)\n            await trio.lowlevel.checkpoint()\n\n        with pytest.raises(trio.TrioInternalError):\n            trivial_guest_run(crash_in_io)\n\n    with monkeypatch.context() as m:\n\n        async def crash_in_worker_thread_io(in_host: InHost) -> None:\n            t = threading.current_thread()\n            old_get_events = trio._core._run.TheIOManager.get_events\n\n            def bad_get_events(\n                self: trio._core._run.TheIOManager,\n                timeout: float,\n            ) -> trio._core._run.EventResult:\n                if threading.current_thread() is not t:\n                    raise ValueError(\"oh no!\")\n                else:\n                    return old_get_events(self, timeout)\n\n            m.setattr(\"trio._core._run.TheIOManager.get_events\", bad_get_events)\n\n            await trio.sleep(1)\n\n        with pytest.raises(trio.TrioInternalError):\n            trivial_guest_run(crash_in_worker_thread_io)\n\n    gc_collect_harder()\n\n\ndef test_guest_mode_ki() -> None:\n    assert signal.getsignal(signal.SIGINT) is signal.default_int_handler\n\n    # Check SIGINT in Trio func and in host func\n    async def trio_main(in_host: InHost) -> None:\n        with pytest.raises(KeyboardInterrupt):\n            signal.raise_signal(signal.SIGINT)\n\n        # Host SIGINT should get injected into Trio\n        in_host(partial(signal.raise_signal, signal.SIGINT))\n        await trio.sleep(10)\n\n    with pytest.raises(KeyboardInterrupt) as excinfo:\n        trivial_guest_run(trio_main)\n    assert excinfo.value.__context__ is None\n    # Signal handler should be restored properly on exit\n    assert signal.getsignal(signal.SIGINT) is signal.default_int_handler\n\n    # Also check chaining in the case where KI is injected after main exits\n    final_exc = KeyError(\"whoa\")\n\n    async def trio_main_raising(in_host: InHost) -> NoReturn:\n        in_host(partial(signal.raise_signal, signal.SIGINT))\n        raise final_exc\n\n    with pytest.raises(KeyboardInterrupt) as excinfo:\n        trivial_guest_run(trio_main_raising)\n    assert excinfo.value.__context__ is final_exc\n\n    assert signal.getsignal(signal.SIGINT) is signal.default_int_handler\n\n\ndef test_guest_mode_autojump_clock_threshold_changing() -> None:\n    # This is super obscure and probably no-one will ever notice, but\n    # technically mutating the MockClock.autojump_threshold from the host\n    # should wake up the guest, so let's test it.\n\n    clock = trio.testing.MockClock()\n\n    DURATION = 120\n\n    async def trio_main(in_host: InHost) -> None:\n        assert trio.current_time() == 0\n        in_host(lambda: setattr(clock, \"autojump_threshold\", 0))\n        await trio.sleep(DURATION)\n        assert trio.current_time() == DURATION\n\n    start = time.monotonic()\n    trivial_guest_run(trio_main, clock=clock)\n    end = time.monotonic()\n    # Should be basically instantaneous, but we'll leave a generous buffer to\n    # account for any CI weirdness\n    assert end - start < DURATION / 2\n\n\n@restore_unraisablehook()\ndef test_guest_mode_asyncgens() -> None:\n    record = set()\n\n    async def agen(label: str) -> AsyncGenerator[int, None]:\n        assert sniffio.current_async_library() == label\n        try:\n            yield 1\n        finally:\n            library = sniffio.current_async_library()\n            with contextlib.suppress(trio.Cancelled):\n                await sys.modules[library].sleep(0)\n            record.add((label, library))\n\n    async def iterate_in_aio() -> None:\n        await agen(\"asyncio\").asend(None)\n\n    async def trio_main() -> None:\n        task = asyncio.ensure_future(iterate_in_aio())\n        done_evt = trio.Event()\n        task.add_done_callback(lambda _: done_evt.set())\n        with trio.fail_after(1):\n            await done_evt.wait()\n\n        await agen(\"trio\").asend(None)\n\n        gc_collect_harder()\n\n    aiotrio_run(trio_main, host_uses_signal_set_wakeup_fd=True)\n\n    assert record == {(\"asyncio\", \"asyncio\"), (\"trio\", \"trio\")}\n\n\n@restore_unraisablehook()\ndef test_guest_mode_asyncgens_garbage_collection() -> None:\n    record: set[tuple[str, str, bool]] = set()\n\n    async def agen(label: str) -> AsyncGenerator[int, None]:\n        class A:\n            pass\n\n        a = A()\n        a_wr = weakref.ref(a)\n        assert sniffio.current_async_library() == label\n        try:\n            yield 1\n        finally:\n            library = sniffio.current_async_library()\n            with contextlib.suppress(trio.Cancelled):\n                await sys.modules[library].sleep(0)\n\n            del a\n            if sys.implementation.name == \"pypy\":\n                gc_collect_harder()\n\n            record.add((label, library, a_wr() is None))\n\n    async def iterate_in_aio() -> None:\n        await agen(\"asyncio\").asend(None)\n\n    async def trio_main() -> None:\n        task = asyncio.ensure_future(iterate_in_aio())\n        done_evt = trio.Event()\n        task.add_done_callback(lambda _: done_evt.set())\n        with trio.fail_after(1):\n            await done_evt.wait()\n\n        await agen(\"trio\").asend(None)\n\n        gc_collect_harder()\n\n    aiotrio_run(trio_main, host_uses_signal_set_wakeup_fd=True)\n\n    assert record == {(\"asyncio\", \"asyncio\", True), (\"trio\", \"trio\", True)}\n"
  },
  {
    "path": "src/trio/_core/_tests/test_instrumentation.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport attrs\nimport pytest\n\nfrom ... import _abc, _core\nfrom .tutil import check_sequence_matches\n\nif TYPE_CHECKING:\n    from collections.abc import Container, Iterable\n\n    from ...lowlevel import Task\n\n\n@attrs.define(eq=False, slots=False)\nclass TaskRecorder(_abc.Instrument):\n    record: list[tuple[str, Task | None]] = attrs.Factory(list)\n\n    def before_run(self) -> None:\n        self.record.append((\"before_run\", None))\n\n    def task_scheduled(self, task: Task) -> None:\n        self.record.append((\"schedule\", task))\n\n    def before_task_step(self, task: Task) -> None:\n        assert task is _core.current_task()\n        self.record.append((\"before\", task))\n\n    def after_task_step(self, task: Task) -> None:\n        assert task is _core.current_task()\n        self.record.append((\"after\", task))\n\n    def after_run(self) -> None:\n        self.record.append((\"after_run\", None))\n\n    def filter_tasks(self, tasks: Container[Task]) -> Iterable[tuple[str, Task | None]]:\n        for item in self.record:\n            if item[0] in (\"schedule\", \"before\", \"after\") and item[1] in tasks:\n                yield item\n            if item[0] in (\"before_run\", \"after_run\"):\n                yield item\n\n\ndef test_instruments(recwarn: object) -> None:\n    r1 = TaskRecorder()\n    r2 = TaskRecorder()\n    r3 = TaskRecorder()\n\n    task = None\n\n    # We use a child task for this, because the main task does some extra\n    # bookkeeping stuff that can leak into the instrument results, and we\n    # don't want to deal with it.\n    async def task_fn() -> None:\n        nonlocal task\n        task = _core.current_task()\n\n        for _ in range(4):\n            await _core.checkpoint()\n        # replace r2 with r3, to test that we can manipulate them as we go\n        _core.remove_instrument(r2)\n        with pytest.raises(KeyError):\n            _core.remove_instrument(r2)\n        # add is idempotent\n        _core.add_instrument(r3)\n        _core.add_instrument(r3)\n        for _ in range(1):\n            await _core.checkpoint()\n\n    async def main() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(task_fn)\n\n    _core.run(main, instruments=[r1, r2])\n\n    # It sleeps 5 times, so it runs 6 times.  Note that checkpoint()\n    # reschedules the task immediately upon yielding, before the\n    # after_task_step event fires.\n    expected = (\n        [(\"before_run\", None), (\"schedule\", task)]\n        + [(\"before\", task), (\"schedule\", task), (\"after\", task)] * 5\n        + [(\"before\", task), (\"after\", task), (\"after_run\", None)]\n    )\n    assert r1.record == r2.record + r3.record\n    assert task is not None\n    assert list(r1.filter_tasks([task])) == expected\n\n\ndef test_instruments_interleave() -> None:\n    tasks = {}\n\n    async def two_step1() -> None:\n        tasks[\"t1\"] = _core.current_task()\n        await _core.checkpoint()\n\n    async def two_step2() -> None:\n        tasks[\"t2\"] = _core.current_task()\n        await _core.checkpoint()\n\n    async def main() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(two_step1)\n            nursery.start_soon(two_step2)\n\n    r = TaskRecorder()\n    _core.run(main, instruments=[r])\n\n    expected = [\n        (\"before_run\", None),\n        (\"schedule\", tasks[\"t1\"]),\n        (\"schedule\", tasks[\"t2\"]),\n        {\n            (\"before\", tasks[\"t1\"]),\n            (\"schedule\", tasks[\"t1\"]),\n            (\"after\", tasks[\"t1\"]),\n            (\"before\", tasks[\"t2\"]),\n            (\"schedule\", tasks[\"t2\"]),\n            (\"after\", tasks[\"t2\"]),\n        },\n        {\n            (\"before\", tasks[\"t1\"]),\n            (\"after\", tasks[\"t1\"]),\n            (\"before\", tasks[\"t2\"]),\n            (\"after\", tasks[\"t2\"]),\n        },\n        (\"after_run\", None),\n    ]\n    print(list(r.filter_tasks(tasks.values())))\n    check_sequence_matches(list(r.filter_tasks(tasks.values())), expected)\n\n\ndef test_null_instrument() -> None:\n    # undefined instrument methods are skipped\n    class NullInstrument(_abc.Instrument):\n        def something_unrelated(self) -> None:\n            pass  # pragma: no cover\n\n    async def main() -> None:\n        await _core.checkpoint()\n\n    _core.run(main, instruments=[NullInstrument()])\n\n\ndef test_instrument_before_after_run() -> None:\n    record = []\n\n    class BeforeAfterRun(_abc.Instrument):\n        def before_run(self) -> None:\n            record.append(\"before_run\")\n\n        def after_run(self) -> None:\n            record.append(\"after_run\")\n\n    async def main() -> None:\n        pass\n\n    _core.run(main, instruments=[BeforeAfterRun()])\n    assert record == [\"before_run\", \"after_run\"]\n\n\ndef test_instrument_task_spawn_exit() -> None:\n    record = []\n\n    class SpawnExitRecorder(_abc.Instrument):\n        def task_spawned(self, task: Task) -> None:\n            record.append((\"spawned\", task))\n\n        def task_exited(self, task: Task) -> None:\n            record.append((\"exited\", task))\n\n    async def main() -> Task:\n        return _core.current_task()\n\n    main_task = _core.run(main, instruments=[SpawnExitRecorder()])\n    assert (\"spawned\", main_task) in record\n    assert (\"exited\", main_task) in record\n\n\n# This test also tests having a crash before the initial task is even spawned,\n# which is very difficult to handle.\ndef test_instruments_crash(caplog: pytest.LogCaptureFixture) -> None:\n    record = []\n\n    class BrokenInstrument(_abc.Instrument):\n        def task_scheduled(self, task: Task) -> NoReturn:\n            record.append(\"scheduled\")\n            raise ValueError(\"oops\")\n\n        def close(self) -> None:\n            # Shouldn't be called -- tests that the instrument disabling logic\n            # works right.\n            record.append(\"closed\")  # pragma: no cover\n\n    async def main() -> Task:\n        record.append(\"main ran\")\n        return _core.current_task()\n\n    r = TaskRecorder()\n    main_task = _core.run(main, instruments=[r, BrokenInstrument()])\n    assert record == [\"scheduled\", \"main ran\"]\n    # the TaskRecorder kept going throughout, even though the BrokenInstrument\n    # was disabled\n    assert (\"after\", main_task) in r.record\n    assert (\"after_run\", None) in r.record\n    # And we got a log message\n    assert caplog.records[0].exc_info is not None\n    exc_type, exc_value, _exc_traceback = caplog.records[0].exc_info\n    assert exc_type is ValueError\n    assert str(exc_value) == \"oops\"\n    assert \"Instrument has been disabled\" in caplog.records[0].message\n\n\ndef test_instruments_monkeypatch() -> None:\n    class NullInstrument(_abc.Instrument):\n        pass\n\n    instrument = NullInstrument()\n\n    async def main() -> None:\n        record: list[Task] = []\n\n        # Changing the set of hooks implemented by an instrument after\n        # it's installed doesn't make them start being called right away\n        instrument.before_task_step = (  # type: ignore[method-assign]\n            record.append  # type: ignore[assignment] # append is pos-only\n        )\n\n        await _core.checkpoint()\n        await _core.checkpoint()\n        assert len(record) == 0\n\n        # But if we remove and re-add the instrument, the new hooks are\n        # picked up\n        _core.remove_instrument(instrument)\n        _core.add_instrument(instrument)\n        await _core.checkpoint()\n        await _core.checkpoint()\n        assert record.count(_core.current_task()) == 2\n\n        _core.remove_instrument(instrument)\n        await _core.checkpoint()\n        await _core.checkpoint()\n        assert record.count(_core.current_task()) == 2\n\n    _core.run(main, instruments=[instrument])\n\n\ndef test_instrument_that_raises_on_getattr() -> None:\n    class EvilInstrument(_abc.Instrument):\n        def task_exited(self, task: Task) -> NoReturn:\n            raise AssertionError(\"this should never happen\")  # pragma: no cover\n\n        @property\n        def after_run(self) -> NoReturn:\n            raise ValueError(\"oops\")\n\n    async def main() -> None:\n        with pytest.raises(ValueError, match=r\"^oops$\"):\n            _core.add_instrument(EvilInstrument())\n\n        # Make sure the instrument is fully removed from the per-method lists\n        runner = _core.current_task()._runner\n        assert \"after_run\" not in runner.instruments\n        assert \"task_exited\" not in runner.instruments\n\n    _core.run(main)\n\n\ndef test_instrument_call_trio_context() -> None:\n    called = set()\n\n    class Instrument(_abc.Instrument):\n        pass\n\n    hooks = {\n        # not run in task context\n        \"after_io_wait\": (True, False),\n        \"before_io_wait\": (True, False),\n        \"before_run\": (True, False),\n        \"after_run\": (True, False),\n        # run in task context\n        \"before_task_step\": (True, True),\n        \"after_task_step\": (True, True),\n        \"task_exited\": (True, True),\n        # depends\n        \"task_scheduled\": (True, None),\n        \"task_spawned\": (True, None),\n    }\n    for hook, val in hooks.items():\n\n        def h(\n            self: Instrument,\n            *args: object,\n            hook: str = hook,\n            val: tuple[bool, bool | None] = val,\n        ) -> None:\n            fail_str = f\"failed in {hook}\"\n\n            assert _core.in_trio_run() == val[0], fail_str\n            if val[1] is not None:\n                assert _core.in_trio_task() == val[1], fail_str\n            called.add(hook)\n\n        setattr(Instrument, hook, h)\n\n    async def main() -> None:\n        await _core.checkpoint()\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(_core.checkpoint)\n\n    _core.run(main, instruments=[Instrument()])\n    assert called == set(hooks)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_io.py",
    "content": "from __future__ import annotations\n\nimport random\nimport select\nimport socket as stdlib_socket\nimport sys\nfrom collections.abc import Awaitable, Callable\nfrom contextlib import suppress\nfrom typing import TYPE_CHECKING, TypeVar\n\nimport pytest\n\nimport trio\n\nfrom ... import _core\nfrom ...testing import assert_checkpoints, wait_all_tasks_blocked\n\n# Cross-platform tests for IO handling\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n    from typing_extensions import ParamSpec\n\n    ArgsT = ParamSpec(\"ArgsT\")\n\n\ndef fill_socket(sock: stdlib_socket.socket) -> None:\n    try:\n        while True:\n            sock.send(b\"x\" * 65536)\n    except BlockingIOError:\n        pass\n\n\ndef drain_socket(sock: stdlib_socket.socket) -> None:\n    try:\n        while True:\n            sock.recv(65536)\n    except BlockingIOError:\n        pass\n\n\nWaitSocket = Callable[[stdlib_socket.socket], Awaitable[object]]\nSocketPair = tuple[stdlib_socket.socket, stdlib_socket.socket]\nRetT = TypeVar(\"RetT\")\n\n\n@pytest.fixture\ndef socketpair() -> Generator[SocketPair, None, None]:\n    pair = stdlib_socket.socketpair()\n    for sock in pair:\n        sock.setblocking(False)\n    yield pair\n    for sock in pair:\n        sock.close()\n\n\ndef also_using_fileno(\n    fn: Callable[[stdlib_socket.socket | int], RetT],\n) -> list[Callable[[stdlib_socket.socket], RetT]]:\n    def fileno_wrapper(fileobj: stdlib_socket.socket) -> RetT:\n        return fn(fileobj.fileno())\n\n    name = f\"<{fn.__name__} on fileno>\"\n    fileno_wrapper.__name__ = fileno_wrapper.__qualname__ = name\n    return [fn, fileno_wrapper]\n\n\n# Decorators that feed in different settings for wait_readable / wait_writable\n# / notify_closing.\n# Note that if you use all three decorators on the same test, it will run all\n# N**3 *combinations*\nread_socket_test = pytest.mark.parametrize(\n    \"wait_readable\",\n    also_using_fileno(trio.lowlevel.wait_readable),\n    ids=lambda fn: fn.__name__,\n)\nwrite_socket_test = pytest.mark.parametrize(\n    \"wait_writable\",\n    also_using_fileno(trio.lowlevel.wait_writable),\n    ids=lambda fn: fn.__name__,\n)\nnotify_closing_test = pytest.mark.parametrize(\n    \"notify_closing\",\n    also_using_fileno(trio.lowlevel.notify_closing),\n    ids=lambda fn: fn.__name__,\n)\n\n\n# XX These tests are all a bit dicey because they can't distinguish between\n# wait_on_{read,writ}able blocking the way it should, versus blocking\n# momentarily and then immediately resuming.\n@read_socket_test\n@write_socket_test\nasync def test_wait_basic(\n    socketpair: SocketPair,\n    wait_readable: WaitSocket,\n    wait_writable: WaitSocket,\n) -> None:\n    a, b = socketpair\n\n    # They start out writable()\n    with assert_checkpoints():\n        await wait_writable(a)\n\n    # But readable() blocks until data arrives\n    record = []\n\n    async def block_on_read() -> None:\n        try:\n            with assert_checkpoints():\n                await wait_readable(a)\n        except _core.Cancelled:\n            record.append(\"cancelled\")\n        else:\n            record.append(\"readable\")\n            assert a.recv(10) == b\"x\"\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(block_on_read)\n        await wait_all_tasks_blocked()\n        assert record == []\n        b.send(b\"x\")\n\n    fill_socket(a)\n\n    # Now writable will block, but readable won't\n    with assert_checkpoints():\n        await wait_readable(b)\n    record = []\n\n    async def block_on_write() -> None:\n        try:\n            with assert_checkpoints():\n                await wait_writable(a)\n        except _core.Cancelled:\n            record.append(\"cancelled\")\n        else:\n            record.append(\"writable\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(block_on_write)\n        await wait_all_tasks_blocked()\n        assert record == []\n        drain_socket(b)\n\n    # check cancellation\n    record = []\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(block_on_read)\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n    assert record == [\"cancelled\"]\n\n    fill_socket(a)\n    record = []\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(block_on_write)\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n    assert record == [\"cancelled\"]\n\n\n@read_socket_test\nasync def test_double_read(socketpair: SocketPair, wait_readable: WaitSocket) -> None:\n    a, _b = socketpair\n\n    # You can't have two tasks trying to read from a socket at the same time\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(wait_readable, a)\n        await wait_all_tasks_blocked()\n        with pytest.raises(_core.BusyResourceError):\n            await wait_readable(a)\n        nursery.cancel_scope.cancel()\n\n\n@write_socket_test\nasync def test_double_write(socketpair: SocketPair, wait_writable: WaitSocket) -> None:\n    a, _b = socketpair\n\n    # You can't have two tasks trying to write to a socket at the same time\n    fill_socket(a)\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(wait_writable, a)\n        await wait_all_tasks_blocked()\n        with pytest.raises(_core.BusyResourceError):\n            await wait_writable(a)\n        nursery.cancel_scope.cancel()\n\n\n@read_socket_test\n@write_socket_test\n@notify_closing_test\nasync def test_interrupted_by_close(\n    socketpair: SocketPair,\n    wait_readable: WaitSocket,\n    wait_writable: WaitSocket,\n    notify_closing: Callable[[stdlib_socket.socket], object],\n) -> None:\n    a, _b = socketpair\n\n    async def reader() -> None:\n        with pytest.raises(_core.ClosedResourceError):\n            await wait_readable(a)\n\n    async def writer() -> None:\n        with pytest.raises(_core.ClosedResourceError):\n            await wait_writable(a)\n\n    fill_socket(a)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(reader)\n        nursery.start_soon(writer)\n        await wait_all_tasks_blocked()\n        notify_closing(a)\n\n\n@read_socket_test\n@write_socket_test\nasync def test_socket_simultaneous_read_write(\n    socketpair: SocketPair,\n    wait_readable: WaitSocket,\n    wait_writable: WaitSocket,\n) -> None:\n    record: list[str] = []\n\n    async def r_task(sock: stdlib_socket.socket) -> None:\n        await wait_readable(sock)\n        record.append(\"r_task\")\n\n    async def w_task(sock: stdlib_socket.socket) -> None:\n        await wait_writable(sock)\n        record.append(\"w_task\")\n\n    a, b = socketpair\n    fill_socket(a)\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(r_task, a)\n        nursery.start_soon(w_task, a)\n        await wait_all_tasks_blocked()\n        assert record == []\n        b.send(b\"x\")\n        await wait_all_tasks_blocked()\n        assert record == [\"r_task\"]\n        drain_socket(b)\n        await wait_all_tasks_blocked()\n        assert record == [\"r_task\", \"w_task\"]\n\n\n@read_socket_test\n@write_socket_test\nasync def test_socket_actual_streaming(\n    socketpair: SocketPair,\n    wait_readable: WaitSocket,\n    wait_writable: WaitSocket,\n) -> None:\n    a, b = socketpair\n\n    # Use a small send buffer on one of the sockets to increase the chance of\n    # getting partial writes\n    a.setsockopt(stdlib_socket.SOL_SOCKET, stdlib_socket.SO_SNDBUF, 10000)\n\n    N = 1000000  # 1 megabyte\n    MAX_CHUNK = 65536\n\n    results: dict[str, int] = {}\n\n    async def sender(sock: stdlib_socket.socket, seed: int, key: str) -> None:\n        r = random.Random(seed)\n        sent = 0\n        while sent < N:\n            print(\"sent\", sent)\n            chunk = bytearray(r.randrange(MAX_CHUNK))\n            while chunk:\n                with assert_checkpoints():\n                    await wait_writable(sock)\n                this_chunk_size = sock.send(chunk)\n                sent += this_chunk_size\n                del chunk[:this_chunk_size]\n        sock.shutdown(stdlib_socket.SHUT_WR)\n        results[key] = sent\n\n    async def receiver(sock: stdlib_socket.socket, key: str) -> None:\n        received = 0\n        while True:\n            print(\"received\", received)\n            with assert_checkpoints():\n                await wait_readable(sock)\n            this_chunk_size = len(sock.recv(MAX_CHUNK))\n            if not this_chunk_size:\n                break\n            received += this_chunk_size\n        results[key] = received\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(sender, a, 0, \"send_a\")\n        nursery.start_soon(sender, b, 1, \"send_b\")\n        nursery.start_soon(receiver, a, \"recv_a\")\n        nursery.start_soon(receiver, b, \"recv_b\")\n\n    assert results[\"send_a\"] == results[\"recv_b\"]\n    assert results[\"send_b\"] == results[\"recv_a\"]\n\n\nasync def test_notify_closing_on_invalid_object() -> None:\n    # It should either be a no-op (generally on Unix, where we don't know\n    # which fds are valid), or an OSError (on Windows, where we currently only\n    # support sockets, so we have to do some validation to figure out whether\n    # it's a socket or a regular handle).\n    got_oserror = False\n    got_no_error = False\n    try:\n        trio.lowlevel.notify_closing(-1)\n    except OSError:\n        got_oserror = True\n    else:\n        got_no_error = True\n    assert got_oserror or got_no_error\n\n\nasync def test_wait_on_invalid_object() -> None:\n    # We definitely want to raise an error everywhere if you pass in an\n    # invalid fd to wait_*\n    for wait in [trio.lowlevel.wait_readable, trio.lowlevel.wait_writable]:\n        with stdlib_socket.socket() as s:\n            fileno = s.fileno()\n        # We just closed the socket and don't do anything else in between, so\n        # we can be confident that the fileno hasn't be reassigned.\n        with pytest.raises(\n            OSError,\n            match=r\"^\\[\\w+ \\d+] (Bad file descriptor|An operation was attempted on something that is not a socket)$\",\n        ):\n            await wait(fileno)\n\n\nasync def test_io_manager_statistics() -> None:\n    def check(*, expected_readers: int, expected_writers: int) -> None:\n        statistics = _core.current_statistics()\n        print(statistics)\n        iostats = statistics.io_statistics\n        if iostats.backend == \"epoll\" or iostats.backend == \"windows\":\n            assert iostats.tasks_waiting_read == expected_readers\n            assert iostats.tasks_waiting_write == expected_writers\n        else:\n            assert iostats.backend == \"kqueue\"\n            assert iostats.monitors == 0\n            assert iostats.tasks_waiting == expected_readers + expected_writers\n\n    a1, b1 = stdlib_socket.socketpair()\n    a2, b2 = stdlib_socket.socketpair()\n    a3, b3 = stdlib_socket.socketpair()\n    for sock in [a1, b1, a2, b2, a3, b3]:\n        sock.setblocking(False)\n    with a1, b1, a2, b2, a3, b3:\n        # let the call_soon_task settle down\n        await wait_all_tasks_blocked()\n\n        # 1 for call_soon_task\n        check(expected_readers=1, expected_writers=0)\n\n        # We want:\n        # - one socket with a writer blocked\n        # - two sockets with a reader blocked\n        # - a socket with both blocked\n        fill_socket(a1)\n        fill_socket(a3)\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(_core.wait_writable, a1)\n            nursery.start_soon(_core.wait_readable, a2)\n            nursery.start_soon(_core.wait_readable, b2)\n            nursery.start_soon(_core.wait_writable, a3)\n            nursery.start_soon(_core.wait_readable, a3)\n\n            await wait_all_tasks_blocked()\n\n            # +1 for call_soon_task\n            check(expected_readers=3 + 1, expected_writers=2)\n\n            nursery.cancel_scope.cancel()\n\n        # 1 for call_soon_task\n        check(expected_readers=1, expected_writers=0)\n\n\n@pytest.mark.filterwarnings(\"ignore:.*UnboundedQueue:trio.TrioDeprecationWarning\")\nasync def test_io_manager_kqueue_monitors_statistics() -> None:\n    def check(\n        *,\n        expected_monitors: int,\n        expected_readers: int,\n        expected_writers: int,\n    ) -> None:\n        statistics = _core.current_statistics()\n        print(statistics)\n        iostats = statistics.io_statistics\n        assert iostats.backend == \"kqueue\"\n        assert iostats.monitors == expected_monitors\n        assert iostats.tasks_waiting == expected_readers + expected_writers\n\n    a1, b1 = stdlib_socket.socketpair()\n    for sock in [a1, b1]:\n        sock.setblocking(False)\n\n    with a1, b1:\n        # let the call_soon_task settle down\n        await wait_all_tasks_blocked()\n\n        if sys.platform != \"win32\" and sys.platform != \"linux\":\n            # 1 for call_soon_task\n            check(expected_monitors=0, expected_readers=1, expected_writers=0)\n\n            with _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):\n                with (\n                    pytest.raises(_core.BusyResourceError),\n                    _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ),\n                ):\n                    pass  # pragma: no cover\n                check(expected_monitors=1, expected_readers=1, expected_writers=0)\n\n            check(expected_monitors=0, expected_readers=1, expected_writers=0)\n\n\nasync def test_can_survive_unnotified_close() -> None:\n    # An \"unnotified\" close is when the user closes an fd/socket/handle\n    # directly, without calling notify_closing first. This should never happen\n    # -- users should call notify_closing before closing things. But, just in\n    # case they don't, we would still like to avoid exploding.\n    #\n    # Acceptable behaviors:\n    # - wait_* never return, but can be cancelled cleanly\n    # - wait_* exit cleanly\n    # - wait_* raise an OSError\n    #\n    # Not acceptable:\n    # - getting stuck in an uncancellable state\n    # - TrioInternalError blowing up the whole run\n    #\n    # This test exercises some tricky \"unnotified close\" scenarios, to make\n    # sure we get the \"acceptable\" behaviors.\n\n    async def allow_OSError(\n        async_func: Callable[ArgsT, Awaitable[object]],\n        *args: ArgsT.args,\n        **kwargs: ArgsT.kwargs,\n    ) -> None:\n        with suppress(OSError):\n            await async_func(*args, **kwargs)\n\n    with stdlib_socket.socket() as s:\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, s)\n            await wait_all_tasks_blocked()\n            s.close()\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n    # We hit different paths on Windows depending on whether we close the last\n    # handle to the object (which produces a LOCAL_CLOSE notification and\n    # wakes up wait_readable), or only close one of the handles (which leaves\n    # wait_readable pending until cancelled).\n    with stdlib_socket.socket() as s, s.dup() as s2:  # noqa: F841\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, s)\n            await wait_all_tasks_blocked()\n            s.close()\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n    # A more elaborate case, with two tasks waiting. On windows and epoll,\n    # the two tasks get muxed together onto a single underlying wait\n    # operation. So when they're cancelled, there's a brief moment where one\n    # of the tasks is cancelled but the other isn't, so we try to re-issue the\n    # underlying wait operation. But here, the handle we were going to use to\n    # do that has been pulled out from under our feet... so test that we can\n    # survive this.\n    a, b = stdlib_socket.socketpair()\n    with a, b, a.dup() as a2:\n        a.setblocking(False)\n        b.setblocking(False)\n        fill_socket(a)\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, a)\n            nursery.start_soon(allow_OSError, trio.lowlevel.wait_writable, a)\n            await wait_all_tasks_blocked()\n            a.close()\n            nursery.cancel_scope.cancel()\n\n    # A similar case, but now the single-task-wakeup happens due to I/O\n    # arriving, not a cancellation, so the operation gets re-issued from\n    # handle_io context rather than abort context.\n    a, b = stdlib_socket.socketpair()\n    with a, b, a.dup() as a2:\n        print(f\"a={a.fileno()}, b={b.fileno()}, a2={a2.fileno()}\")\n        a.setblocking(False)\n        b.setblocking(False)\n        fill_socket(a)\n        e = trio.Event()\n\n        # We want to wait for the kernel to process the wakeup on 'a', if any.\n        # But depending on the platform, we might not get a wakeup on 'a'. So\n        # we put one task to sleep waiting on 'a', and we put a second task to\n        # sleep waiting on 'a2', with the idea that the 'a2' notification will\n        # definitely arrive, and when it does then we can assume that whatever\n        # notification was going to arrive for 'a' has also arrived.\n        async def wait_readable_a2_then_set() -> None:\n            await trio.lowlevel.wait_readable(a2)\n            e.set()\n\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, a)\n            nursery.start_soon(allow_OSError, trio.lowlevel.wait_writable, a)\n            nursery.start_soon(wait_readable_a2_then_set)\n            await wait_all_tasks_blocked()\n            a.close()\n            b.send(b\"x\")\n            # Make sure that the wakeup has been received and everything has\n            # settled before cancelling the wait_writable.\n            await e.wait()\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n"
  },
  {
    "path": "src/trio/_core/_tests/test_ki.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport inspect\nimport signal\nimport sys\nimport threading\nimport weakref\nfrom collections.abc import AsyncIterator, Callable, Iterator\nfrom typing import TYPE_CHECKING, TypeVar\n\nimport outcome\nimport pytest\n\nfrom .tutil import gc_collect_harder\n\ntry:\n    from async_generator import async_generator, yield_\nexcept ImportError:  # pragma: no cover\n    async_generator = yield_ = None\n\nfrom ... import _core\nfrom ..._abc import Instrument\nfrom ..._core import _ki\nfrom ..._timeouts import sleep\nfrom ...testing import wait_all_tasks_blocked\n\nif TYPE_CHECKING:\n    from collections.abc import (\n        AsyncGenerator,\n        AsyncIterator,\n        Callable,\n        Generator,\n        Iterator,\n    )\n\n    from ..._core import Abort, RaiseCancelT\n\n\ndef ki_self() -> None:\n    signal.raise_signal(signal.SIGINT)\n\n\ndef test_ki_self() -> None:\n    with pytest.raises(KeyboardInterrupt):\n        ki_self()\n\n\nasync def test_ki_enabled() -> None:\n    # Regular tasks aren't KI-protected\n    assert not _core.currently_ki_protected()\n\n    # Low-level call-soon callbacks are KI-protected\n    token = _core.current_trio_token()\n    record = []\n\n    def check() -> None:\n        record.append(_core.currently_ki_protected())\n\n    token.run_sync_soon(check)\n    await wait_all_tasks_blocked()\n    assert record == [True]\n\n    @_core.enable_ki_protection\n    def protected() -> None:\n        assert _core.currently_ki_protected()\n        unprotected()\n\n    @_core.disable_ki_protection\n    def unprotected() -> None:\n        assert not _core.currently_ki_protected()\n\n    protected()\n\n    @_core.enable_ki_protection\n    async def aprotected() -> None:\n        assert _core.currently_ki_protected()\n        await aunprotected()\n\n    @_core.disable_ki_protection\n    async def aunprotected() -> None:\n        assert not _core.currently_ki_protected()\n\n    await aprotected()\n\n    # make sure that the decorator here overrides the automatic manipulation\n    # that start_soon() does:\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(aprotected)\n        nursery.start_soon(aunprotected)\n\n    @_core.enable_ki_protection\n    def gen_protected() -> Iterator[None]:\n        assert _core.currently_ki_protected()\n        yield\n\n    for _ in gen_protected():\n        pass\n\n    @_core.disable_ki_protection\n    def gen_unprotected() -> Iterator[None]:\n        assert not _core.currently_ki_protected()\n        yield\n\n    for _ in gen_unprotected():\n        pass\n\n\n# This used to be broken due to\n#\n#   https://bugs.python.org/issue29590\n#\n# Specifically, after a coroutine is resumed with .throw(), then the stack\n# makes it look like the immediate caller is the function that called\n# .throw(), not the actual caller. So child() here would have a caller deep in\n# the guts of the run loop, and always be protected, even when it shouldn't\n# have been. (Solution: we don't use .throw() anymore.)\nasync def test_ki_enabled_after_yield_briefly() -> None:\n    @_core.enable_ki_protection\n    async def protected() -> None:\n        await child(True)\n\n    @_core.disable_ki_protection\n    async def unprotected() -> None:\n        await child(False)\n\n    async def child(expected: bool) -> None:\n        import traceback\n\n        traceback.print_stack()\n        assert _core.currently_ki_protected() == expected\n        await _core.checkpoint()\n        traceback.print_stack()\n        assert _core.currently_ki_protected() == expected\n\n    await protected()\n    await unprotected()\n\n\n# This also used to be broken due to\n#   https://bugs.python.org/issue29590\nasync def test_generator_based_context_manager_throw() -> None:\n    @contextlib.contextmanager\n    @_core.enable_ki_protection\n    def protected_manager() -> Iterator[None]:\n        assert _core.currently_ki_protected()\n        try:\n            yield\n        finally:\n            assert _core.currently_ki_protected()\n\n    with protected_manager():\n        assert not _core.currently_ki_protected()\n\n    with pytest.raises(KeyError):\n        # This is the one that used to fail\n        with protected_manager():\n            raise KeyError\n\n\n# the async_generator package isn't typed, hence all the type: ignores\n@pytest.mark.skipif(async_generator is None, reason=\"async_generator not installed\")\nasync def test_async_generator_agen_protection() -> None:\n    @_core.enable_ki_protection\n    @async_generator  # type: ignore[untyped-decorator]\n    async def agen_protected1() -> None:  # type: ignore[misc] # untyped generator\n        assert _core.currently_ki_protected()\n        try:\n            await yield_()\n        finally:\n            assert _core.currently_ki_protected()\n\n    @_core.disable_ki_protection\n    @async_generator  # type: ignore[untyped-decorator]\n    async def agen_unprotected1() -> None:  # type: ignore[misc] # untyped generator\n        assert not _core.currently_ki_protected()\n        try:\n            await yield_()\n        finally:\n            assert not _core.currently_ki_protected()\n\n    # Swap the order of the decorators:\n    @async_generator  # type: ignore[untyped-decorator]\n    @_core.enable_ki_protection\n    async def agen_protected2() -> None:  # type: ignore[misc] # untyped generator\n        assert _core.currently_ki_protected()\n        try:\n            await yield_()\n        finally:\n            assert _core.currently_ki_protected()\n\n    @async_generator  # type: ignore[untyped-decorator]\n    @_core.disable_ki_protection\n    async def agen_unprotected2() -> None:  # type: ignore[misc] # untyped generator\n        assert not _core.currently_ki_protected()\n        try:\n            await yield_()\n        finally:\n            assert not _core.currently_ki_protected()\n\n    await _check_agen(agen_protected1)\n    await _check_agen(agen_protected2)\n    await _check_agen(agen_unprotected1)\n    await _check_agen(agen_unprotected2)\n\n\nasync def test_native_agen_protection() -> None:\n    # Native async generators\n    @_core.enable_ki_protection\n    async def agen_protected() -> AsyncIterator[None]:\n        assert _core.currently_ki_protected()\n        try:\n            yield\n        finally:\n            assert _core.currently_ki_protected()\n\n    @_core.disable_ki_protection\n    async def agen_unprotected() -> AsyncIterator[None]:\n        assert not _core.currently_ki_protected()\n        try:\n            yield\n        finally:\n            assert not _core.currently_ki_protected()\n\n    await _check_agen(agen_protected)\n    await _check_agen(agen_unprotected)\n\n\nasync def _check_agen(agen_fn: Callable[[], AsyncIterator[None]]) -> None:\n    async for _ in agen_fn():\n        assert not _core.currently_ki_protected()\n\n    # asynccontextmanager insists that the function passed must itself be an\n    # async gen function, not a wrapper around one\n    if inspect.isasyncgenfunction(agen_fn):\n        async with contextlib.asynccontextmanager(agen_fn)():\n            assert not _core.currently_ki_protected()\n\n        # Another case that's tricky due to:\n        #   https://bugs.python.org/issue29590\n        with pytest.raises(KeyError):\n            async with contextlib.asynccontextmanager(agen_fn)():\n                raise KeyError\n\n\n# Test the case where there's no magic local anywhere in the call stack\ndef test_ki_disabled_out_of_context() -> None:\n    assert _core.currently_ki_protected()\n\n\ndef test_ki_disabled_in_del() -> None:\n    def nestedfunction() -> bool:\n        return _core.currently_ki_protected()\n\n    def __del__() -> None:\n        assert _core.currently_ki_protected()\n        assert nestedfunction()\n\n    @_core.disable_ki_protection\n    def outerfunction() -> None:\n        assert not _core.currently_ki_protected()\n        assert not nestedfunction()\n        __del__()\n\n    __del__()\n    outerfunction()\n    assert nestedfunction()\n\n\ndef test_ki_protection_works() -> None:\n    async def sleeper(name: str, record: set[str]) -> None:\n        try:\n            while True:\n                await _core.checkpoint()\n        except _core.Cancelled:\n            record.add(name + \" ok\")\n\n    async def raiser(name: str, record: set[str]) -> None:\n        try:\n            # os.kill runs signal handlers before returning, so we don't need\n            # to worry that the handler will be delayed\n            print(\"killing, protection =\", _core.currently_ki_protected())\n            ki_self()\n        except KeyboardInterrupt:\n            print(\"raised!\")\n            # Make sure we aren't getting cancelled as well as siginted\n            await _core.checkpoint()\n            record.add(name + \" raise ok\")\n            raise\n        else:\n            print(\"didn't raise!\")\n            # If we didn't raise (b/c protected), then we *should* get\n            # cancelled at the next opportunity\n            try:\n                await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)\n            except _core.Cancelled:\n                record.add(name + \" cancel ok\")\n\n    # simulated control-C during raiser, which is *unprotected*\n    print(\"check 1\")\n    record_set: set[str] = set()\n\n    async def check_unprotected_kill() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sleeper, \"s1\", record_set)\n            nursery.start_soon(sleeper, \"s2\", record_set)\n            nursery.start_soon(raiser, \"r1\", record_set)\n\n    # raises inside a nursery, so the KeyboardInterrupt is wrapped in an ExceptionGroup\n    with pytest.RaisesGroup(KeyboardInterrupt):\n        _core.run(check_unprotected_kill)\n    assert record_set == {\"s1 ok\", \"s2 ok\", \"r1 raise ok\"}\n\n    # simulated control-C during raiser, which is *protected*, so the KI gets\n    # delivered to the main task instead\n    print(\"check 2\")\n    record_set = set()\n\n    async def check_protected_kill() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sleeper, \"s1\", record_set)\n            nursery.start_soon(sleeper, \"s2\", record_set)\n            nursery.start_soon(_core.enable_ki_protection(raiser), \"r1\", record_set)\n            # __aexit__ blocks, and then receives the KI\n\n    # raises inside a nursery, so the KeyboardInterrupt is wrapped in an ExceptionGroup\n    with pytest.RaisesGroup(KeyboardInterrupt):\n        _core.run(check_protected_kill)\n    assert record_set == {\"s1 ok\", \"s2 ok\", \"r1 cancel ok\"}\n\n    # kill at last moment still raises (run_sync_soon until it raises an\n    # error, then kill)\n    print(\"check 3\")\n\n    async def check_kill_during_shutdown() -> None:\n        token = _core.current_trio_token()\n\n        def kill_during_shutdown() -> None:\n            assert _core.currently_ki_protected()\n            try:\n                token.run_sync_soon(kill_during_shutdown)\n            except _core.RunFinishedError:\n                # it's too late for regular handling! handle this!\n                print(\"kill! kill!\")\n                ki_self()\n\n        token.run_sync_soon(kill_during_shutdown)\n\n    # no nurseries involved, so the KeyboardInterrupt isn't wrapped\n    with pytest.raises(KeyboardInterrupt):\n        _core.run(check_kill_during_shutdown)\n\n    # KI arrives very early, before main is even spawned\n    print(\"check 4\")\n\n    class InstrumentOfDeath(Instrument):\n        def before_run(self) -> None:\n            ki_self()\n\n    async def main_1() -> None:\n        await _core.checkpoint()\n\n    # no nurseries involved, so the KeyboardInterrupt isn't wrapped\n    with pytest.raises(KeyboardInterrupt):\n        _core.run(main_1, instruments=[InstrumentOfDeath()])\n\n    # checkpoint_if_cancelled notices pending KI\n    print(\"check 5\")\n\n    @_core.enable_ki_protection\n    async def main_2() -> None:\n        assert _core.currently_ki_protected()\n        ki_self()\n        with pytest.raises(KeyboardInterrupt):\n            await _core.checkpoint_if_cancelled()\n\n    _core.run(main_2)\n\n    # KI arrives while main task is not abortable, b/c already scheduled\n    print(\"check 6\")\n\n    @_core.enable_ki_protection\n    async def main_3() -> None:\n        assert _core.currently_ki_protected()\n        ki_self()\n        await _core.cancel_shielded_checkpoint()\n        await _core.cancel_shielded_checkpoint()\n        await _core.cancel_shielded_checkpoint()\n        with pytest.raises(KeyboardInterrupt):\n            await _core.checkpoint()\n\n    _core.run(main_3)\n\n    # KI arrives while main task is not abortable, b/c refuses to be aborted\n    print(\"check 7\")\n\n    @_core.enable_ki_protection\n    async def main_4() -> None:\n        assert _core.currently_ki_protected()\n        ki_self()\n        task = _core.current_task()\n\n        def abort(_: RaiseCancelT) -> Abort:\n            _core.reschedule(task, outcome.Value(1))\n            return _core.Abort.FAILED\n\n        assert await _core.wait_task_rescheduled(abort) == 1\n        with pytest.raises(KeyboardInterrupt):\n            await _core.checkpoint()\n\n    _core.run(main_4)\n\n    # KI delivered via slow abort\n    print(\"check 8\")\n\n    @_core.enable_ki_protection\n    async def main_5() -> None:\n        assert _core.currently_ki_protected()\n        ki_self()\n        task = _core.current_task()\n\n        def abort(raise_cancel: RaiseCancelT) -> Abort:\n            result = outcome.capture(raise_cancel)\n            _core.reschedule(task, result)\n            return _core.Abort.FAILED\n\n        with pytest.raises(KeyboardInterrupt):\n            assert await _core.wait_task_rescheduled(abort)\n        await _core.checkpoint()\n\n    _core.run(main_5)\n\n    # KI arrives just before main task exits, so the run_sync_soon machinery\n    # is still functioning and will accept the callback to deliver the KI, but\n    # by the time the callback is actually run, main has exited and can't be\n    # aborted.\n    print(\"check 9\")\n\n    @_core.enable_ki_protection\n    async def main_6() -> None:\n        ki_self()\n\n    with pytest.raises(KeyboardInterrupt):\n        _core.run(main_6)\n\n    print(\"check 10\")\n    # KI in unprotected code, with\n    # restrict_keyboard_interrupt_to_checkpoints=True\n    record_list = []\n\n    async def main_7() -> None:\n        # We're not KI protected...\n        assert not _core.currently_ki_protected()\n        ki_self()\n        # ...but even after the KI, we keep running uninterrupted...\n        record_list.append(\"ok\")\n        # ...until we hit a checkpoint:\n        with pytest.raises(KeyboardInterrupt):\n            await sleep(10)\n\n    _core.run(main_7, restrict_keyboard_interrupt_to_checkpoints=True)\n    assert record_list == [\"ok\"]\n    record_list = []\n    # Exact same code raises KI early if we leave off the argument, doesn't\n    # even reach the record.append call:\n    with pytest.raises(KeyboardInterrupt):\n        _core.run(main_7)\n    assert record_list == []\n\n    # KI arrives while main task is inside a cancelled cancellation scope\n    # the KeyboardInterrupt should take priority\n    print(\"check 11\")\n\n    @_core.enable_ki_protection\n    async def main_8() -> None:\n        assert _core.currently_ki_protected()\n        with _core.CancelScope() as cancel_scope:\n            cancel_scope.cancel()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n            ki_self()\n            with pytest.raises(KeyboardInterrupt):\n                await _core.checkpoint()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n\n    _core.run(main_8)\n\n\ndef test_ki_is_good_neighbor() -> None:\n    # in the unlikely event someone overwrites our signal handler, we leave\n    # the overwritten one be\n    try:\n        orig = signal.getsignal(signal.SIGINT)\n\n        def my_handler(signum: object, frame: object) -> None:  # pragma: no cover\n            pass\n\n        async def main() -> None:\n            signal.signal(signal.SIGINT, my_handler)\n\n        _core.run(main)\n\n        assert signal.getsignal(signal.SIGINT) is my_handler\n    finally:\n        signal.signal(signal.SIGINT, orig)\n\n\n# Regression test for #461\n# don't know if _active not being visible is a problem\ndef test_ki_with_broken_threads() -> None:\n    thread = threading.main_thread()\n\n    # scary!\n    original = threading._active[thread.ident]  # type: ignore[attr-defined]\n\n    # put this in a try finally so we don't have a chance of cascading a\n    # breakage down to everything else\n    try:\n        del threading._active[thread.ident]  # type: ignore[attr-defined]\n\n        @_core.enable_ki_protection\n        async def inner() -> None:\n            assert signal.getsignal(signal.SIGINT) != signal.default_int_handler\n\n        _core.run(inner)\n    finally:\n        threading._active[thread.ident] = original  # type: ignore[attr-defined]\n\n\n_T = TypeVar(\"_T\")\n\n\ndef _identity(v: _T) -> _T:\n    return v\n\n\n@pytest.mark.xfail(\n    strict=True,\n    raises=AssertionError,\n    reason=(\n        \"it was decided not to protect against this case, see discussion in: \"\n        \"https://github.com/python-trio/trio/pull/3110#discussion_r1802123644\"\n    ),\n)\nasync def test_ki_does_not_leak_across_different_calls_to_inner_functions() -> None:\n    assert not _core.currently_ki_protected()\n\n    def factory(enabled: bool) -> Callable[[], bool]:\n        @_core.enable_ki_protection if enabled else _identity\n        def decorated() -> bool:\n            return _core.currently_ki_protected()\n\n        return decorated\n\n    decorated_enabled = factory(True)\n    decorated_disabled = factory(False)\n    assert decorated_enabled()\n    assert not decorated_disabled()\n\n\nasync def test_ki_protection_check_does_not_freeze_locals() -> None:\n    class A:\n        pass\n\n    a = A()\n    wr_a = weakref.ref(a)\n    assert not _core.currently_ki_protected()\n    del a\n    if sys.implementation.name == \"pypy\":\n        gc_collect_harder()\n    assert wr_a() is None\n\n\ndef test_identity_weakref_internals() -> None:\n    \"\"\"To cover the parts WeakKeyIdentityDictionary won't ever reach.\"\"\"\n\n    class A:\n        def __eq__(self, other: object) -> bool:\n            return False\n\n    a = A()\n    assert a != a\n    wr = _ki._IdRef(a)\n    wr_other_is_self = wr\n\n    # dict always checks identity before equality so we need to do it here\n    # to cover `if self is other`\n    assert wr == wr_other_is_self\n\n    # we want to cover __ne__ and `return NotImplemented`\n    assert wr != object()\n\n\ndef test_weak_key_identity_dict_remove_callback_keyerror() -> None:\n    \"\"\"We need to cover the KeyError in self._remove.\"\"\"\n\n    class A:\n        def __eq__(self, other: object) -> bool:\n            return False\n\n    a = A()\n    assert a != a\n    d: _ki.WeakKeyIdentityDictionary[A, bool] = _ki.WeakKeyIdentityDictionary()\n\n    d[a] = True\n\n    data_copy = d._data.copy()\n    d._data.clear()\n    del a\n\n    gc_collect_harder()  # would call sys.unraisablehook if there's a problem\n    assert data_copy\n\n\ndef test_weak_key_identity_dict_remove_callback_selfref_expired() -> None:\n    \"\"\"We need to cover the KeyError in self._remove.\"\"\"\n\n    class A:\n        def __eq__(self, other: object) -> bool:\n            return False\n\n    a = A()\n    assert a != a\n    d: _ki.WeakKeyIdentityDictionary[A, bool] = _ki.WeakKeyIdentityDictionary()\n\n    d[a] = True\n\n    data_copy = d._data.copy()\n    wr_d = weakref.ref(d)\n    del d\n    gc_collect_harder()  # would call sys.unraisablehook if there's a problem\n    assert wr_d() is None\n    del a\n    gc_collect_harder()\n    assert data_copy\n\n\n@_core.enable_ki_protection\nasync def _protected_async_gen_fn() -> AsyncGenerator[None, None]:\n    yield\n\n\n@_core.enable_ki_protection\nasync def _protected_async_fn() -> None:\n    pass\n\n\n@_core.enable_ki_protection\ndef _protected_gen_fn() -> Generator[None, None, None]:\n    yield\n\n\n@_core.disable_ki_protection\nasync def _unprotected_async_gen_fn() -> AsyncGenerator[None, None]:\n    yield\n\n\n@_core.disable_ki_protection\nasync def _unprotected_async_fn() -> None:\n    pass\n\n\n@_core.disable_ki_protection\ndef _unprotected_gen_fn() -> Generator[None, None, None]:\n    yield\n\n\nasync def _consume_async_generator(agen: AsyncGenerator[None, None]) -> None:\n    try:\n        with pytest.raises(StopAsyncIteration):\n            while True:\n                await agen.asend(None)\n    finally:\n        await agen.aclose()\n\n\ndef _consume_function_for_coverage(\n    fn: Callable[[], object],\n) -> None:\n    result = fn()\n    if inspect.isasyncgen(result):\n        result = _consume_async_generator(result)\n\n    assert inspect.isgenerator(result) or inspect.iscoroutine(result)\n    with pytest.raises(StopIteration):\n        while True:\n            result.send(None)\n\n\ndef test_enable_disable_ki_protection_passes_on_inspect_flags() -> None:\n    assert inspect.isasyncgenfunction(_protected_async_gen_fn)\n    _consume_function_for_coverage(_protected_async_gen_fn)\n    assert inspect.iscoroutinefunction(_protected_async_fn)\n    _consume_function_for_coverage(_protected_async_fn)\n    assert inspect.isgeneratorfunction(_protected_gen_fn)\n    _consume_function_for_coverage(_protected_gen_fn)\n    assert inspect.isasyncgenfunction(_unprotected_async_gen_fn)\n    _consume_function_for_coverage(_unprotected_async_gen_fn)\n    assert inspect.iscoroutinefunction(_unprotected_async_fn)\n    _consume_function_for_coverage(_unprotected_async_fn)\n    assert inspect.isgeneratorfunction(_unprotected_gen_fn)\n    _consume_function_for_coverage(_unprotected_gen_fn)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_local.py",
    "content": "import pytest\n\nfrom trio import run\nfrom trio.lowlevel import RunVar, RunVarToken\n\nfrom ... import _core\n\n\n# scary runvar tests\ndef test_runvar_smoketest() -> None:\n    t1 = RunVar[str](\"test1\")\n    t2 = RunVar[str](\"test2\", default=\"catfish\")\n\n    assert repr(t1) == \"<RunVar name='test1'>\"\n\n    async def first_check() -> None:\n        with pytest.raises(LookupError):\n            t1.get()\n\n        t1.set(\"swordfish\")\n        assert t1.get() == \"swordfish\"\n        assert t2.get() == \"catfish\"\n        assert t2.get(default=\"eel\") == \"eel\"\n\n        t2.set(\"goldfish\")\n        assert t2.get() == \"goldfish\"\n        assert t2.get(default=\"tuna\") == \"goldfish\"\n\n    async def second_check() -> None:\n        with pytest.raises(LookupError):\n            t1.get()\n\n        assert t2.get() == \"catfish\"\n\n    run(first_check)\n    run(second_check)\n\n\ndef test_runvar_resetting() -> None:\n    t1 = RunVar[str](\"test1\")\n    t2 = RunVar[str](\"test2\", default=\"dogfish\")\n    t3 = RunVar[str](\"test3\")\n\n    async def reset_check() -> None:\n        token = t1.set(\"moonfish\")\n        assert t1.get() == \"moonfish\"\n        t1.reset(token)\n\n        with pytest.raises(TypeError):\n            t1.reset(None)  # type: ignore[arg-type]\n\n        with pytest.raises(LookupError):\n            t1.get()\n\n        token2 = t2.set(\"catdogfish\")\n        assert t2.get() == \"catdogfish\"\n        t2.reset(token2)\n        assert t2.get() == \"dogfish\"\n\n        with pytest.raises(ValueError, match=r\"^token has already been used$\"):\n            t2.reset(token2)\n\n        token3 = t3.set(\"basculin\")\n        assert t3.get() == \"basculin\"\n\n        with pytest.raises(ValueError, match=r\"^token is not for us$\"):\n            t1.reset(token3)\n\n    run(reset_check)\n\n\ndef test_runvar_sync() -> None:\n    t1 = RunVar[str](\"test1\")\n\n    async def sync_check() -> None:\n        async def task1() -> None:\n            t1.set(\"plaice\")\n            assert t1.get() == \"plaice\"\n\n        async def task2(tok: RunVarToken[str]) -> None:\n            t1.reset(tok)\n\n            with pytest.raises(LookupError):\n                t1.get()\n\n            t1.set(\"haddock\")\n\n        async with _core.open_nursery() as n:\n            token = t1.set(\"cod\")\n            assert t1.get() == \"cod\"\n\n            n.start_soon(task1)\n            await _core.wait_all_tasks_blocked()\n            assert t1.get() == \"plaice\"\n\n            n.start_soon(task2, token)\n            await _core.wait_all_tasks_blocked()\n            assert t1.get() == \"haddock\"\n\n    run(sync_check)\n\n\ndef test_accessing_runvar_outside_run_call_fails() -> None:\n    t1 = RunVar[str](\"test1\")\n\n    with pytest.raises(RuntimeError):\n        t1.set(\"asdf\")\n\n    with pytest.raises(RuntimeError):\n        t1.get()\n\n    async def get_token() -> RunVarToken[str]:\n        return t1.set(\"ok\")\n\n    token = run(get_token)\n\n    with pytest.raises(RuntimeError):\n        t1.reset(token)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_mock_clock.py",
    "content": "import time\nfrom math import inf\n\nimport pytest\n\nfrom trio import sleep\n\nfrom ... import _core\nfrom .. import wait_all_tasks_blocked\nfrom .._mock_clock import MockClock\nfrom .._run import GLOBAL_RUN_CONTEXT\nfrom .tutil import slow\n\n\ndef test_mock_clock() -> None:\n    REAL_NOW = 123.0\n    c = MockClock()\n    c._real_clock = lambda: REAL_NOW\n    repr(c)  # smoke test\n    assert c.rate == 0\n    assert c.current_time() == 0\n    c.jump(1.2)\n    assert c.current_time() == 1.2\n    with pytest.raises(ValueError, match=r\"^time can't go backwards$\"):\n        c.jump(-1)\n    assert c.current_time() == 1.2\n    assert c.deadline_to_sleep_time(1.1) == 0\n    assert c.deadline_to_sleep_time(1.2) == 0\n    assert c.deadline_to_sleep_time(1.3) > 999999\n\n    with pytest.raises(ValueError, match=r\"^rate must be >= 0$\"):\n        c.rate = -1\n    assert c.rate == 0\n\n    c.rate = 2\n    assert c.current_time() == 1.2\n    REAL_NOW += 1\n    assert c.current_time() == 3.2\n    assert c.deadline_to_sleep_time(3.1) == 0\n    assert c.deadline_to_sleep_time(3.2) == 0\n    assert c.deadline_to_sleep_time(4.2) == 0.5\n\n    c.rate = 0.5\n    assert c.current_time() == 3.2\n    assert c.deadline_to_sleep_time(3.1) == 0\n    assert c.deadline_to_sleep_time(3.2) == 0\n    assert c.deadline_to_sleep_time(4.2) == 2.0\n\n    c.jump(0.8)\n    assert c.current_time() == 4.0\n    REAL_NOW += 1\n    assert c.current_time() == 4.5\n\n    c2 = MockClock(rate=3)\n    assert c2.rate == 3\n    assert c2.current_time() < 10\n\n\nasync def test_mock_clock_autojump(mock_clock: MockClock) -> None:\n    assert mock_clock.autojump_threshold == inf\n\n    mock_clock.autojump_threshold = 0\n    assert mock_clock.autojump_threshold == 0\n\n    real_start = time.perf_counter()\n\n    virtual_start = _core.current_time()\n    for i in range(10):\n        print(f\"sleeping {10 * i} seconds\")\n        await sleep(10 * i)\n        print(\"woke up!\")\n        assert virtual_start + 10 * i == _core.current_time()\n        virtual_start = _core.current_time()\n\n    real_duration = time.perf_counter() - real_start\n    print(f\"Slept {10 * sum(range(10))} seconds in {real_duration} seconds\")\n    assert real_duration < 1\n\n    mock_clock.autojump_threshold = 0.02\n    t = _core.current_time()\n    # this should wake up before the autojump threshold triggers, so time\n    # shouldn't change\n    await wait_all_tasks_blocked()\n    assert t == _core.current_time()\n    # this should too\n    await wait_all_tasks_blocked(0.01)\n    assert t == _core.current_time()\n\n    # set up a situation where the autojump task is blocked for a long long\n    # time, to make sure that cancel-and-adjust-threshold logic is working\n    mock_clock.autojump_threshold = 10000\n    await wait_all_tasks_blocked()\n    mock_clock.autojump_threshold = 0\n    # if the above line didn't take affect immediately, then this would be\n    # bad:\n    # ignore ASYNC116, not sleep_forever, trying to test a large but finite sleep\n    await sleep(100000)  # noqa: ASYNC116\n\n\nasync def test_mock_clock_autojump_interference(mock_clock: MockClock) -> None:\n    mock_clock.autojump_threshold = 0.02\n\n    mock_clock2 = MockClock()\n    # messing with the autojump threshold of a clock that isn't actually\n    # installed in the run loop shouldn't do anything.\n    mock_clock2.autojump_threshold = 0.01\n\n    # if the autojump_threshold of 0.01 were in effect, then the next line\n    # would block forever, as the autojump task kept waking up to try to\n    # jump the clock.\n    await wait_all_tasks_blocked(0.015)\n\n    # but the 0.02 limit does apply\n    # ignore ASYNC116, not sleep_forever, trying to test a large but finite sleep\n    await sleep(100000)  # noqa: ASYNC116\n\n\ndef test_mock_clock_autojump_preset() -> None:\n    # Check that we can set the autojump_threshold before the clock is\n    # actually in use, and it gets picked up\n    mock_clock = MockClock(autojump_threshold=0.1)\n    mock_clock.autojump_threshold = 0.01\n    real_start = time.perf_counter()\n    _core.run(sleep, 10000, clock=mock_clock)\n    assert time.perf_counter() - real_start < 1\n\n\nasync def test_mock_clock_autojump_0_and_wait_all_tasks_blocked_0(\n    mock_clock: MockClock,\n) -> None:\n    # Checks that autojump_threshold=0 doesn't interfere with\n    # calling wait_all_tasks_blocked with the default cushion=0.\n\n    mock_clock.autojump_threshold = 0\n\n    record = []\n\n    async def sleeper() -> None:\n        await sleep(100)\n        record.append(\"yawn\")\n\n    async def waiter() -> None:\n        await wait_all_tasks_blocked()\n        record.append(\"waiter woke\")\n        await sleep(1000)\n        record.append(\"waiter done\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(sleeper)\n        nursery.start_soon(waiter)\n\n    assert record == [\"waiter woke\", \"yawn\", \"waiter done\"]\n\n\n@slow\nasync def test_mock_clock_autojump_0_and_wait_all_tasks_blocked_nonzero(\n    mock_clock: MockClock,\n) -> None:\n    # Checks that autojump_threshold=0 doesn't interfere with\n    # calling wait_all_tasks_blocked with a non-zero cushion.\n\n    mock_clock.autojump_threshold = 0\n\n    record = []\n\n    async def sleeper() -> None:\n        await sleep(100)\n        record.append(\"yawn\")\n\n    async def waiter() -> None:\n        await wait_all_tasks_blocked(1)\n        record.append(\"waiter done\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(sleeper)\n        nursery.start_soon(waiter)\n\n    assert record == [\"waiter done\", \"yawn\"]\n\n\nasync def test_initialization_doesnt_mutate_runner() -> None:\n    before = (\n        GLOBAL_RUN_CONTEXT.runner.clock,\n        GLOBAL_RUN_CONTEXT.runner.clock_autojump_threshold,\n    )\n\n    MockClock(autojump_threshold=2, rate=3)\n\n    after = (\n        GLOBAL_RUN_CONTEXT.runner.clock,\n        GLOBAL_RUN_CONTEXT.runner.clock_autojump_threshold,\n    )\n    assert before == after\n"
  },
  {
    "path": "src/trio/_core/_tests/test_parking_lot.py",
    "content": "from __future__ import annotations\n\nimport re\nfrom typing import TypeVar\n\nimport pytest\n\nimport trio\nfrom trio.lowlevel import (\n    add_parking_lot_breaker,\n    current_task,\n    remove_parking_lot_breaker,\n)\n\nfrom ... import _core\nfrom ...testing import wait_all_tasks_blocked\nfrom .._parking_lot import ParkingLot\nfrom .tutil import check_sequence_matches\n\nT = TypeVar(\"T\")\n\n\nasync def test_parking_lot_basic() -> None:\n    record = []\n\n    async def waiter(i: int, lot: ParkingLot) -> None:\n        record.append(f\"sleep {i}\")\n        await lot.park()\n        record.append(f\"wake {i}\")\n\n    async with _core.open_nursery() as nursery:\n        lot = ParkingLot()\n        assert not lot\n        assert len(lot) == 0\n        assert lot.statistics().tasks_waiting == 0\n        for i in range(3):\n            nursery.start_soon(waiter, i, lot)\n        await wait_all_tasks_blocked()\n        assert len(record) == 3\n        assert bool(lot)\n        assert len(lot) == 3\n        assert lot.statistics().tasks_waiting == 3\n        lot.unpark_all()\n        assert lot.statistics().tasks_waiting == 0\n        await wait_all_tasks_blocked()\n        assert len(record) == 6\n\n    check_sequence_matches(\n        record,\n        [{\"sleep 0\", \"sleep 1\", \"sleep 2\"}, {\"wake 0\", \"wake 1\", \"wake 2\"}],\n    )\n\n    async with _core.open_nursery() as nursery:\n        record = []\n        for i in range(3):\n            nursery.start_soon(waiter, i, lot)\n            await wait_all_tasks_blocked()\n        assert len(record) == 3\n        for _ in range(3):\n            lot.unpark()\n            await wait_all_tasks_blocked()\n        # 1-by-1 wakeups are strict FIFO\n        assert record == [\n            \"sleep 0\",\n            \"sleep 1\",\n            \"sleep 2\",\n            \"wake 0\",\n            \"wake 1\",\n            \"wake 2\",\n        ]\n\n    # It's legal (but a no-op) to try and unpark while there's nothing parked\n    lot.unpark()\n    lot.unpark(count=1)\n    lot.unpark(count=100)\n\n    # Check unpark with count\n    async with _core.open_nursery() as nursery:\n        record = []\n        for i in range(3):\n            nursery.start_soon(waiter, i, lot)\n            await wait_all_tasks_blocked()\n        lot.unpark(count=2)\n        await wait_all_tasks_blocked()\n        check_sequence_matches(\n            record,\n            [\"sleep 0\", \"sleep 1\", \"sleep 2\", {\"wake 0\", \"wake 1\"}],\n        )\n        lot.unpark_all()\n\n    with pytest.raises(\n        ValueError,\n        match=r\"^Cannot pop a non-integer number of tasks\\.$\",\n    ):\n        lot.unpark(count=1.5)\n\n\nasync def cancellable_waiter(\n    name: T,\n    lot: ParkingLot,\n    scopes: dict[T, _core.CancelScope],\n    record: list[str],\n) -> None:\n    with _core.CancelScope() as scope:\n        scopes[name] = scope\n        record.append(f\"sleep {name}\")\n        try:\n            await lot.park()\n        except _core.Cancelled:\n            record.append(f\"cancelled {name}\")\n        else:\n            record.append(f\"wake {name}\")\n\n\nasync def test_parking_lot_cancel() -> None:\n    record: list[str] = []\n    scopes: dict[int, _core.CancelScope] = {}\n\n    async with _core.open_nursery() as nursery:\n        lot = ParkingLot()\n        nursery.start_soon(cancellable_waiter, 1, lot, scopes, record)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(cancellable_waiter, 2, lot, scopes, record)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(cancellable_waiter, 3, lot, scopes, record)\n        await wait_all_tasks_blocked()\n        assert len(record) == 3\n\n        scopes[2].cancel()\n        await wait_all_tasks_blocked()\n        assert len(record) == 4\n        lot.unpark_all()\n        await wait_all_tasks_blocked()\n        assert len(record) == 6\n\n    check_sequence_matches(\n        record,\n        [\"sleep 1\", \"sleep 2\", \"sleep 3\", \"cancelled 2\", {\"wake 1\", \"wake 3\"}],\n    )\n\n\nasync def test_parking_lot_repark() -> None:\n    record: list[str] = []\n    scopes: dict[int, _core.CancelScope] = {}\n    lot1 = ParkingLot()\n    lot2 = ParkingLot()\n\n    with pytest.raises(TypeError):\n        lot1.repark([])  # type: ignore[arg-type]\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(cancellable_waiter, 1, lot1, scopes, record)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(cancellable_waiter, 2, lot1, scopes, record)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(cancellable_waiter, 3, lot1, scopes, record)\n        await wait_all_tasks_blocked()\n        assert len(record) == 3\n\n        assert len(lot1) == 3\n        lot1.repark(lot2)\n        assert len(lot1) == 2\n        assert len(lot2) == 1\n        lot2.unpark_all()\n        await wait_all_tasks_blocked()\n        assert len(record) == 4\n        assert record == [\"sleep 1\", \"sleep 2\", \"sleep 3\", \"wake 1\"]\n\n        lot1.repark_all(lot2)\n        assert len(lot1) == 0\n        assert len(lot2) == 2\n\n        scopes[2].cancel()\n        await wait_all_tasks_blocked()\n        assert len(lot2) == 1\n        assert record == [\n            \"sleep 1\",\n            \"sleep 2\",\n            \"sleep 3\",\n            \"wake 1\",\n            \"cancelled 2\",\n        ]\n\n        lot2.unpark_all()\n        await wait_all_tasks_blocked()\n        assert record == [\n            \"sleep 1\",\n            \"sleep 2\",\n            \"sleep 3\",\n            \"wake 1\",\n            \"cancelled 2\",\n            \"wake 3\",\n        ]\n\n\nasync def test_parking_lot_repark_with_count() -> None:\n    record: list[str] = []\n    scopes: dict[int, _core.CancelScope] = {}\n    lot1 = ParkingLot()\n    lot2 = ParkingLot()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(cancellable_waiter, 1, lot1, scopes, record)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(cancellable_waiter, 2, lot1, scopes, record)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(cancellable_waiter, 3, lot1, scopes, record)\n        await wait_all_tasks_blocked()\n        assert len(record) == 3\n\n        assert len(lot1) == 3\n        assert len(lot2) == 0\n        lot1.repark(lot2, count=2)\n        assert len(lot1) == 1\n        assert len(lot2) == 2\n        while lot2:\n            lot2.unpark()\n            await wait_all_tasks_blocked()\n        assert record == [\n            \"sleep 1\",\n            \"sleep 2\",\n            \"sleep 3\",\n            \"wake 1\",\n            \"wake 2\",\n        ]\n        lot1.unpark_all()\n\n\nasync def dummy_task(\n    task_status: _core.TaskStatus[_core.Task] = trio.TASK_STATUS_IGNORED,\n) -> None:\n    task_status.started(_core.current_task())\n    await trio.sleep_forever()\n\n\nasync def test_parking_lot_breaker_basic() -> None:\n    \"\"\"Test basic functionality for breaking lots.\"\"\"\n    lot = ParkingLot()\n    task = current_task()\n\n    # defaults to current task\n    lot.break_lot()\n    assert lot.broken_by == [task]\n\n    # breaking the lot again with the same task appends another copy in `broken_by`\n    lot.break_lot()\n    assert lot.broken_by == [task, task]\n\n    # trying to park in broken lot errors\n    broken_by_str = re.escape(str([task, task]))\n    with pytest.raises(\n        _core.BrokenResourceError,\n        match=f\"^Attempted to park in parking lot broken by {broken_by_str}$\",\n    ):\n        await lot.park()\n\n\nasync def test_parking_lot_break_parking_tasks() -> None:\n    \"\"\"Checks that tasks currently waiting to park raise an error when the breaker exits.\"\"\"\n\n    async def bad_parker(lot: ParkingLot, scope: _core.CancelScope) -> None:\n        add_parking_lot_breaker(current_task(), lot)\n        with scope:\n            await trio.sleep_forever()\n\n    lot = ParkingLot()\n    cs = _core.CancelScope()\n\n    # check that parked task errors\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(_core.BrokenResourceError, match=\"^Parking lot broken by\"),\n    ):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(bad_parker, lot, cs)\n            await wait_all_tasks_blocked()\n\n            nursery.start_soon(lot.park)\n            await wait_all_tasks_blocked()\n\n            cs.cancel()\n\n\nasync def test_parking_lot_breaker_registration() -> None:\n    lot = ParkingLot()\n    task = current_task()\n\n    with pytest.raises(\n        RuntimeError,\n        match=\"Attempted to remove task as breaker for a lot it is not registered for\",\n    ):\n        remove_parking_lot_breaker(task, lot)\n\n    # check that a task can be registered as breaker for the same lot multiple times\n    add_parking_lot_breaker(task, lot)\n    add_parking_lot_breaker(task, lot)\n    remove_parking_lot_breaker(task, lot)\n    remove_parking_lot_breaker(task, lot)\n\n    with pytest.raises(\n        RuntimeError,\n        match=\"Attempted to remove task as breaker for a lot it is not registered for\",\n    ):\n        remove_parking_lot_breaker(task, lot)\n\n    # registering a task as breaker on an already broken lot is fine\n    lot.break_lot()\n    child_task: _core.Task | None = None\n    async with trio.open_nursery() as nursery:\n        child_task = await nursery.start(dummy_task)\n        assert isinstance(child_task, _core.Task)\n        add_parking_lot_breaker(child_task, lot)\n        nursery.cancel_scope.cancel()\n    assert lot.broken_by == [task, child_task]\n\n    # manually breaking a lot with an already exited task is fine\n    lot = ParkingLot()\n    lot.break_lot(child_task)\n    assert lot.broken_by == [child_task]\n\n\nasync def test_parking_lot_breaker_rebreak() -> None:\n    lot = ParkingLot()\n    task = current_task()\n    lot.break_lot()\n\n    # breaking an already broken lot with a different task is allowed\n    # The nursery is only to create a task we can pass to lot.break_lot\n    async with trio.open_nursery() as nursery:\n        child_task = await nursery.start(dummy_task)\n        lot.break_lot(child_task)\n        nursery.cancel_scope.cancel()\n\n    assert lot.broken_by == [task, child_task]\n\n\nasync def test_parking_lot_multiple_breakers_exit() -> None:\n    # register multiple tasks as lot breakers, then have them all exit\n    lot = ParkingLot()\n    async with trio.open_nursery() as nursery:\n        child_task1 = await nursery.start(dummy_task)\n        child_task2 = await nursery.start(dummy_task)\n        child_task3 = await nursery.start(dummy_task)\n        assert isinstance(child_task1, _core.Task)\n        assert isinstance(child_task2, _core.Task)\n        assert isinstance(child_task3, _core.Task)\n        add_parking_lot_breaker(child_task1, lot)\n        add_parking_lot_breaker(child_task2, lot)\n        add_parking_lot_breaker(child_task3, lot)\n        nursery.cancel_scope.cancel()\n\n    # I think the order is guaranteed currently, but doesn't hurt to be safe.\n    assert set(lot.broken_by) == {child_task1, child_task2, child_task3}\n\n\nasync def test_parking_lot_breaker_register_exited_task() -> None:\n    lot = ParkingLot()\n    child_task: _core.Task | None = None\n    async with trio.open_nursery() as nursery:\n        value = await nursery.start(dummy_task)\n        assert isinstance(value, _core.Task)\n        child_task = value\n        nursery.cancel_scope.cancel()\n    # trying to register an exited task as lot breaker errors\n    with pytest.raises(\n        trio.BrokenResourceError,\n        match=r\"^Attempted to add already exited task as lot breaker.$\",\n    ):\n        add_parking_lot_breaker(child_task, lot)\n\n\nasync def test_parking_lot_break_itself() -> None:\n    \"\"\"Break a parking lot, where the breakee is parked.\n    Doing this is weird, but should probably be supported.\n    \"\"\"\n\n    async def return_me_and_park(\n        lot: ParkingLot,\n        *,\n        task_status: _core.TaskStatus[_core.Task] = trio.TASK_STATUS_IGNORED,\n    ) -> None:\n        task_status.started(_core.current_task())\n        await lot.park()\n\n    lot = ParkingLot()\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(_core.BrokenResourceError, match=\"^Parking lot broken by\"),\n    ):\n        async with _core.open_nursery() as nursery:\n            child_task = await nursery.start(return_me_and_park, lot)\n            lot.break_lot(child_task)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_run.py",
    "content": "from __future__ import annotations\n\nimport contextvars\nimport functools\nimport gc\nimport sys\nimport threading\nimport time\nimport types\nimport weakref\nfrom contextlib import (\n    AsyncExitStack,\n    ExitStack,\n    asynccontextmanager,\n    contextmanager,\n    suppress,\n)\nfrom math import inf, nan\nfrom typing import TYPE_CHECKING, NoReturn, TypeVar\nfrom unittest import mock\n\nimport outcome\nimport pytest\nimport sniffio\n\nfrom ... import _core\nfrom ..._threads import to_thread_run_sync\nfrom ..._timeouts import fail_after, sleep\nfrom ...testing import (\n    Sequencer,\n    assert_checkpoints,\n    wait_all_tasks_blocked,\n)\nfrom .._run import DEADLINE_HEAP_MIN_PRUNE_THRESHOLD, _count_context_run_tb_frames\nfrom .tutil import (\n    check_sequence_matches,\n    create_asyncio_future_in_new_loop,\n    gc_collect_harder,\n    ignore_coroutine_never_awaited_warnings,\n    restore_unraisablehook,\n    slow,\n)\n\nif TYPE_CHECKING:\n    from collections.abc import (\n        AsyncGenerator,\n        AsyncIterator,\n        Awaitable,\n        Callable,\n        Generator,\n    )\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup, ExceptionGroup\n\n\nT = TypeVar(\"T\")\n\n\n# slightly different from _timeouts.sleep_forever because it returns the value\n# its rescheduled with, which is really only useful for tests of\n# rescheduling...\nasync def sleep_forever() -> object:\n    return await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)\n\n\ndef not_none(x: T | None) -> T:\n    \"\"\"Assert that this object is not None.\n\n    This is just to satisfy type checkers, if this ever fails the test is broken.\n    \"\"\"\n    assert x is not None\n    return x\n\n\ndef test_basic() -> None:\n    async def trivial(x: T) -> T:\n        return x\n\n    assert _core.run(trivial, 8) == 8\n\n    with pytest.raises(TypeError):\n        # Missing an argument\n        _core.run(trivial)  # type: ignore[arg-type]\n\n    with pytest.raises(TypeError):\n        # Not an async function\n        _core.run(lambda: None)  # type: ignore\n\n    async def trivial2(x: T) -> T:\n        await _core.checkpoint()\n        return x\n\n    assert _core.run(trivial2, 1) == 1\n\n\ndef test_initial_task_error() -> None:\n    async def main(x: object) -> NoReturn:\n        raise ValueError(x)\n\n    with pytest.raises(ValueError, match=r\"^17$\") as excinfo:\n        _core.run(main, 17)\n    assert excinfo.value.args == (17,)\n\n\ndef test_run_nesting() -> None:\n    async def inception() -> None:\n        async def main() -> None:  # pragma: no cover\n            pass\n\n        return _core.run(main)\n\n    with pytest.raises(RuntimeError) as excinfo:\n        _core.run(inception)\n    assert \"from inside\" in str(excinfo.value)\n\n\nasync def test_nursery_warn_use_async_with() -> None:\n    on = _core.open_nursery()\n    with pytest.raises(RuntimeError) as excinfo:\n        with on:  # type: ignore\n            pass  # pragma: no cover\n    excinfo.match(\n        r\"use 'async with open_nursery\\(...\\)', not 'with open_nursery\\(...\\)'\",\n    )\n\n    # avoid unawaited coro.\n    async with on:\n        pass\n\n\nasync def test_nursery_main_block_error_basic() -> None:\n    exc = ValueError(\"whoops\")\n\n    with pytest.RaisesGroup(pytest.RaisesExc(check=lambda e: e is exc)):\n        async with _core.open_nursery():\n            raise exc\n\n\nasync def test_child_crash_basic() -> None:\n    my_exc = ValueError(\"uh oh\")\n\n    async def erroring() -> NoReturn:\n        raise my_exc\n\n    with pytest.RaisesGroup(pytest.RaisesExc(check=lambda e: e is my_exc)):\n        # nursery.__aexit__ propagates exception from child back to parent\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(erroring)\n\n\nasync def test_basic_interleave() -> None:\n    async def looper(whoami: str, record: list[tuple[str, int]]) -> None:\n        for i in range(3):\n            record.append((whoami, i))\n            await _core.checkpoint()\n\n    record: list[tuple[str, int]] = []\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(looper, \"a\", record)\n        nursery.start_soon(looper, \"b\", record)\n\n    check_sequence_matches(\n        record,\n        [{(\"a\", 0), (\"b\", 0)}, {(\"a\", 1), (\"b\", 1)}, {(\"a\", 2), (\"b\", 2)}],\n    )\n\n\ndef test_task_crash_propagation() -> None:\n    looper_record: list[str] = []\n\n    async def looper() -> None:\n        try:\n            while True:\n                await _core.checkpoint()\n        except _core.Cancelled:\n            print(\"looper cancelled\")\n            looper_record.append(\"cancelled\")\n\n    async def crasher() -> NoReturn:\n        raise ValueError(\"argh\")\n\n    async def main() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(looper)\n            nursery.start_soon(crasher)\n\n    with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match=\"^argh$\")):\n        _core.run(main)\n\n    assert looper_record == [\"cancelled\"]\n\n\ndef test_main_and_task_both_crash() -> None:\n    # If main crashes and there's also a task crash, then we get both in an\n    # ExceptionGroup\n    async def crasher() -> NoReturn:\n        raise ValueError\n\n    async def main() -> NoReturn:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(crasher)\n            raise KeyError\n\n    with pytest.RaisesGroup(ValueError, KeyError):\n        _core.run(main)\n\n\ndef test_two_child_crashes() -> None:\n    async def crasher(etype: type[Exception]) -> NoReturn:\n        raise etype\n\n    async def main() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(crasher, KeyError)\n            nursery.start_soon(crasher, ValueError)\n\n    with pytest.RaisesGroup(ValueError, KeyError):\n        _core.run(main)\n\n\nasync def test_child_crash_wakes_parent() -> None:\n    async def crasher() -> NoReturn:\n        raise ValueError(\"this is a crash\")\n\n    with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match=\"^this is a crash$\")):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(crasher)\n            await sleep_forever()\n\n\nasync def test_reschedule() -> None:\n    t1: _core.Task | None = None\n    t2: _core.Task | None = None\n\n    async def child1() -> None:\n        nonlocal t1, t2\n        t1 = _core.current_task()\n        print(\"child1 start\")\n        x = await sleep_forever()\n        print(\"child1 woke\")\n        assert x == 0\n        print(\"child1 rescheduling t2\")\n        _core.reschedule(not_none(t2), outcome.Error(ValueError(\"error message\")))\n        print(\"child1 exit\")\n\n    async def child2() -> None:\n        nonlocal t1, t2\n        print(\"child2 start\")\n        t2 = _core.current_task()\n        _core.reschedule(not_none(t1), outcome.Value(0))\n        print(\"child2 sleep\")\n        with pytest.raises(ValueError, match=r\"^error message$\"):\n            await sleep_forever()\n        print(\"child2 successful exit\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child1)\n        # let t1 run and fall asleep\n        await _core.checkpoint()\n        nursery.start_soon(child2)\n\n\nasync def test_current_time() -> None:\n    t1 = _core.current_time()\n    # Windows clock is pretty low-resolution -- appveyor tests fail unless we\n    # sleep for a bit here.\n    time.sleep(time.get_clock_info(\"perf_counter\").resolution)  # noqa: ASYNC251\n    t2 = _core.current_time()\n    assert t1 < t2\n\n\nasync def test_current_time_with_mock_clock(mock_clock: _core.MockClock) -> None:\n    start = mock_clock.current_time()\n    assert mock_clock.current_time() == _core.current_time()\n    assert mock_clock.current_time() == _core.current_time()\n    mock_clock.jump(3.15)\n    assert start + 3.15 == mock_clock.current_time() == _core.current_time()\n\n\nasync def test_current_clock(mock_clock: _core.MockClock) -> None:\n    assert mock_clock is _core.current_clock()\n\n\nasync def test_current_task() -> None:\n    parent_task = _core.current_task()\n\n    async def child() -> None:\n        assert not_none(_core.current_task().parent_nursery).parent_task is parent_task\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child)\n\n\nasync def test_root_task() -> None:\n    root = not_none(_core.current_root_task())\n    assert root.parent_nursery is root.eventual_parent_nursery is None\n\n\ndef test_out_of_context() -> None:\n    with pytest.raises(RuntimeError):\n        _core.current_task()\n    with pytest.raises(RuntimeError):\n        _core.current_time()\n\n\nasync def test_current_statistics(mock_clock: _core.MockClock) -> None:\n    # Make sure all the early startup stuff has settled down\n    await wait_all_tasks_blocked()\n\n    # A child that sticks around to make some interesting stats:\n    async def child() -> None:\n        with suppress(_core.Cancelled):\n            await sleep_forever()\n\n    stats = _core.current_statistics()\n    print(stats)\n    # 2 system tasks + us\n    assert stats.tasks_living == 3\n    assert stats.run_sync_soon_queue_size == 0\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child)\n        await wait_all_tasks_blocked()\n        token = _core.current_trio_token()\n        token.run_sync_soon(lambda: None)\n        token.run_sync_soon(lambda: None, idempotent=True)\n        stats = _core.current_statistics()\n        print(stats)\n        # 2 system tasks + us + child\n        assert stats.tasks_living == 4\n        # the exact value here might shift if we change how we do accounting\n        # (currently it only counts tasks that we already know will be\n        # runnable on the next pass), but still useful to at least test the\n        # difference between now and after we wake up the child:\n        assert stats.tasks_runnable == 0\n        assert stats.run_sync_soon_queue_size == 2\n\n        nursery.cancel_scope.cancel()\n        stats = _core.current_statistics()\n        print(stats)\n        assert stats.tasks_runnable == 1\n\n    # Give the child a chance to die and the run_sync_soon a chance to clear\n    await _core.checkpoint()\n    await _core.checkpoint()\n\n    with _core.CancelScope(deadline=_core.current_time() + 5):\n        stats = _core.current_statistics()\n        print(stats)\n        assert stats.seconds_to_next_deadline == 5\n    stats = _core.current_statistics()\n    print(stats)\n    assert stats.seconds_to_next_deadline == inf\n\n\nasync def test_cancel_scope_repr(mock_clock: _core.MockClock) -> None:\n    scope = _core.CancelScope()\n    assert \"unbound\" in repr(scope)\n    with scope:\n        assert \"active\" in repr(scope)\n        scope.deadline = _core.current_time() - 1\n        assert \"deadline is 1.00 seconds ago\" in repr(scope)\n        scope.deadline = _core.current_time() + 10\n        assert \"deadline is 10.00 seconds from now\" in repr(scope)\n        # when not in async context, can't get the current time\n        assert \"deadline\" not in await to_thread_run_sync(repr, scope)\n        scope.cancel()\n        assert \"cancelled\" in repr(scope)\n    assert \"exited\" in repr(scope)\n\n\nasync def test_cancel_scope_validation() -> None:\n    with pytest.raises(\n        ValueError,\n        match=r\"^Cannot specify both a deadline and a relative deadline$\",\n    ):\n        _core.CancelScope(deadline=7, relative_deadline=3)\n\n    with pytest.raises(ValueError, match=r\"^deadline must not be NaN$\"):\n        _core.CancelScope(deadline=nan)\n    with pytest.raises(ValueError, match=r\"^relative deadline must not be NaN$\"):\n        _core.CancelScope(relative_deadline=nan)\n\n    with pytest.raises(ValueError, match=r\"^timeout must be non-negative$\"):\n        _core.CancelScope(relative_deadline=-3)\n\n    scope = _core.CancelScope()\n\n    with pytest.raises(ValueError, match=r\"^deadline must not be NaN$\"):\n        scope.deadline = nan\n    with pytest.raises(ValueError, match=r\"^relative deadline must not be NaN$\"):\n        scope.relative_deadline = nan\n\n    with pytest.raises(ValueError, match=r\"^relative deadline must be non-negative$\"):\n        scope.relative_deadline = -3\n    scope.relative_deadline = 5\n    assert scope.relative_deadline == 5\n\n    # several related tests of CancelScope are implicitly handled by test_timeouts.py\n\n\ndef test_cancel_points() -> None:\n    async def main1() -> None:\n        with _core.CancelScope() as scope:\n            await _core.checkpoint_if_cancelled()\n            scope.cancel()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint_if_cancelled()\n\n    _core.run(main1)\n\n    async def main2() -> None:\n        with _core.CancelScope() as scope:\n            await _core.checkpoint()\n            scope.cancel()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n\n    _core.run(main2)\n\n    async def main3() -> None:\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            with pytest.raises(_core.Cancelled):\n                await sleep_forever()\n\n    _core.run(main3)\n\n    async def main4() -> None:\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            await _core.cancel_shielded_checkpoint()\n            await _core.cancel_shielded_checkpoint()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n\n    _core.run(main4)\n\n\nasync def test_cancel_edge_cases() -> None:\n    with _core.CancelScope() as scope:\n        # Two cancels in a row -- idempotent\n        scope.cancel()\n        scope.cancel()\n        await _core.checkpoint()\n    assert scope.cancel_called\n    assert scope.cancelled_caught\n\n    with _core.CancelScope() as scope:\n        # Check level-triggering\n        scope.cancel()\n        with pytest.raises(_core.Cancelled):\n            await sleep_forever()\n        with pytest.raises(_core.Cancelled):\n            await sleep_forever()\n\n\nasync def test_cancel_scope_exceptiongroup_filtering() -> None:\n    async def crasher() -> NoReturn:\n        raise KeyError\n\n    # This is outside the outer scope, so all the Cancelled\n    # exceptions should have been absorbed, leaving just a regular\n    # KeyError from crasher(), wrapped in an ExceptionGroup\n    with pytest.RaisesGroup(KeyError):\n        with _core.CancelScope() as outer:\n            # Since the outer scope became cancelled before the\n            # nursery block exited, all cancellations inside the\n            # nursery block continue propagating to reach the\n            # outer scope.\n            with pytest.RaisesGroup(\n                _core.Cancelled,\n                _core.Cancelled,\n                _core.Cancelled,\n                KeyError,\n            ) as excinfo:\n                async with _core.open_nursery() as nursery:\n                    # Two children that get cancelled by the nursery scope\n                    nursery.start_soon(sleep_forever)  # t1\n                    nursery.start_soon(sleep_forever)  # t2\n                    nursery.cancel_scope.cancel()\n                    with _core.CancelScope(shield=True):\n                        await wait_all_tasks_blocked()\n                    # One child that gets cancelled by the outer scope\n                    nursery.start_soon(sleep_forever)  # t3\n                    outer.cancel()\n                    # And one that raises a different error\n                    nursery.start_soon(crasher)  # t4\n                # and then our __aexit__ also receives an outer Cancelled\n            # reraise the exception caught by pytest.RaisesGroup for the\n            # CancelScope to handle\n            raise excinfo.value\n\n\nasync def test_precancelled_task() -> None:\n    # a task that gets spawned into an already-cancelled nursery should begin\n    # execution (https://github.com/python-trio/trio/issues/41), but get a\n    # cancelled error at its first blocking call.\n    record: list[str] = []\n\n    async def blocker() -> None:\n        record.append(\"started\")\n        await sleep_forever()\n\n    async with _core.open_nursery() as nursery:\n        nursery.cancel_scope.cancel()\n        nursery.start_soon(blocker)\n    assert record == [\"started\"]\n\n\nasync def test_cancel_shielding() -> None:\n    with _core.CancelScope() as outer:\n        with _core.CancelScope() as inner:\n            await _core.checkpoint()\n            outer.cancel()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n\n            assert inner.shield is False\n            with pytest.raises(TypeError):\n                inner.shield = \"hello\"  # type: ignore\n            assert inner.shield is False\n\n            inner.shield = True\n            assert inner.shield is True\n            # shield protects us from 'outer'\n            await _core.checkpoint()\n\n            with _core.CancelScope() as innerest:\n                innerest.cancel()\n                # but it doesn't protect us from scope inside inner\n                with pytest.raises(_core.Cancelled):\n                    await _core.checkpoint()\n            await _core.checkpoint()\n\n            inner.shield = False\n            # can disable shield again\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n\n            # re-enable shield\n            inner.shield = True\n            await _core.checkpoint()\n            # shield doesn't protect us from inner itself\n            inner.cancel()\n            # This should now raise, but be absorbed by the inner scope\n            await _core.checkpoint()\n        assert inner.cancelled_caught\n\n\n# make sure that cancellation propagates immediately to all children\nasync def test_cancel_inheritance() -> None:\n    record: set[str] = set()\n\n    async def leaf(ident: str) -> None:\n        try:\n            await sleep_forever()\n        except _core.Cancelled:\n            record.add(ident)\n\n    async def worker(ident: str) -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(leaf, ident + \"-l1\")\n            nursery.start_soon(leaf, ident + \"-l2\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(worker, \"w1\")\n        nursery.start_soon(worker, \"w2\")\n        nursery.cancel_scope.cancel()\n\n    assert record == {\"w1-l1\", \"w1-l2\", \"w2-l1\", \"w2-l2\"}\n\n\nasync def test_cancel_shield_abort() -> None:\n    with _core.CancelScope() as outer:\n        async with _core.open_nursery() as nursery:\n            outer.cancel()\n            nursery.cancel_scope.shield = True\n            # The outer scope is cancelled, but this task is protected by the\n            # shield, so it manages to get to sleep\n            record = []\n\n            async def sleeper() -> None:\n                record.append(\"sleeping\")\n                try:\n                    await sleep_forever()\n                except _core.Cancelled:\n                    record.append(\"cancelled\")\n\n            nursery.start_soon(sleeper)\n            await wait_all_tasks_blocked()\n            assert record == [\"sleeping\"]\n            # now when we unshield, it should abort the sleep.\n            nursery.cancel_scope.shield = False\n            # wait for the task to finish before entering the nursery\n            # __aexit__, because __aexit__ could make it spuriously look like\n            # this worked by cancelling the nursery scope. (When originally\n            # written, without these last few lines, the test spuriously\n            # passed, even though shield assignment was buggy.)\n            with _core.CancelScope(shield=True):\n                await wait_all_tasks_blocked()\n                assert record == [\"sleeping\", \"cancelled\"]\n\n\nasync def test_basic_timeout(mock_clock: _core.MockClock) -> None:\n    start = _core.current_time()\n    with _core.CancelScope() as scope:\n        assert scope.deadline == inf\n        scope.deadline = start + 1\n        assert scope.deadline == start + 1\n    assert not scope.cancel_called\n    mock_clock.jump(2)\n    await _core.checkpoint()\n    await _core.checkpoint()\n    await _core.checkpoint()\n    assert not scope.cancel_called\n\n    start = _core.current_time()\n    with _core.CancelScope(deadline=start + 1) as scope:\n        mock_clock.jump(2)\n        await sleep_forever()\n    # But then the scope swallowed the exception... but we can still see it\n    # here:\n    assert scope.cancel_called\n    assert scope.cancelled_caught\n\n    # changing deadline\n    start = _core.current_time()\n    with _core.CancelScope() as scope:\n        await _core.checkpoint()\n        scope.deadline = start + 10\n        await _core.checkpoint()\n        mock_clock.jump(5)\n        await _core.checkpoint()\n        scope.deadline = start + 1\n        with pytest.raises(_core.Cancelled):\n            await _core.checkpoint()\n        with pytest.raises(_core.Cancelled):\n            await _core.checkpoint()\n\n\nasync def test_cancel_scope_nesting() -> None:\n    # Nested scopes: if two triggering at once, the outer one wins\n    with _core.CancelScope() as scope1:\n        with _core.CancelScope() as scope2:\n            with _core.CancelScope() as scope3:\n                scope3.cancel()\n                scope2.cancel()\n                await sleep_forever()\n    assert scope3.cancel_called\n    assert not scope3.cancelled_caught\n    assert scope2.cancel_called\n    assert scope2.cancelled_caught\n    assert not scope1.cancel_called\n    assert not scope1.cancelled_caught\n\n    # shielding\n    with _core.CancelScope() as scope1:\n        with _core.CancelScope() as scope2:\n            scope1.cancel()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n            scope2.shield = True\n            await _core.checkpoint()\n            scope2.cancel()\n            with pytest.raises(_core.Cancelled):\n                await _core.checkpoint()\n\n    # if a scope is pending, but then gets popped off the stack, then it\n    # isn't delivered\n    with _core.CancelScope() as scope:\n        scope.cancel()\n        await _core.cancel_shielded_checkpoint()\n    await _core.checkpoint()\n    assert not scope.cancelled_caught\n\n\n# Regression test for https://github.com/python-trio/trio/issues/1175\nasync def test_unshield_while_cancel_propagating() -> None:\n    with _core.CancelScope() as outer:\n        with _core.CancelScope() as inner:\n            outer.cancel()\n            try:\n                await _core.checkpoint()\n            finally:\n                inner.shield = True\n    assert outer.cancelled_caught\n    assert not inner.cancelled_caught\n\n\nasync def test_cancel_unbound() -> None:\n    async def sleep_until_cancelled(scope: _core.CancelScope) -> None:\n        with scope, fail_after(1):\n            await sleep_forever()\n\n    # Cancel before entry\n    scope = _core.CancelScope()\n    scope.cancel()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(sleep_until_cancelled, scope)\n\n    # Cancel after entry\n    scope = _core.CancelScope()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(sleep_until_cancelled, scope)\n        await wait_all_tasks_blocked()\n        scope.cancel()\n\n    # Shield before entry\n    scope = _core.CancelScope()\n    scope.shield = True\n    with _core.CancelScope() as outer, scope:\n        outer.cancel()\n        await _core.checkpoint()\n        scope.shield = False\n        with pytest.raises(_core.Cancelled):\n            await _core.checkpoint()\n\n    # Can't reuse\n    with _core.CancelScope() as scope:\n        await _core.checkpoint()\n    scope.cancel()\n    await _core.checkpoint()\n    assert scope.cancel_called\n    assert not scope.cancelled_caught\n    with pytest.raises(RuntimeError) as exc_info:\n        with scope:\n            pass  # pragma: no cover\n    assert \"single 'with' block\" in str(exc_info.value)\n\n    # Can't reenter\n    with _core.CancelScope() as scope:\n        with pytest.raises(RuntimeError) as exc_info:\n            with scope:\n                pass  # pragma: no cover\n        assert \"single 'with' block\" in str(exc_info.value)\n\n    # Can't enter from multiple tasks simultaneously\n    scope = _core.CancelScope()\n\n    async def enter_scope() -> None:\n        with scope:\n            await sleep_forever()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(enter_scope, name=\"this one\")\n        await wait_all_tasks_blocked()\n\n        with pytest.raises(RuntimeError) as exc_info:\n            with scope:\n                pass  # pragma: no cover\n        assert \"single 'with' block\" in str(exc_info.value)\n        nursery.cancel_scope.cancel()\n\n    # If not yet entered, cancel_called is true when the deadline has passed\n    # even if cancel() hasn't been called yet\n    scope = _core.CancelScope(deadline=_core.current_time() + 1)\n    assert not scope.cancel_called\n    scope.deadline -= 1\n    assert scope.cancel_called\n    scope.deadline += 1\n    assert scope.cancel_called  # never become un-cancelled\n\n\nasync def test_cancel_scope_misnesting_1() -> None:\n    outer = _core.CancelScope()\n    inner = _core.CancelScope()\n    with ExitStack() as stack:\n        stack.enter_context(outer)\n        with inner:\n            with pytest.raises(RuntimeError, match=\"still within its child\"):\n                stack.close()\n        # No further error is raised when exiting the inner context\n\n\nasync def test_cancel_scope_misnesting_2() -> None:\n    # If there are other tasks inside the abandoned part of the cancel tree,\n    # they get cancelled when the misnesting is detected\n    async def task1() -> None:\n        with pytest.raises(_core.Cancelled):\n            await sleep_forever()\n\n    # Even if inside another cancel scope\n    async def task2() -> None:\n        with _core.CancelScope():\n            with pytest.raises(\n                _core.Cancelled,\n                match=r\"^cancelled due to unknown with reason 'misnesting'$\",\n            ):\n                await sleep_forever()\n\n    with ExitStack() as stack:\n        stack.enter_context(_core.CancelScope())\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(task1)\n            nursery.start_soon(task2)\n            await wait_all_tasks_blocked()\n            with pytest.raises(RuntimeError, match=\"still within its child\"):\n                stack.close()\n\n    # Variant that makes the child tasks direct children of the scope\n    # that noticed the misnesting:\n    nursery_mgr = _core.open_nursery()\n    nursery = await nursery_mgr.__aenter__()\n    try:\n        nursery.start_soon(task1)\n        nursery.start_soon(task2)\n        nursery.start_soon(sleep_forever)\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.__exit__(None, None, None)\n    finally:\n        with pytest.raises(\n            RuntimeError,\n            match=\"which had already been exited\",\n        ) as exc_info:\n            await nursery_mgr.__aexit__(*sys.exc_info())\n\n    def no_context(exc: RuntimeError) -> bool:\n        return exc.__context__ is None\n\n    msg = \"closed before the task exited\"\n    group = pytest.RaisesGroup(\n        pytest.RaisesExc(RuntimeError, match=msg, check=no_context),\n        pytest.RaisesExc(RuntimeError, match=msg, check=no_context),\n        # sleep_forever\n        pytest.RaisesExc(\n            RuntimeError,\n            match=msg,\n            check=lambda x: isinstance(x.__context__, _core.Cancelled),\n        ),\n    )\n    assert group.matches(exc_info.value.__context__)\n\n\nasync def test_cancel_scope_misnesting_3() -> None:\n    # Trying to exit a cancel scope from an unrelated task raises an error\n    # without affecting any state\n    async def task3(task_status: _core.TaskStatus[_core.CancelScope]) -> None:\n        with _core.CancelScope() as scope:\n            task_status.started(scope)\n            await sleep_forever()\n\n    async with _core.open_nursery() as nursery:\n        value = await nursery.start(task3)\n        assert isinstance(value, _core.CancelScope)\n        scope: _core.CancelScope = value\n        with pytest.raises(RuntimeError, match=\"from unrelated\"):\n            scope.__exit__(None, None, None)\n        scope.cancel()\n\n\n# helper to check we're not outputting overly verbose tracebacks\ndef no_cause_or_context(e: BaseException) -> bool:\n    return e.__cause__ is None and e.__context__ is None\n\n\nasync def test_nursery_misnest() -> None:\n    # See https://github.com/python-trio/trio/issues/3298\n    async def inner_func() -> None:\n        inner_nursery = await inner_cm.__aenter__()\n        inner_nursery.start_soon(sleep, 1)\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(\n            RuntimeError, match=\"Nursery stack corrupted\", check=no_cause_or_context\n        ),\n        check=no_cause_or_context,\n    ):\n        async with _core.open_nursery() as outer_nursery:\n            inner_cm = _core.open_nursery()\n            outer_nursery.start_soon(inner_func)\n\n\ndef test_nursery_nested_child_misnest() -> None:\n    # Note that this example does *not* raise an exception group.\n    async def main() -> None:\n        async with _core.open_nursery():\n            inner_cm = _core.open_nursery()\n            await inner_cm.__aenter__()\n\n    with pytest.raises(RuntimeError, match=\"Nursery stack corrupted\") as excinfo:\n        _core.run(main)\n    assert excinfo.value.__cause__ is None\n    # This AssertionError is kind of redundant, but I don't think we want to remove\n    # the assertion and don't think we care enough to suppress it in this specific case.\n    assert pytest.RaisesExc(\n        AssertionError, match=\"^Nursery misnesting detected!$\"\n    ).matches(excinfo.value.__context__)\n    assert excinfo.value.__context__.__cause__ is None\n    assert excinfo.value.__context__.__context__ is None\n\n\nasync def test_asyncexitstack_nursery_misnest() -> None:\n    # This example is trickier than the above ones, and is the one that requires\n    # special logic of abandoned nurseries to avoid nasty internal errors that masks\n    # the RuntimeError.\n    @asynccontextmanager\n    async def asynccontextmanager_that_creates_a_nursery_internally() -> (\n        AsyncGenerator[None]\n    ):\n        async with _core.open_nursery() as nursery:\n            await nursery.start(started_sleeper)\n            nursery.start_soon(unstarted_task)\n            yield\n\n    async def started_sleeper(task_status: _core.TaskStatus[None]) -> None:\n        task_status.started()\n        await sleep_forever()\n\n    async def unstarted_task() -> None:\n        await _core.checkpoint()\n\n    with pytest.RaisesGroup(\n        pytest.RaisesGroup(\n            pytest.RaisesExc(\n                RuntimeError, match=\"Nursery stack corrupted\", check=no_cause_or_context\n            ),\n            check=no_cause_or_context,\n        ),\n        check=no_cause_or_context,\n    ):\n        async with AsyncExitStack() as stack, _core.open_nursery() as nursery:\n            # The asynccontextmanager is going to create a nursery that outlives this nursery!\n            nursery.start_soon(\n                stack.enter_async_context,\n                asynccontextmanager_that_creates_a_nursery_internally(),\n            )\n\n\ndef test_asyncexitstack_nursery_misnest_cleanup() -> None:\n    # We guarantee that abandoned tasks get to do cleanup *eventually*, but exceptions\n    # are lost. With more effort it's possible we could reschedule child tasks to exit\n    # promptly.\n    finally_entered = []\n\n    async def main() -> None:\n        async def unstarted_task() -> None:\n            try:\n                await _core.checkpoint()\n            finally:\n                finally_entered.append(True)\n                raise ValueError(\"this exception is lost\")\n\n        # rest of main() is ~identical to the above test\n        @asynccontextmanager\n        async def asynccontextmanager_that_creates_a_nursery_internally() -> (\n            AsyncGenerator[None]\n        ):\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(unstarted_task)\n                yield\n\n        with pytest.RaisesGroup(\n            pytest.RaisesGroup(\n                pytest.RaisesExc(\n                    RuntimeError,\n                    match=\"Nursery stack corrupted\",\n                    check=no_cause_or_context,\n                ),\n                check=no_cause_or_context,\n            ),\n            check=no_cause_or_context,\n        ):\n            async with AsyncExitStack() as stack, _core.open_nursery() as nursery:\n                # The asynccontextmanager is going to create a nursery that outlives this nursery!\n                nursery.start_soon(\n                    stack.enter_async_context,\n                    asynccontextmanager_that_creates_a_nursery_internally(),\n                )\n        assert not finally_entered  # abandoned task still hasn't been cleaned up\n\n    _core.run(main)\n    assert finally_entered  # now it has\n\n\n@slow\nasync def test_timekeeping() -> None:\n    # probably a good idea to use a real clock for *one* test anyway...\n    TARGET = 1.0\n    # give it a few tries in case of random CI server flakiness\n    for _ in range(4):\n        real_start = time.perf_counter()\n        with _core.CancelScope() as scope:\n            scope.deadline = _core.current_time() + TARGET\n            await sleep_forever()\n        real_duration = time.perf_counter() - real_start\n        accuracy = real_duration / TARGET\n        print(accuracy)\n        # Actual time elapsed should always be >= target time\n        # (== is possible depending on system behavior for time.perf_counter resolution\n        if 1.0 <= accuracy < 2:  # pragma: no branch\n            break\n    else:  # pragma: no cover\n        raise AssertionError()\n\n\nasync def test_failed_abort() -> None:\n    stubborn_task: _core.Task | None = None\n    stubborn_scope: _core.CancelScope | None = None\n    record: list[str] = []\n\n    async def stubborn_sleeper() -> None:\n        nonlocal stubborn_task, stubborn_scope\n        stubborn_task = _core.current_task()\n        with _core.CancelScope() as scope:\n            stubborn_scope = scope\n            record.append(\"sleep\")\n            x = await _core.wait_task_rescheduled(lambda _: _core.Abort.FAILED)\n            assert x == 1\n            record.append(\"woke\")\n            try:\n                await _core.checkpoint_if_cancelled()\n            except _core.Cancelled:\n                record.append(\"cancelled\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(stubborn_sleeper)\n        await wait_all_tasks_blocked()\n        assert record == [\"sleep\"]\n        not_none(stubborn_scope).cancel()\n        await wait_all_tasks_blocked()\n        # cancel didn't wake it up\n        assert record == [\"sleep\"]\n        # wake it up again by hand\n        _core.reschedule(not_none(stubborn_task), outcome.Value(1))\n    assert record == [\"sleep\", \"woke\", \"cancelled\"]\n\n\n@restore_unraisablehook()\ndef test_broken_abort() -> None:\n    async def main() -> None:\n        # These yields are here to work around an annoying warning -- we're\n        # going to crash the main loop, and if we (by chance) do this before\n        # the run_sync_soon task runs for the first time, then Python gives us\n        # a spurious warning about it not being awaited. (I mean, the warning\n        # is correct, but here we're testing our ability to deliver a\n        # semi-meaningful error after things have gone totally pear-shaped, so\n        # it's not relevant.) By letting the run_sync_soon_task run first, we\n        # avoid the warning.\n        await _core.checkpoint()\n        await _core.checkpoint()\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            # None is not a legal return value here\n            await _core.wait_task_rescheduled(lambda _: None)  # type: ignore\n\n    with pytest.raises(_core.TrioInternalError):\n        _core.run(main)\n\n    # Because this crashes, various __del__ methods print complaints on\n    # stderr. Make sure that they get run now, so the output is attached to\n    # this test.\n    gc_collect_harder()\n\n\n# This segfaults, so we need to skipif. Remember to remove the skipif once\n# the upstream issue is resolved.\n@restore_unraisablehook()\n@pytest.mark.skipif(\n    sys.version_info[:2] == (3, 14),\n    reason=\"https://github.com/python/cpython/issues/133932\",\n)\ndef test_error_in_run_loop() -> None:\n    # Blow stuff up real good to check we at least get a TrioInternalError\n    async def main() -> None:\n        task = _core.current_task()\n        task._schedule_points = \"hello!\"  # type: ignore\n        await _core.checkpoint()\n\n    with ignore_coroutine_never_awaited_warnings():\n        with pytest.raises(_core.TrioInternalError):\n            _core.run(main)\n\n\nasync def test_spawn_system_task() -> None:\n    record: list[tuple[str, int]] = []\n\n    async def system_task(x: int) -> None:\n        record.append((\"x\", x))\n        record.append((\"ki\", _core.currently_ki_protected()))\n        await _core.checkpoint()\n\n    _core.spawn_system_task(system_task, 1)\n    await wait_all_tasks_blocked()\n    assert record == [(\"x\", 1), (\"ki\", True)]\n\n\n# intentionally make a system task crash\ndef test_system_task_crash() -> None:\n    async def crasher() -> NoReturn:\n        raise KeyError\n\n    async def main() -> None:\n        _core.spawn_system_task(crasher)\n        await sleep_forever()\n\n    with pytest.raises(_core.TrioInternalError):\n        _core.run(main)\n\n\ndef test_system_task_crash_ExceptionGroup() -> None:\n    async def crasher1() -> NoReturn:\n        raise KeyError\n\n    async def crasher2() -> NoReturn:\n        raise ValueError\n\n    async def system_task() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(crasher1)\n            nursery.start_soon(crasher2)\n\n    async def main() -> None:\n        _core.spawn_system_task(system_task)\n        await sleep_forever()\n\n    # TrioInternalError is not wrapped\n    with pytest.raises(_core.TrioInternalError) as excinfo:\n        _core.run(main)\n\n    # the first exceptiongroup is from the first nursery opened in Runner.init()\n    # the second exceptiongroup is from the second nursery opened in Runner.init()\n    # the third exceptongroup is from the nursery defined in `system_task` above\n    assert pytest.RaisesGroup(\n        pytest.RaisesGroup(pytest.RaisesGroup(KeyError, ValueError))\n    ).matches(\n        excinfo.value.__cause__,\n    )\n\n\ndef test_system_task_crash_plus_Cancelled() -> None:\n    # Set up a situation where a system task crashes with a\n    # ExceptionGroup([Cancelled, ValueError])\n    async def crasher() -> None:\n        try:\n            await sleep_forever()\n        except _core.Cancelled:\n            raise ValueError from None\n\n    async def cancelme() -> None:\n        await sleep_forever()\n\n    async def system_task() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(crasher)\n            nursery.start_soon(cancelme)\n\n    async def main() -> None:\n        _core.spawn_system_task(system_task)\n        # then we exit, triggering a cancellation\n\n    with pytest.raises(_core.TrioInternalError) as excinfo:\n        _core.run(main)\n\n    # See explanation for triple-wrap in test_system_task_crash_ExceptionGroup\n    assert pytest.RaisesGroup(\n        pytest.RaisesGroup(pytest.RaisesGroup(ValueError))\n    ).matches(\n        excinfo.value.__cause__,\n    )\n\n\ndef test_system_task_crash_KeyboardInterrupt() -> None:\n    async def ki() -> NoReturn:\n        raise KeyboardInterrupt\n\n    async def main() -> None:\n        _core.spawn_system_task(ki)\n        await sleep_forever()\n\n    with pytest.raises(_core.TrioInternalError) as excinfo:\n        _core.run(main)\n    # \"Only\" double-wrapped since ki() doesn't create an exceptiongroup\n    assert pytest.RaisesGroup(pytest.RaisesGroup(KeyboardInterrupt)).matches(\n        excinfo.value.__cause__\n    )\n\n\n# This used to fail because checkpoint was a yield followed by an immediate\n# reschedule. So we had:\n# 1) this task yields\n# 2) this task is rescheduled\n# ...\n# 3) next iteration of event loop starts, runs timeouts\n# 4) this task has timed out\n# 5) ...but it's on the run queue, so the timeout is queued to be delivered\n#    the next time that it's blocked.\nasync def test_yield_briefly_checks_for_timeout(mock_clock: _core.MockClock) -> None:\n    with _core.CancelScope(deadline=_core.current_time() + 5):\n        await _core.checkpoint()\n        mock_clock.jump(10)\n        with pytest.raises(_core.Cancelled):\n            await _core.checkpoint()\n\n\n# This tests that sys.exc_info is properly saved/restored as we swap between\n# tasks. It turns out that the interpreter automagically handles this for us\n# so there's no special code in Trio required to pass this test, but it's\n# still nice to know that it works :-).\n#\n# Update: it turns out I was right to be nervous! see the next test...\nasync def test_exc_info() -> None:\n    record: list[str] = []\n    seq = Sequencer()\n\n    async def child1() -> None:\n        async with seq(0):\n            pass  # we don't yield until seq(2) below\n        record.append(\"child1 raise\")\n        with pytest.raises(ValueError, match=r\"^child1$\") as excinfo:\n            try:\n                raise ValueError(\"child1\")\n            except ValueError:\n                record.append(\"child1 sleep\")\n                async with seq(2):\n                    pass\n                assert \"child2 wake\" in record\n                record.append(\"child1 re-raise\")\n                raise\n        assert excinfo.value.__context__ is None\n        record.append(\"child1 success\")\n\n    async def child2() -> None:\n        async with seq(1):\n            pass  # we don't yield until seq(3) below\n        assert \"child1 sleep\" in record\n        record.append(\"child2 wake\")\n        assert sys.exc_info() == (None, None, None)\n        with pytest.raises(KeyError) as excinfo:\n            try:\n                raise KeyError(\"child2\")\n            except KeyError:\n                record.append(\"child2 sleep again\")\n                async with seq(3):\n                    pass\n                assert \"child1 re-raise\" in record\n                record.append(\"child2 re-raise\")\n                raise\n        assert excinfo.value.__context__ is None\n        record.append(\"child2 success\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child1)\n        nursery.start_soon(child2)\n\n    assert record == [\n        \"child1 raise\",\n        \"child1 sleep\",\n        \"child2 wake\",\n        \"child2 sleep again\",\n        \"child1 re-raise\",\n        \"child1 success\",\n        \"child2 re-raise\",\n        \"child2 success\",\n    ]\n\n\n# On all CPython versions (at time of writing), using .throw() to raise an\n# exception inside a coroutine/generator can cause the original `exc_info` state\n# to be lost, so things like re-raising and exception chaining are broken unless\n# Trio implements its workaround. At time of writing, CPython main (3.13-dev)\n# and every CPython release (excluding releases for old Python versions not\n# supported by Trio) is affected (albeit in differing ways).\n#\n# If the `ValueError()` gets sent in via `throw` and is suppressed, then CPython\n# loses track of the original `exc_info`:\n#   https://bugs.python.org/issue25612 (Example 1)\n#   https://bugs.python.org/issue29587 (Example 2)\n# This is fixed in CPython >= 3.7.\nasync def test_exc_info_after_throw_suppressed() -> None:\n    child_task: _core.Task | None = None\n\n    async def child() -> None:\n        nonlocal child_task\n        child_task = _core.current_task()\n\n        try:\n            raise KeyError\n        except KeyError:\n            with suppress(ValueError):\n                await sleep_forever()\n            raise\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(KeyError, check=lambda e: e.__context__ is None)\n    ):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(child)\n            await wait_all_tasks_blocked()\n            _core.reschedule(not_none(child_task), outcome.Error(ValueError()))\n\n\n# Similar to previous test -- if the `ValueError()` gets sent in via 'throw' and\n# propagates out, then CPython doesn't set its `__context__` so normal implicit\n# exception chaining is broken:\n#   https://bugs.python.org/issue25612 (Example 2)\n#   https://bugs.python.org/issue25683\n#   https://bugs.python.org/issue29587 (Example 1)\n# This is fixed in CPython >= 3.9, but kept as a regression test.\nasync def test_exception_chaining_after_throw() -> None:\n    child_task: _core.Task | None = None\n\n    async def child() -> None:\n        nonlocal child_task\n        child_task = _core.current_task()\n\n        try:\n            raise KeyError\n        except KeyError:\n            await sleep_forever()\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(\n            ValueError,\n            match=\"error text\",\n            check=lambda e: isinstance(e.__context__, KeyError),\n        ),\n    ):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(child)\n            await wait_all_tasks_blocked()\n            _core.reschedule(\n                not_none(child_task),\n                outcome.Error(ValueError(\"error text\")),\n            )\n\n\n# Similar to previous tests -- if the `ValueError()` gets sent into an inner\n# `await` via 'throw' and is suppressed there, then CPython loses track of\n# `exc_info` in the inner coroutine:\n#   https://github.com/python/cpython/issues/108668\n# This is unfixed in CPython at time of writing.\nasync def test_exc_info_after_throw_to_inner_suppressed() -> None:\n    child_task: _core.Task | None = None\n\n    async def child() -> None:\n        nonlocal child_task\n        child_task = _core.current_task()\n\n        try:\n            raise KeyError\n        except KeyError as exc:\n            await inner(exc)\n            raise\n\n    async def inner(exc: BaseException) -> None:\n        with suppress(ValueError):\n            await sleep_forever()\n        assert not_none(sys.exc_info()[1]) is exc\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(KeyError, check=lambda e: e.__context__ is None)\n    ):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(child)\n            await wait_all_tasks_blocked()\n            _core.reschedule(not_none(child_task), outcome.Error(ValueError()))\n\n\n# Similar to previous tests -- if the `ValueError()` gets sent into an inner\n# `await` via `throw` and propagates out, then CPython incorrectly sets its\n# `__context__` so normal implicit exception chaining is broken:\n#   https://bugs.python.org/issue40694\n# This is unfixed in CPython at time of writing.\nasync def test_exception_chaining_after_throw_to_inner() -> None:\n    child_task: _core.Task | None = None\n\n    async def child() -> None:\n        nonlocal child_task\n        child_task = _core.current_task()\n\n        try:\n            raise KeyError\n        except KeyError:\n            await inner()\n\n    async def inner() -> None:\n        try:\n            raise IndexError\n        except IndexError:\n            await sleep_forever()\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(\n            ValueError,\n            match=\"^Unique Text$\",\n            check=lambda e: isinstance(e.__context__, IndexError)\n            and isinstance(e.__context__.__context__, KeyError),\n        ),\n    ):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(child)\n            await wait_all_tasks_blocked()\n            _core.reschedule(\n                not_none(child_task),\n                outcome.Error(ValueError(\"Unique Text\")),\n            )\n\n\nasync def test_nursery_exception_chaining_doesnt_make_context_loops() -> None:\n    async def crasher() -> NoReturn:\n        raise KeyError\n\n    # the ExceptionGroup should not have the KeyError or ValueError as context\n    with pytest.RaisesGroup(\n        ValueError, KeyError, check=lambda x: x.__context__ is None\n    ):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(crasher)\n            raise ValueError\n\n\ndef test_TrioToken_identity() -> None:\n    async def get_and_check_token() -> _core.TrioToken:\n        token = _core.current_trio_token()\n        # Two calls in the same run give the same object\n        assert token is _core.current_trio_token()\n        return token\n\n    t1 = _core.run(get_and_check_token)\n    t2 = _core.run(get_and_check_token)\n    assert t1 is not t2\n    assert t1 != t2\n    assert hash(t1) != hash(t2)\n\n\nasync def test_TrioToken_run_sync_soon_basic() -> None:\n    record: list[tuple[str, int]] = []\n\n    def cb(x: int) -> None:\n        record.append((\"cb\", x))\n\n    token = _core.current_trio_token()\n    token.run_sync_soon(cb, 1)\n    assert not record\n    await wait_all_tasks_blocked()\n    assert record == [(\"cb\", 1)]\n\n\ndef test_TrioToken_run_sync_soon_too_late() -> None:\n    token: _core.TrioToken | None = None\n\n    async def main() -> None:\n        nonlocal token\n        token = _core.current_trio_token()\n\n    _core.run(main)\n    with pytest.raises(_core.RunFinishedError):\n        not_none(token).run_sync_soon(lambda: None)  # pragma: no branch\n\n\nasync def test_TrioToken_run_sync_soon_idempotent() -> None:\n    record: list[int] = []\n\n    def cb(x: int) -> None:\n        record.append(x)\n\n    token = _core.current_trio_token()\n    token.run_sync_soon(cb, 1)\n    token.run_sync_soon(cb, 1, idempotent=True)\n    token.run_sync_soon(cb, 1, idempotent=True)\n    token.run_sync_soon(cb, 1, idempotent=True)\n    token.run_sync_soon(cb, 2, idempotent=True)\n    token.run_sync_soon(cb, 2, idempotent=True)\n    await wait_all_tasks_blocked()\n    assert len(record) == 3\n    assert sorted(record) == [1, 1, 2]\n\n    # ordering test\n    record = []\n    for _ in range(3):\n        for i in range(100):\n            token.run_sync_soon(cb, i, idempotent=True)\n    await wait_all_tasks_blocked()\n    # We guarantee FIFO\n    assert record == list(range(100))\n\n\ndef test_TrioToken_run_sync_soon_idempotent_requeue() -> None:\n    # We guarantee that if a call has finished, queueing it again will call it\n    # again. Due to the lack of synchronization, this effectively means that\n    # we have to guarantee that once a call has *started*, queueing it again\n    # will call it again. Also this is much easier to test :-)\n    record: list[None] = []\n\n    def redo(token: _core.TrioToken) -> None:\n        record.append(None)\n        with suppress(_core.RunFinishedError):\n            token.run_sync_soon(redo, token, idempotent=True)\n\n    async def main() -> None:\n        token = _core.current_trio_token()\n        token.run_sync_soon(redo, token, idempotent=True)\n        await _core.checkpoint()\n        await _core.checkpoint()\n        await _core.checkpoint()\n\n    _core.run(main)\n\n    assert len(record) >= 2\n\n\ndef test_TrioToken_run_sync_soon_after_main_crash() -> None:\n    record: list[str] = []\n\n    async def main() -> None:\n        token = _core.current_trio_token()\n        # After main exits but before finally cleaning up, callback processed\n        # normally\n        token.run_sync_soon(lambda: record.append(\"sync-cb\"))\n        raise ValueError(\"error text\")\n\n    with pytest.raises(ValueError, match=r\"^error text$\"):\n        _core.run(main)\n\n    assert record == [\"sync-cb\"]\n\n\ndef test_TrioToken_run_sync_soon_crashes() -> None:\n    record: set[str] = set()\n\n    async def main() -> None:\n        token = _core.current_trio_token()\n        token.run_sync_soon(lambda: {}[\"nope\"])  # type: ignore[index]\n        # check that a crashing run_sync_soon callback doesn't stop further\n        # calls to run_sync_soon\n        token.run_sync_soon(lambda: record.add(\"2nd run_sync_soon ran\"))\n        try:\n            await sleep_forever()\n        except _core.Cancelled:\n            record.add(\"cancelled!\")\n\n    with pytest.raises(_core.TrioInternalError) as excinfo:\n        _core.run(main)\n    # the first exceptiongroup is from the first nursery opened in Runner.init()\n    # the second exceptiongroup is from the second nursery opened in Runner.init()\n    assert pytest.RaisesGroup(pytest.RaisesGroup(KeyError)).matches(\n        excinfo.value.__cause__\n    )\n    assert record == {\"2nd run_sync_soon ran\", \"cancelled!\"}\n\n\nasync def test_TrioToken_run_sync_soon_FIFO() -> None:\n    N = 100\n    record = []\n    token = _core.current_trio_token()\n    for i in range(N):\n        token.run_sync_soon(lambda j: record.append(j), i)\n    await wait_all_tasks_blocked()\n    assert record == list(range(N))\n\n\ndef test_TrioToken_run_sync_soon_starvation_resistance() -> None:\n    # Even if we push callbacks in from callbacks, so that the callback queue\n    # never empties out, then we still can't starve out other tasks from\n    # running.\n    token: _core.TrioToken | None = None\n    record: list[tuple[str, int]] = []\n\n    def naughty_cb(i: int) -> None:\n        try:\n            not_none(token).run_sync_soon(naughty_cb, i + 1)\n        except _core.RunFinishedError:\n            record.append((\"run finished\", i))\n\n    async def main() -> None:\n        nonlocal token\n        token = _core.current_trio_token()\n        token.run_sync_soon(naughty_cb, 0)\n        record.append((\"starting\", 0))\n        for _ in range(20):\n            await _core.checkpoint()\n\n    _core.run(main)\n    assert len(record) == 2\n    assert record[0] == (\"starting\", 0)\n    assert record[1][0] == \"run finished\"\n    assert record[1][1] >= 19\n\n\ndef test_TrioToken_run_sync_soon_threaded_stress_test() -> None:\n    cb_counter = 0\n\n    def cb() -> None:\n        nonlocal cb_counter\n        cb_counter += 1\n\n    def stress_thread(token: _core.TrioToken) -> None:\n        try:\n            while True:\n                token.run_sync_soon(cb)\n                time.sleep(0)\n        except _core.RunFinishedError:\n            pass\n\n    async def main() -> None:\n        token = _core.current_trio_token()\n        thread = threading.Thread(target=stress_thread, args=(token,))\n        thread.start()\n        for _ in range(10):\n            start_value = cb_counter\n            while cb_counter == start_value:\n                await sleep(0.01)\n\n    _core.run(main)\n    print(cb_counter)\n\n\nasync def test_TrioToken_run_sync_soon_massive_queue() -> None:\n    # There are edge cases in the wakeup fd code when the wakeup fd overflows,\n    # so let's try to make that happen. This is also just a good stress test\n    # in general. (With the current-as-of-2017-02-14 code using a socketpair\n    # with minimal buffer, Linux takes 6 wakeups to fill the buffer and macOS\n    # takes 1 wakeup. So 1000 is overkill if anything. Windows OTOH takes\n    # ~600,000 wakeups, but has the same code paths...)\n    COUNT = 1000\n    token = _core.current_trio_token()\n    counter = [0]\n\n    def cb(i: int) -> None:\n        # This also tests FIFO ordering of callbacks\n        assert counter[0] == i\n        counter[0] += 1\n\n    for i in range(COUNT):\n        token.run_sync_soon(cb, i)\n    await wait_all_tasks_blocked()\n    assert counter[0] == COUNT\n\n\ndef test_TrioToken_run_sync_soon_late_crash() -> None:\n    # Crash after system nursery is closed -- easiest way to do that is\n    # from an async generator finalizer.\n    record: list[str] = []\n    saved: list[AsyncGenerator[int, None]] = []\n\n    async def agen() -> AsyncGenerator[int, None]:\n        token = _core.current_trio_token()\n        try:\n            yield 1\n        finally:\n            token.run_sync_soon(lambda: {}[\"nope\"])  # type: ignore[index]\n            token.run_sync_soon(lambda: record.append(\"2nd ran\"))\n\n    async def main() -> None:\n        saved.append(agen())\n        await saved[-1].asend(None)\n        record.append(\"main exiting\")\n\n    with pytest.raises(_core.TrioInternalError) as excinfo:\n        _core.run(main)\n\n    assert pytest.RaisesGroup(KeyError).matches(excinfo.value.__cause__)\n    assert record == [\"main exiting\", \"2nd ran\"]\n\n\nasync def test_slow_abort_basic() -> None:\n    with _core.CancelScope() as scope:\n        scope.cancel()\n\n        task = _core.current_task()\n        token = _core.current_trio_token()\n\n        def slow_abort(raise_cancel: _core.RaiseCancelT) -> _core.Abort:\n            result = outcome.capture(raise_cancel)\n            token.run_sync_soon(_core.reschedule, task, result)\n            return _core.Abort.FAILED\n\n        with pytest.raises(_core.Cancelled):\n            await _core.wait_task_rescheduled(slow_abort)\n\n\nasync def test_slow_abort_edge_cases() -> None:\n    record: list[str] = []\n\n    async def slow_aborter() -> None:\n        task = _core.current_task()\n        token = _core.current_trio_token()\n\n        def slow_abort(raise_cancel: _core.RaiseCancelT) -> _core.Abort:\n            record.append(\"abort-called\")\n            result = outcome.capture(raise_cancel)\n            token.run_sync_soon(_core.reschedule, task, result)\n            return _core.Abort.FAILED\n\n        record.append(\"sleeping\")\n        with pytest.raises(_core.Cancelled):\n            await _core.wait_task_rescheduled(slow_abort)\n        record.append(\"cancelled\")\n        # blocking again, this time it's okay, because we're shielded\n        await _core.checkpoint()\n        record.append(\"done\")\n\n    with _core.CancelScope() as outer1:\n        with _core.CancelScope() as outer2:\n            async with _core.open_nursery() as nursery:\n                # So we have a task blocked on an operation that can't be\n                # aborted immediately\n                nursery.start_soon(slow_aborter)\n                await wait_all_tasks_blocked()\n                assert record == [\"sleeping\"]\n                # And then we cancel it, so the abort callback gets run\n                outer1.cancel()\n                assert record == [\"sleeping\", \"abort-called\"]\n                # In fact that happens twice! (This used to cause the abort\n                # callback to be run twice)\n                outer2.cancel()\n                assert record == [\"sleeping\", \"abort-called\"]\n                # But then before the abort finishes, the task gets shielded!\n                nursery.cancel_scope.shield = True\n                # Now we wait for the task to finish...\n            # The cancellation was delivered, even though it was shielded\n            assert record == [\"sleeping\", \"abort-called\", \"cancelled\", \"done\"]\n\n\nasync def test_task_tree_introspection() -> None:\n    tasks: dict[str, _core.Task] = {}\n    nurseries: dict[str, _core.Nursery] = {}\n\n    async def parent(\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        tasks[\"parent\"] = _core.current_task()\n\n        assert tasks[\"parent\"].child_nurseries == []\n\n        async with _core.open_nursery() as nursery1:\n            async with _core.open_nursery() as nursery2:\n                assert tasks[\"parent\"].child_nurseries == [nursery1, nursery2]\n\n        assert tasks[\"parent\"].child_nurseries == []\n\n        nursery: _core.Nursery | None\n        async with _core.open_nursery() as nursery:\n            nurseries[\"parent\"] = nursery\n            await nursery.start(child1)\n\n        # Upward links survive after tasks/nurseries exit\n        assert nurseries[\"parent\"].parent_task is tasks[\"parent\"]\n        assert tasks[\"child1\"].parent_nursery is nurseries[\"parent\"]\n        assert nurseries[\"child1\"].parent_task is tasks[\"child1\"]\n        assert tasks[\"child2\"].parent_nursery is nurseries[\"child1\"]\n\n        nursery = _core.current_task().parent_nursery\n        # Make sure that chaining eventually gives a nursery of None (and not,\n        # for example, an error)\n        while nursery is not None:\n            t = nursery.parent_task\n            nursery = t.parent_nursery\n\n    async def child2() -> None:\n        tasks[\"child2\"] = _core.current_task()\n        assert tasks[\"parent\"].child_nurseries == [nurseries[\"parent\"]]\n        assert nurseries[\"parent\"].child_tasks == frozenset({tasks[\"child1\"]})\n        assert tasks[\"child1\"].child_nurseries == [nurseries[\"child1\"]]\n        assert nurseries[\"child1\"].child_tasks == frozenset({tasks[\"child2\"]})\n        assert tasks[\"child2\"].child_nurseries == []\n\n    async def child1(\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        me = tasks[\"child1\"] = _core.current_task()\n        assert not_none(me.parent_nursery).parent_task is tasks[\"parent\"]\n        assert me.parent_nursery is not nurseries[\"parent\"]\n        assert me.eventual_parent_nursery is nurseries[\"parent\"]\n        task_status.started()\n        assert me.parent_nursery is nurseries[\"parent\"]\n        assert me.eventual_parent_nursery is None\n\n        # Wait for the start() call to return and close its internal nursery, to\n        # ensure consistent results in child2:\n        await _core.wait_all_tasks_blocked()\n\n        async with _core.open_nursery() as nursery:\n            nurseries[\"child1\"] = nursery\n            nursery.start_soon(child2)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(parent)\n\n    # There are no pending starts, so no one should have a non-None\n    # eventual_parent_nursery\n    for task in tasks.values():\n        assert task.eventual_parent_nursery is None\n\n\nasync def test_nursery_closure() -> None:\n    async def child1(nursery: _core.Nursery) -> None:\n        # We can add new tasks to the nursery even after entering __aexit__,\n        # so long as there are still tasks running\n        nursery.start_soon(child2)\n\n    async def child2() -> None:\n        pass\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child1, nursery)\n\n    # But once we've left __aexit__, the nursery is closed\n    with pytest.raises(RuntimeError):\n        nursery.start_soon(child2)\n\n\nasync def test_spawn_name() -> None:\n    async def func1(expected: str) -> None:\n        task = _core.current_task()\n        assert expected in task.name\n\n    async def func2() -> None:  # pragma: no cover\n        pass\n\n    async def check(  # type: ignore[explicit-any]\n        spawn_fn: Callable[..., object],\n    ) -> None:\n        spawn_fn(func1, \"func1\")\n        spawn_fn(func1, \"func2\", name=func2)\n        spawn_fn(func1, \"func3\", name=\"func3\")\n        spawn_fn(functools.partial(func1, \"func1\"))\n        spawn_fn(func1, \"object\", name=object())\n\n    async with _core.open_nursery() as nursery:\n        await check(nursery.start_soon)\n    await check(_core.spawn_system_task)\n\n\nasync def test_current_effective_deadline(mock_clock: _core.MockClock) -> None:\n    assert _core.current_effective_deadline() == inf\n\n    with _core.CancelScope(deadline=5) as scope1:\n        with _core.CancelScope(deadline=10) as scope2:\n            assert _core.current_effective_deadline() == 5\n            scope2.deadline = 3\n            assert _core.current_effective_deadline() == 3\n            scope2.deadline = 10\n            assert _core.current_effective_deadline() == 5\n            scope2.shield = True\n            assert _core.current_effective_deadline() == 10\n            scope2.shield = False\n            assert _core.current_effective_deadline() == 5\n            scope1.cancel()\n            assert _core.current_effective_deadline() == -inf\n            scope2.shield = True\n            assert _core.current_effective_deadline() == 10\n        assert _core.current_effective_deadline() == -inf\n    assert _core.current_effective_deadline() == inf\n\n\ndef test_nice_error_on_bad_calls_to_run_or_spawn() -> None:\n    def bad_call_run(  # type: ignore[explicit-any]\n        func: Callable[..., Awaitable[object]],\n        *args: tuple[object, ...],\n    ) -> None:\n        _core.run(func, *args)\n\n    def bad_call_spawn(  # type: ignore[explicit-any]\n        func: Callable[..., Awaitable[object]],\n        *args: tuple[object, ...],\n    ) -> None:\n        async def main() -> None:\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(func, *args)\n\n        _core.run(main)\n\n    async def f() -> None:  # pragma: no cover\n        pass\n\n    async def async_gen(arg: T) -> AsyncGenerator[T, None]:  # pragma: no cover\n        yield arg\n\n    # If/when RaisesGroup/Matcher is added to pytest in some form this test can be\n    # rewritten to use a loop again, and avoid specifying the exceptions twice in\n    # different ways\n    with pytest.raises(\n        TypeError,\n        match=r\"^Trio was expecting an async function, but instead it got a coroutine object <.*>\",\n    ):\n        bad_call_run(f())  # type: ignore[arg-type]\n    with pytest.raises(\n        TypeError,\n        match=r\"expected an async function but got an async generator\",\n    ):\n        bad_call_run(async_gen, 0)  # type: ignore\n\n    # bad_call_spawn calls the function inside a nursery, so the exception will be\n    # wrapped in an exceptiongroup\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(TypeError, match=\"expecting an async function\")\n    ):\n        bad_call_spawn(f())  # type: ignore[arg-type]\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(\n            TypeError, match=\"expected an async function but got an async generator\"\n        ),\n    ):\n        bad_call_spawn(async_gen, 0)  # type: ignore\n\n\ndef test_calling_asyncio_function_gives_nice_error() -> None:\n    async def child_xyzzy() -> None:\n        await create_asyncio_future_in_new_loop()\n\n    async def misguided() -> None:\n        await child_xyzzy()\n\n    with pytest.raises(TypeError, match=\"asyncio\") as excinfo:\n        _core.run(misguided)\n\n    # The traceback should point to the location of the foreign await\n    assert any(  # pragma: no branch\n        entry.name == \"child_xyzzy\" for entry in excinfo.traceback\n    )\n\n\nasync def test_asyncio_function_inside_nursery_does_not_explode() -> None:\n    # Regression test for https://github.com/python-trio/trio/issues/552\n    with pytest.RaisesGroup(pytest.RaisesExc(TypeError, match=\"asyncio\")):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sleep_forever)\n            await create_asyncio_future_in_new_loop()\n\n\nasync def test_trivial_yields() -> None:\n    with assert_checkpoints():\n        await _core.checkpoint()\n\n    with assert_checkpoints():\n        await _core.checkpoint_if_cancelled()\n        await _core.cancel_shielded_checkpoint()\n\n    # Weird case: opening and closing a nursery schedules, but doesn't check\n    # for cancellation (unless something inside the nursery does)\n    task = _core.current_task()\n    before_schedule_points = task._schedule_points\n    with _core.CancelScope() as cs:\n        cs.cancel()\n        async with _core.open_nursery():\n            pass\n    assert not cs.cancelled_caught\n    assert task._schedule_points > before_schedule_points\n\n    before_schedule_points = task._schedule_points\n\n    async def noop_with_no_checkpoint() -> None:\n        pass\n\n    with _core.CancelScope() as cs:\n        cs.cancel()\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(noop_with_no_checkpoint)\n    assert not cs.cancelled_caught\n\n    assert task._schedule_points > before_schedule_points\n\n    with _core.CancelScope() as cancel_scope:\n        cancel_scope.cancel()\n        with pytest.RaisesGroup(KeyError):\n            async with _core.open_nursery():\n                raise KeyError\n\n\nasync def test_nursery_start(autojump_clock: _core.MockClock) -> None:\n    async def no_args() -> None:  # pragma: no cover\n        pass\n\n    # Errors in calling convention get raised immediately from start\n    async with _core.open_nursery() as nursery:\n        with pytest.raises(TypeError):\n            await nursery.start(no_args)\n\n    async def sleep_then_start(\n        seconds: int,\n        *,\n        task_status: _core.TaskStatus[int] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        repr(task_status)  # smoke test\n        await sleep(seconds)\n        task_status.started(seconds)\n        await sleep(seconds)\n\n    # Basic happy-path check: start waits for the task to call started(), then\n    # returns, passes back the value, and the given nursery then waits for it\n    # to exit.\n    for seconds in [1, 2]:\n        async with _core.open_nursery() as nursery:\n            assert len(nursery.child_tasks) == 0\n            t0 = _core.current_time()\n            assert await nursery.start(sleep_then_start, seconds) == seconds\n            assert _core.current_time() - t0 == seconds\n            assert len(nursery.child_tasks) == 1\n        assert _core.current_time() - t0 == 2 * seconds\n\n    # Make sure TASK_STATUS_IGNORED works so task function can be called\n    # directly\n    t0 = _core.current_time()\n    await sleep_then_start(3)\n    assert _core.current_time() - t0 == 2 * 3\n\n    # calling started twice\n    async def double_started(\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        task_status.started()\n        with pytest.raises(RuntimeError):\n            task_status.started()\n\n    async with _core.open_nursery() as nursery:\n        await nursery.start(double_started)\n\n    # child crashes before calling started -> error comes out of .start()\n    async def raise_keyerror(\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        raise KeyError(\"oops\")\n\n    async with _core.open_nursery() as nursery:\n        with pytest.raises(KeyError):\n            await nursery.start(raise_keyerror)\n\n    # child exiting cleanly before calling started -> triggers a RuntimeError\n    async def nothing(\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        return\n\n    async with _core.open_nursery() as nursery:\n        with pytest.raises(RuntimeError) as excinfo1:\n            await nursery.start(nothing)\n        assert \"exited without calling\" in str(excinfo1.value)\n\n    # if the call to start() is cancelled, then the call to started() does\n    # nothing -- the child keeps executing under start(). The value it passed\n    # is ignored; start() raises Cancelled.\n    async def just_started(\n        *,\n        task_status: _core.TaskStatus[str] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        task_status.started(\"hi\")\n        await _core.checkpoint()\n\n    async with _core.open_nursery() as nursery:\n        with _core.CancelScope() as cs:\n            cs.cancel()\n            with pytest.raises(_core.Cancelled):\n                await nursery.start(just_started)\n\n    # but if the task does not execute any checkpoints, and exits, then start()\n    # doesn't raise Cancelled, since the task completed successfully.\n    async def started_with_no_checkpoint(\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        task_status.started(None)\n\n    async with _core.open_nursery() as nursery:\n        with _core.CancelScope() as cs:\n            cs.cancel()\n            await nursery.start(started_with_no_checkpoint)\n        assert not cs.cancelled_caught\n\n    # and since starting in a cancelled context makes started() a no-op, if\n    # the child crashes after calling started(), the error can *still* come\n    # out of start()\n    async def raise_keyerror_after_started(\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        task_status.started()\n        raise KeyError(\"whoopsiedaisy\")\n\n    async with _core.open_nursery() as nursery:\n        with _core.CancelScope() as cs:\n            cs.cancel()\n            with pytest.raises(KeyError):\n                await nursery.start(raise_keyerror_after_started)\n\n    # trying to start in a closed nursery raises an error immediately\n    async with _core.open_nursery() as closed_nursery:\n        pass\n    t0 = _core.current_time()\n    with pytest.raises(RuntimeError):\n        await closed_nursery.start(sleep_then_start, 7)\n    # sub-second delays can be caused by unrelated multitasking by an OS\n    assert int(_core.current_time()) == int(t0)\n\n\nasync def test_task_nursery_stack() -> None:\n    task = _core.current_task()\n    assert task._child_nurseries == []\n    async with _core.open_nursery() as nursery1:\n        assert task._child_nurseries == [nursery1]\n        with pytest.RaisesGroup(KeyError):\n            async with _core.open_nursery() as nursery2:\n                assert task._child_nurseries == [nursery1, nursery2]\n                raise KeyError\n        assert task._child_nurseries == [nursery1]\n    assert task._child_nurseries == []\n\n\nasync def test_nursery_start_with_cancelled_nursery() -> None:\n    # This function isn't testing task_status, it's using task_status as a\n    # convenient way to get a nursery that we can test spawning stuff into.\n    async def setup_nursery(\n        task_status: _core.TaskStatus[_core.Nursery] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        async with _core.open_nursery() as nursery:\n            task_status.started(nursery)\n            await sleep_forever()\n\n    # Calls started() while children are asleep, so we can make sure\n    # that the cancellation machinery notices and aborts when a sleeping task\n    # is moved into a cancelled scope.\n    async def sleeping_children(\n        fn: Callable[[], object],\n        *,\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sleep_forever)\n            nursery.start_soon(sleep_forever)\n            await wait_all_tasks_blocked()\n            fn()\n            task_status.started()\n\n    # Cancelling the setup_nursery just *before* calling started()\n    async with _core.open_nursery() as nursery:\n        value = await nursery.start(setup_nursery)\n        assert isinstance(value, _core.Nursery)\n        target_nursery: _core.Nursery = value\n        await target_nursery.start(\n            sleeping_children,\n            target_nursery.cancel_scope.cancel,\n        )\n\n    # Cancelling the setup_nursery just *after* calling started()\n    async with _core.open_nursery() as nursery:\n        value = await nursery.start(setup_nursery)\n        assert isinstance(value, _core.Nursery)\n        target_nursery = value\n        await target_nursery.start(sleeping_children, lambda: None)\n        target_nursery.cancel_scope.cancel()\n\n\nasync def test_nursery_start_keeps_nursery_open(\n    autojump_clock: _core.MockClock,\n) -> None:\n    async def sleep_a_bit(\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        await sleep(2)\n        task_status.started()\n        await sleep(3)\n\n    async with _core.open_nursery() as nursery1:\n        t0 = _core.current_time()\n        async with _core.open_nursery() as nursery2:\n            # Start the 'start' call running in the background\n            nursery1.start_soon(nursery2.start, sleep_a_bit)\n            # Sleep a bit\n            await sleep(1)\n            # Start another one.\n            nursery1.start_soon(nursery2.start, sleep_a_bit)\n            # Then exit this nursery. At this point, there are no tasks\n            # present in this nursery -- the only thing keeping it open is\n            # that the tasks will be placed into it soon, when they call\n            # started().\n        assert _core.current_time() - t0 == 6\n\n    # Check that it still works even if the task that the nursery is waiting\n    # for ends up crashing, and never actually enters the nursery.\n    async def sleep_then_crash(\n        task_status: _core.TaskStatus[None] = _core.TASK_STATUS_IGNORED,\n    ) -> None:\n        await sleep(7)\n        raise KeyError\n\n    async def start_sleep_then_crash(nursery: _core.Nursery) -> None:\n        with pytest.raises(KeyError):\n            await nursery.start(sleep_then_crash)\n\n    async with _core.open_nursery() as nursery1:\n        t0 = _core.current_time()\n        async with _core.open_nursery() as nursery2:\n            nursery1.start_soon(start_sleep_then_crash, nursery2)\n            await wait_all_tasks_blocked()\n        assert _core.current_time() - t0 == 7\n\n\nasync def test_nursery_explicit_exception() -> None:\n    with pytest.RaisesGroup(KeyError):\n        async with _core.open_nursery():\n            raise KeyError()\n\n\nasync def test_nursery_stop_iteration() -> None:\n    async def fail() -> NoReturn:\n        raise ValueError\n\n    with pytest.RaisesGroup(StopIteration, ValueError):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(fail)\n            raise StopIteration\n\n\nasync def test_nursery_stop_async_iteration() -> None:\n    class it:\n        def __init__(self, count: int) -> None:\n            self.count = count\n            self.val = 0\n\n        async def __anext__(self) -> int:\n            await sleep(0)\n            val = self.val\n            if val >= self.count:\n                raise StopAsyncIteration\n            self.val += 1\n            return val\n\n    class async_zip:\n        def __init__(self, *largs: it) -> None:\n            self.nexts = [obj.__anext__ for obj in largs]\n\n        async def _accumulate(\n            self,\n            f: Callable[[], Awaitable[int]],\n            items: list[int],\n            i: int,\n        ) -> None:\n            items[i] = await f()\n\n        def __aiter__(self) -> async_zip:\n            return self\n\n        async def __anext__(self) -> list[int]:\n            nexts = self.nexts\n            items: list[int] = [-1] * len(nexts)\n\n            try:\n                async with _core.open_nursery() as nursery:\n                    for i, f in enumerate(nexts):\n                        nursery.start_soon(self._accumulate, f, items, i)\n            except ExceptionGroup as e:\n                # With strict_exception_groups enabled, users now need to unwrap\n                # StopAsyncIteration and re-raise it.\n                # This would be relatively clean on python3.11+ with except*.\n                # We could also use pytest.RaisesGroup, but that's primarily meant as\n                # test infra, not as a runtime tool.\n                if len(e.exceptions) == 1 and isinstance(\n                    e.exceptions[0],\n                    StopAsyncIteration,\n                ):\n                    raise e.exceptions[0] from None\n                else:  # pragma: no cover\n                    raise AssertionError(\"unknown error in _accumulate\") from e\n\n            return items\n\n    result: list[list[int]] = [vals async for vals in async_zip(it(4), it(2))]\n    assert result == [[0, 0], [1, 1]]\n\n\nasync def test_traceback_frame_removal() -> None:\n    async def my_child_task() -> NoReturn:\n        raise KeyError()\n\n    def check_traceback(exc: KeyError) -> bool:\n        # The top frame in the exception traceback should be inside the child\n        # task, not trio/contextvars internals. And there's only one frame\n        # inside the child task, so this will also detect if our frame-removal\n        # is too eager.\n        tb = exc.__traceback__\n        assert tb is not None\n        return tb.tb_frame.f_code is my_child_task.__code__\n\n    with pytest.RaisesGroup(pytest.RaisesExc(KeyError, check=check_traceback)):\n        # For now cancel/nursery scopes still leave a bunch of tb gunk behind.\n        # But if there's an Exceptiongroup, they leave it on the group,\n        # which lets us get a clean look at the KeyError itself.\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(my_child_task)\n\n\ndef test_contextvar_support() -> None:\n    var: contextvars.ContextVar[str] = contextvars.ContextVar(\"test\")\n    var.set(\"before\")\n\n    assert var.get() == \"before\"\n\n    async def inner() -> None:\n        task = _core.current_task()\n        assert task.context.get(var) == \"before\"\n        assert var.get() == \"before\"\n        var.set(\"after\")\n        assert var.get() == \"after\"\n        assert var in task.context\n        assert task.context.get(var) == \"after\"\n\n    _core.run(inner)\n    assert var.get() == \"before\"\n\n\nasync def test_contextvar_multitask() -> None:\n    var = contextvars.ContextVar(\"test\", default=\"hmmm\")\n\n    async def t1() -> None:\n        assert var.get() == \"hmmm\"\n        var.set(\"hmmmm\")\n        assert var.get() == \"hmmmm\"\n\n    async def t2() -> None:\n        assert var.get() == \"hmmmm\"\n\n    async with _core.open_nursery() as n:\n        n.start_soon(t1)\n        await wait_all_tasks_blocked()\n        assert var.get() == \"hmmm\"\n        var.set(\"hmmmm\")\n        n.start_soon(t2)\n        await wait_all_tasks_blocked()\n\n\ndef test_system_task_contexts() -> None:\n    cvar: contextvars.ContextVar[str] = contextvars.ContextVar(\"qwilfish\")\n    cvar.set(\"water\")\n\n    async def system_task() -> None:\n        assert cvar.get() == \"water\"\n\n    async def regular_task() -> None:\n        assert cvar.get() == \"poison\"\n\n    async def inner() -> None:\n        async with _core.open_nursery() as nursery:\n            cvar.set(\"poison\")\n            nursery.start_soon(regular_task)\n            _core.spawn_system_task(system_task)\n            await wait_all_tasks_blocked()\n\n    _core.run(inner)\n\n\nasync def test_Nursery_init() -> None:\n    \"\"\"Test that nurseries cannot be constructed directly.\"\"\"\n    # This function is async so that we have access to a task object we can\n    # pass in. It should never be accessed though.\n    task = _core.current_task()\n    scope = _core.CancelScope()\n    with pytest.raises(TypeError):\n        _core._run.Nursery(task, scope, True)\n\n\nasync def test_Nursery_private_init() -> None:\n    # context manager creation should not raise\n    async with _core.open_nursery() as nursery:\n        assert not nursery._closed\n\n\ndef test_Nursery_subclass() -> None:\n    with pytest.raises(TypeError):\n        type(\"Subclass\", (_core._run.Nursery,), {})\n\n\ndef test_CancelScope_subclass() -> None:\n    with pytest.raises(TypeError):\n        type(\"Subclass\", (_core.CancelScope,), {})\n\n\ndef test_sniffio_integration() -> None:\n    with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n        sniffio.current_async_library()\n\n    async def check_inside_trio() -> None:\n        assert sniffio.current_async_library() == \"trio\"\n\n    def check_function_returning_coroutine() -> Awaitable[object]:\n        assert sniffio.current_async_library() == \"trio\"\n        return check_inside_trio()\n\n    _core.run(check_inside_trio)\n\n    with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n        sniffio.current_async_library()\n\n    @contextmanager\n    def alternate_sniffio_library() -> Generator[None, None, None]:\n        prev_token = sniffio.current_async_library_cvar.set(\"nullio\")\n        prev_library, sniffio.thread_local.name = sniffio.thread_local.name, \"nullio\"\n        try:\n            yield\n            assert sniffio.current_async_library() == \"nullio\"\n        finally:\n            sniffio.thread_local.name = prev_library\n            sniffio.current_async_library_cvar.reset(prev_token)\n\n    async def check_new_task_resets_sniffio_library() -> None:\n        with alternate_sniffio_library():\n            _core.spawn_system_task(check_inside_trio)\n        async with _core.open_nursery() as nursery:\n            with alternate_sniffio_library():\n                nursery.start_soon(check_inside_trio)\n                nursery.start_soon(check_function_returning_coroutine)\n\n    _core.run(check_new_task_resets_sniffio_library)\n\n\nasync def test_Task_custom_sleep_data() -> None:\n    task = _core.current_task()\n    assert task.custom_sleep_data is None\n    task.custom_sleep_data = 1\n    assert task.custom_sleep_data == 1\n    await _core.checkpoint()\n    assert task.custom_sleep_data is None\n\n\n@types.coroutine\ndef async_yield(value: T) -> Generator[T, None, None]:\n    yield value\n\n\nasync def test_permanently_detach_coroutine_object() -> None:\n    task: _core.Task | None = None\n    pdco_outcome: outcome.Outcome[str] | None = None\n\n    async def detachable_coroutine(\n        task_outcome: outcome.Outcome[None],\n        yield_value: object,\n    ) -> None:\n        await sleep(0)\n        nonlocal task, pdco_outcome\n        task = _core.current_task()\n        # `No overload variant of \"acapture\" matches argument types \"Callable[[Outcome[object]], Coroutine[Any, Any, object]]\", \"Outcome[None]\"`\n        pdco_outcome = await outcome.acapture(  # type: ignore[call-overload]\n            _core.permanently_detach_coroutine_object,\n            task_outcome,\n        )\n        await async_yield(yield_value)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(detachable_coroutine, outcome.Value(None), \"I'm free!\")\n\n    # If we get here then Trio thinks the task has exited... but the coroutine\n    # is still iterable. At that point anything can be sent into the coroutine, so the .coro type\n    # is wrong.\n    assert pdco_outcome is None\n    # `Argument 1 to \"send\" of \"Coroutine\" has incompatible type \"str\"; expected \"Outcome[object]\"`\n    assert not_none(task).coro.send(\"be free!\") == \"I'm free!\"  # type: ignore[arg-type]\n    assert pdco_outcome == outcome.Value(\"be free!\")\n    with pytest.raises(StopIteration):\n        not_none(task).coro.send(None)  # type: ignore[arg-type]\n\n    # Check the exception paths too\n    task = None\n    pdco_outcome = None\n    with pytest.RaisesGroup(KeyError):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(detachable_coroutine, outcome.Error(KeyError()), \"uh oh\")\n    throw_in = ValueError()\n    assert isinstance(task, _core.Task)  # For type checkers.\n    assert not_none(task).coro.throw(throw_in) == \"uh oh\"\n    assert pdco_outcome == outcome.Error(throw_in)\n    with pytest.raises(StopIteration):\n        task.coro.send(None)\n\n    async def bad_detach() -> None:\n        async with _core.open_nursery():\n            with pytest.raises(RuntimeError) as excinfo:\n                await _core.permanently_detach_coroutine_object(outcome.Value(None))\n            assert \"open nurser\" in str(excinfo.value)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(bad_detach)\n\n\nasync def test_detach_and_reattach_coroutine_object() -> None:\n    unrelated_task: _core.Task | None = None\n    task: _core.Task | None = None\n\n    async def unrelated_coroutine() -> None:\n        nonlocal unrelated_task\n        unrelated_task = _core.current_task()\n\n    async def reattachable_coroutine() -> None:\n        nonlocal task\n        await sleep(0)\n\n        task = _core.current_task()\n\n        def abort_fn(_: _core.RaiseCancelT) -> _core.Abort:  # pragma: no cover\n            return _core.Abort.FAILED\n\n        got = await _core.temporarily_detach_coroutine_object(abort_fn)\n        assert got == \"not trio!\"\n\n        await async_yield(1)\n        await async_yield(2)\n\n        with pytest.raises(RuntimeError) as excinfo:\n            await _core.reattach_detached_coroutine_object(\n                not_none(unrelated_task),\n                None,\n            )\n        assert \"does not match\" in str(excinfo.value)\n\n        await _core.reattach_detached_coroutine_object(task, \"byebye\")\n\n        await sleep(0)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(unrelated_coroutine)\n        nursery.start_soon(reattachable_coroutine)\n        await wait_all_tasks_blocked()\n\n        # Okay, it's detached. Here's our coroutine runner:\n        # `Argument 1 to \"send\" of \"Coroutine\" has incompatible type \"str\"; expected \"Outcome[object]\"`\n        assert not_none(task).coro.send(\"not trio!\") == 1  # type: ignore[arg-type]\n        assert not_none(task).coro.send(None) == 2  # type: ignore[arg-type]\n        assert not_none(task).coro.send(None) == \"byebye\"  # type: ignore[arg-type]\n\n        # Now it's been reattached, and we can leave the nursery\n\n\nasync def test_detached_coroutine_cancellation() -> None:\n    abort_fn_called = False\n    task: _core.Task | None = None\n\n    async def reattachable_coroutine() -> None:\n        await sleep(0)\n\n        nonlocal task\n        task = _core.current_task()\n\n        def abort_fn(_: _core.RaiseCancelT) -> _core.Abort:\n            nonlocal abort_fn_called\n            abort_fn_called = True\n            return _core.Abort.FAILED\n\n        await _core.temporarily_detach_coroutine_object(abort_fn)\n        await _core.reattach_detached_coroutine_object(task, None)\n        with pytest.raises(_core.Cancelled):\n            await sleep(0)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(reattachable_coroutine)\n        await wait_all_tasks_blocked()\n        assert task is not None\n        nursery.cancel_scope.cancel()\n        # `Argument 1 to \"send\" of \"Coroutine\" has incompatible type \"None\"; expected \"Outcome[object]\"`\n        task.coro.send(None)  # type: ignore[arg-type]\n\n    assert abort_fn_called\n\n\n@restore_unraisablehook()\ndef test_async_function_implemented_in_C() -> None:\n    # These used to crash because we'd try to mutate the coroutine object's\n    # cr_frame, but C functions don't have Python frames.\n\n    async def agen_fn(record: list[str]) -> AsyncIterator[None]:\n        assert not _core.currently_ki_protected()\n        record.append(\"the generator ran\")\n        yield\n\n    run_record: list[str] = []\n    agen = agen_fn(run_record)\n    _core.run(agen.__anext__)\n    assert run_record == [\"the generator ran\"]\n\n    async def main() -> None:\n        start_soon_record: list[str] = []\n        agen = agen_fn(start_soon_record)\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(agen.__anext__)\n        assert start_soon_record == [\"the generator ran\"]\n\n    _core.run(main)\n\n\nasync def test_very_deep_cancel_scope_nesting() -> None:\n    # This used to crash with a RecursionError in CancelStatus.recalculate\n    with ExitStack() as exit_stack:\n        outermost_scope = _core.CancelScope()\n        exit_stack.enter_context(outermost_scope)\n        for _ in range(5000):\n            exit_stack.enter_context(_core.CancelScope())\n        outermost_scope.cancel()\n\n\nasync def test_cancel_scope_deadline_duplicates() -> None:\n    # This exercises an assert in Deadlines._prune, by intentionally creating\n    # duplicate entries in the deadline heap.\n    now = _core.current_time()\n    with _core.CancelScope() as cscope:\n        for _ in range(DEADLINE_HEAP_MIN_PRUNE_THRESHOLD * 2):\n            cscope.deadline = now + 9998\n            cscope.deadline = now + 9999\n        await sleep(0.01)\n\n\n# I don't know if this one can fail anymore, the `del` next to the comment that used to\n# refer to this only seems to break test_cancel_scope_exit_doesnt_create_cyclic_garbage\n# We're keeping it for now to cover Outcome and potential future refactoring\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Only makes sense with refcounting GC\",\n)\nasync def test_simple_cancel_scope_usage_doesnt_create_cyclic_garbage() -> None:\n    # https://github.com/python-trio/trio/issues/1770\n    gc.collect()\n\n    async def do_a_cancel() -> None:\n        with _core.CancelScope() as cscope:\n            cscope.cancel()\n            await sleep_forever()\n\n    async def crasher() -> NoReturn:\n        raise ValueError(\"this is a crash\")\n\n    old_flags = gc.get_debug()\n    try:\n        gc.collect()\n        gc.set_debug(gc.DEBUG_SAVEALL)\n\n        # cover outcome.Error.unwrap\n        # (See https://github.com/python-trio/outcome/pull/29)\n        await do_a_cancel()\n        # cover outcome.Error.unwrap if unrolled_run hangs on to exception refs\n        # (See https://github.com/python-trio/trio/pull/1864)\n        await do_a_cancel()\n\n        with pytest.RaisesGroup(\n            pytest.RaisesExc(ValueError, match=\"^this is a crash$\")\n        ):\n            async with _core.open_nursery() as nursery:\n                # cover NurseryManager.__aexit__\n                nursery.start_soon(crasher)\n\n        gc.collect()\n        assert not gc.garbage\n    finally:\n        gc.set_debug(old_flags)\n        gc.garbage.clear()\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Only makes sense with refcounting GC\",\n)\nasync def test_cancel_scope_exit_doesnt_create_cyclic_garbage() -> None:\n    # https://github.com/python-trio/trio/pull/2063\n    gc.collect()\n\n    async def crasher() -> NoReturn:\n        raise ValueError(\"this is a crash\")\n\n    old_flags = gc.get_debug()\n    try:\n        with (\n            pytest.RaisesGroup(\n                pytest.RaisesExc(ValueError, match=\"^this is a crash$\"),\n            ),\n            _core.CancelScope() as outer,\n        ):\n            async with _core.open_nursery() as nursery:\n                gc.collect()\n                gc.set_debug(gc.DEBUG_SAVEALL)\n                # One child that gets cancelled by the outer scope\n                nursery.start_soon(sleep_forever)\n                outer.cancel()\n                # And one that raises a different error\n                nursery.start_soon(crasher)\n                # so that outer filters a Cancelled from the ExceptionGroup and\n                # covers CancelScope.__exit__ (and NurseryManager.__aexit__)\n                # (See https://github.com/python-trio/trio/pull/2063)\n\n        gc.collect()\n        assert not gc.garbage\n    finally:\n        gc.set_debug(old_flags)\n        gc.garbage.clear()\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Only makes sense with refcounting GC\",\n)\nasync def test_nursery_cancel_doesnt_create_cyclic_garbage() -> None:\n    collected = False\n\n    # https://github.com/python-trio/trio/issues/1770#issuecomment-730229423\n    def toggle_collected() -> None:\n        nonlocal collected\n        collected = True\n\n    gc.collect()\n    old_flags = gc.get_debug()\n    try:\n        gc.set_debug(0)\n        gc.collect()\n        gc.set_debug(gc.DEBUG_SAVEALL)\n\n        # cover Nursery._nested_child_finished\n        async with _core.open_nursery() as nursery:\n            nursery.cancel_scope.cancel()\n\n        weakref.finalize(nursery, toggle_collected)\n        del nursery\n        # a checkpoint clears the nursery from the internals, apparently\n        # TODO: stop event loop from hanging on to the nursery at this point\n        await _core.checkpoint()\n\n        assert collected\n        gc.collect()\n        assert not gc.garbage\n    finally:\n        gc.set_debug(old_flags)\n        gc.garbage.clear()\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Only makes sense with refcounting GC\",\n)\nasync def test_locals_destroyed_promptly_on_cancel() -> None:\n    destroyed = False\n\n    def finalizer() -> None:\n        nonlocal destroyed\n        destroyed = True\n\n    class A:\n        pass\n\n    async def task() -> None:\n        a = A()\n        weakref.finalize(a, finalizer)\n        await _core.checkpoint()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(task)\n        nursery.cancel_scope.cancel()\n    assert destroyed\n\n\ndef _create_kwargs(strictness: bool | None) -> dict[str, bool]:\n    \"\"\"Turn a bool|None into a kwarg dict that can be passed to `run` or `open_nursery`\"\"\"\n\n    if strictness is None:\n        return {}\n    return {\"strict_exception_groups\": strictness}\n\n\n@pytest.mark.filterwarnings(\n    \"ignore:.*strict_exception_groups=False:trio.TrioDeprecationWarning\",\n)\n@pytest.mark.parametrize(\"run_strict\", [True, False, None])\n@pytest.mark.parametrize(\"open_nursery_strict\", [True, False, None])\n@pytest.mark.parametrize(\"multiple_exceptions\", [True, False])\ndef test_setting_strict_exception_groups(\n    run_strict: bool | None,\n    open_nursery_strict: bool | None,\n    multiple_exceptions: bool,\n) -> None:\n    \"\"\"\n    Test default values and that nurseries can both inherit and override the global context\n    setting of strict_exception_groups.\n    \"\"\"\n\n    async def raise_error() -> NoReturn:\n        raise RuntimeError(\"test error\")\n\n    async def main() -> None:\n        \"\"\"Open a nursery, and raise one or two errors inside\"\"\"\n        async with _core.open_nursery(**_create_kwargs(open_nursery_strict)) as nursery:\n            nursery.start_soon(raise_error)\n            if multiple_exceptions:\n                nursery.start_soon(raise_error)\n\n    def run_main() -> None:\n        # mypy doesn't like kwarg magic\n        _core.run(main, **_create_kwargs(run_strict))  # type: ignore[arg-type]\n\n    matcher = pytest.RaisesExc(RuntimeError, match=r\"^test error$\")\n\n    if multiple_exceptions:\n        with pytest.RaisesGroup(matcher, matcher):\n            run_main()\n    elif open_nursery_strict or (\n        open_nursery_strict is None and run_strict is not False\n    ):\n        with pytest.RaisesGroup(matcher):\n            run_main()\n    else:\n        with pytest.raises(RuntimeError, match=r\"^test error$\"):\n            run_main()\n\n\n@pytest.mark.filterwarnings(\n    \"ignore:.*strict_exception_groups=False:trio.TrioDeprecationWarning\",\n)\n@pytest.mark.parametrize(\"strict\", [True, False, None])\nasync def test_nursery_collapse(strict: bool | None) -> None:\n    \"\"\"\n    Test that a single exception from a nested nursery gets collapsed correctly\n    depending on strict_exception_groups value when CancelledErrors are stripped from it.\n    \"\"\"\n\n    async def raise_error() -> NoReturn:\n        raise RuntimeError(\"test error\")\n\n    # mypy requires explicit type for conditional expression\n    maybe_wrapped_runtime_error: (\n        type[RuntimeError] | pytest.RaisesGroup[RuntimeError]\n    ) = (RuntimeError if strict is False else pytest.RaisesGroup(RuntimeError))\n\n    with pytest.RaisesGroup(RuntimeError, maybe_wrapped_runtime_error):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sleep_forever)\n            nursery.start_soon(raise_error)\n            async with _core.open_nursery(**_create_kwargs(strict)) as nursery2:\n                nursery2.start_soon(sleep_forever)\n                nursery2.start_soon(raise_error)\n                nursery.cancel_scope.cancel()\n\n\nasync def test_cancel_scope_no_cancellederror() -> None:\n    \"\"\"\n    Test that when a cancel scope encounters an exception group that does NOT contain\n    a Cancelled exception, it will NOT set the ``cancelled_caught`` flag.\n    \"\"\"\n\n    with pytest.RaisesGroup(RuntimeError, RuntimeError, match=\"test\"):\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            raise ExceptionGroup(\"test\", [RuntimeError(), RuntimeError()])\n\n    assert not scope.cancelled_caught\n\n\n@pytest.mark.filterwarnings(\n    \"ignore:.*strict_exception_groups=False:trio.TrioDeprecationWarning\",\n)\n@pytest.mark.parametrize(\"run_strict\", [False, True])\n@pytest.mark.parametrize(\"start_raiser_strict\", [False, True, None])\n@pytest.mark.parametrize(\"raise_after_started\", [False, True])\n@pytest.mark.parametrize(\"raise_custom_exc_grp\", [False, True])\ndef test_trio_run_strict_before_started(\n    run_strict: bool,\n    start_raiser_strict: bool | None,\n    raise_after_started: bool,\n    raise_custom_exc_grp: bool,\n) -> None:\n    \"\"\"\n    Regression tests for #2611, where exceptions raised before\n    `TaskStatus.started()` caused `Nursery.start()` to wrap them in an\n    ExceptionGroup when using `run(..., strict_exception_groups=True)`.\n\n    Regression tests for #2844, where #2611 was initially fixed in a way that\n    had unintended side effects.\n    \"\"\"\n\n    raiser_exc: ValueError | ExceptionGroup[ValueError]\n    if raise_custom_exc_grp:\n        raiser_exc = ExceptionGroup(\"my group\", [ValueError()])\n    else:\n        raiser_exc = ValueError()\n\n    async def raiser(*, task_status: _core.TaskStatus[None]) -> None:\n        if raise_after_started:\n            task_status.started()\n        raise raiser_exc\n\n    async def start_raiser() -> None:\n        try:\n            async with _core.open_nursery(\n                strict_exception_groups=start_raiser_strict,\n            ) as nursery:\n                await nursery.start(raiser)\n        except BaseExceptionGroup as exc_group:\n            if start_raiser_strict:\n                # Iff the code using the nursery *forced* it to be strict\n                # (overriding the runner setting) then it may replace the bland\n                # exception group raised by trio with a more specific one (subtype,\n                # different message, etc.).\n                raise BaseExceptionGroup(\n                    \"start_raiser nursery custom message\",\n                    exc_group.exceptions,\n                ) from None\n            raise\n\n    with pytest.raises(BaseException) as exc_info:  # noqa: PT011  # no `match`\n        _core.run(start_raiser, strict_exception_groups=run_strict)\n\n    if start_raiser_strict or (run_strict and start_raiser_strict is None):\n        # start_raiser's nursery was strict.\n        assert isinstance(exc_info.value, BaseExceptionGroup)\n        if start_raiser_strict:\n            # start_raiser didn't unknowingly inherit its nursery strictness\n            # from `run`---it explicitly chose for its nursery to be strict.\n            assert exc_info.value.message == \"start_raiser nursery custom message\"\n        assert len(exc_info.value.exceptions) == 1\n        should_be_raiser_exc = exc_info.value.exceptions[0]\n    else:\n        # start_raiser's nursery was not strict.\n        should_be_raiser_exc = exc_info.value\n    if isinstance(raiser_exc, ValueError):\n        assert should_be_raiser_exc is raiser_exc\n    else:\n        # Check attributes, not identity, because should_be_raiser_exc may be a\n        # copy of raiser_exc rather than raiser_exc by identity.\n        assert type(should_be_raiser_exc) is type(raiser_exc)\n        assert should_be_raiser_exc.message == raiser_exc.message\n        assert should_be_raiser_exc.exceptions == raiser_exc.exceptions\n\n\nasync def test_start_exception_preserves_cause_and_context() -> None:\n    \"\"\"Regression test for #3261: exceptions raised before task_status.started()\n    should preserve __cause__ and __context__.\"\"\"\n\n    async def task(*, task_status: _core.TaskStatus[None]) -> None:\n        e = ValueError(\"foo\")\n        e.__cause__ = SyntaxError(\"bar\")\n        e.__context__ = TypeError(\"baz\")\n        raise e\n\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(\n            ValueError,\n            check=lambda exc: isinstance(exc.__cause__, SyntaxError)\n            and isinstance(exc.__context__, TypeError),\n        ),\n    ):\n        async with _core.open_nursery() as nursery:\n            await nursery.start(task)\n\n\nasync def test_internal_error_old_nursery_multiple_tasks() -> None:\n    async def error_func() -> None:\n        raise ValueError\n\n    async def spawn_tasks_in_old_nursery(task_status: _core.TaskStatus[None]) -> None:\n        old_nursery = _core.current_task().parent_nursery\n        assert old_nursery is not None\n        old_nursery.start_soon(error_func)\n        old_nursery.start_soon(error_func)\n\n    async with _core.open_nursery() as nursery:\n        with pytest.raises(_core.TrioInternalError) as excinfo:\n            await nursery.start(spawn_tasks_in_old_nursery)\n    assert pytest.RaisesGroup(ValueError, ValueError).matches(excinfo.value.__cause__)\n\n\nif sys.version_info >= (3, 11):\n\n    def no_other_refs() -> list[object]:\n        return []\n\nelse:\n\n    def no_other_refs() -> list[object]:\n        return [sys._getframe(1)]\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Only makes sense with refcounting GC\",\n)\n@pytest.mark.xfail(\n    sys.version_info >= (3, 14),\n    reason=\"https://github.com/python/cpython/issues/125603\",\n)\nasync def test_ki_protection_doesnt_leave_cyclic_garbage() -> None:\n    class MyException(Exception):\n        pass\n\n    async def demo() -> None:\n        async def handle_error() -> None:\n            try:\n                raise MyException\n            except MyException as e:\n                exceptions.append(e)\n\n        exceptions: list[MyException] = []\n        try:\n            async with _core.open_nursery() as n:\n                n.start_soon(handle_error)\n            raise ExceptionGroup(\"errors\", exceptions)\n        finally:\n            exceptions = []\n\n    exc: Exception | None = None\n    try:\n        await demo()\n    except ExceptionGroup as excs:\n        exc = excs.exceptions[0]\n\n    assert isinstance(exc, MyException)\n    assert gc.get_referrers(exc) == no_other_refs()\n\n\ndef test_context_run_tb_frames() -> None:\n    class Context:\n        def run(self, fn: Callable[[], object]) -> object:\n            return fn()\n\n    with mock.patch(\"trio._core._run.copy_context\", return_value=Context()):\n        assert _count_context_run_tb_frames() == 1\n\n\n@restore_unraisablehook()\ndef test_trio_context_detection() -> None:\n    assert not _core.in_trio_run()\n    assert not _core.in_trio_task()\n\n    def inner() -> None:\n        assert _core.in_trio_run()\n        assert _core.in_trio_task()\n\n    def sync_inner() -> None:\n        assert not _core.in_trio_run()\n        assert not _core.in_trio_task()\n\n    def inner_abort(_: object) -> _core.Abort:\n        assert _core.in_trio_run()\n        assert _core.in_trio_task()\n        return _core.Abort.SUCCEEDED\n\n    async def main() -> None:\n        assert _core.in_trio_run()\n        assert _core.in_trio_task()\n\n        inner()\n\n        await to_thread_run_sync(sync_inner)\n        with _core.CancelScope(deadline=_core.current_time() - 1):\n            await _core.wait_task_rescheduled(inner_abort)\n\n    _core.run(main)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_thread_cache.py",
    "content": "from __future__ import annotations\n\nimport os\nimport threading\nimport time\nfrom contextlib import contextmanager\nfrom queue import Queue\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport pytest\n\nfrom .. import _thread_cache\nfrom .._thread_cache import ThreadCache, start_thread_soon\nfrom .tutil import gc_collect_harder, slow\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from outcome import Outcome\n\n\ndef test_thread_cache_basics() -> None:\n    q: Queue[Outcome[object]] = Queue()\n\n    def fn() -> NoReturn:\n        raise RuntimeError(\"hi\")\n\n    def deliver(outcome: Outcome[object]) -> None:\n        q.put(outcome)\n\n    start_thread_soon(fn, deliver)\n\n    outcome = q.get()\n    with pytest.raises(RuntimeError, match=\"hi\"):\n        outcome.unwrap()\n\n\ndef test_thread_cache_deref() -> None:\n    res = [False]\n\n    class del_me:\n        def __call__(self) -> int:\n            return 42\n\n        def __del__(self) -> None:\n            res[0] = True\n\n    q: Queue[Outcome[int]] = Queue()\n\n    def deliver(outcome: Outcome[int]) -> None:\n        q.put(outcome)\n\n    start_thread_soon(del_me(), deliver)\n    outcome = q.get()\n    assert outcome.unwrap() == 42\n\n    gc_collect_harder()\n    assert res[0]\n\n\n@slow\ndef test_spawning_new_thread_from_deliver_reuses_starting_thread() -> None:\n    # We know that no-one else is using the thread cache, so if we keep\n    # submitting new jobs the instant the previous one is finished, we should\n    # keep getting the same thread over and over. This tests both that the\n    # thread cache is LIFO, and that threads can be assigned new work *before*\n    # deliver exits.\n\n    # Make sure there are a few threads running, so if we weren't LIFO then we\n    # could grab the wrong one.\n    q: Queue[Outcome[object]] = Queue()\n    COUNT = 5\n    for _ in range(COUNT):\n        start_thread_soon(lambda: time.sleep(1), lambda result: q.put(result))\n    for _ in range(COUNT):\n        q.get().unwrap()\n\n    seen_threads = set()\n    done = threading.Event()\n\n    def deliver(n: int, _: object) -> None:\n        print(n)\n        seen_threads.add(threading.current_thread())\n        if n == 0:\n            done.set()\n        else:\n            start_thread_soon(lambda: None, lambda _: deliver(n - 1, _))\n\n    start_thread_soon(lambda: None, lambda _: deliver(5, _))\n\n    done.wait()\n\n    assert len(seen_threads) == 1\n\n\n@slow\ndef test_idle_threads_exit(monkeypatch: pytest.MonkeyPatch) -> None:\n    # Temporarily set the idle timeout to something tiny, to speed up the\n    # test. (But non-zero, so that the worker loop will at least yield the\n    # CPU.)\n    monkeypatch.setattr(_thread_cache, \"IDLE_TIMEOUT\", 0.0001)\n\n    q: Queue[threading.Thread] = Queue()\n    start_thread_soon(lambda: None, lambda _: q.put(threading.current_thread()))\n    seen_thread = q.get()\n    # Since the idle timeout is 0, after sleeping for 1 second, the thread\n    # should have exited\n    time.sleep(1)\n    assert not seen_thread.is_alive()\n\n\n@contextmanager\ndef _join_started_threads() -> Iterator[None]:\n    before = frozenset(threading.enumerate())\n    try:\n        yield\n    finally:\n        for thread in threading.enumerate():\n            if thread not in before:\n                thread.join(timeout=1.0)\n                assert not thread.is_alive()\n\n\ndef test_race_between_idle_exit_and_job_assignment(\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    # This is a lock where the first few times you try to acquire it with a\n    # timeout, it waits until the lock is available and then pretends to time\n    # out. Using this in our thread cache implementation causes the following\n    # sequence:\n    #\n    # 1. start_thread_soon grabs the worker thread, assigns it a job, and\n    #    releases its lock.\n    # 2. The worker thread wakes up (because the lock has been released), but\n    #    the JankyLock lies to it and tells it that the lock timed out. So the\n    #    worker thread tries to exit.\n    # 3. The worker thread checks for the race between exiting and being\n    #    assigned a job, and discovers that it *is* in the process of being\n    #    assigned a job, so it loops around and tries to acquire the lock\n    #    again.\n    # 4. Eventually the JankyLock admits that the lock is available, and\n    #    everything proceeds as normal.\n\n    class JankyLock:\n        def __init__(self) -> None:\n            self._lock = threading.Lock()\n            self._counter = 3\n\n        def acquire(self, timeout: int = -1) -> bool:\n            got_it = self._lock.acquire(timeout=timeout)\n            if timeout == -1:\n                return True\n            elif got_it:\n                if self._counter > 0:\n                    self._counter -= 1\n                    self._lock.release()\n                    return False\n                return True\n            else:\n                return False\n\n        def release(self) -> None:\n            self._lock.release()\n\n    monkeypatch.setattr(_thread_cache, \"Lock\", JankyLock)\n\n    with _join_started_threads():\n        tc = ThreadCache()\n        done = threading.Event()\n        tc.start_thread_soon(lambda: None, lambda _: done.set())\n        done.wait()\n        # Let's kill the thread we started, so it doesn't hang around until the\n        # test suite finishes. Doesn't really do any harm, but it can be confusing\n        # to see it in debug output.\n        monkeypatch.setattr(_thread_cache, \"IDLE_TIMEOUT\", 0.0001)\n        tc.start_thread_soon(lambda: None, lambda _: None)\n\n\ndef test_raise_in_deliver(capfd: pytest.CaptureFixture[str]) -> None:\n    seen_threads = set()\n\n    def track_threads() -> None:\n        seen_threads.add(threading.current_thread())\n\n    def deliver(_: object) -> NoReturn:\n        done.set()\n        raise RuntimeError(\"don't do this\")\n\n    done = threading.Event()\n    start_thread_soon(track_threads, deliver)\n    done.wait()\n    done = threading.Event()\n    start_thread_soon(track_threads, lambda _: done.set())\n    done.wait()\n    assert len(seen_threads) == 1\n    err = capfd.readouterr().err\n    assert \"don't do this\" in err\n    assert \"delivering result\" in err\n\n\n@pytest.mark.skipif(not hasattr(os, \"fork\"), reason=\"os.fork isn't supported\")\ndef test_clear_thread_cache_after_fork() -> None:\n    assert hasattr(os, \"fork\")\n\n    def foo() -> None:\n        pass\n\n    # ensure the thread cache exists\n    done = threading.Event()\n    start_thread_soon(foo, lambda _: done.set())\n    done.wait()\n\n    child_pid = os.fork()\n\n    # try using it\n    done = threading.Event()\n    start_thread_soon(foo, lambda _: done.set())\n    done.wait()\n\n    if child_pid != 0:\n        # if this test fails, this will hang, triggering a timeout.\n        os.waitpid(child_pid, 0)\n    else:  # pragma: no cover  # coverage is shut down by os._exit(0)\n        # we would *want* to allow coverage to take a snapshot here. check\n        # git blame for how to do that. however, that times out for some\n        # reason when having `tests/` in `source` for coverage.py.\n        os._exit(0)\n"
  },
  {
    "path": "src/trio/_core/_tests/test_tutil.py",
    "content": "import pytest\n\nfrom .tutil import check_sequence_matches\n\n\ndef test_check_sequence_matches() -> None:\n    check_sequence_matches([1, 2, 3], [1, 2, 3])\n    with pytest.raises(AssertionError):\n        check_sequence_matches([1, 3, 2], [1, 2, 3])\n    check_sequence_matches([1, 2, 3, 4], [1, {2, 3}, 4])\n    check_sequence_matches([1, 3, 2, 4], [1, {2, 3}, 4])\n    with pytest.raises(AssertionError):\n        check_sequence_matches([1, 2, 4, 3], [1, {2, 3}, 4])\n"
  },
  {
    "path": "src/trio/_core/_tests/test_unbounded_queue.py",
    "content": "from __future__ import annotations\n\nimport itertools\n\nimport pytest\n\nfrom ... import _core\nfrom ...testing import assert_checkpoints, wait_all_tasks_blocked\n\npytestmark = pytest.mark.filterwarnings(\n    \"ignore:.*UnboundedQueue:trio.TrioDeprecationWarning\",\n)\n\n\nasync def test_UnboundedQueue_basic() -> None:\n    q: _core.UnboundedQueue[str | int | None] = _core.UnboundedQueue()\n    q.put_nowait(\"hi\")\n    assert await q.get_batch() == [\"hi\"]\n    with pytest.raises(_core.WouldBlock):\n        q.get_batch_nowait()\n    q.put_nowait(1)\n    q.put_nowait(2)\n    q.put_nowait(3)\n    assert q.get_batch_nowait() == [1, 2, 3]\n\n    assert q.empty()\n    assert q.qsize() == 0\n    q.put_nowait(None)\n    assert not q.empty()\n    assert q.qsize() == 1\n\n    stats = q.statistics()\n    assert stats.qsize == 1\n    assert stats.tasks_waiting == 0\n\n    # smoke test\n    repr(q)\n\n\nasync def test_UnboundedQueue_blocking() -> None:\n    record = []\n    q = _core.UnboundedQueue[int]()\n\n    async def get_batch_consumer() -> None:\n        while True:\n            batch = await q.get_batch()\n            assert batch\n            record.append(batch)\n\n    async def aiter_consumer() -> None:\n        async for batch in q:\n            assert batch\n            record.append(batch)\n\n    for consumer in (get_batch_consumer, aiter_consumer):\n        record.clear()\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(consumer)\n            await _core.wait_all_tasks_blocked()\n            stats = q.statistics()\n            assert stats.qsize == 0\n            assert stats.tasks_waiting == 1\n            q.put_nowait(10)\n            q.put_nowait(11)\n            await _core.wait_all_tasks_blocked()\n            q.put_nowait(12)\n            await _core.wait_all_tasks_blocked()\n            assert record == [[10, 11], [12]]\n            nursery.cancel_scope.cancel()\n\n\nasync def test_UnboundedQueue_fairness() -> None:\n    q = _core.UnboundedQueue[int]()\n\n    # If there's no-one else around, we can put stuff in and take it out\n    # again, no problem\n    q.put_nowait(1)\n    q.put_nowait(2)\n    assert q.get_batch_nowait() == [1, 2]\n\n    result = None\n\n    async def get_batch(q: _core.UnboundedQueue[int]) -> None:\n        nonlocal result\n        result = await q.get_batch()\n\n    # But if someone else is waiting to read, then they get dibs\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(get_batch, q)\n        await _core.wait_all_tasks_blocked()\n        q.put_nowait(3)\n        q.put_nowait(4)\n        with pytest.raises(_core.WouldBlock):\n            q.get_batch_nowait()\n    assert result == [3, 4]\n\n    # If two tasks are trying to read, they alternate\n    record = []\n\n    async def reader(name: str) -> None:\n        while True:\n            record.append((name, await q.get_batch()))\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(reader, \"a\")\n        await _core.wait_all_tasks_blocked()\n        nursery.start_soon(reader, \"b\")\n        await _core.wait_all_tasks_blocked()\n\n        for i in range(20):\n            q.put_nowait(i)\n            await _core.wait_all_tasks_blocked()\n\n        nursery.cancel_scope.cancel()\n\n    assert record == list(zip(itertools.cycle(\"ab\"), [[i] for i in range(20)]))\n\n\nasync def test_UnboundedQueue_trivial_yields() -> None:\n    q = _core.UnboundedQueue[None]()\n\n    q.put_nowait(None)\n    with assert_checkpoints():\n        await q.get_batch()\n\n    q.put_nowait(None)\n    with assert_checkpoints():\n        async for _ in q:  # pragma: no branch\n            break\n\n\nasync def test_UnboundedQueue_no_spurious_wakeups() -> None:\n    # If we have two tasks waiting, and put two items into the queue... then\n    # only one task wakes up\n    record = []\n\n    async def getter(q: _core.UnboundedQueue[int], i: int) -> None:\n        got = await q.get_batch()\n        record.append((i, got))\n\n    async with _core.open_nursery() as nursery:\n        q = _core.UnboundedQueue[int]()\n        nursery.start_soon(getter, q, 1)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(getter, q, 2)\n        await wait_all_tasks_blocked()\n\n        for i in range(10):\n            q.put_nowait(i)\n        await wait_all_tasks_blocked()\n\n        assert record == [(1, list(range(10)))]\n\n        nursery.cancel_scope.cancel()\n"
  },
  {
    "path": "src/trio/_core/_tests/test_windows.py",
    "content": "from __future__ import annotations\n\nimport os\nimport sys\nimport tempfile\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\nfrom unittest.mock import create_autospec\n\nimport pytest\n\non_windows = os.name == \"nt\"\n# Mark all the tests in this file as being windows-only\npytestmark = pytest.mark.skipif(not on_windows, reason=\"windows only\")\n\nassert (\n    sys.platform == \"win32\" or not TYPE_CHECKING\n)  # Skip type checking when not on Windows\n\nfrom ... import _core, sleep\nfrom ...testing import wait_all_tasks_blocked\nfrom .tutil import gc_collect_harder, restore_unraisablehook, slow\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n    from io import BufferedWriter\n\nif on_windows:\n    from .._windows_cffi import (\n        INVALID_HANDLE_VALUE,\n        FileFlags,\n        Handle,\n        ffi,\n        kernel32,\n        raise_winerror,\n    )\n\n\ndef test_winerror(monkeypatch: pytest.MonkeyPatch) -> None:\n    # this is unfortunately needed as the generated ffi is read-only\n    class FFIWrapper:\n        def getwinerror(self) -> None:\n            raise NotImplementedError(\"this is a fake implementation\")\n\n    mock = create_autospec(ffi.getwinerror)\n    monkeypatch.setattr(\"trio._core._windows_cffi.ffi\", FFIWrapper)\n    monkeypatch.setattr(\"trio._core._windows_cffi.ffi.getwinerror\", mock)\n\n    # Returning none = no error, should not happen.\n    mock.return_value = None\n    with pytest.raises(RuntimeError, match=r\"^No error set\\?$\"):\n        raise_winerror()\n    mock.assert_called_once_with()\n    mock.reset_mock()\n\n    with pytest.raises(RuntimeError, match=r\"^No error set\\?$\"):\n        raise_winerror(38)\n    mock.assert_called_once_with(38)\n    mock.reset_mock()\n\n    mock.return_value = (12, \"test error\")\n    with pytest.raises(\n        OSError,\n        match=r\"^\\[WinError 12\\] test error: 'file_1' -> 'file_2'$\",\n    ) as exc:\n        raise_winerror(filename=\"file_1\", filename2=\"file_2\")\n    mock.assert_called_once_with()\n    mock.reset_mock()\n    assert exc.value.winerror == 12\n    assert exc.value.strerror == \"test error\"\n    assert exc.value.filename == \"file_1\"\n    assert exc.value.filename2 == \"file_2\"\n\n    # With an explicit number passed in, it overrides what getwinerror() returns.\n    with pytest.raises(\n        OSError,\n        match=r\"^\\[WinError 18\\] test error: 'a/file' -> 'b/file'$\",\n    ) as exc:\n        raise_winerror(18, filename=\"a/file\", filename2=\"b/file\")\n    mock.assert_called_once_with(18)\n    mock.reset_mock()\n    assert exc.value.winerror == 18\n    assert exc.value.strerror == \"test error\"\n    assert exc.value.filename == \"a/file\"\n    assert exc.value.filename2 == \"b/file\"\n\n\n# The undocumented API that this is testing should be changed to stop using\n# UnboundedQueue (or just removed until we have time to redo it), but until\n# then we filter out the warning.\n@pytest.mark.filterwarnings(\"ignore:.*UnboundedQueue:trio.TrioDeprecationWarning\")\nasync def test_completion_key_listen() -> None:\n    from .. import _io_windows\n\n    async def post(key: int) -> None:\n        iocp = Handle(ffi.cast(\"HANDLE\", _core.current_iocp()))\n        for i in range(10):\n            print(\"post\", i)\n            if i % 3 == 0:\n                await _core.checkpoint()\n            success = kernel32.PostQueuedCompletionStatus(iocp, i, key, ffi.NULL)\n            assert success\n\n    with _core.monitor_completion_key() as (key, queue):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(post, key)\n            i = 0\n            print(\"loop\")\n            async for batch in queue:  # pragma: no branch\n                print(\"got some\", batch)\n                for info in batch:\n                    assert isinstance(info, _io_windows.CompletionKeyEventInfo)\n                    assert info.lpOverlapped == 0\n                    assert info.dwNumberOfBytesTransferred == i\n                    i += 1\n                if i == 10:\n                    break\n            print(\"end loop\")\n\n\nasync def test_readinto_overlapped() -> None:\n    data = b\"1\" * 1024 + b\"2\" * 1024 + b\"3\" * 1024 + b\"4\" * 1024\n    buffer = bytearray(len(data))\n\n    with tempfile.TemporaryDirectory() as tdir:\n        tfile = os.path.join(tdir, \"numbers.txt\")\n        with open(  # noqa: ASYNC230  # This is a test, synchronous is ok\n            tfile,\n            \"wb\",\n        ) as fp:\n            fp.write(data)\n            fp.flush()\n\n        rawname = tfile.encode(\"utf-16le\") + b\"\\0\\0\"\n        rawname_buf = ffi.from_buffer(rawname)\n        handle = kernel32.CreateFileW(\n            ffi.cast(\"LPCWSTR\", rawname_buf),\n            FileFlags.GENERIC_READ,\n            FileFlags.FILE_SHARE_READ,\n            ffi.NULL,  # no security attributes\n            FileFlags.OPEN_EXISTING,\n            FileFlags.FILE_FLAG_OVERLAPPED,\n            ffi.NULL,  # no template file\n        )\n        if handle == INVALID_HANDLE_VALUE:  # pragma: no cover\n            raise_winerror()\n\n        try:\n            with memoryview(buffer) as buffer_view:\n\n                async def read_region(start: int, end: int) -> None:\n                    await _core.readinto_overlapped(\n                        handle,\n                        buffer_view[start:end],\n                        start,\n                    )\n\n                _core.register_with_iocp(handle)\n                async with _core.open_nursery() as nursery:\n                    for start in range(0, 4096, 512):\n                        nursery.start_soon(read_region, start, start + 512)\n\n                assert buffer == data\n\n                with pytest.raises((BufferError, TypeError)):\n                    await _core.readinto_overlapped(handle, b\"immutable\")\n        finally:\n            kernel32.CloseHandle(handle)\n\n\n@contextmanager\ndef pipe_with_overlapped_read() -> Generator[tuple[BufferedWriter, int], None, None]:\n    import msvcrt\n    from asyncio.windows_utils import pipe\n\n    read_handle, write_handle = pipe(overlapped=(True, False))\n    try:\n        write_fd = msvcrt.open_osfhandle(write_handle, 0)\n        yield os.fdopen(write_fd, \"wb\", closefd=False), read_handle\n    finally:\n        kernel32.CloseHandle(Handle(ffi.cast(\"HANDLE\", read_handle)))\n        kernel32.CloseHandle(Handle(ffi.cast(\"HANDLE\", write_handle)))\n\n\n@restore_unraisablehook()\ndef test_forgot_to_register_with_iocp() -> None:\n    with pipe_with_overlapped_read() as (write_fp, read_handle):\n        with write_fp:\n            write_fp.write(b\"test\\n\")\n\n        left_run_yet = False\n\n        async def main() -> None:\n            target = bytearray(1)\n            try:\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(\n                        _core.readinto_overlapped,\n                        read_handle,\n                        target,\n                        name=\"xyz\",\n                    )\n                    await wait_all_tasks_blocked()\n                    nursery.cancel_scope.cancel()\n            finally:\n                # Run loop is exited without unwinding running tasks, so\n                # we don't get here until the main() coroutine is GC'ed\n                assert left_run_yet\n\n        with pytest.raises(_core.TrioInternalError) as exc_info:\n            _core.run(main)\n        left_run_yet = True\n        assert \"Failed to cancel overlapped I/O in xyz \" in str(exc_info.value)\n        assert \"forget to call register_with_iocp()?\" in str(exc_info.value)\n\n        # Make sure the Nursery.__del__ assertion about dangling children\n        # gets put with the correct test\n        del exc_info\n        gc_collect_harder()\n\n\n@slow\nasync def test_too_late_to_cancel() -> None:\n    import time\n\n    with pipe_with_overlapped_read() as (write_fp, read_handle):\n        _core.register_with_iocp(read_handle)\n        target = bytearray(6)\n        async with _core.open_nursery() as nursery:\n            # Start an async read in the background\n            nursery.start_soon(_core.readinto_overlapped, read_handle, target)\n            await wait_all_tasks_blocked()\n\n            # Synchronous write to the other end of the pipe\n            with write_fp:\n                write_fp.write(b\"test1\\ntest2\\n\")\n\n            # Note: not trio.sleep! We're making sure the OS level\n            # ReadFile completes, before Trio has a chance to execute\n            # another checkpoint and notice it completed.\n            time.sleep(1)  # noqa: ASYNC251\n            nursery.cancel_scope.cancel()\n        assert target[:6] == b\"test1\\n\"\n\n        # Do another I/O to make sure we've actually processed the\n        # fallback completion that was posted when CancelIoEx failed.\n        assert await _core.readinto_overlapped(read_handle, target) == 6\n        assert target[:6] == b\"test2\\n\"\n\n\ndef test_lsp_that_hooks_select_gives_good_error(\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    from .. import _io_windows\n    from .._windows_cffi import CData, WSAIoctls, _handle\n\n    def patched_get_underlying(\n        sock: int | CData,\n        *,\n        which: int = WSAIoctls.SIO_BASE_HANDLE,\n    ) -> CData:\n        if hasattr(sock, \"fileno\"):  # pragma: no branch\n            sock = sock.fileno()\n        if which == WSAIoctls.SIO_BSP_HANDLE_SELECT:\n            return _handle(sock + 1)\n        else:\n            return _handle(sock)\n\n    monkeypatch.setattr(_io_windows, \"_get_underlying_socket\", patched_get_underlying)\n    with pytest.raises(\n        RuntimeError,\n        match=\"SIO_BASE_HANDLE and SIO_BSP_HANDLE_SELECT differ\",\n    ):\n        _core.run(sleep, 0)\n\n\ndef test_lsp_that_completely_hides_base_socket_gives_good_error(\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    # This tests behavior with an LSP that fails SIO_BASE_HANDLE and returns\n    # self for SIO_BSP_HANDLE_SELECT (like Komodia), but also returns\n    # self for SIO_BSP_HANDLE_POLL. No known LSP does this, but we want to\n    # make sure we get an error rather than an infinite loop.\n\n    from .. import _io_windows\n    from .._windows_cffi import CData, WSAIoctls, _handle\n\n    def patched_get_underlying(\n        sock: int | CData,\n        *,\n        which: int = WSAIoctls.SIO_BASE_HANDLE,\n    ) -> CData:\n        if hasattr(sock, \"fileno\"):  # pragma: no branch\n            sock = sock.fileno()\n        if which == WSAIoctls.SIO_BASE_HANDLE:\n            raise OSError(\"nope\")\n        else:\n            return _handle(sock)\n\n    monkeypatch.setattr(_io_windows, \"_get_underlying_socket\", patched_get_underlying)\n    with pytest.raises(\n        RuntimeError,\n        match=\"SIO_BASE_HANDLE failed and SIO_BSP_HANDLE_POLL didn't return a diff\",\n    ):\n        _core.run(sleep, 0)\n"
  },
  {
    "path": "src/trio/_core/_tests/tutil.py",
    "content": "# Utilities for testing\nfrom __future__ import annotations\n\nimport asyncio\nimport gc\nimport os\nimport socket as stdlib_socket\nimport sys\nimport warnings\nfrom contextlib import closing, contextmanager\nfrom typing import TYPE_CHECKING, TypeVar\n\nimport pytest\n\n# See trio/_tests/pytest_plugin.py for the other half of this\nfrom trio._tests.pytest_plugin import RUN_SLOW\n\nif TYPE_CHECKING:\n    from collections.abc import Generator, Iterable, Sequence\n\nslow = pytest.mark.skipif(not RUN_SLOW, reason=\"use --run-slow to run slow tests\")\n\nT = TypeVar(\"T\")\n\ntry:\n    s = stdlib_socket.socket(stdlib_socket.AF_INET6, stdlib_socket.SOCK_STREAM, 0)\nexcept OSError:  # pragma: no cover\n    # Some systems don't even support creating an IPv6 socket, let alone\n    # binding it. (ex: Linux with 'ipv6.disable=1' in the kernel command line)\n    # We don't have any of those in our CI, and there's nothing that gets\n    # tested _only_ if can_create_ipv6 = False, so we'll just no-cover this.\n    can_create_ipv6 = False\n    can_bind_ipv6 = False\nelse:\n    can_create_ipv6 = True\n    with s:\n        try:\n            s.bind((\"::1\", 0))\n        except OSError:  # pragma: no cover # since support for 3.7 was removed\n            can_bind_ipv6 = False\n        else:\n            can_bind_ipv6 = True\n\ncreates_ipv6 = pytest.mark.skipif(not can_create_ipv6, reason=\"need IPv6\")\nbinds_ipv6 = pytest.mark.skipif(not can_bind_ipv6, reason=\"need IPv6\")\n\n\ndef gc_collect_harder() -> None:\n    # In the test suite we sometimes want to call gc.collect() to make sure\n    # that any objects with noisy __del__ methods (e.g. unawaited coroutines)\n    # get collected before we continue, so their noise doesn't leak into\n    # unrelated tests.\n    #\n    # On PyPy, coroutine objects (for example) can survive at least 1 round of\n    # garbage collection, because executing their __del__ method to print the\n    # warning can cause them to be resurrected. So we call collect a few times\n    # to make sure.\n    for _ in range(5):\n        gc.collect()\n\n\n# Some of our tests need to leak coroutines, and thus trigger the\n# \"RuntimeWarning: coroutine '...' was never awaited\" message. This context\n# manager should be used anywhere this happens to hide those messages, because\n# when expected they're clutter.\n@contextmanager\ndef ignore_coroutine_never_awaited_warnings() -> Generator[None, None, None]:\n    with warnings.catch_warnings():\n        warnings.filterwarnings(\"ignore\", message=\"coroutine '.*' was never awaited\")\n        try:\n            yield\n        finally:\n            # Make sure to trigger any coroutine __del__ methods now, before\n            # we leave the context manager.\n            gc_collect_harder()\n\n\ndef _noop(*args: object, **kwargs: object) -> None:\n    pass  # pragma: no cover\n\n\n@contextmanager\ndef restore_unraisablehook() -> Generator[None, None, None]:\n    sys.unraisablehook, prev = sys.__unraisablehook__, sys.unraisablehook\n    try:\n        yield\n    finally:\n        sys.unraisablehook = prev\n\n\n# Used to check sequences that might have some elements out of order.\n# Example usage:\n# The sequences [1, 2.1, 2.2, 3] and [1, 2.2, 2.1, 3] are both\n# matched by the template [1, {2.1, 2.2}, 3]\ndef check_sequence_matches(seq: Sequence[T], template: Iterable[T | set[T]]) -> None:\n    i = 0\n    for pattern in template:\n        if not isinstance(pattern, set):\n            pattern = {pattern}\n        got = set(seq[i : i + len(pattern)])\n        assert got == pattern\n        i += len(got)\n\n\n# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=246350\nskip_if_fbsd_pipes_broken = pytest.mark.skipif(\n    sys.platform != \"win32\"  # prevent mypy from complaining about missing uname\n    and hasattr(os, \"uname\")\n    and os.uname().sysname == \"FreeBSD\"\n    and os.uname().release[:4] < \"12.2\",\n    reason=\"hangs on FreeBSD 12.1 and earlier, due to FreeBSD bug #246350\",\n)\n\n\ndef create_asyncio_future_in_new_loop() -> asyncio.Future[object]:\n    with closing(asyncio.new_event_loop()) as loop:\n        return loop.create_future()\n"
  },
  {
    "path": "src/trio/_core/_tests/type_tests/nursery_start.py",
    "content": "\"\"\"Test variadic generic typing for Nursery.start[_soon]().\"\"\"\n\nfrom typing import TYPE_CHECKING\n\nfrom trio import TASK_STATUS_IGNORED, Nursery, TaskStatus\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n\nasync def task_0() -> None: ...\n\n\nasync def task_1a(value: int) -> None: ...\n\n\nasync def task_1b(value: str) -> None: ...\n\n\nasync def task_2a(a: int, b: str) -> None: ...\n\n\nasync def task_2b(a: str, b: int) -> None: ...\n\n\nasync def task_2c(a: str, b: int, optional: bool = False) -> None: ...\n\n\nasync def task_requires_kw(a: int, *, b: bool) -> None: ...\n\n\nasync def task_startable_1(\n    a: str,\n    *,\n    task_status: TaskStatus[bool] = TASK_STATUS_IGNORED,\n) -> None: ...\n\n\nasync def task_startable_2(\n    a: str,\n    b: float,\n    *,\n    task_status: TaskStatus[bool] = TASK_STATUS_IGNORED,\n) -> None: ...\n\n\nasync def task_requires_start(*, task_status: TaskStatus[str]) -> None:\n    \"\"\"Check a function requiring start() to be used.\"\"\"\n\n\nasync def task_pos_or_kw(value: str, task_status: TaskStatus[int]) -> None:\n    \"\"\"Check a function which doesn't use the *-syntax works.\"\"\"\n\n\ndef check_start_soon(nursery: Nursery) -> None:\n    \"\"\"start_soon() functionality.\"\"\"\n    nursery.start_soon(task_0)\n    nursery.start_soon(task_1a)  # type: ignore\n    nursery.start_soon(task_2b)  # type: ignore\n\n    nursery.start_soon(task_0, 45)  # type: ignore\n    nursery.start_soon(task_1a, 32)\n    nursery.start_soon(task_1b, 32)  # type: ignore\n    nursery.start_soon(task_1a, \"abc\")  # type: ignore\n    nursery.start_soon(task_1b, \"abc\")\n\n    nursery.start_soon(task_2b, \"abc\")  # type: ignore\n    nursery.start_soon(task_2a, 38, \"46\")\n    nursery.start_soon(task_2c, \"abc\", 12, True)\n\n    nursery.start_soon(task_2c, \"abc\", 12)\n    task_2c_cast: Callable[[str, int], Awaitable[object]] = (\n        task_2c  # The assignment makes it work.\n    )\n    nursery.start_soon(task_2c_cast, \"abc\", 12)\n\n    nursery.start_soon(task_requires_kw, 12, True)  # type: ignore\n    # Tasks following the start() API can be made to work.\n    nursery.start_soon(task_startable_1, \"cdf\")\n"
  },
  {
    "path": "src/trio/_core/_tests/type_tests/run.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, overload\n\nimport trio\nfrom typing_extensions import assert_type\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n\nasync def sleep_sort(values: Sequence[float]) -> list[float]:\n    return [1]\n\n\nasync def has_optional(arg: int | None = None) -> int:\n    return 5\n\n\n@overload\nasync def foo_overloaded(arg: int) -> str: ...\n\n\n@overload\nasync def foo_overloaded(arg: str) -> int: ...\n\n\nasync def foo_overloaded(arg: int | str) -> int | str:\n    if isinstance(arg, str):\n        return 5\n    return \"hello\"\n\n\nv = trio.run(\n    sleep_sort,\n    (1, 3, 5, 2, 4),\n    clock=trio.testing.MockClock(autojump_threshold=0),\n)\nassert_type(v, \"list[float]\")\ntrio.run(sleep_sort, [\"hi\", \"there\"])  # type: ignore[arg-type]\ntrio.run(sleep_sort)  # type: ignore[arg-type]\n\nr = trio.run(has_optional)\nassert_type(r, int)\nr = trio.run(has_optional, 5)\ntrio.run(has_optional, 7, 8)  # type: ignore[arg-type]\ntrio.run(has_optional, \"hello\")  # type: ignore[arg-type]\n\n\nassert_type(trio.run(foo_overloaded, 5), str)\nassert_type(trio.run(foo_overloaded, \"\"), int)\n"
  },
  {
    "path": "src/trio/_core/_thread_cache.py",
    "content": "from __future__ import annotations\n\nimport ctypes\nimport ctypes.util\nimport os\nimport sys\nimport traceback\nfrom functools import partial\nfrom itertools import count\nfrom threading import Lock, Thread\nfrom typing import TYPE_CHECKING, Any, Generic, TypeVar\n\nimport outcome\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\nRetT = TypeVar(\"RetT\")\n\n\ndef _to_os_thread_name(name: str) -> bytes:\n    # ctypes handles the trailing \\00\n    return name.encode(\"ascii\", errors=\"replace\")[:15]\n\n\n# used to construct the method used to set os thread name, or None, depending on platform.\n# called once on import\ndef get_os_thread_name_func() -> Callable[[int | None, str], None] | None:\n    def namefunc(\n        setname: Callable[[int, bytes], int],\n        ident: int | None,\n        name: str,\n    ) -> None:\n        # Thread.ident is None \"if it has not been started\". Unclear if that can happen\n        # with current usage.\n        if ident is not None:  # pragma: no cover\n            setname(ident, _to_os_thread_name(name))\n\n    # namefunc on Mac also takes an ident, even if pthread_setname_np doesn't/can't use it\n    # so the caller don't need to care about platform.\n    def darwin_namefunc(\n        setname: Callable[[bytes], int],\n        ident: int | None,\n        name: str,\n    ) -> None:\n        # I don't know if Mac can rename threads that hasn't been started, but default\n        # to no to be on the safe side.\n        if ident is not None:  # pragma: no cover\n            setname(_to_os_thread_name(name))\n\n    # find the pthread library\n    # this will fail on windows and musl\n    libpthread_path = ctypes.util.find_library(\"pthread\")\n    if not libpthread_path:\n        # musl includes pthread functions directly in libc.so\n        # (but note that find_library(\"c\") does not work on musl,\n        #  see: https://github.com/python/cpython/issues/65821)\n        # so try that library instead\n        # if it doesn't exist, CDLL() will fail below\n        libpthread_path = \"libc.so\"\n\n    # Sometimes windows can find the path, but gives a permission error when\n    # accessing it. Catching a wider exception in case of more esoteric errors.\n    # https://github.com/python-trio/trio/issues/2688\n    try:\n        libpthread = ctypes.CDLL(libpthread_path)\n    except Exception:  # pragma: no cover\n        return None\n\n    # get the setname method from it\n    # afaik this should never fail\n    pthread_setname_np = getattr(libpthread, \"pthread_setname_np\", None)\n    if pthread_setname_np is None:  # pragma: no cover\n        return None\n\n    # specify function prototype\n    pthread_setname_np.restype = ctypes.c_int\n\n    # on mac OSX pthread_setname_np does not take a thread id,\n    # it only lets threads name themselves, which is not a problem for us.\n    # Just need to make sure to call it correctly\n    if sys.platform == \"darwin\":\n        pthread_setname_np.argtypes = [ctypes.c_char_p]\n        return partial(darwin_namefunc, pthread_setname_np)\n\n    # otherwise assume linux parameter conventions. Should also work on *BSD\n    pthread_setname_np.argtypes = [ctypes.c_void_p, ctypes.c_char_p]\n    return partial(namefunc, pthread_setname_np)\n\n\n# construct os thread name method\nset_os_thread_name = get_os_thread_name_func()\n\n# The \"thread cache\" is a simple unbounded thread pool, i.e., it automatically\n# spawns as many threads as needed to handle all the requests its given. Its\n# only purpose is to cache worker threads so that they don't have to be\n# started from scratch every time we want to delegate some work to a thread.\n# It's expected that some higher-level code will track how many threads are in\n# use to avoid overwhelming the system (e.g. the limiter= argument to\n# trio.to_thread.run_sync).\n#\n# To maximize sharing, there's only one thread cache per process, even if you\n# have multiple calls to trio.run.\n#\n# Guarantees:\n#\n# It's safe to call start_thread_soon simultaneously from\n# multiple threads.\n#\n# Idle threads are chosen in LIFO order, i.e. we *don't* spread work evenly\n# over all threads. Instead we try to let some threads do most of the work\n# while others sit idle as much as possible. Compared to FIFO, this has better\n# memory cache behavior, and it makes it easier to detect when we have too\n# many threads, so idle ones can exit.\n#\n# This code assumes that 'dict' has the following properties:\n#\n# - __setitem__, __delitem__, and popitem are all thread-safe and atomic with\n#   respect to each other. This is guaranteed by the GIL.\n#\n# - popitem returns the most-recently-added item (i.e., __setitem__ + popitem\n#   give you a LIFO queue). This relies on dicts being insertion-ordered, like\n#   they are in py36+.\n\n# How long a thread will idle waiting for new work before gives up and exits.\n# This value is pretty arbitrary; I don't think it matters too much.\nIDLE_TIMEOUT = 10  # seconds\n\nname_counter = count()\n\n\nclass WorkerThread(Generic[RetT]):\n    __slots__ = (\"_default_name\", \"_job\", \"_thread\", \"_thread_cache\", \"_worker_lock\")\n\n    def __init__(self, thread_cache: ThreadCache) -> None:\n        self._job: (\n            tuple[\n                Callable[[], RetT],\n                Callable[[outcome.Outcome[RetT]], object],\n                str | None,\n            ]\n            | None\n        ) = None\n        self._thread_cache = thread_cache\n        # This Lock is used in an unconventional way.\n        #\n        # \"Unlocked\" means we have a pending job that's been assigned to us;\n        # \"locked\" means that we don't.\n        #\n        # Initially we have no job, so it starts out in locked state.\n        self._worker_lock = Lock()\n        self._worker_lock.acquire()\n        self._default_name = f\"Trio thread {next(name_counter)}\"\n\n        self._thread = Thread(target=self._work, name=self._default_name, daemon=True)\n\n        if set_os_thread_name:\n            set_os_thread_name(self._thread.ident, self._default_name)\n        self._thread.start()\n\n    def _handle_job(self) -> None:\n        # Handle job in a separate method to ensure user-created\n        # objects are cleaned up in a consistent manner.\n        assert self._job is not None\n        fn, deliver, name = self._job\n        self._job = None\n\n        # set name\n        if name is not None:\n            self._thread.name = name\n            if set_os_thread_name:\n                set_os_thread_name(self._thread.ident, name)\n        result = outcome.capture(fn)\n\n        # reset name if it was changed\n        if name is not None:\n            self._thread.name = self._default_name\n            if set_os_thread_name:\n                set_os_thread_name(self._thread.ident, self._default_name)\n\n        # Tell the cache that we're available to be assigned a new\n        # job. We do this *before* calling 'deliver', so that if\n        # 'deliver' triggers a new job, it can be assigned to us\n        # instead of spawning a new thread.\n        self._thread_cache._idle_workers[self] = None\n        try:\n            deliver(result)\n        except BaseException as e:\n            print(\"Exception while delivering result of thread\", file=sys.stderr)\n            traceback.print_exception(type(e), e, e.__traceback__)\n\n    def _work(self) -> None:\n        while True:\n            if self._worker_lock.acquire(timeout=IDLE_TIMEOUT):\n                # We got a job\n                self._handle_job()\n            else:\n                # Timeout acquiring lock, so we can probably exit. But,\n                # there's a race condition: we might be assigned a job *just*\n                # as we're about to exit. So we have to check.\n                try:\n                    del self._thread_cache._idle_workers[self]\n                except KeyError:\n                    # Someone else removed us from the idle worker queue, so\n                    # they must be in the process of assigning us a job - loop\n                    # around and wait for it.\n                    continue\n                else:\n                    # We successfully removed ourselves from the idle\n                    # worker queue, so no more jobs are incoming; it's safe to\n                    # exit.\n                    return\n\n\nclass ThreadCache:\n    __slots__ = (\"_idle_workers\",)\n\n    def __init__(self) -> None:\n        self._idle_workers: dict[WorkerThread[Any], None] = {}  # type: ignore[explicit-any]\n\n    def start_thread_soon(\n        self,\n        fn: Callable[[], RetT],\n        deliver: Callable[[outcome.Outcome[RetT]], object],\n        name: str | None = None,\n    ) -> None:\n        worker: WorkerThread[RetT]\n        try:\n            worker, _ = self._idle_workers.popitem()\n        except KeyError:\n            worker = WorkerThread(self)\n        worker._job = (fn, deliver, name)\n        worker._worker_lock.release()\n\n\nTHREAD_CACHE = ThreadCache()\n\n\ndef start_thread_soon(\n    fn: Callable[[], RetT],\n    deliver: Callable[[outcome.Outcome[RetT]], object],\n    name: str | None = None,\n) -> None:\n    \"\"\"Runs ``deliver(outcome.capture(fn))`` in a worker thread.\n\n    Generally ``fn`` does some blocking work, and ``deliver`` delivers the\n    result back to whoever is interested.\n\n    This is a low-level, no-frills interface, very similar to using\n    `threading.Thread` to spawn a thread directly. The main difference is\n    that this function tries to reuse threads when possible, so it can be\n    a bit faster than `threading.Thread`.\n\n    Worker threads have the `~threading.Thread.daemon` flag set, which means\n    that if your main thread exits, worker threads will automatically be\n    killed. If you want to make sure that your ``fn`` runs to completion, then\n    you should make sure that the main thread remains alive until ``deliver``\n    is called.\n\n    It is safe to call this function simultaneously from multiple threads.\n\n    Args:\n\n        fn (sync function): Performs arbitrary blocking work.\n\n        deliver (sync function): Takes the `outcome.Outcome` of ``fn``, and\n          delivers it. *Must not block.*\n\n    Because worker threads are cached and reused for multiple calls, neither\n    function should mutate thread-level state, like `threading.local` objects\n    – or if they do, they should be careful to revert their changes before\n    returning.\n\n    Note:\n\n        The split between ``fn`` and ``deliver`` serves two purposes. First,\n        it's convenient, since most callers need something like this anyway.\n\n        Second, it avoids a small race condition that could cause too many\n        threads to be spawned. Consider a program that wants to run several\n        jobs sequentially on a thread, so the main thread submits a job, waits\n        for it to finish, submits another job, etc. In theory, this program\n        should only need one worker thread. But what could happen is:\n\n        1. Worker thread: First job finishes, and calls ``deliver``.\n\n        2. Main thread: receives notification that the job finished, and calls\n           ``start_thread_soon``.\n\n        3. Main thread: sees that no worker threads are marked idle, so spawns\n           a second worker thread.\n\n        4. Original worker thread: marks itself as idle.\n\n        To avoid this, threads mark themselves as idle *before* calling\n        ``deliver``.\n\n        Is this potential extra thread a major problem? Maybe not, but it's\n        easy enough to avoid, and we figure that if the user is trying to\n        limit how many threads they're using then it's polite to respect that.\n\n    \"\"\"\n    THREAD_CACHE.start_thread_soon(fn, deliver, name)\n\n\ndef clear_worker_threads() -> (\n    None\n):  # pragma: no cover # see test_clear_thread_cache_after_fork\n    # This is OK because the child process does not actually have any\n    # worker threads. Additionally, while WorkerThread keeps a strong\n    # reference and so would get affected, the only place those are\n    # stored is here.\n    THREAD_CACHE._idle_workers.clear()\n\n\nif hasattr(os, \"register_at_fork\"):\n    os.register_at_fork(after_in_child=clear_worker_threads)\n"
  },
  {
    "path": "src/trio/_core/_traps.py",
    "content": "\"\"\"These are the only functions that ever yield back to the task runner.\"\"\"\n\nfrom __future__ import annotations\n\nimport enum\nimport types\n\n# typing.Callable is necessary because collections.abc.Callable breaks\n# test_static_tool_sees_all_symbols in 3.10.\nfrom typing import (  # noqa: UP035\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    NoReturn,\n    TypeAlias,\n    cast,\n)\n\nimport attrs\nimport outcome\n\nfrom . import _run\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Generator\n\n    from ._run import Task\n\nRaiseCancelT: TypeAlias = Callable[[], NoReturn]\n\n\n# This class object is used as a singleton.\n# Not exported in the trio._core namespace, but imported directly by _run.\nclass CancelShieldedCheckpoint:\n    __slots__ = ()\n\n\n# Not exported in the trio._core namespace, but imported directly by _run.\n@attrs.frozen(slots=False)\nclass WaitTaskRescheduled:\n    abort_func: Callable[[RaiseCancelT], Abort]\n\n\n# Not exported in the trio._core namespace, but imported directly by _run.\n@attrs.frozen(slots=False)\nclass PermanentlyDetachCoroutineObject:\n    final_outcome: outcome.Outcome[object]\n\n\nMessageType: TypeAlias = (\n    type[CancelShieldedCheckpoint]\n    | WaitTaskRescheduled\n    | PermanentlyDetachCoroutineObject\n    | object\n)\n\n\n# Helper for the bottommost 'yield'. You can't use 'yield' inside an async\n# function, but you can inside a generator, and if you decorate your generator\n# with @types.coroutine, then it's even awaitable. However, it's still not a\n# real async function: in particular, it isn't recognized by\n# inspect.iscoroutinefunction, and it doesn't trigger the unawaited coroutine\n# tracking machinery. Since our traps are public APIs, we make them real async\n# functions, and then this helper takes care of the actual yield:\n@types.coroutine\ndef _real_async_yield(\n    obj: MessageType,\n) -> Generator[MessageType, None, None]:\n    # \"Using `yield` and `return {value}` in a generator function can\n    # lead to confusing behavior\"\n    return (yield obj)  # noqa: B901\n\n\n# Real yield value is from trio's main loop, but type checkers can't\n# understand that, so we cast it to make type checkers understand.\n_async_yield = cast(\n    \"Callable[[MessageType], Awaitable[outcome.Outcome[object]]]\",\n    _real_async_yield,\n)\n\n\nasync def cancel_shielded_checkpoint() -> None:\n    \"\"\"Introduce a schedule point, but not a cancel point.\n\n    This is *not* a :ref:`checkpoint <checkpoints>`, but it is half of a\n    checkpoint, and when combined with :func:`checkpoint_if_cancelled` it can\n    make a full checkpoint.\n\n    Equivalent to (but potentially more efficient than)::\n\n        with trio.CancelScope(shield=True):\n            await trio.lowlevel.checkpoint()\n\n    \"\"\"\n    (await _async_yield(CancelShieldedCheckpoint)).unwrap()\n\n\n# Return values for abort functions\nclass Abort(enum.Enum):\n    \"\"\":class:`enum.Enum` used as the return value from abort functions.\n\n    See :func:`wait_task_rescheduled` for details.\n\n    .. data:: SUCCEEDED\n              FAILED\n\n    \"\"\"\n\n    SUCCEEDED = 1\n    FAILED = 2\n\n\n# Should always return the type a Task \"expects\", unless you willfully reschedule it\n# with a bad value.\nasync def wait_task_rescheduled(  # type: ignore[explicit-any]\n    abort_func: Callable[[RaiseCancelT], Abort],\n) -> Any:\n    \"\"\"Put the current task to sleep, with cancellation support.\n\n    This is the lowest-level API for blocking in Trio. Every time a\n    :class:`~trio.lowlevel.Task` blocks, it does so by calling this function\n    (usually indirectly via some higher-level API).\n\n    This is a tricky interface with no guard rails. If you can use\n    :class:`ParkingLot` or the built-in I/O wait functions instead, then you\n    should.\n\n    Generally the way it works is that before calling this function, you make\n    arrangements for \"someone\" to call :func:`reschedule` on the current task\n    at some later point.\n\n    Then you call :func:`wait_task_rescheduled`, passing in ``abort_func``, an\n    \"abort callback\".\n\n    (Terminology: in Trio, \"aborting\" is the process of attempting to\n    interrupt a blocked task to deliver a cancellation.)\n\n    There are two possibilities for what happens next:\n\n    1. \"Someone\" calls :func:`reschedule` on the current task, and\n       :func:`wait_task_rescheduled` returns or raises whatever value or error\n       was passed to :func:`reschedule`.\n\n    2. The call's context transitions to a cancelled state (e.g. due to a\n       timeout expiring). When this happens, the ``abort_func`` is called. Its\n       interface looks like::\n\n           def abort_func(raise_cancel):\n               ...\n               return trio.lowlevel.Abort.SUCCEEDED  # or FAILED\n\n       It should attempt to clean up any state associated with this call, and\n       in particular, arrange that :func:`reschedule` will *not* be called\n       later. If (and only if!) it is successful, then it should return\n       :data:`Abort.SUCCEEDED`, in which case the task will automatically be\n       rescheduled with an appropriate :exc:`~trio.Cancelled` error.\n\n       Otherwise, it should return :data:`Abort.FAILED`. This means that the\n       task can't be cancelled at this time, and still has to make sure that\n       \"someone\" eventually calls :func:`reschedule`.\n\n       At that point there are again two possibilities. You can simply ignore\n       the cancellation altogether: wait for the operation to complete and\n       then reschedule and continue as normal. (For example, this is what\n       :func:`trio.to_thread.run_sync` does if cancellation is disabled.)\n       The other possibility is that the ``abort_func`` does succeed in\n       cancelling the operation, but for some reason isn't able to report that\n       right away. (Example: on Windows, it's possible to request that an\n       async (\"overlapped\") I/O operation be cancelled, but this request is\n       *also* asynchronous – you don't find out until later whether the\n       operation was actually cancelled or not.)  To report a delayed\n       cancellation, then you should reschedule the task yourself, and call\n       the ``raise_cancel`` callback passed to ``abort_func`` to raise a\n       :exc:`~trio.Cancelled` (or possibly :exc:`KeyboardInterrupt`) exception\n       into this task. Either of the approaches sketched below can work::\n\n          # Option 1:\n          # Catch the exception from raise_cancel and inject it into the task.\n          # (This is what Trio does automatically for you if you return\n          # Abort.SUCCEEDED.)\n          trio.lowlevel.reschedule(task, outcome.capture(raise_cancel))\n\n          # Option 2:\n          # wait to be woken by \"someone\", and then decide whether to raise\n          # the error from inside the task.\n          outer_raise_cancel = None\n          def abort(inner_raise_cancel):\n              nonlocal outer_raise_cancel\n              outer_raise_cancel = inner_raise_cancel\n              TRY_TO_CANCEL_OPERATION()\n              return trio.lowlevel.Abort.FAILED\n          await wait_task_rescheduled(abort)\n          if OPERATION_WAS_SUCCESSFULLY_CANCELLED:\n              # raises the error\n              outer_raise_cancel()\n\n       In any case it's guaranteed that we only call the ``abort_func`` at most\n       once per call to :func:`wait_task_rescheduled`.\n\n    Sometimes, it's useful to be able to share some mutable sleep-related data\n    between the sleeping task, the abort function, and the waking task. You\n    can use the sleeping task's :data:`~Task.custom_sleep_data` attribute to\n    store this data, and Trio won't touch it, except to make sure that it gets\n    cleared when the task is rescheduled.\n\n    .. warning::\n\n       If your ``abort_func`` raises an error, or returns any value other than\n       :data:`Abort.SUCCEEDED` or :data:`Abort.FAILED`, then Trio will crash\n       violently. Be careful! Similarly, it is entirely possible to deadlock a\n       Trio program by failing to reschedule a blocked task, or cause havoc by\n       calling :func:`reschedule` too many times. Remember what we said up\n       above about how you should use a higher-level API if at all possible?\n\n    \"\"\"\n    return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()\n\n\nasync def permanently_detach_coroutine_object(\n    final_outcome: outcome.Outcome[object],\n) -> object:\n    \"\"\"Permanently detach the current task from the Trio scheduler.\n\n    Normally, a Trio task doesn't exit until its coroutine object exits. When\n    you call this function, Trio acts like the coroutine object just exited\n    and the task terminates with the given outcome. This is useful if you want\n    to permanently switch the coroutine object over to a different coroutine\n    runner.\n\n    When the calling coroutine enters this function it's running under Trio,\n    and when the function returns it's running under the foreign coroutine\n    runner.\n\n    You should make sure that the coroutine object has released any\n    Trio-specific resources it has acquired (e.g. nurseries).\n\n    Args:\n      final_outcome (outcome.Outcome): Trio acts as if the current task exited\n          with the given return value or exception.\n\n    Returns or raises whatever value or exception the new coroutine runner\n    uses to resume the coroutine.\n\n    \"\"\"\n    if _run.current_task().child_nurseries:\n        raise RuntimeError(\n            \"can't permanently detach a coroutine object with open nurseries\",\n        )\n    return await _async_yield(PermanentlyDetachCoroutineObject(final_outcome))\n\n\nasync def temporarily_detach_coroutine_object(\n    abort_func: Callable[[RaiseCancelT], Abort],\n) -> object:\n    \"\"\"Temporarily detach the current coroutine object from the Trio\n    scheduler.\n\n    When the calling coroutine enters this function it's running under Trio,\n    and when the function returns it's running under the foreign coroutine\n    runner.\n\n    The Trio :class:`Task` will continue to exist, but will be suspended until\n    you use :func:`reattach_detached_coroutine_object` to resume it. In the\n    mean time, you can use another coroutine runner to schedule the coroutine\n    object. In fact, you have to – the function doesn't return until the\n    coroutine is advanced from outside.\n\n    Note that you'll need to save the current :class:`Task` object to later\n    resume; you can retrieve it with :func:`current_task`. You can also use\n    this :class:`Task` object to retrieve the coroutine object – see\n    :data:`Task.coro`.\n\n    Args:\n      abort_func: Same as for :func:`wait_task_rescheduled`, except that it\n          must return :data:`Abort.FAILED`. (If it returned\n          :data:`Abort.SUCCEEDED`, then Trio would attempt to reschedule the\n          detached task directly without going through\n          :func:`reattach_detached_coroutine_object`, which would be bad.)\n          Your ``abort_func`` should still arrange for whatever the coroutine\n          object is doing to be cancelled, and then reattach to Trio and call\n          the ``raise_cancel`` callback, if possible.\n\n    Returns or raises whatever value or exception the new coroutine runner\n    uses to resume the coroutine.\n\n    \"\"\"\n    return await _async_yield(WaitTaskRescheduled(abort_func))\n\n\nasync def reattach_detached_coroutine_object(task: Task, yield_value: object) -> None:\n    \"\"\"Reattach a coroutine object that was detached using\n    :func:`temporarily_detach_coroutine_object`.\n\n    When the calling coroutine enters this function it's running under the\n    foreign coroutine runner, and when the function returns it's running under\n    Trio.\n\n    This must be called from inside the coroutine being resumed, and yields\n    whatever value you pass in. (Presumably you'll pass a value that will\n    cause the current coroutine runner to stop scheduling this task.) Then the\n    coroutine is resumed by the Trio scheduler at the next opportunity.\n\n    Args:\n      task (Task): The Trio task object that the current coroutine was\n          detached from.\n      yield_value (object): The object to yield to the current coroutine\n          runner.\n\n    \"\"\"\n    # This is a kind of crude check – in particular, it can fail if the\n    # passed-in task is where the coroutine *runner* is running. But this is\n    # an experts-only interface, and there's no easy way to do a more accurate\n    # check, so I guess that's OK.\n    if not task.coro.cr_running:\n        raise RuntimeError(\"given task does not match calling coroutine\")\n    _run.reschedule(task, outcome.Value(\"reattaching\"))\n    value = await _async_yield(yield_value)\n    assert value == outcome.Value(\"reattaching\")\n"
  },
  {
    "path": "src/trio/_core/_unbounded_queue.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Generic, TypeVar\n\nimport attrs\n\nfrom .. import _core\nfrom .._deprecate import deprecated\nfrom .._util import final\n\nT = TypeVar(\"T\")\n\nif TYPE_CHECKING:\n    from typing_extensions import Self\n\n\n@attrs.frozen\nclass UnboundedQueueStatistics:\n    \"\"\"An object containing debugging information.\n\n    Currently, the following fields are defined:\n\n    * ``qsize``: The number of items currently in the queue.\n    * ``tasks_waiting``: The number of tasks blocked on this queue's\n      :meth:`get_batch` method.\n\n    \"\"\"\n\n    qsize: int\n    tasks_waiting: int\n\n\n@final\nclass UnboundedQueue(Generic[T]):\n    \"\"\"An unbounded queue suitable for certain unusual forms of inter-task\n    communication.\n\n    This class is designed for use as a queue in cases where the producer for\n    some reason cannot be subjected to back-pressure, i.e., :meth:`put_nowait`\n    has to always succeed. In order to prevent the queue backlog from actually\n    growing without bound, the consumer API is modified to dequeue items in\n    \"batches\". If a consumer task processes each batch without yielding, then\n    this helps achieve (but does not guarantee) an effective bound on the\n    queue's memory use, at the cost of potentially increasing system latencies\n    in general. You should generally prefer to use a memory channel\n    instead if you can.\n\n    Currently each batch completely empties the queue, but `this may change in\n    the future <https://github.com/python-trio/trio/issues/51>`__.\n\n    A :class:`UnboundedQueue` object can be used as an asynchronous iterator,\n    where each iteration returns a new batch of items. I.e., these two loops\n    are equivalent::\n\n       async for batch in queue:\n           ...\n\n       while True:\n           obj = await queue.get_batch()\n           ...\n\n    \"\"\"\n\n    @deprecated(\n        \"0.9.0\",\n        issue=497,\n        thing=\"trio.lowlevel.UnboundedQueue\",\n        instead=\"trio.open_memory_channel(math.inf)\",\n        use_triodeprecationwarning=True,\n    )\n    def __init__(self) -> None:\n        self._lot = _core.ParkingLot()\n        self._data: list[T] = []\n        # used to allow handoff from put to the first task in the lot\n        self._can_get = False\n\n    def __repr__(self) -> str:\n        return f\"<UnboundedQueue holding {len(self._data)} items>\"\n\n    def qsize(self) -> int:\n        \"\"\"Returns the number of items currently in the queue.\"\"\"\n        return len(self._data)\n\n    def empty(self) -> bool:\n        \"\"\"Returns True if the queue is empty, False otherwise.\n\n        There is some subtlety to interpreting this method's return value: see\n        `issue #63 <https://github.com/python-trio/trio/issues/63>`__.\n\n        \"\"\"\n        return not self._data\n\n    @_core.enable_ki_protection\n    def put_nowait(self, obj: T) -> None:\n        \"\"\"Put an object into the queue, without blocking.\n\n        This always succeeds, because the queue is unbounded. We don't provide\n        a blocking ``put`` method, because it would never need to block.\n\n        Args:\n          obj (object): The object to enqueue.\n\n        \"\"\"\n        if not self._data:\n            assert not self._can_get\n            if self._lot:\n                self._lot.unpark(count=1)\n            else:\n                self._can_get = True\n        self._data.append(obj)\n\n    def _get_batch_protected(self) -> list[T]:\n        data = self._data.copy()\n        self._data.clear()\n        self._can_get = False\n        return data\n\n    def get_batch_nowait(self) -> list[T]:\n        \"\"\"Attempt to get the next batch from the queue, without blocking.\n\n        Returns:\n          list: A list of dequeued items, in order. On a successful call this\n              list is always non-empty; if it would be empty we raise\n              :exc:`~trio.WouldBlock` instead.\n\n        Raises:\n          ~trio.WouldBlock: if the queue is empty.\n\n        \"\"\"\n        if not self._can_get:\n            raise _core.WouldBlock\n        return self._get_batch_protected()\n\n    async def get_batch(self) -> list[T]:\n        \"\"\"Get the next batch from the queue, blocking as necessary.\n\n        Returns:\n          list: A list of dequeued items, in order. This list is always\n              non-empty.\n\n        \"\"\"\n        await _core.checkpoint_if_cancelled()\n        if not self._can_get:\n            await self._lot.park()\n            return self._get_batch_protected()\n        else:\n            try:\n                return self._get_batch_protected()\n            finally:\n                await _core.cancel_shielded_checkpoint()\n\n    def statistics(self) -> UnboundedQueueStatistics:\n        \"\"\"Return an :class:`UnboundedQueueStatistics` object containing debugging information.\"\"\"\n        return UnboundedQueueStatistics(\n            qsize=len(self._data),\n            tasks_waiting=self._lot.statistics().tasks_waiting,\n        )\n\n    def __aiter__(self) -> Self:\n        return self\n\n    async def __anext__(self) -> list[T]:\n        return await self.get_batch()\n"
  },
  {
    "path": "src/trio/_core/_wakeup_socketpair.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport signal\nimport socket\nimport warnings\n\nfrom .. import _core\nfrom .._util import is_main_thread\n\n\nclass WakeupSocketpair:\n    def __init__(self) -> None:\n        # explicitly typed to please `pyright --verifytypes` without `--ignoreexternal`\n        self.wakeup_sock: socket.socket\n        self.write_sock: socket.socket\n\n        self.wakeup_sock, self.write_sock = socket.socketpair()\n        self.wakeup_sock.setblocking(False)\n        self.write_sock.setblocking(False)\n        # This somewhat reduces the amount of memory wasted queueing up data\n        # for wakeups. With these settings, maximum number of 1-byte sends\n        # before getting BlockingIOError:\n        #   Linux 4.8: 6\n        #   macOS (darwin 15.5): 1\n        #   Windows 10: 525347\n        # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send\n        # blocking, even on non-blocking sockets, so don't do that.)\n        self.wakeup_sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1)\n        self.write_sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)\n        # On Windows this is a TCP socket so this might matter. On other\n        # platforms this fails b/c AF_UNIX sockets aren't actually TCP.\n        with contextlib.suppress(OSError):\n            self.write_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)\n        self.old_wakeup_fd: int | None = None\n\n    def wakeup_thread_and_signal_safe(self) -> None:\n        with contextlib.suppress(BlockingIOError):\n            self.write_sock.send(b\"\\x00\")\n\n    async def wait_woken(self) -> None:\n        await _core.wait_readable(self.wakeup_sock)\n        self.drain()\n\n    def drain(self) -> None:\n        try:\n            while True:\n                self.wakeup_sock.recv(2**16)\n        except BlockingIOError:\n            pass\n\n    def wakeup_on_signals(self) -> None:\n        assert self.old_wakeup_fd is None\n        if not is_main_thread():\n            return\n        fd = self.write_sock.fileno()\n        self.old_wakeup_fd = signal.set_wakeup_fd(fd, warn_on_full_buffer=False)\n        if self.old_wakeup_fd != -1:\n            warnings.warn(\n                RuntimeWarning(\n                    \"It looks like Trio's signal handling code might have \"\n                    \"collided with another library you're using. If you're \"\n                    \"running Trio in guest mode, then this might mean you \"\n                    \"should set host_uses_signal_set_wakeup_fd=True. \"\n                    \"Otherwise, file a bug on Trio and we'll help you figure \"\n                    \"out what's going on.\",\n                ),\n                stacklevel=1,\n            )\n\n    def close(self) -> None:\n        self.wakeup_sock.close()\n        self.write_sock.close()\n        if self.old_wakeup_fd is not None:\n            signal.set_wakeup_fd(self.old_wakeup_fd)\n"
  },
  {
    "path": "src/trio/_core/_windows_cffi.py",
    "content": "from __future__ import annotations\n\nimport enum\nfrom typing import TYPE_CHECKING, NewType, NoReturn, Protocol, TypeAlias, cast\n\nif TYPE_CHECKING:\n    import cffi\n\n    CData: TypeAlias = cffi.api.FFI.CData\n    CType: TypeAlias = cffi.api.FFI.CType\n\nfrom ._generated_windows_ffi import ffi\n\n################################################################\n# Functions and types\n################################################################\n\nif not TYPE_CHECKING:\n    CData: TypeAlias = ffi.CData\n    CType: TypeAlias = ffi.CType\n\nAlwaysNull: TypeAlias = CData  # We currently always pass ffi.NULL here.\nHandle = NewType(\"Handle\", CData)\nHandleArray = NewType(\"HandleArray\", CData)\n\n\nclass _Kernel32(Protocol):\n    \"\"\"Statically typed version of the kernel32.dll functions we use.\"\"\"\n\n    def CreateIoCompletionPort(\n        self,\n        FileHandle: Handle,\n        ExistingCompletionPort: CData | AlwaysNull,\n        CompletionKey: int,\n        NumberOfConcurrentThreads: int,\n        /,\n    ) -> Handle: ...\n\n    def CreateEventA(\n        self,\n        lpEventAttributes: AlwaysNull,\n        bManualReset: bool,\n        bInitialState: bool,\n        lpName: AlwaysNull,\n        /,\n    ) -> Handle: ...\n\n    def SetFileCompletionNotificationModes(\n        self,\n        handle: Handle,\n        flags: CompletionModes,\n        /,\n    ) -> int: ...\n\n    def PostQueuedCompletionStatus(\n        self,\n        CompletionPort: Handle,\n        dwNumberOfBytesTransferred: int,\n        dwCompletionKey: int,\n        lpOverlapped: CData | AlwaysNull,\n        /,\n    ) -> bool: ...\n\n    def CancelIoEx(\n        self,\n        hFile: Handle,\n        lpOverlapped: CData | AlwaysNull,\n        /,\n    ) -> bool: ...\n\n    def WriteFile(\n        self,\n        hFile: Handle,\n        # not sure about this type\n        lpBuffer: CData,\n        nNumberOfBytesToWrite: int,\n        lpNumberOfBytesWritten: AlwaysNull,\n        lpOverlapped: _Overlapped,\n        /,\n    ) -> bool: ...\n\n    def ReadFile(\n        self,\n        hFile: Handle,\n        # not sure about this type\n        lpBuffer: CData,\n        nNumberOfBytesToRead: int,\n        lpNumberOfBytesRead: AlwaysNull,\n        lpOverlapped: _Overlapped,\n        /,\n    ) -> bool: ...\n\n    def GetQueuedCompletionStatusEx(\n        self,\n        CompletionPort: Handle,\n        lpCompletionPortEntries: CData,\n        ulCount: int,\n        ulNumEntriesRemoved: CData,\n        dwMilliseconds: int,\n        fAlertable: bool | int,\n        /,\n    ) -> CData: ...\n\n    def CreateFileW(\n        self,\n        lpFileName: CData,\n        dwDesiredAccess: FileFlags,\n        dwShareMode: FileFlags,\n        lpSecurityAttributes: AlwaysNull,\n        dwCreationDisposition: FileFlags,\n        dwFlagsAndAttributes: FileFlags,\n        hTemplateFile: AlwaysNull,\n        /,\n    ) -> Handle: ...\n\n    def WaitForSingleObject(self, hHandle: Handle, dwMilliseconds: int, /) -> CData: ...\n\n    def WaitForMultipleObjects(\n        self,\n        nCount: int,\n        lpHandles: HandleArray,\n        bWaitAll: bool,\n        dwMilliseconds: int,\n        /,\n    ) -> ErrorCodes: ...\n\n    def SetEvent(self, handle: Handle, /) -> None: ...\n\n    def CloseHandle(self, handle: Handle, /) -> bool: ...\n\n    def DeviceIoControl(\n        self,\n        hDevice: Handle,\n        dwIoControlCode: int,\n        # this is wrong (it's not always null)\n        lpInBuffer: AlwaysNull,\n        nInBufferSize: int,\n        # this is also wrong\n        lpOutBuffer: AlwaysNull,\n        nOutBufferSize: int,\n        lpBytesReturned: AlwaysNull,\n        lpOverlapped: CData,\n        /,\n    ) -> bool: ...\n\n\nclass _Nt(Protocol):\n    \"\"\"Statically typed version of the dtdll.dll functions we use.\"\"\"\n\n    def RtlNtStatusToDosError(self, status: int, /) -> ErrorCodes: ...\n\n\nclass _Ws2(Protocol):\n    \"\"\"Statically typed version of the ws2_32.dll functions we use.\"\"\"\n\n    def WSAGetLastError(self) -> int: ...\n\n    def WSAIoctl(\n        self,\n        socket: CData,\n        dwIoControlCode: WSAIoctls,\n        lpvInBuffer: AlwaysNull,\n        cbInBuffer: int,\n        lpvOutBuffer: CData,\n        cbOutBuffer: int,\n        lpcbBytesReturned: CData,  # int*\n        lpOverlapped: AlwaysNull,\n        # actually LPWSAOVERLAPPED_COMPLETION_ROUTINE\n        lpCompletionRoutine: AlwaysNull,\n        /,\n    ) -> int: ...\n\n\nclass _DummyStruct(Protocol):\n    Offset: int\n    OffsetHigh: int\n\n\nclass _DummyUnion(Protocol):\n    DUMMYSTRUCTNAME: _DummyStruct\n    Pointer: object\n\n\nclass _Overlapped(Protocol):\n    Internal: int\n    InternalHigh: int\n    DUMMYUNIONNAME: _DummyUnion\n    hEvent: Handle\n\n\nkernel32 = cast(\"_Kernel32\", ffi.dlopen(\"kernel32.dll\"))\nntdll = cast(\"_Nt\", ffi.dlopen(\"ntdll.dll\"))\nws2_32 = cast(\"_Ws2\", ffi.dlopen(\"ws2_32.dll\"))\n\n################################################################\n# Magic numbers\n################################################################\n\n# Here's a great resource for looking these up:\n#   https://www.magnumdb.com\n# (Tip: check the box to see \"Hex value\")\n\nINVALID_HANDLE_VALUE = Handle(ffi.cast(\"HANDLE\", -1))\n\n\nclass ErrorCodes(enum.IntEnum):\n    STATUS_TIMEOUT = 0x102\n    WAIT_TIMEOUT = 0x102\n    WAIT_ABANDONED = 0x80\n    WAIT_OBJECT_0 = 0x00  # object is signaled\n    WAIT_FAILED = 0xFFFFFFFF\n    ERROR_IO_PENDING = 997\n    ERROR_OPERATION_ABORTED = 995\n    ERROR_ABANDONED_WAIT_0 = 735\n    ERROR_INVALID_HANDLE = 6\n    ERROR_INVALID_PARAMETER = 87\n    ERROR_NOT_FOUND = 1168\n    ERROR_NOT_SOCKET = 10038\n\n\nclass FileFlags(enum.IntFlag):\n    GENERIC_READ = 0x80000000\n    SYNCHRONIZE = 0x00100000\n    FILE_FLAG_OVERLAPPED = 0x40000000\n    FILE_SHARE_READ = 1\n    FILE_SHARE_WRITE = 2\n    FILE_SHARE_DELETE = 4\n    CREATE_NEW = 1\n    CREATE_ALWAYS = 2\n    OPEN_EXISTING = 3\n    OPEN_ALWAYS = 4\n    TRUNCATE_EXISTING = 5\n\n\nclass AFDPollFlags(enum.IntFlag):\n    # These are drawn from a combination of:\n    #   https://github.com/piscisaureus/wepoll/blob/master/src/afd.h\n    #   https://github.com/reactos/reactos/blob/master/sdk/include/reactos/drivers/afd/shared.h\n    AFD_POLL_RECEIVE = 0x0001\n    AFD_POLL_RECEIVE_EXPEDITED = 0x0002  # OOB/urgent data\n    AFD_POLL_SEND = 0x0004\n    AFD_POLL_DISCONNECT = 0x0008  # received EOF (FIN)\n    AFD_POLL_ABORT = 0x0010  # received RST\n    AFD_POLL_LOCAL_CLOSE = 0x0020  # local socket object closed\n    AFD_POLL_CONNECT = 0x0040  # socket is successfully connected\n    AFD_POLL_ACCEPT = 0x0080  # you can call accept on this socket\n    AFD_POLL_CONNECT_FAIL = 0x0100  # connect() terminated unsuccessfully\n    # See WSAEventSelect docs for more details on these four:\n    AFD_POLL_QOS = 0x0200\n    AFD_POLL_GROUP_QOS = 0x0400\n    AFD_POLL_ROUTING_INTERFACE_CHANGE = 0x0800\n    AFD_POLL_EVENT_ADDRESS_LIST_CHANGE = 0x1000\n\n\nclass WSAIoctls(enum.IntEnum):\n    SIO_BASE_HANDLE = 0x48000022\n    SIO_BSP_HANDLE_SELECT = 0x4800001C\n    SIO_BSP_HANDLE_POLL = 0x4800001D\n\n\nclass CompletionModes(enum.IntFlag):\n    FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1\n    FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2\n\n\nclass IoControlCodes(enum.IntEnum):\n    IOCTL_AFD_POLL = 0x00012024\n\n\n################################################################\n# Generic helpers\n################################################################\n\n\ndef _handle(obj: int | CData) -> Handle:\n    # For now, represent handles as either cffi HANDLEs or as ints.  If you\n    # try to pass in a file descriptor instead, it's not going to work\n    # out. (For that msvcrt.get_osfhandle does the trick, but I don't know if\n    # we'll actually need that for anything...) For sockets this doesn't\n    # matter, Python never allocates an fd. So let's wait until we actually\n    # encounter the problem before worrying about it.\n    if isinstance(obj, int):\n        return Handle(ffi.cast(\"HANDLE\", obj))\n    return Handle(obj)\n\n\ndef handle_array(count: int) -> HandleArray:\n    \"\"\"Make an array of handles.\"\"\"\n    return HandleArray(ffi.new(f\"HANDLE[{count}]\"))\n\n\ndef raise_winerror(\n    winerror: int | None = None,\n    *,\n    filename: str | None = None,\n    filename2: str | None = None,\n) -> NoReturn:\n    # assert sys.platform == \"win32\"  # TODO: make this work in MyPy\n    # ... in the meanwhile, ffi.getwinerror() is undefined on non-Windows, necessitating the type\n    # ignores.\n\n    if winerror is None:\n        err = ffi.getwinerror()  # type: ignore[attr-defined,unused-ignore]\n        if err is None:\n            raise RuntimeError(\"No error set?\")\n        winerror, msg = err\n    else:\n        err = ffi.getwinerror(winerror)  # type: ignore[attr-defined,unused-ignore]\n        if err is None:\n            raise RuntimeError(\"No error set?\")\n        _, msg = err\n    # https://docs.python.org/3/library/exceptions.html#OSError\n    raise OSError(0, msg, filename, winerror, filename2)\n"
  },
  {
    "path": "src/trio/_deprecate.py",
    "content": "from __future__ import annotations\n\nimport sys\nimport warnings\nfrom functools import wraps\nfrom typing import TYPE_CHECKING, ClassVar, TypeVar\n\nimport attrs\n\nfrom ._util import final\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from typing_extensions import ParamSpec\n\n    ArgsT = ParamSpec(\"ArgsT\")\n\nRetT = TypeVar(\"RetT\")\n\n\n# We want our warnings to be visible by default (at least for now), but we\n# also want it to be possible to override that using the -W switch. AFAICT\n# this means we cannot inherit from DeprecationWarning, because the only way\n# to make it visible by default then would be to add our own filter at import\n# time, but that would override -W switches...\nclass TrioDeprecationWarning(FutureWarning):\n    \"\"\"Warning emitted if you use deprecated Trio functionality.\n\n    While a relatively mature project, Trio remains committed to refining its\n    design and improving usability. As part of this, we occasionally deprecate\n    or remove functionality that proves suboptimal. If you use Trio, we\n    recommend `subscribing to issue #1\n    <https://github.com/python-trio/trio/issues/1>`__ to get information about\n    upcoming deprecations and other backwards compatibility breaking changes.\n\n    Despite the name, this class currently inherits from\n    :class:`FutureWarning`, not :class:`DeprecationWarning`, because until a\n    1.0 release, we want these warnings to be visible by default. You can hide\n    them by installing a filter or with the ``-W`` switch: see the\n    :mod:`warnings` documentation for details.\n    \"\"\"\n\n\ndef _url_for_issue(issue: int) -> str:\n    return f\"https://github.com/python-trio/trio/issues/{issue}\"\n\n\ndef _stringify(thing: object) -> str:\n    if hasattr(thing, \"__module__\") and hasattr(thing, \"__qualname__\"):\n        return f\"{thing.__module__}.{thing.__qualname__}\"\n    return str(thing)\n\n\ndef warn_deprecated(\n    thing: object,\n    version: str,\n    *,\n    issue: int | None,\n    instead: object,\n    stacklevel: int = 2,\n    use_triodeprecationwarning: bool = False,\n) -> None:\n    stacklevel += 1\n    msg = f\"{_stringify(thing)} is deprecated since Trio {version}\"\n    if instead is None:\n        msg += \" with no replacement\"\n    else:\n        msg += f\"; use {_stringify(instead)} instead\"\n    if issue is not None:\n        msg += f\" ({_url_for_issue(issue)})\"\n    if use_triodeprecationwarning:\n        warning_class: type[Warning] = TrioDeprecationWarning\n    else:\n        warning_class = DeprecationWarning\n    warnings.warn(warning_class(msg), stacklevel=stacklevel)\n\n\n# @deprecated(\"0.2.0\", issue=..., instead=...)\n# def ...\ndef deprecated(\n    version: str,\n    *,\n    thing: object = None,\n    issue: int | None,\n    instead: object,\n    use_triodeprecationwarning: bool = False,\n) -> Callable[[Callable[ArgsT, RetT]], Callable[ArgsT, RetT]]:\n    def do_wrap(fn: Callable[ArgsT, RetT]) -> Callable[ArgsT, RetT]:\n        nonlocal thing\n\n        @wraps(fn)\n        def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT:\n            warn_deprecated(\n                thing,\n                version,\n                instead=instead,\n                issue=issue,\n                use_triodeprecationwarning=use_triodeprecationwarning,\n            )\n            return fn(*args, **kwargs)\n\n        # If our __module__ or __qualname__ get modified, we want to pick up\n        # on that, so we read them off the wrapper object instead of the (now\n        # hidden) fn object\n        if thing is None:\n            thing = wrapper\n\n        if wrapper.__doc__ is not None:\n            doc = wrapper.__doc__\n            doc = doc.rstrip()\n            doc += \"\\n\\n\"\n            doc += f\".. deprecated:: {version}\\n\"\n            if instead is not None:\n                doc += f\"   Use {_stringify(instead)} instead.\\n\"\n            if issue is not None:\n                doc += f\"   For details, see `issue #{issue} <{_url_for_issue(issue)}>`__.\\n\"\n            doc += \"\\n\"\n            wrapper.__doc__ = doc\n\n        return wrapper\n\n    return do_wrap\n\n\ndef deprecated_alias(\n    old_qualname: str,\n    new_fn: Callable[ArgsT, RetT],\n    version: str,\n    *,\n    issue: int | None,\n) -> Callable[ArgsT, RetT]:\n    @deprecated(version, issue=issue, instead=new_fn)\n    @wraps(new_fn, assigned=(\"__module__\", \"__annotations__\"))\n    def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT:\n        \"\"\"Deprecated alias.\"\"\"\n        return new_fn(*args, **kwargs)\n\n    wrapper.__qualname__ = old_qualname\n    wrapper.__name__ = old_qualname.rpartition(\".\")[-1]\n    return wrapper\n\n\n@final\n@attrs.frozen(slots=False)\nclass DeprecatedAttribute:\n    _not_set: ClassVar[object] = object()\n\n    value: object\n    version: str\n    issue: int | None\n    instead: object = _not_set\n\n\ndef deprecate_attributes(\n    module_name: str, deprecated_attributes: dict[str, DeprecatedAttribute]\n) -> None:\n    def __getattr__(name: str) -> object:\n        if name in deprecated_attributes:\n            info = deprecated_attributes[name]\n            instead = info.instead\n            if instead is DeprecatedAttribute._not_set:\n                instead = info.value\n            thing = f\"{module_name}.{name}\"\n            warn_deprecated(thing, info.version, issue=info.issue, instead=instead)\n            return info.value\n\n        msg = \"module '{}' has no attribute '{}'\"\n        raise AttributeError(msg.format(module_name, name))\n\n    sys.modules[module_name].__getattr__ = __getattr__  # type: ignore[method-assign]\n"
  },
  {
    "path": "src/trio/_dtls.py",
    "content": "# Implementation of DTLS 1.2, using pyopenssl\n# https://datatracker.ietf.org/doc/html/rfc6347\n#\n# OpenSSL's APIs for DTLS are extremely awkward and limited, which forces us to jump\n# through a *lot* of hoops and implement important chunks of the protocol ourselves.\n# Hopefully they fix this before implementing DTLS 1.3, because it's a very different\n# protocol, and it's probably impossible to pull tricks like we do here.\n\nfrom __future__ import annotations\n\nimport contextlib\nimport enum\nimport errno\nimport hmac\nimport os\nimport struct\nimport warnings\nimport weakref\nfrom itertools import count\nfrom typing import TYPE_CHECKING, Generic, TypeAlias, TypeVar\nfrom weakref import ReferenceType, WeakValueDictionary\n\nimport attrs\n\nimport trio\n\nfrom ._util import NoPublicConstructor, final\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable, Iterable, Iterator\n    from types import TracebackType\n\n    # See DTLSEndpoint.__init__ for why this is imported here\n    from OpenSSL import SSL  # noqa: TC004\n    from typing_extensions import Self, TypeVarTuple, Unpack\n\n    from trio._socket import AddressFormat\n    from trio.socket import SocketType\n\n    PosArgsT = TypeVarTuple(\"PosArgsT\")\n\nMAX_UDP_PACKET_SIZE = 65527\n\n\ndef packet_header_overhead(sock: SocketType) -> int:\n    if sock.family == trio.socket.AF_INET:\n        return 28\n    else:\n        return 48\n\n\ndef worst_case_mtu(sock: SocketType) -> int:\n    if sock.family == trio.socket.AF_INET:\n        return 576 - packet_header_overhead(sock)\n    else:\n        return 1280 - packet_header_overhead(sock)  # TODO: test this line\n\n\ndef best_guess_mtu(sock: SocketType) -> int:\n    return 1500 - packet_header_overhead(sock)\n\n\n# There are a bunch of different RFCs that define these codes, so for a\n# comprehensive collection look here:\n# https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\nclass ContentType(enum.IntEnum):\n    change_cipher_spec = 20\n    alert = 21\n    handshake = 22\n    application_data = 23\n    heartbeat = 24\n\n\nclass HandshakeType(enum.IntEnum):\n    hello_request = 0\n    client_hello = 1\n    server_hello = 2\n    hello_verify_request = 3\n    new_session_ticket = 4\n    end_of_early_data = 4\n    encrypted_extensions = 8\n    certificate = 11\n    server_key_exchange = 12\n    certificate_request = 13\n    server_hello_done = 14\n    certificate_verify = 15\n    client_key_exchange = 16\n    finished = 20\n    certificate_url = 21\n    certificate_status = 22\n    supplemental_data = 23\n    key_update = 24\n    compressed_certificate = 25\n    ekt_key = 26\n    message_hash = 254\n\n\nclass ProtocolVersion:\n    DTLS10 = bytes([254, 255])\n    DTLS12 = bytes([254, 253])\n\n\nEPOCH_MASK = 0xFFFF << (6 * 8)\n\n\n# Conventions:\n# - All functions that handle network data end in _untrusted.\n# - All functions end in _untrusted MUST make sure that bad data from the\n#   network cannot *only* cause BadPacket to be raised. No IndexError or\n#   struct.error or whatever.\nclass BadPacket(Exception):\n    pass\n\n\n# This checks that the DTLS 'epoch' field is 0, which is true iff we're in the\n# initial handshake. It doesn't check the ContentType, because not all\n# handshake messages have ContentType==handshake -- for example,\n# ChangeCipherSpec is used during the handshake but has its own ContentType.\n#\n# Cannot fail.\ndef part_of_handshake_untrusted(packet: bytes) -> bool:\n    # If the packet is too short, then slicing will successfully return a\n    # short string, which will necessarily fail to match.\n    return packet[3:5] == b\"\\x00\\x00\"\n\n\n# Cannot fail\ndef is_client_hello_untrusted(packet: bytes) -> bool:\n    try:\n        return (\n            packet[0] == ContentType.handshake\n            and packet[13] == HandshakeType.client_hello\n        )\n    except IndexError:\n        # Invalid DTLS record\n        return False\n\n\n# DTLS records are:\n# - 1 byte content type\n# - 2 bytes version\n# - 8 bytes epoch+seqno\n#    Technically this is 2 bytes epoch then 6 bytes seqno, but we treat it as\n#    a single 8-byte integer, where epoch changes are represented as jumping\n#    forward by 2**(6*8).\n# - 2 bytes payload length (unsigned big-endian)\n# - payload\nRECORD_HEADER = struct.Struct(\"!B2sQH\")\n\n\ndef to_hex(data: bytes) -> str:  # pragma: no cover\n    return data.hex()\n\n\n@attrs.frozen\nclass Record:\n    content_type: int\n    version: bytes = attrs.field(repr=to_hex)\n    epoch_seqno: int\n    payload: bytes = attrs.field(repr=to_hex)\n\n\ndef records_untrusted(packet: bytes) -> Iterator[Record]:\n    i = 0\n    while i < len(packet):\n        try:\n            ct, version, epoch_seqno, payload_len = RECORD_HEADER.unpack_from(packet, i)\n        # Marked as no-cover because at time of writing, this code is unreachable\n        # (records_untrusted only gets called on packets that are either trusted or that\n        # have passed is_client_hello_untrusted, which filters out short packets)\n        except struct.error as exc:  # pragma: no cover\n            raise BadPacket(\"invalid record header\") from exc\n        i += RECORD_HEADER.size\n        payload = packet[i : i + payload_len]\n        if len(payload) != payload_len:\n            raise BadPacket(\"short record\")\n        i += payload_len\n        yield Record(ct, version, epoch_seqno, payload)\n\n\ndef encode_record(record: Record) -> bytes:\n    header = RECORD_HEADER.pack(\n        record.content_type,\n        record.version,\n        record.epoch_seqno,\n        len(record.payload),\n    )\n    return header + record.payload\n\n\n# Handshake messages are:\n# - 1 byte message type\n# - 3 bytes total message length\n# - 2 bytes message sequence number\n# - 3 bytes fragment offset\n# - 3 bytes fragment length\nHANDSHAKE_MESSAGE_HEADER = struct.Struct(\"!B3sH3s3s\")\n\n\n@attrs.frozen\nclass HandshakeFragment:\n    msg_type: int\n    msg_len: int\n    msg_seq: int\n    frag_offset: int\n    frag_len: int\n    frag: bytes = attrs.field(repr=to_hex)\n\n\ndef decode_handshake_fragment_untrusted(payload: bytes) -> HandshakeFragment:\n    # Raises BadPacket if decoding fails\n    try:\n        (\n            msg_type,\n            msg_len_bytes,\n            msg_seq,\n            frag_offset_bytes,\n            frag_len_bytes,\n        ) = HANDSHAKE_MESSAGE_HEADER.unpack_from(payload)\n    except struct.error as exc:  # TODO: test this line\n        raise BadPacket(\"bad handshake message header\") from exc\n    # 'struct' doesn't have built-in support for 24-bit integers, so we\n    # have to do it by hand. These can't fail.\n    msg_len = int.from_bytes(msg_len_bytes, \"big\")\n    frag_offset = int.from_bytes(frag_offset_bytes, \"big\")\n    frag_len = int.from_bytes(frag_len_bytes, \"big\")\n    frag = payload[HANDSHAKE_MESSAGE_HEADER.size :]\n    if len(frag) != frag_len:\n        raise BadPacket(\"handshake fragment length doesn't match record length\")\n    return HandshakeFragment(\n        msg_type,\n        msg_len,\n        msg_seq,\n        frag_offset,\n        frag_len,\n        frag,\n    )\n\n\ndef encode_handshake_fragment(hsf: HandshakeFragment) -> bytes:\n    hs_header = HANDSHAKE_MESSAGE_HEADER.pack(\n        hsf.msg_type,\n        hsf.msg_len.to_bytes(3, \"big\"),\n        hsf.msg_seq,\n        hsf.frag_offset.to_bytes(3, \"big\"),\n        hsf.frag_len.to_bytes(3, \"big\"),\n    )\n    return hs_header + hsf.frag\n\n\ndef decode_client_hello_untrusted(packet: bytes) -> tuple[int, bytes, bytes]:\n    # Raises BadPacket if parsing fails\n    # Returns (record epoch_seqno, cookie from the packet, data that should be\n    # hashed into cookie)\n    try:\n        # ClientHello has to be the first record in the packet\n        record = next(records_untrusted(packet))\n        # no-cover because at time of writing, this is unreachable:\n        # decode_client_hello_untrusted is only called on packets that have passed\n        # is_client_hello_untrusted, which confirms the content type.\n        if record.content_type != ContentType.handshake:  # pragma: no cover\n            raise BadPacket(\"not a handshake record\")\n        fragment = decode_handshake_fragment_untrusted(record.payload)\n        if fragment.msg_type != HandshakeType.client_hello:\n            raise BadPacket(\"not a ClientHello\")\n        # ClientHello can't be fragmented, because reassembly requires holding\n        # per-connection state, and we refuse to allocate per-connection state\n        # until after we get a valid ClientHello.\n        if fragment.frag_offset != 0:\n            raise BadPacket(\"fragmented ClientHello\")\n        if fragment.frag_len != fragment.msg_len:\n            raise BadPacket(\"fragmented ClientHello\")\n\n        # As per RFC 6347:\n        #\n        #   When responding to a HelloVerifyRequest, the client MUST use the\n        #   same parameter values (version, random, session_id, cipher_suites,\n        #   compression_method) as it did in the original ClientHello.  The\n        #   server SHOULD use those values to generate its cookie and verify that\n        #   they are correct upon cookie receipt.\n        #\n        # However, the record-layer framing can and will change (e.g. the\n        # second ClientHello will have a new record-layer sequence number). So\n        # we need to pull out the handshake message alone, discarding the\n        # record-layer stuff, and then we're going to hash all of it *except*\n        # the cookie.\n\n        body = fragment.frag\n        # ClientHello is:\n        #\n        # - 2 bytes client_version\n        # - 32 bytes random\n        # - 1 byte session_id length\n        # - session_id\n        # - 1 byte cookie length\n        # - cookie\n        # - everything else\n        #\n        # So to find the cookie, so we need to figure out how long the\n        # session_id is and skip past it.\n        session_id_len = body[2 + 32]\n        cookie_len_offset = 2 + 32 + 1 + session_id_len\n        cookie_len = body[cookie_len_offset]\n\n        cookie_start = cookie_len_offset + 1\n        cookie_end = cookie_start + cookie_len\n\n        before_cookie = body[:cookie_len_offset]\n        cookie = body[cookie_start:cookie_end]\n        after_cookie = body[cookie_end:]\n\n        if len(cookie) != cookie_len:\n            raise BadPacket(\"short cookie\")\n        return (record.epoch_seqno, cookie, before_cookie + after_cookie)\n\n    except (struct.error, IndexError) as exc:\n        raise BadPacket(\"bad ClientHello\") from exc\n\n\n@attrs.frozen\nclass HandshakeMessage:\n    record_version: bytes = attrs.field(repr=to_hex)\n    msg_type: HandshakeType\n    msg_seq: int\n    body: bytearray = attrs.field(repr=to_hex)\n\n\n# ChangeCipherSpec is part of the handshake, but it's not a \"handshake\n# message\" and can't be fragmented the same way. Sigh.\n@attrs.frozen\nclass PseudoHandshakeMessage:\n    record_version: bytes = attrs.field(repr=to_hex)\n    content_type: int\n    payload: bytes = attrs.field(repr=to_hex)\n\n\n# The final record in a handshake is Finished, which is encrypted, can't be fragmented\n# (at least by us), and keeps its record number (because it's in a new epoch). So we\n# just pass it through unchanged. (Fortunately, the payload is only a single hash value,\n# so the largest it will ever be is 64 bytes for a 512-bit hash. Which is small enough\n# that it never requires fragmenting to fit into a UDP packet.\n@attrs.frozen\nclass OpaqueHandshakeMessage:\n    record: Record\n\n\n_AnyHandshakeMessage: TypeAlias = (\n    HandshakeMessage | PseudoHandshakeMessage | OpaqueHandshakeMessage\n)\n\n\n# This takes a raw outgoing handshake volley that openssl generated, and\n# reconstructs the handshake messages inside it, so that we can repack them\n# into records while retransmitting. So the data ought to be well-behaved --\n# it's not coming from the network.\ndef decode_volley_trusted(\n    volley: bytes,\n) -> list[_AnyHandshakeMessage]:\n    messages: list[_AnyHandshakeMessage] = []\n    messages_by_seq = {}\n    for record in records_untrusted(volley):\n        # ChangeCipherSpec isn't a handshake message, so it can't be fragmented.\n        # Handshake messages with epoch > 0 are encrypted, so we can't fragment them\n        # either. Fortunately, ChangeCipherSpec has a 1 byte payload, and the only\n        # encrypted handshake message is Finished, whose payload is a single hash value\n        # -- so 32 bytes for SHA-256, 64 for SHA-512, etc. Neither is going to be so\n        # large that it has to be fragmented to fit into a single packet.\n        if record.epoch_seqno & EPOCH_MASK:\n            messages.append(OpaqueHandshakeMessage(record))\n        elif record.content_type in (ContentType.change_cipher_spec, ContentType.alert):\n            messages.append(\n                PseudoHandshakeMessage(\n                    record.version,\n                    record.content_type,\n                    record.payload,\n                ),\n            )\n        else:\n            assert record.content_type == ContentType.handshake\n            fragment = decode_handshake_fragment_untrusted(record.payload)\n            msg_type = HandshakeType(fragment.msg_type)\n            if fragment.msg_seq not in messages_by_seq:\n                msg = HandshakeMessage(\n                    record.version,\n                    msg_type,\n                    fragment.msg_seq,\n                    bytearray(fragment.msg_len),\n                )\n                messages.append(msg)\n                messages_by_seq[fragment.msg_seq] = msg\n            else:\n                msg = messages_by_seq[fragment.msg_seq]\n            assert msg.msg_type == fragment.msg_type\n            assert msg.msg_seq == fragment.msg_seq\n            assert len(msg.body) == fragment.msg_len\n\n            msg.body[\n                fragment.frag_offset : fragment.frag_offset + fragment.frag_len\n            ] = fragment.frag\n\n    return messages\n\n\nclass RecordEncoder:\n    def __init__(self) -> None:\n        self._record_seq = count()\n\n    def set_first_record_number(self, n: int) -> None:\n        self._record_seq = count(n)\n\n    def encode_volley(\n        self,\n        messages: Iterable[_AnyHandshakeMessage],\n        mtu: int,\n    ) -> list[bytearray]:\n        packets = []\n        packet = bytearray()\n        for message in messages:\n            if isinstance(message, OpaqueHandshakeMessage):\n                encoded = encode_record(message.record)\n                if mtu - len(packet) - len(encoded) <= 0:  # TODO: test this line\n                    packets.append(packet)\n                    packet = bytearray()\n                packet += encoded\n                assert len(packet) <= mtu\n            elif isinstance(message, PseudoHandshakeMessage):\n                space = mtu - len(packet) - RECORD_HEADER.size - len(message.payload)\n                if space <= 0:  # TODO: test this line\n                    packets.append(packet)\n                    packet = bytearray()\n                packet += RECORD_HEADER.pack(\n                    message.content_type,\n                    message.record_version,\n                    next(self._record_seq),\n                    len(message.payload),\n                )\n                packet += message.payload\n                assert len(packet) <= mtu\n            else:\n                msg_len_bytes = len(message.body).to_bytes(3, \"big\")\n                frag_offset = 0\n                frags_encoded = 0\n                # If message.body is empty, then we still want to encode it in one\n                # fragment, not zero.\n                while frag_offset < len(message.body) or not frags_encoded:\n                    space = (\n                        mtu\n                        - len(packet)\n                        - RECORD_HEADER.size\n                        - HANDSHAKE_MESSAGE_HEADER.size\n                    )\n                    if space <= 0:\n                        packets.append(packet)\n                        packet = bytearray()\n                        continue\n                    frag = message.body[frag_offset : frag_offset + space]\n                    frag_offset_bytes = frag_offset.to_bytes(3, \"big\")\n                    frag_len_bytes = len(frag).to_bytes(3, \"big\")\n                    frag_offset += len(frag)\n\n                    packet += RECORD_HEADER.pack(\n                        ContentType.handshake,\n                        message.record_version,\n                        next(self._record_seq),\n                        HANDSHAKE_MESSAGE_HEADER.size + len(frag),\n                    )\n\n                    packet += HANDSHAKE_MESSAGE_HEADER.pack(\n                        message.msg_type,\n                        msg_len_bytes,\n                        message.msg_seq,\n                        frag_offset_bytes,\n                        frag_len_bytes,\n                    )\n\n                    packet += frag\n\n                    frags_encoded += 1\n                    assert len(packet) <= mtu\n\n        if packet:\n            packets.append(packet)\n\n        return packets\n\n\n# This bit requires implementing a bona fide cryptographic protocol, so even though it's\n# a simple one let's take a moment to discuss the design.\n#\n# Our goal is to force new incoming handshakes that claim to be coming from a\n# given ip:port to prove that they can also receive packets sent to that\n# ip:port. (There's nothing in UDP to stop someone from forging the return\n# address, and it's often used for stuff like DoS reflection attacks, where\n# an attacker tries to trick us into sending data at some innocent victim.)\n# For more details, see:\n#\n#    https://datatracker.ietf.org/doc/html/rfc6347#section-4.2.1\n#\n# To do this, when we receive an initial ClientHello, we calculate a magic\n# cookie, and send it back as a HelloVerifyRequest. Then the client sends us a\n# second ClientHello, this time with the magic cookie included, and after we\n# check that this cookie is valid we go ahead and start the handshake proper.\n#\n# So the magic cookie needs the following properties:\n# - No-one can forge it without knowing our secret key\n# - It ensures that the ip, port, and ClientHello contents from the response\n#   match those in the challenge\n# - It expires after a short-ish period (so that if an attacker manages to steal one, it\n#   won't be useful for long)\n# - It doesn't require storing any peer-specific state on our side\n#\n# To do that, we take the ip/port/ClientHello data and compute an HMAC of them, using a\n# secret key we generate on startup. We also include:\n#\n# - The current time (using Trio's clock), rounded to the nearest 30 seconds\n# - A random salt\n#\n# Then the cookie is the salt and the HMAC digest concatenated together.\n#\n# When verifying a cookie, we use the salt + new ip/port/ClientHello data to recompute\n# the HMAC digest, for both the current time and the current time minus 30 seconds, and\n# if either of them match, we consider the cookie good.\n#\n# Including the rounded-off time like this means that each cookie is good for at least\n# 30 seconds, and possibly as much as 60 seconds.\n#\n# The salt is probably not necessary -- I'm pretty sure that all it does is make it hard\n# for an attacker to figure out when our clock ticks over a 30 second boundary. Which is\n# probably pretty harmless? But it's easier to add the salt than to convince myself that\n# it's *completely* harmless, so, salt it is.\n\nCOOKIE_REFRESH_INTERVAL = 30  # seconds\nKEY_BYTES = 32\nCOOKIE_HASH = \"sha256\"\nSALT_BYTES = 8\n# 32 bytes was the maximum cookie length in DTLS 1.0. DTLS 1.2 raised it to 255. I doubt\n# there are any DTLS 1.0 implementations still in the wild, but really 32 bytes is\n# plenty, and it also gets rid of a confusing warning in Wireshark output.\n#\n# We truncate the cookie to 32 bytes, of which 8 bytes is salt, so that leaves 24 bytes\n# of truncated HMAC = 192 bit security, which is still massive overkill. (TCP uses 32\n# *bits* for this.) HMAC truncation is explicitly noted as safe in RFC 2104:\n#   https://datatracker.ietf.org/doc/html/rfc2104#section-5\nCOOKIE_LENGTH = 32\n\n\ndef _current_cookie_tick() -> int:\n    return int(trio.current_time() / COOKIE_REFRESH_INTERVAL)\n\n\n# Simple deterministic and invertible serializer -- i.e., a useful tool for converting\n# structured data into something we can cryptographically sign.\ndef _signable(*fields: bytes) -> bytes:\n    out: list[bytes] = []\n    for field in fields:\n        out.extend((struct.pack(\"!Q\", len(field)), field))\n    return b\"\".join(out)\n\n\ndef _make_cookie(\n    key: bytes,\n    salt: bytes,\n    tick: int,\n    address: AddressFormat,\n    client_hello_bits: bytes,\n) -> bytes:\n    assert len(salt) == SALT_BYTES\n    assert len(key) == KEY_BYTES\n\n    signable_data = _signable(\n        salt,\n        struct.pack(\"!Q\", tick),\n        # address is a mix of strings and ints, and variable length, so pack\n        # it into a single nested field\n        _signable(*(str(part).encode() for part in address)),\n        client_hello_bits,\n    )\n\n    return (salt + hmac.digest(key, signable_data, COOKIE_HASH))[:COOKIE_LENGTH]\n\n\ndef valid_cookie(\n    key: bytes,\n    cookie: bytes,\n    address: AddressFormat,\n    client_hello_bits: bytes,\n) -> bool:\n    if len(cookie) > SALT_BYTES:\n        salt = cookie[:SALT_BYTES]\n\n        tick = _current_cookie_tick()\n\n        cur_cookie = _make_cookie(key, salt, tick, address, client_hello_bits)\n        old_cookie = _make_cookie(\n            key,\n            salt,\n            max(tick - 1, 0),\n            address,\n            client_hello_bits,\n        )\n\n        # I doubt using a short-circuiting 'or' here would leak any meaningful\n        # information, but why risk it when '|' is just as easy.\n        return hmac.compare_digest(cookie, cur_cookie) | hmac.compare_digest(\n            cookie,\n            old_cookie,\n        )\n    else:\n        return False\n\n\ndef challenge_for(\n    key: bytes,\n    address: AddressFormat,\n    epoch_seqno: int,\n    client_hello_bits: bytes,\n) -> bytes:\n    salt = os.urandom(SALT_BYTES)\n    tick = _current_cookie_tick()\n    cookie = _make_cookie(key, salt, tick, address, client_hello_bits)\n\n    # HelloVerifyRequest body is:\n    # - 2 bytes version\n    # - length-prefixed cookie\n    #\n    # The DTLS 1.2 spec says that for this message specifically we should use\n    # the DTLS 1.0 version.\n    #\n    # (It also says the opposite of that, but that part is a mistake:\n    #    https://www.rfc-editor.org/errata/eid4103\n    # ).\n    #\n    # And I guess we use this for both the message-level and record-level\n    # ProtocolVersions, since we haven't negotiated anything else yet?\n    body = ProtocolVersion.DTLS10 + bytes([len(cookie)]) + cookie\n\n    # RFC says have to copy the client's record number\n    # Errata says it should be handshake message number\n    # Openssl copies back record sequence number, and always sets message seq\n    # number 0. So I guess we'll follow openssl.\n    hs = HandshakeFragment(\n        msg_type=HandshakeType.hello_verify_request,\n        msg_len=len(body),\n        msg_seq=0,\n        frag_offset=0,\n        frag_len=len(body),\n        frag=body,\n    )\n    payload = encode_handshake_fragment(hs)\n\n    packet = encode_record(\n        Record(ContentType.handshake, ProtocolVersion.DTLS10, epoch_seqno, payload),\n    )\n    return packet\n\n\n_T = TypeVar(\"_T\")\n\n\nclass _Queue(Generic[_T]):\n    def __init__(self, incoming_packets_buffer: int | float) -> None:  # noqa: PYI041\n        self.s, self.r = trio.open_memory_channel[_T](incoming_packets_buffer)\n\n\ndef _read_loop(read_fn: Callable[[int], bytes]) -> bytes:\n    chunks = []\n    while True:\n        try:\n            chunk = read_fn(2**14)  # max TLS record size\n        except SSL.WantReadError:\n            break\n        chunks.append(chunk)\n    return b\"\".join(chunks)\n\n\nasync def handle_client_hello_untrusted(\n    endpoint: DTLSEndpoint,\n    address: AddressFormat,\n    packet: bytes,\n) -> None:\n    # it's trivial to write a simple function that directly calls this to\n    # get code coverage, but it should maybe:\n    # 1. be removed\n    # 2. be asserted\n    # 3. Write a complicated test case where this happens \"organically\"\n    if endpoint._listening_context is None:  # pragma: no cover\n        return\n\n    try:\n        epoch_seqno, cookie, bits = decode_client_hello_untrusted(packet)\n    except BadPacket:\n        return\n\n    if endpoint._listening_key is None:\n        endpoint._listening_key = os.urandom(KEY_BYTES)\n\n    if not valid_cookie(endpoint._listening_key, cookie, address, bits):\n        challenge_packet = challenge_for(\n            endpoint._listening_key,\n            address,\n            epoch_seqno,\n            bits,\n        )\n        try:\n            async with endpoint._send_lock:\n                await endpoint.socket.sendto(challenge_packet, address)\n        except (OSError, trio.ClosedResourceError):\n            pass\n    else:\n        # We got a real, valid ClientHello!\n        stream = DTLSChannel._create(endpoint, address, endpoint._listening_context)\n        # Our HelloRetryRequest had some sequence number. We need our future sequence\n        # numbers to be larger than it, so our peer knows that our future records aren't\n        # stale/duplicates. But, we don't know what this sequence number was. What we do\n        # know is:\n        # - the HelloRetryRequest seqno was copied it from the initial ClientHello\n        # - the new ClientHello has a higher seqno than the initial ClientHello\n        # So, if we copy the new ClientHello's seqno into our first real handshake\n        # record and increment from there, that should work.\n        stream._record_encoder.set_first_record_number(epoch_seqno)\n        # Process the ClientHello\n        try:\n            stream._ssl.bio_write(packet)\n            stream._ssl.DTLSv1_listen()\n        except SSL.Error:  # pragma: no cover\n            # ...OpenSSL didn't like it, so I guess we didn't have a valid ClientHello\n            # after all.\n            return\n\n        # Check if we have an existing association\n        old_stream = endpoint._streams.get(address)\n        if old_stream is not None:\n            if old_stream._client_hello == (cookie, bits):\n                # ...This was just a duplicate of the last ClientHello, so never mind.\n                return\n            else:\n                # Ok, this *really is* a new handshake; the old stream should go away.\n                old_stream._set_replaced()\n        stream._client_hello = (cookie, bits)\n        endpoint._streams[address] = stream\n        endpoint._incoming_connections_q.s.send_nowait(stream)\n\n\nasync def dtls_receive_loop(\n    endpoint_ref: ReferenceType[DTLSEndpoint],\n    sock: SocketType,\n) -> None:\n    try:\n        while True:\n            try:\n                packet, address = await sock.recvfrom(MAX_UDP_PACKET_SIZE)\n            except OSError as exc:\n                if exc.errno == errno.ECONNRESET:\n                    # Windows only: \"On a UDP-datagram socket [ECONNRESET]\n                    # indicates a previous send operation resulted in an ICMP Port\n                    # Unreachable message\" -- https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom\n                    #\n                    # This is totally useless -- there's nothing we can do with this\n                    # information. So we just ignore it and retry the recv.\n                    continue\n                else:\n                    raise\n            endpoint = endpoint_ref()\n            try:\n                if endpoint is None:\n                    return\n                if is_client_hello_untrusted(packet):\n                    await handle_client_hello_untrusted(endpoint, address, packet)\n                elif address in endpoint._streams:\n                    stream = endpoint._streams[address]\n                    if stream._did_handshake and part_of_handshake_untrusted(packet):\n                        # The peer just sent us more handshake messages, that aren't a\n                        # ClientHello, and we thought the handshake was done. Some of\n                        # the packets that we sent to finish the handshake must have\n                        # gotten lost. So re-send them. We do this directly here instead\n                        # of just putting it into the queue and letting the receiver do\n                        # it, because there's no guarantee that anyone is reading from\n                        # the queue, because we think the handshake is done!\n                        await stream._resend_final_volley()\n                    else:\n                        try:\n                            stream._q.s.send_nowait(packet)\n                        except trio.WouldBlock:\n                            stream._packets_dropped_in_trio += 1\n                else:\n                    # Drop packet\n                    pass\n            finally:\n                del endpoint\n    except trio.ClosedResourceError:\n        # socket was closed\n        return\n    except OSError as exc:\n        if exc.errno in (errno.EBADF, errno.ENOTSOCK):\n            # socket was closed\n            return\n        else:  # pragma: no cover\n            # ??? shouldn't happen\n            raise\n\n\n@attrs.frozen\nclass DTLSChannelStatistics:\n    \"\"\"Currently this has only one attribute:\n\n    - ``incoming_packets_dropped_in_trio`` (``int``): Gives a count of the number of\n      incoming packets from this peer that Trio successfully received from the\n      network, but then got dropped because the internal channel buffer was full. If\n      this is non-zero, then you might want to call ``receive`` more often, or use a\n      larger ``incoming_packets_buffer``, or just not worry about it because your\n      UDP-based protocol should be able to handle the occasional lost packet, right?\n\n    \"\"\"\n\n    incoming_packets_dropped_in_trio: int\n\n\n@final\nclass DTLSChannel(trio.abc.Channel[bytes], metaclass=NoPublicConstructor):\n    \"\"\"A DTLS connection.\n\n    This class has no public constructor – you get instances by calling\n    `DTLSEndpoint.serve` or `~DTLSEndpoint.connect`.\n\n    .. attribute:: endpoint\n\n       The `DTLSEndpoint` that this connection is using.\n\n    .. attribute:: peer_address\n\n       The IP/port of the remote peer that this connection is associated with.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        endpoint: DTLSEndpoint,\n        peer_address: AddressFormat,\n        ctx: SSL.Context,\n    ) -> None:\n        self.endpoint = endpoint\n        self.peer_address = peer_address\n        self._packets_dropped_in_trio = 0\n        self._client_hello = None\n        self._did_handshake = False\n        self._ssl = SSL.Connection(ctx)\n        self._handshake_mtu = 0\n        # This calls self._ssl.set_ciphertext_mtu, which is important, because if you\n        # don't call it then openssl doesn't work.\n        self.set_ciphertext_mtu(best_guess_mtu(self.endpoint.socket))\n        self._replaced = False\n        self._closed = False\n        self._q = _Queue[bytes](endpoint.incoming_packets_buffer)\n        self._handshake_lock = trio.Lock()\n        self._record_encoder: RecordEncoder = RecordEncoder()\n\n        self._final_volley: list[_AnyHandshakeMessage] = []\n\n    def _set_replaced(self) -> None:\n        self._replaced = True\n        # Any packets we already received could maybe possibly still be processed, but\n        # there are no more coming. So we close this on the sender side.\n        self._q.s.close()\n\n    def _check_replaced(self) -> None:\n        if self._replaced:\n            raise trio.BrokenResourceError(\n                \"peer tore down this connection to start a new one\",\n            )\n\n    # XX on systems where we can (maybe just Linux?) take advantage of the kernel's PMTU\n    # estimate\n\n    # XX should we send close-notify when closing? It seems particularly pointless for\n    # DTLS where packets are all independent and can be lost anyway. We do at least need\n    # to handle receiving it properly though, which might be easier if we send it...\n\n    def close(self) -> None:\n        \"\"\"Close this connection.\n\n        `DTLSChannel`\\\\s don't actually own any OS-level resources – the\n        socket is owned by the `DTLSEndpoint`, not the individual connections. So\n        you don't really *have* to call this. But it will interrupt any other tasks\n        calling `receive` with a `ClosedResourceError`, and cause future attempts to use\n        this connection to fail.\n\n        You can also use this object as a synchronous or asynchronous context manager.\n\n        \"\"\"\n        if self._closed:\n            return\n        self._closed = True\n        if self.endpoint._streams.get(self.peer_address) is self:\n            del self.endpoint._streams[self.peer_address]\n        # Will wake any tasks waiting on self._q.get with a\n        # ClosedResourceError\n        self._q.r.close()\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        return self.close()\n\n    async def aclose(self) -> None:\n        \"\"\"Close this connection, but asynchronously.\n\n        This is included to satisfy the `trio.abc.Channel` contract. It's\n        identical to `close`, but async.\n\n        \"\"\"\n        self.close()\n        await trio.lowlevel.checkpoint()\n\n    async def _send_volley(self, volley_messages: list[_AnyHandshakeMessage]) -> None:\n        packets = self._record_encoder.encode_volley(\n            volley_messages,\n            self._handshake_mtu,\n        )\n        for packet in packets:\n            async with self.endpoint._send_lock:\n                await self.endpoint.socket.sendto(packet, self.peer_address)\n\n    async def _resend_final_volley(self) -> None:\n        await self._send_volley(self._final_volley)\n\n    async def do_handshake(self, *, initial_retransmit_timeout: float = 1.0) -> None:\n        \"\"\"Perform the handshake.\n\n        Calling this is optional – if you don't, then it will be automatically called\n        the first time you call `send` or `receive`. But calling it explicitly can be\n        useful in case you want to control the retransmit timeout, use a cancel scope to\n        place an overall timeout on the handshake, or catch errors from the handshake\n        specifically.\n\n        It's safe to call this multiple times, or call it simultaneously from multiple\n        tasks – the first call will perform the handshake, and the rest will be no-ops.\n\n        Args:\n\n          initial_retransmit_timeout (float): Since UDP is an unreliable protocol, it's\n            possible that some of the packets we send during the handshake will get\n            lost. To handle this, DTLS uses a timer to automatically retransmit\n            handshake packets that don't receive a response. This lets you set the\n            timeout we use to detect packet loss. Ideally, it should be set to ~1.5\n            times the round-trip time to your peer, but 1 second is a reasonable\n            default. There's `some useful guidance here\n            <https://tlswg.org/dtls13-spec/draft-ietf-tls-dtls13.html#name-timer-values>`__.\n\n            This is the *initial* timeout, because if packets keep being lost then Trio\n            will automatically back off to longer values, to avoid overloading the\n            network.\n\n        \"\"\"\n        async with self._handshake_lock:\n            if self._did_handshake:\n                return\n\n            timeout = initial_retransmit_timeout\n            volley_messages: list[_AnyHandshakeMessage] = []\n            volley_failed_sends = 0\n\n            def read_volley() -> list[_AnyHandshakeMessage]:\n                volley_bytes = _read_loop(self._ssl.bio_read)\n                new_volley_messages = decode_volley_trusted(volley_bytes)\n                if (\n                    new_volley_messages\n                    and volley_messages\n                    and isinstance(new_volley_messages[0], HandshakeMessage)\n                    and isinstance(volley_messages[0], HandshakeMessage)\n                    and new_volley_messages[0].msg_seq == volley_messages[0].msg_seq\n                ):\n                    # openssl decided to retransmit; discard because we handle\n                    # retransmits ourselves\n                    return []\n                else:\n                    return new_volley_messages\n\n            # If we're a client, we send the initial volley. If we're a server, then\n            # the initial ClientHello has already been inserted into self._ssl's\n            # read BIO. So either way, we start by generating a new volley.\n            with contextlib.suppress(SSL.WantReadError):\n                self._ssl.do_handshake()\n            volley_messages = read_volley()\n            # If we don't have messages to send in our initial volley, then something\n            # has gone very wrong. (I'm not sure this can actually happen without an\n            # error from OpenSSL, but we check just in case.)\n            if not volley_messages:  # pragma: no cover\n                raise SSL.Error(\"something wrong with peer's ClientHello\")\n\n            while True:\n                # -- at this point, we need to either send or re-send a volley --\n                assert volley_messages\n                self._check_replaced()\n                await self._send_volley(volley_messages)\n                # -- then this is where we wait for a reply --\n                self.endpoint._ensure_receive_loop()\n                with trio.move_on_after(timeout) as cscope:\n                    async for packet in self._q.r:\n                        self._ssl.bio_write(packet)\n                        try:\n                            self._ssl.do_handshake()\n                        # We ignore generic SSL.Error here, because you can get those\n                        # from random invalid packets\n                        except (SSL.WantReadError, SSL.Error):\n                            pass\n                        else:\n                            # No exception -> the handshake is done, and we can\n                            # switch into data transfer mode.\n                            self._did_handshake = True\n                            # Might be empty, but that's ok -- we'll just send no\n                            # packets.\n                            self._final_volley = read_volley()\n                            await self._send_volley(self._final_volley)\n                            return\n                        maybe_volley = read_volley()\n                        if maybe_volley:\n                            if (\n                                isinstance(maybe_volley[0], PseudoHandshakeMessage)\n                                and maybe_volley[0].content_type == ContentType.alert\n                            ):  # TODO: test this line\n                                # we're sending an alert (e.g. due to a corrupted\n                                # packet). We want to send it once, but don't save it to\n                                # retransmit -- keep the last volley as the current\n                                # volley.\n                                await self._send_volley(maybe_volley)\n                            else:\n                                # We managed to get all of the peer's volley and\n                                # generate a new one ourselves! break out of the 'for'\n                                # loop and restart the timer.\n                                volley_messages = maybe_volley\n                                # \"Implementations SHOULD retain the current timer value\n                                # until a transmission without loss occurs, at which\n                                # time the value may be reset to the initial value.\"\n                                if volley_failed_sends == 0:\n                                    timeout = initial_retransmit_timeout\n                                volley_failed_sends = 0\n                                break\n                    else:\n                        assert self._replaced\n                        self._check_replaced()\n                if cscope.cancelled_caught:\n                    # Timeout expired. Double timeout for backoff, with a limit of 60\n                    # seconds (this matches what openssl does, and also the\n                    # recommendation in draft-ietf-tls-dtls13).\n                    timeout = min(2 * timeout, 60.0)\n                    volley_failed_sends += 1\n                    if volley_failed_sends == 2:\n                        # We tried sending this twice and they both failed. Maybe our\n                        # PMTU estimate is wrong? Let's try dropping it to the minimum\n                        # and hope that helps.\n                        self._handshake_mtu = min(\n                            self._handshake_mtu,\n                            worst_case_mtu(self.endpoint.socket),\n                        )\n\n    async def send(self, data: bytes) -> None:\n        \"\"\"Send a packet of data, securely.\"\"\"\n\n        if self._closed:\n            raise trio.ClosedResourceError\n        if not data:\n            raise ValueError(\"openssl doesn't support sending empty DTLS packets\")\n        if not self._did_handshake:\n            await self.do_handshake()\n        self._check_replaced()\n        self._ssl.write(data)\n        async with self.endpoint._send_lock:\n            await self.endpoint.socket.sendto(\n                _read_loop(self._ssl.bio_read),\n                self.peer_address,\n            )\n\n    async def receive(self) -> bytes:\n        \"\"\"Fetch the next packet of data from this connection's peer, waiting if\n        necessary.\n\n        This is safe to call from multiple tasks simultaneously, in case you have some\n        reason to do that. And more importantly, it's cancellation-safe, meaning that\n        cancelling a call to `receive` will never cause a packet to be lost or corrupt\n        the underlying connection.\n\n        \"\"\"\n        if not self._did_handshake:\n            await self.do_handshake()\n        # If the packet isn't really valid, then openssl can decode it to the empty\n        # string (e.g. b/c it's a late-arriving handshake packet, or a duplicate copy of\n        # a data packet). Skip over these instead of returning them.\n        while True:\n            try:\n                packet = await self._q.r.receive()\n            except trio.EndOfChannel:\n                assert self._replaced\n                self._check_replaced()\n            self._ssl.bio_write(packet)\n            cleartext = _read_loop(self._ssl.read)\n            if cleartext:\n                return cleartext\n\n    def set_ciphertext_mtu(self, new_mtu: int) -> None:\n        \"\"\"Tells Trio the `largest amount of data that can be sent in a single packet to\n        this peer <https://en.wikipedia.org/wiki/Maximum_transmission_unit>`__.\n\n        Trio doesn't actually enforce this limit – if you pass a huge packet to `send`,\n        then we'll dutifully encrypt it and attempt to send it. But calling this method\n        does have two useful effects:\n\n        - If called before the handshake is performed, then Trio will automatically\n          fragment handshake messages to fit within the given MTU. It also might\n          fragment them even smaller, if it detects signs of packet loss, so setting\n          this should never be necessary to make a successful connection. But, the\n          packet loss detection only happens after multiple timeouts have expired, so if\n          you have reason to believe that a smaller MTU is required, then you can set\n          this to skip those timeouts and establish the connection more quickly.\n\n        - It changes the value returned from `get_cleartext_mtu`. So if you have some\n          kind of estimate of the network-level MTU, then you can use this to figure out\n          how much overhead DTLS will need for hashes/padding/etc., and how much space\n          you have left for your application data.\n\n        The MTU here is measuring the largest UDP *payload* you think can be sent, the\n        amount of encrypted data that can be handed to the operating system in a single\n        call to `send`. It should *not* include IP/UDP headers. Note that OS estimates\n        of the MTU often are link-layer MTUs, so you have to subtract off 28 bytes on\n        IPv4 and 48 bytes on IPv6 to get the ciphertext MTU.\n\n        By default, Trio assumes an MTU of 1472 bytes on IPv4, and 1452 bytes on IPv6,\n        which correspond to the common Ethernet MTU of 1500 bytes after accounting for\n        IP/UDP overhead.\n\n        \"\"\"\n        self._handshake_mtu = new_mtu\n        self._ssl.set_ciphertext_mtu(new_mtu)\n\n    def get_cleartext_mtu(self) -> int:\n        \"\"\"Returns the largest number of bytes that you can pass in a single call to\n        `send` while still fitting within the network-level MTU.\n\n        See `set_ciphertext_mtu` for more details.\n\n        \"\"\"\n        if not self._did_handshake:\n            raise trio.NeedHandshakeError\n        return self._ssl.get_cleartext_mtu()  # type: ignore[no-any-return]\n\n    def statistics(self) -> DTLSChannelStatistics:\n        \"\"\"Returns a `DTLSChannelStatistics` object with statistics about this connection.\"\"\"\n        return DTLSChannelStatistics(self._packets_dropped_in_trio)\n\n\n@final\nclass DTLSEndpoint:\n    \"\"\"A DTLS endpoint.\n\n    A single UDP socket can handle arbitrarily many DTLS connections simultaneously,\n    acting as a client or server as needed. A `DTLSEndpoint` object holds a UDP socket\n    and manages these connections, which are represented as `DTLSChannel` objects.\n\n    Args:\n      socket: (trio.socket.SocketType): A ``SOCK_DGRAM`` socket. If you want to accept\n        incoming connections in server mode, then you should probably bind the socket to\n        some known port.\n      incoming_packets_buffer (int): Each `DTLSChannel` using this socket has its own\n        buffer that holds incoming packets until you call `~DTLSChannel.receive` to read\n        them. This lets you adjust the size of this buffer. `~DTLSChannel.statistics`\n        lets you check if the buffer has overflowed.\n\n    .. attribute:: socket\n                   incoming_packets_buffer\n\n       Both constructor arguments are also exposed as attributes, in case you need to\n       access them later.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        socket: SocketType,\n        *,\n        incoming_packets_buffer: int = 10,\n    ) -> None:\n        # We do this lazily on first construction, so only people who actually use DTLS\n        # have to install PyOpenSSL.\n        global SSL\n        from OpenSSL import SSL\n\n        # for __del__, in case the next line raises\n        self._initialized: bool = False\n        if socket.type != trio.socket.SOCK_DGRAM:\n            raise ValueError(\"DTLS requires a SOCK_DGRAM socket\")\n        self._initialized = True\n        self.socket: SocketType = socket\n\n        self.incoming_packets_buffer = incoming_packets_buffer\n        self._token = trio.lowlevel.current_trio_token()\n        # We don't need to track handshaking vs non-handshake connections\n        # separately. We only keep one connection per remote address; as soon\n        # as a peer provides a valid cookie, we can immediately tear down the\n        # old connection.\n        # {remote address: DTLSChannel}\n        self._streams: WeakValueDictionary[AddressFormat, DTLSChannel] = (\n            WeakValueDictionary()\n        )\n        self._listening_context: SSL.Context | None = None\n        self._listening_key: bytes | None = None\n        self._incoming_connections_q = _Queue[DTLSChannel](float(\"inf\"))\n        self._send_lock = trio.Lock()\n        self._closed = False\n        self._receive_loop_spawned = False\n\n    def _ensure_receive_loop(self) -> None:\n        # We have to spawn this lazily, because on Windows it will immediately error out\n        # if the socket isn't already bound -- which for clients might not happen until\n        # after we send our first packet.\n        if not self._receive_loop_spawned:\n            trio.lowlevel.spawn_system_task(\n                dtls_receive_loop,\n                weakref.ref(self),\n                self.socket,\n            )\n            self._receive_loop_spawned = True\n\n    def __del__(self) -> None:\n        # Do nothing if this object was never fully constructed\n        if not self._initialized:\n            return\n        # Close the socket in Trio context (if our Trio context still exists), so that\n        # the background task gets notified about the closure and can exit.\n        if not self._closed:\n            with contextlib.suppress(RuntimeError):\n                self._token.run_sync_soon(self.close)\n            # Do this last, because it might raise an exception\n            warnings.warn(\n                f\"unclosed DTLS endpoint {self!r}\",\n                ResourceWarning,\n                source=self,\n                stacklevel=1,\n            )\n\n    def close(self) -> None:\n        \"\"\"Close this socket, and all associated DTLS connections.\n\n        This object can also be used as a context manager.\n\n        \"\"\"\n        self._closed = True\n        self.socket.close()\n        for stream in list(self._streams.values()):\n            stream.close()\n        self._incoming_connections_q.s.close()\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        return self.close()\n\n    def _check_closed(self) -> None:\n        if self._closed:\n            raise trio.ClosedResourceError\n\n    async def serve(\n        self,\n        ssl_context: SSL.Context,\n        async_fn: Callable[[DTLSChannel, Unpack[PosArgsT]], Awaitable[object]],\n        *args: Unpack[PosArgsT],\n        task_status: trio.TaskStatus[None] = trio.TASK_STATUS_IGNORED,\n    ) -> None:\n        \"\"\"Listen for incoming connections, and spawn a handler for each using an\n        internal nursery.\n\n        Similar to `~trio.serve_tcp`, this function never returns until cancelled, or\n        the `DTLSEndpoint` is closed and all handlers have exited.\n\n        Usage commonly looks like::\n\n            async def handler(dtls_channel):\n                ...\n\n            async with trio.open_nursery() as nursery:\n                await nursery.start(dtls_endpoint.serve, ssl_context, handler)\n                # ... do other things here ...\n\n        The ``dtls_channel`` passed into the handler function has already performed the\n        \"cookie exchange\" part of the DTLS handshake, so the peer address is\n        trustworthy. But the actual cryptographic handshake doesn't happen until you\n        start using it, giving you a chance for any last minute configuration, and the\n        option to catch and handle handshake errors.\n\n        Args:\n          ssl_context (OpenSSL.SSL.Context): The PyOpenSSL context object to use for\n            incoming connections.\n          async_fn: The handler function that will be invoked for each incoming\n            connection.\n          *args: Additional arguments to pass to the handler function.\n\n        \"\"\"\n        self._check_closed()\n        if self._listening_context is not None:\n            raise trio.BusyResourceError(\"another task is already listening\")\n        try:\n            self.socket.getsockname()\n        except OSError:  # TODO: test this line\n            raise RuntimeError(\n                \"DTLS socket must be bound before it can serve\",\n            ) from None\n        self._ensure_receive_loop()\n        # We do cookie verification ourselves, so tell OpenSSL not to worry about it.\n        # (See also _inject_client_hello_untrusted.)\n        ssl_context.set_cookie_verify_callback(lambda *_: True)\n        set_ssl_context_options(ssl_context)\n        try:\n            self._listening_context = ssl_context\n            task_status.started()\n\n            async def handler_wrapper(stream: DTLSChannel) -> None:\n                with stream:\n                    await async_fn(stream, *args)\n\n            async with trio.open_nursery() as nursery:\n                async for stream in self._incoming_connections_q.r:  # pragma: no branch\n                    nursery.start_soon(handler_wrapper, stream)\n        finally:\n            self._listening_context = None\n\n    def connect(\n        self,\n        address: tuple[str, int],\n        ssl_context: SSL.Context,\n    ) -> DTLSChannel:\n        \"\"\"Initiate an outgoing DTLS connection.\n\n        Notice that this is a synchronous method. That's because it doesn't actually\n        initiate any I/O – it just sets up a `DTLSChannel` object. The actual handshake\n        doesn't occur until you start using the `DTLSChannel`. This gives you a chance\n        to do further configuration first, like setting MTU etc.\n\n        Args:\n          address: The address to connect to. Usually a (host, port) tuple, like\n            ``(\"127.0.0.1\", 12345)``.\n          ssl_context (OpenSSL.SSL.Context): The PyOpenSSL context object to use for\n            this connection.\n\n        Returns:\n          DTLSChannel\n\n        \"\"\"\n        # it would be nice if we could detect when 'address' is our own endpoint (a\n        # loopback connection), because that can't work\n        # but I don't see how to do it reliably\n        self._check_closed()\n        set_ssl_context_options(ssl_context)\n        channel = DTLSChannel._create(self, address, ssl_context)\n        channel._ssl.set_connect_state()\n        old_channel = self._streams.get(address)\n        if old_channel is not None:\n            old_channel._set_replaced()\n        self._streams[address] = channel\n        return channel\n\n\ndef set_ssl_context_options(ctx: SSL.Context) -> None:\n    # These are mandatory for all DTLS connections. OP_NO_QUERY_MTU is required to\n    # stop openssl from trying to query the memory BIO's MTU and then breaking, and\n    # OP_NO_RENEGOTIATION disables renegotiation, which is too complex for us to\n    # support and isn't useful anyway -- especially for DTLS where it's equivalent\n    # to just performing a new handshake.\n    ctx.set_options(\n        SSL.OP_NO_QUERY_MTU | SSL.OP_NO_RENEGOTIATION,  # type: ignore[attr-defined]\n    )\n"
  },
  {
    "path": "src/trio/_file_io.py",
    "content": "from __future__ import annotations\n\nimport io\nfrom collections.abc import Callable, Iterable\nfrom functools import partial\nfrom typing import (\n    IO,\n    TYPE_CHECKING,\n    Any,\n    AnyStr,\n    BinaryIO,\n    Generic,\n    Literal,\n    TypeVar,\n    Union,\n    overload,\n)\n\nimport trio\n\nfrom ._util import async_wraps\nfrom .abc import AsyncResource\n\nif TYPE_CHECKING:\n    from _typeshed import (\n        OpenBinaryMode,\n        OpenBinaryModeReading,\n        OpenBinaryModeUpdating,\n        OpenBinaryModeWriting,\n        OpenTextMode,\n        StrOrBytesPath,\n    )\n\n    from ._sync import CapacityLimiter\n\n# This list is also in the docs, make sure to keep them in sync\n_FILE_SYNC_ATTRS: set[str] = {\n    \"closed\",\n    \"encoding\",\n    \"errors\",\n    \"fileno\",\n    \"isatty\",\n    \"newlines\",\n    \"readable\",\n    \"seekable\",\n    \"writable\",\n    # not defined in *IOBase:\n    \"buffer\",\n    \"raw\",\n    \"line_buffering\",\n    \"closefd\",\n    \"name\",\n    \"mode\",\n    \"getvalue\",\n    \"getbuffer\",\n}\n\n# This list is also in the docs, make sure to keep them in sync\n_FILE_ASYNC_METHODS: set[str] = {\n    \"flush\",\n    \"read\",\n    \"read1\",\n    \"readall\",\n    \"readinto\",\n    \"readline\",\n    \"readlines\",\n    \"seek\",\n    \"tell\",\n    \"truncate\",\n    \"write\",\n    \"writelines\",\n    # not defined in *IOBase:\n    \"readinto1\",\n    \"peek\",\n}\n\n\nFileT = TypeVar(\"FileT\")\nFileT_co = TypeVar(\"FileT_co\", covariant=True)\nT = TypeVar(\"T\")\nT_co = TypeVar(\"T_co\", covariant=True)\nT_contra = TypeVar(\"T_contra\", contravariant=True)\nAnyStr_co = TypeVar(\"AnyStr_co\", str, bytes, covariant=True)\nAnyStr_contra = TypeVar(\"AnyStr_contra\", str, bytes, contravariant=True)\n\n# This is a little complicated. IO objects have a lot of methods, and which are available on\n# different types varies wildly. We want to match the interface of whatever file we're wrapping.\n# This pile of protocols each has one sync method/property, meaning they're going to be compatible\n# with a file class that supports that method/property. The ones parameterized with AnyStr take\n# either str or bytes depending.\n\n# The wrapper is then a generic class, where the typevar is set to the type of the sync file we're\n# wrapping. For generics, adding a type to self has a special meaning - properties/methods can be\n# conditional - it's only valid to call them if the object you're accessing them on is compatible\n# with that type hint. By using the protocols, the type checker will be checking to see if the\n# wrapped type has that method, and only allow the methods that do to be called. We can then alter\n# the signature however it needs to match runtime behaviour.\n# More info: https://mypy.readthedocs.io/en/stable/more_types.html#advanced-uses-of-self-types\nif TYPE_CHECKING:\n    from typing_extensions import Buffer, Protocol\n\n    # fmt: off\n\n    class _HasClosed(Protocol):\n        @property\n        def closed(self) -> bool: ...\n\n    class _HasEncoding(Protocol):\n        @property\n        def encoding(self) -> str: ...\n\n    class _HasErrors(Protocol):\n        @property\n        def errors(self) -> str | None: ...\n\n    class _HasFileNo(Protocol):\n        def fileno(self) -> int: ...\n\n    class _HasIsATTY(Protocol):\n        def isatty(self) -> bool: ...\n\n    class _HasNewlines(Protocol[T_co]):\n        # Type varies here - documented to be None, tuple of strings, strings. Typeshed uses Any.\n        @property\n        def newlines(self) -> T_co: ...\n\n    class _HasReadable(Protocol):\n        def readable(self) -> bool: ...\n\n    class _HasSeekable(Protocol):\n        def seekable(self) -> bool: ...\n\n    class _HasWritable(Protocol):\n        def writable(self) -> bool: ...\n\n    class _HasBuffer(Protocol):\n        @property\n        def buffer(self) -> BinaryIO: ...\n\n    class _HasRaw(Protocol):\n        @property\n        def raw(self) -> io.RawIOBase: ...\n\n    class _HasLineBuffering(Protocol):\n        @property\n        def line_buffering(self) -> bool: ...\n\n    class _HasCloseFD(Protocol):\n        @property\n        def closefd(self) -> bool: ...\n\n    class _HasName(Protocol):\n        @property\n        def name(self) -> str: ...\n\n    class _HasMode(Protocol):\n        @property\n        def mode(self) -> str: ...\n\n    class _CanGetValue(Protocol[AnyStr_co]):\n        def getvalue(self) -> AnyStr_co: ...\n\n    class _CanGetBuffer(Protocol):\n        def getbuffer(self) -> memoryview: ...\n\n    class _CanFlush(Protocol):\n        def flush(self) -> None: ...\n\n    class _CanRead(Protocol[AnyStr_co]):\n        def read(self, size: int | None = ..., /) -> AnyStr_co: ...\n\n    class _CanRead1(Protocol):\n        def read1(self, size: int | None = ..., /) -> bytes: ...\n\n    class _CanReadAll(Protocol[AnyStr_co]):\n        def readall(self) -> AnyStr_co: ...\n\n    class _CanReadInto(Protocol):\n        def readinto(self, buf: Buffer, /) -> int | None: ...\n\n    class _CanReadInto1(Protocol):\n        def readinto1(self, buffer: Buffer, /) -> int: ...\n\n    class _CanReadLine(Protocol[AnyStr_co]):\n        def readline(self, size: int = ..., /) -> AnyStr_co: ...\n\n    class _CanReadLines(Protocol[AnyStr]):\n        def readlines(self, hint: int = ..., /) -> list[AnyStr]: ...\n\n    class _CanSeek(Protocol):\n        def seek(self, target: int, whence: int = 0, /) -> int: ...\n\n    class _CanTell(Protocol):\n        def tell(self) -> int: ...\n\n    class _CanTruncate(Protocol):\n        def truncate(self, size: int | None = ..., /) -> int: ...\n\n    class _CanWrite(Protocol[T_contra]):\n        def write(self, data: T_contra, /) -> int: ...\n\n    class _CanWriteLines(Protocol[T_contra]):\n        # The lines parameter varies for bytes/str, so use a typevar to make the async match.\n        def writelines(self, lines: Iterable[T_contra], /) -> None: ...\n\n    class _CanPeek(Protocol[AnyStr_co]):\n        def peek(self, size: int = 0, /) -> AnyStr_co: ...\n\n    class _CanDetach(Protocol[T_co]):\n        # The T typevar will be the unbuffered/binary file this file wraps.\n        def detach(self) -> T_co: ...\n\n    class _CanClose(Protocol):\n        def close(self) -> None: ...\n\n\n# FileT needs to be covariant for the protocol trick to work - the real IO types are effectively a\n# subtype of the protocols.\nclass AsyncIOWrapper(AsyncResource, Generic[FileT_co]):\n    \"\"\"A generic :class:`~io.IOBase` wrapper that implements the :term:`asynchronous\n    file object` interface. Wrapped methods that could block are executed in\n    :meth:`trio.to_thread.run_sync`.\n\n    All properties and methods defined in :mod:`~io` are exposed by this\n    wrapper, if they exist in the wrapped file object.\n    \"\"\"\n\n    def __init__(self, file: FileT_co) -> None:\n        self._wrapped = file\n\n    @property\n    def wrapped(self) -> FileT_co:\n        \"\"\"object: A reference to the wrapped file object\"\"\"\n\n        return self._wrapped\n\n    if not TYPE_CHECKING:\n\n        def __getattr__(self, name: str) -> object:\n            if name in _FILE_SYNC_ATTRS:\n                return getattr(self._wrapped, name)\n            if name in _FILE_ASYNC_METHODS:\n                meth = getattr(self._wrapped, name)\n\n                @async_wraps(self.__class__, self._wrapped.__class__, name)\n                async def wrapper(\n                    *args: Callable[..., T],\n                    **kwargs: object | str | bool | CapacityLimiter | None,\n                ) -> T:\n                    func = partial(meth, *args, **kwargs)\n                    return await trio.to_thread.run_sync(func)\n\n                # cache the generated method\n                setattr(self, name, wrapper)\n                return wrapper\n\n            raise AttributeError(name)\n\n    def __dir__(self) -> Iterable[str]:\n        attrs = set(super().__dir__())\n        attrs.update(a for a in _FILE_SYNC_ATTRS if hasattr(self.wrapped, a))\n        attrs.update(a for a in _FILE_ASYNC_METHODS if hasattr(self.wrapped, a))\n        return attrs\n\n    def __aiter__(self) -> AsyncIOWrapper[FileT_co]:\n        return self\n\n    async def __anext__(self: AsyncIOWrapper[_CanReadLine[AnyStr]]) -> AnyStr:\n        line = await self.readline()\n        if line:\n            return line\n        else:\n            raise StopAsyncIteration\n\n    async def detach(self: AsyncIOWrapper[_CanDetach[T]]) -> AsyncIOWrapper[T]:\n        \"\"\"Like :meth:`io.BufferedIOBase.detach`, but async.\n\n        This also re-wraps the result in a new :term:`asynchronous file object`\n        wrapper.\n\n        \"\"\"\n\n        raw = await trio.to_thread.run_sync(self._wrapped.detach)\n        return wrap_file(raw)\n\n    async def aclose(self: AsyncIOWrapper[_CanClose]) -> None:\n        \"\"\"Like :meth:`io.IOBase.close`, but async.\n\n        This is also shielded from cancellation; if a cancellation scope is\n        cancelled, the wrapped file object will still be safely closed.\n\n        \"\"\"\n\n        # ensure the underling file is closed during cancellation\n        with trio.CancelScope(shield=True):\n            await trio.to_thread.run_sync(self._wrapped.close)\n\n        await trio.lowlevel.checkpoint_if_cancelled()\n\n    if TYPE_CHECKING:\n        # fmt: off\n        # Based on typing.IO and io stubs.\n        @property\n        def closed(self: AsyncIOWrapper[_HasClosed]) -> bool: ...\n        @property\n        def encoding(self: AsyncIOWrapper[_HasEncoding]) -> str: ...\n        @property\n        def errors(self: AsyncIOWrapper[_HasErrors]) -> str | None: ...\n        @property\n        def newlines(self: AsyncIOWrapper[_HasNewlines[T]]) -> T: ...\n        @property\n        def buffer(self: AsyncIOWrapper[_HasBuffer]) -> BinaryIO: ...\n        @property\n        def raw(self: AsyncIOWrapper[_HasRaw]) -> io.RawIOBase: ...\n        @property\n        def line_buffering(self: AsyncIOWrapper[_HasLineBuffering]) -> int: ...\n        @property\n        def closefd(self: AsyncIOWrapper[_HasCloseFD]) -> bool: ...\n        @property\n        def name(self: AsyncIOWrapper[_HasName]) -> str: ...\n        @property\n        def mode(self: AsyncIOWrapper[_HasMode]) -> str: ...\n\n        def fileno(self: AsyncIOWrapper[_HasFileNo]) -> int: ...\n        def isatty(self: AsyncIOWrapper[_HasIsATTY]) -> bool: ...\n        def readable(self: AsyncIOWrapper[_HasReadable]) -> bool: ...\n        def seekable(self: AsyncIOWrapper[_HasSeekable]) -> bool: ...\n        def writable(self: AsyncIOWrapper[_HasWritable]) -> bool: ...\n        def getvalue(self: AsyncIOWrapper[_CanGetValue[AnyStr]]) -> AnyStr: ...\n        def getbuffer(self: AsyncIOWrapper[_CanGetBuffer]) -> memoryview: ...\n        async def flush(self: AsyncIOWrapper[_CanFlush]) -> None: ...\n        async def read(self: AsyncIOWrapper[_CanRead[AnyStr]], size: int | None = -1, /) -> AnyStr: ...\n        async def read1(self: AsyncIOWrapper[_CanRead1], size: int | None = -1, /) -> bytes: ...\n        async def readall(self: AsyncIOWrapper[_CanReadAll[AnyStr]]) -> AnyStr: ...\n        async def readinto(self: AsyncIOWrapper[_CanReadInto], buf: Buffer, /) -> int | None: ...\n        async def readline(self: AsyncIOWrapper[_CanReadLine[AnyStr]], size: int = -1, /) -> AnyStr: ...\n        async def readlines(self: AsyncIOWrapper[_CanReadLines[AnyStr]]) -> list[AnyStr]: ...\n        async def seek(self: AsyncIOWrapper[_CanSeek], target: int, whence: int = 0, /) -> int: ...\n        async def tell(self: AsyncIOWrapper[_CanTell]) -> int: ...\n        async def truncate(self: AsyncIOWrapper[_CanTruncate], size: int | None = None, /) -> int: ...\n        async def write(self: AsyncIOWrapper[_CanWrite[T]], data: T, /) -> int: ...\n        async def writelines(self: AsyncIOWrapper[_CanWriteLines[T]], lines: Iterable[T], /) -> None: ...\n        async def readinto1(self: AsyncIOWrapper[_CanReadInto1], buffer: Buffer, /) -> int: ...\n        async def peek(self: AsyncIOWrapper[_CanPeek[AnyStr]], size: int = 0, /) -> AnyStr: ...\n\n\n# Type hints are copied from builtin open.\n_OpenFile = Union[\"StrOrBytesPath\", int]\n_Opener = Callable[[str, int], int]\n\n\n@overload\nasync def open_file(\n    file: _OpenFile,\n    mode: OpenTextMode = \"r\",\n    buffering: int = -1,\n    encoding: str | None = None,\n    errors: str | None = None,\n    newline: str | None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[io.TextIOWrapper]: ...\n\n\n@overload\nasync def open_file(\n    file: _OpenFile,\n    mode: OpenBinaryMode,\n    buffering: Literal[0],\n    encoding: None = None,\n    errors: None = None,\n    newline: None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[io.FileIO]: ...\n\n\n@overload\nasync def open_file(\n    file: _OpenFile,\n    mode: OpenBinaryModeUpdating,\n    buffering: Literal[-1, 1] = -1,\n    encoding: None = None,\n    errors: None = None,\n    newline: None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[io.BufferedRandom]: ...\n\n\n@overload\nasync def open_file(\n    file: _OpenFile,\n    mode: OpenBinaryModeWriting,\n    buffering: Literal[-1, 1] = -1,\n    encoding: None = None,\n    errors: None = None,\n    newline: None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[io.BufferedWriter]: ...\n\n\n@overload\nasync def open_file(\n    file: _OpenFile,\n    mode: OpenBinaryModeReading,\n    buffering: Literal[-1, 1] = -1,\n    encoding: None = None,\n    errors: None = None,\n    newline: None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[io.BufferedReader]: ...\n\n\n@overload\nasync def open_file(\n    file: _OpenFile,\n    mode: OpenBinaryMode,\n    buffering: int,\n    encoding: None = None,\n    errors: None = None,\n    newline: None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[BinaryIO]: ...\n\n\n@overload\nasync def open_file(  # type: ignore[explicit-any]  # Any usage matches builtins.open().\n    file: _OpenFile,\n    mode: str,\n    buffering: int = -1,\n    encoding: str | None = None,\n    errors: str | None = None,\n    newline: str | None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[IO[Any]]: ...\n\n\nasync def open_file(\n    file: _OpenFile,\n    mode: str = \"r\",\n    buffering: int = -1,\n    encoding: str | None = None,\n    errors: str | None = None,\n    newline: str | None = None,\n    closefd: bool = True,\n    opener: _Opener | None = None,\n) -> AsyncIOWrapper[object]:\n    \"\"\"Asynchronous version of :func:`open`.\n\n    Returns:\n        An :term:`asynchronous file object`\n\n    Example::\n\n        async with await trio.open_file(filename) as f:\n            async for line in f:\n                pass\n\n        assert f.closed\n\n    See also:\n      :func:`trio.Path.open`\n\n    \"\"\"\n    file_ = wrap_file(\n        await trio.to_thread.run_sync(\n            io.open,\n            file,\n            mode,\n            buffering,\n            encoding,\n            errors,\n            newline,\n            closefd,\n            opener,\n        ),\n    )\n    return file_\n\n\ndef wrap_file(file: FileT) -> AsyncIOWrapper[FileT]:\n    \"\"\"This wraps any file object in a wrapper that provides an asynchronous\n    file object interface.\n\n    Args:\n        file: a :term:`file object`\n\n    Returns:\n        An :term:`asynchronous file object` that wraps ``file``\n\n    Example::\n\n        async_file = trio.wrap_file(StringIO('asdf'))\n\n        assert await async_file.read() == 'asdf'\n\n    \"\"\"\n\n    def has(attr: str) -> bool:\n        return hasattr(file, attr) and callable(getattr(file, attr))\n\n    if not (has(\"close\") and (has(\"read\") or has(\"write\"))):\n        raise TypeError(\n            f\"{file} does not implement required duck-file methods: \"\n            \"close and (read or write)\",\n        )\n\n    return AsyncIOWrapper(file)\n"
  },
  {
    "path": "src/trio/_highlevel_generic.py",
    "content": "from __future__ import annotations\n\nfrom typing import Generic, TypeGuard, TypeVar\n\nimport attrs\n\nimport trio\nfrom trio._util import final\n\nfrom .abc import AsyncResource, HalfCloseableStream, ReceiveStream, SendStream\n\nSendStreamT = TypeVar(\"SendStreamT\", bound=SendStream)\nReceiveStreamT = TypeVar(\"ReceiveStreamT\", bound=ReceiveStream)\n\n\nasync def aclose_forcefully(resource: AsyncResource) -> None:\n    \"\"\"Close an async resource or async generator immediately, without\n    blocking to do any graceful cleanup.\n\n    :class:`~trio.abc.AsyncResource` objects guarantee that if their\n    :meth:`~trio.abc.AsyncResource.aclose` method is cancelled, then they will\n    still close the resource (albeit in a potentially ungraceful\n    fashion). :func:`aclose_forcefully` is a convenience function that\n    exploits this behavior to let you force a resource to be closed without\n    blocking: it works by calling ``await resource.aclose()`` and then\n    cancelling it immediately.\n\n    Most users won't need this, but it may be useful on cleanup paths where\n    you can't afford to block, or if you want to close a resource and don't\n    care about handling it gracefully. For example, if\n    :class:`~trio.SSLStream` encounters an error and cannot perform its\n    own graceful close, then there's no point in waiting to gracefully shut\n    down the underlying transport either, so it calls ``await\n    aclose_forcefully(self.transport_stream)``.\n\n    Note that this function is async, and that it acts as a checkpoint, but\n    unlike most async functions it cannot block indefinitely (at least,\n    assuming the underlying resource object is correctly implemented).\n\n    \"\"\"\n    with trio.CancelScope() as cs:\n        cs.cancel(reason=\"cancelled during aclose_forcefully\")\n        await resource.aclose()\n\n\ndef _is_halfclosable(stream: SendStream) -> TypeGuard[HalfCloseableStream]:\n    \"\"\"Check if the stream has a send_eof() method.\"\"\"\n    return hasattr(stream, \"send_eof\")\n\n\n@final\n@attrs.define(eq=False, slots=False)\nclass StapledStream(\n    HalfCloseableStream,\n    Generic[SendStreamT, ReceiveStreamT],\n):\n    \"\"\"This class `staples <https://en.wikipedia.org/wiki/Staple_(fastener)>`__\n    together two unidirectional streams to make single bidirectional stream.\n\n    Args:\n      send_stream (~trio.abc.SendStream): The stream to use for sending.\n      receive_stream (~trio.abc.ReceiveStream): The stream to use for\n          receiving.\n\n    Example:\n\n       A silly way to make a stream that echoes back whatever you write to\n       it::\n\n          left, right = trio.testing.memory_stream_pair()\n          echo_stream = StapledStream(SocketStream(left), SocketStream(right))\n          await echo_stream.send_all(b\"x\")\n          assert await echo_stream.receive_some() == b\"x\"\n\n    :class:`StapledStream` objects implement the methods in the\n    :class:`~trio.abc.HalfCloseableStream` interface. They also have two\n    additional public attributes:\n\n    .. attribute:: send_stream\n\n       The underlying :class:`~trio.abc.SendStream`. :meth:`send_all` and\n       :meth:`wait_send_all_might_not_block` are delegated to this object.\n\n    .. attribute:: receive_stream\n\n       The underlying :class:`~trio.abc.ReceiveStream`. :meth:`receive_some`\n       is delegated to this object.\n\n    \"\"\"\n\n    send_stream: SendStreamT\n    receive_stream: ReceiveStreamT\n\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        \"\"\"Calls ``self.send_stream.send_all``.\"\"\"\n        return await self.send_stream.send_all(data)\n\n    async def wait_send_all_might_not_block(self) -> None:\n        \"\"\"Calls ``self.send_stream.wait_send_all_might_not_block``.\"\"\"\n        return await self.send_stream.wait_send_all_might_not_block()\n\n    async def send_eof(self) -> None:\n        \"\"\"Shuts down the send side of the stream.\n\n        If :meth:`self.send_stream.send_eof() <trio.abc.HalfCloseableStream.send_eof>` exists,\n        then this calls it. Otherwise, this calls\n        :meth:`self.send_stream.aclose() <trio.abc.AsyncResource.aclose>`.\n        \"\"\"\n        stream = self.send_stream\n        if _is_halfclosable(stream):\n            return await stream.send_eof()\n        else:\n            return await stream.aclose()\n\n    # we intentionally accept more types from the caller than we support returning\n    async def receive_some(self, max_bytes: int | None = None) -> bytes:\n        \"\"\"Calls ``self.receive_stream.receive_some``.\"\"\"\n        return await self.receive_stream.receive_some(max_bytes)\n\n    async def aclose(self) -> None:\n        \"\"\"Calls ``aclose`` on both underlying streams.\"\"\"\n        try:\n            await self.send_stream.aclose()\n        finally:\n            await self.receive_stream.aclose()\n"
  },
  {
    "path": "src/trio/_highlevel_open_tcp_listeners.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport trio\nfrom trio import TaskStatus\n\nfrom . import socket as tsocket\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import ExceptionGroup\n\n\n# Default backlog size:\n#\n# Having the backlog too low can cause practical problems (a perfectly healthy\n# service that starts failing to accept connections if they arrive in a\n# burst).\n#\n# Having it too high doesn't really cause any problems. Like any buffer, you\n# want backlog queue to be zero usually, and it won't save you if you're\n# getting connection attempts faster than you can call accept() on an ongoing\n# basis. But unlike other buffers, this one doesn't really provide any\n# backpressure. If a connection gets stuck waiting in the backlog queue, then\n# from the peer's point of view the connection succeeded but then their\n# send/recv will stall until we get to it, possibly for a long time. OTOH if\n# there isn't room in the backlog queue, then their connect stalls, possibly\n# for a long time, which is pretty much the same thing.\n#\n# A large backlog can also use a bit more kernel memory, but this seems fairly\n# negligible these days.\n#\n# So this suggests we should make the backlog as large as possible. This also\n# matches what Golang does. However, they do it in a weird way, where they\n# have a bunch of code to sniff out the configured upper limit for backlog on\n# different operating systems. But on every system, passing in a too-large\n# backlog just causes it to be silently truncated to the configured maximum,\n# so this is unnecessary -- we can just pass in \"infinity\" and get the maximum\n# that way. (Verified on Windows, Linux, macOS using\n# https://github.com/python-trio/trio/wiki/notes-to-self#measure-listen-backlogpy\ndef _compute_backlog(backlog: int | None) -> int:\n    # Many systems (Linux, BSDs, ...) store the backlog in a uint16 and are\n    # missing overflow protection, so we apply our own overflow protection.\n    # https://github.com/golang/go/issues/5030\n    if not isinstance(backlog, int) and backlog is not None:\n        raise TypeError(f\"backlog must be an int or None, not {backlog!r}\")\n    if backlog is None:\n        return 0xFFFF\n    return min(backlog, 0xFFFF)\n\n\nasync def open_tcp_listeners(\n    port: int,\n    *,\n    host: str | bytes | None = None,\n    backlog: int | None = None,\n) -> list[trio.SocketListener]:\n    \"\"\"Create :class:`SocketListener` objects to listen for TCP connections.\n\n    Args:\n\n      port (int): The port to listen on.\n\n          If you use 0 as your port, then the kernel will automatically pick\n          an arbitrary open port. But be careful: if you use this feature when\n          binding to multiple IP addresses, then each IP address will get its\n          own random port, and the returned listeners will probably be\n          listening on different ports. In particular, this will happen if you\n          use ``host=None`` – which is the default – because in this case\n          :func:`open_tcp_listeners` will bind to both the IPv4 wildcard\n          address (``0.0.0.0``) and also the IPv6 wildcard address (``::``).\n\n      host (str, bytes, or None): The local interface to bind to. This is\n          passed to :func:`~socket.getaddrinfo` with the ``AI_PASSIVE`` flag\n          set.\n\n          If you want to bind to the wildcard address on both IPv4 and IPv6,\n          in order to accept connections on all available interfaces, then\n          pass ``None``. This is the default.\n\n          If you have a specific interface you want to bind to, pass its IP\n          address or hostname here. If a hostname resolves to multiple IP\n          addresses, this function will open one listener on each of them.\n\n          If you want to use only IPv4, or only IPv6, but want to accept on\n          all interfaces, pass the family-specific wildcard address:\n          ``\"0.0.0.0\"`` for IPv4-only and ``\"::\"`` for IPv6-only.\n\n      backlog (int or None): The listen backlog to use. If you leave this as\n          ``None`` then Trio will pick a good default. (Currently: whatever\n          your system has configured as the maximum backlog.)\n\n    Returns:\n      list of :class:`SocketListener`\n\n    Raises:\n      :class:`TypeError` if invalid arguments.\n\n    \"\"\"\n    # getaddrinfo sometimes allows port=None, sometimes not (depending on\n    # whether host=None). And on some systems it treats \"\" as 0, others it\n    # doesn't:\n    #   http://klickverbot.at/blog/2012/01/getaddrinfo-edge-case-behavior-on-windows-linux-and-osx/\n    if not isinstance(port, int):\n        raise TypeError(f\"port must be an int not {port!r}\")\n\n    computed_backlog = _compute_backlog(backlog)\n\n    addresses = await tsocket.getaddrinfo(\n        host,\n        port,\n        type=tsocket.SOCK_STREAM,\n        flags=tsocket.AI_PASSIVE,\n    )\n\n    listeners = []\n    unsupported_address_families = []\n    try:\n        for family, type_, proto, _, sockaddr in addresses:\n            try:\n                sock = tsocket.socket(family, type_, proto)\n            except OSError as ex:\n                if ex.errno == errno.EAFNOSUPPORT:\n                    # If a system only supports IPv4, or only IPv6, it\n                    # is still likely that getaddrinfo will return\n                    # both an IPv4 and an IPv6 address. As long as at\n                    # least one of the returned addresses can be\n                    # turned into a socket, we won't complain about a\n                    # failure to create the other.\n                    unsupported_address_families.append(ex)\n                    continue\n                else:\n                    raise\n            try:\n                # See https://github.com/python-trio/trio/issues/39\n                if sys.platform != \"win32\":\n                    sock.setsockopt(tsocket.SOL_SOCKET, tsocket.SO_REUSEADDR, 1)\n\n                if family == tsocket.AF_INET6:\n                    sock.setsockopt(tsocket.IPPROTO_IPV6, tsocket.IPV6_V6ONLY, 1)\n\n                await sock.bind(sockaddr)\n                sock.listen(computed_backlog)\n\n                listeners.append(trio.SocketListener(sock))\n            except:\n                sock.close()\n                raise\n    except:\n        for listener in listeners:\n            listener.socket.close()\n        raise\n\n    if unsupported_address_families and not listeners:\n        msg = (\n            \"This system doesn't support any of the kinds of \"\n            \"socket that that address could use\"\n        )\n        raise OSError(errno.EAFNOSUPPORT, msg) from ExceptionGroup(\n            msg,\n            unsupported_address_families,\n        )\n\n    return listeners\n\n\nasync def serve_tcp(\n    handler: Callable[[trio.SocketStream], Awaitable[object]],\n    port: int,\n    *,\n    host: str | bytes | None = None,\n    backlog: int | None = None,\n    handler_nursery: trio.Nursery | None = None,\n    task_status: TaskStatus[list[trio.SocketListener]] = trio.TASK_STATUS_IGNORED,\n) -> None:\n    \"\"\"Listen for incoming TCP connections, and for each one start a task\n    running ``handler(stream)``.\n\n    This is a thin convenience wrapper around :func:`open_tcp_listeners` and\n    :func:`serve_listeners` – see them for full details.\n\n    .. warning::\n\n       If ``handler`` raises an exception, then this function doesn't do\n       anything special to catch it – so by default the exception will\n       propagate out and crash your server. If you don't want this, then catch\n       exceptions inside your ``handler``, or use a ``handler_nursery`` object\n       that responds to exceptions in some other way.\n\n    When used with ``nursery.start`` you get back the newly opened listeners.\n    So, for example, if you want to start a server in your test suite and then\n    connect to it to check that it's working properly, you can use something\n    like::\n\n        from trio import SocketListener, SocketStream\n        from trio.testing import open_stream_to_socket_listener\n\n        async with trio.open_nursery() as nursery:\n            listeners: list[SocketListener] = await nursery.start(serve_tcp, handler, 0)\n            client_stream: SocketStream = await open_stream_to_socket_listener(listeners[0])\n\n            # Then send and receive data on 'client_stream', for example:\n            await client_stream.send_all(b\"GET / HTTP/1.0\\\\r\\\\n\\\\r\\\\n\")\n\n    This avoids several common pitfalls:\n\n    1. It lets the kernel pick a random open port, so your test suite doesn't\n       depend on any particular port being open.\n\n    2. It waits for the server to be accepting connections on that port before\n       ``start`` returns, so there's no race condition where the incoming\n       connection arrives before the server is ready.\n\n    3. It uses the Listener object to find out which port was picked, so it\n       can connect to the right place.\n\n    Args:\n      handler: The handler to start for each incoming connection. Passed to\n          :func:`serve_listeners`.\n\n      port: The port to listen on. Use 0 to let the kernel pick an open port.\n          Passed to :func:`open_tcp_listeners`.\n\n      host (str, bytes, or None): The host interface to listen on; use\n          ``None`` to bind to the wildcard address. Passed to\n          :func:`open_tcp_listeners`.\n\n      backlog: The listen backlog, or None to have a good default picked.\n          Passed to :func:`open_tcp_listeners`.\n\n      handler_nursery: The nursery to start handlers in, or None to use an\n          internal nursery. Passed to :func:`serve_listeners`.\n\n      task_status: This function can be used with ``nursery.start``.\n\n    Returns:\n      This function only returns when cancelled.\n\n    \"\"\"\n    listeners = await trio.open_tcp_listeners(port, host=host, backlog=backlog)\n    await trio.serve_listeners(\n        handler,\n        listeners,\n        handler_nursery=handler_nursery,\n        task_status=task_status,\n    )\n"
  },
  {
    "path": "src/trio/_highlevel_open_tcp_stream.py",
    "content": "from __future__ import annotations\n\nimport sys\nfrom contextlib import contextmanager, suppress\nfrom typing import TYPE_CHECKING, Any\n\nimport trio\nfrom trio.socket import SOCK_STREAM, SocketType, getaddrinfo, socket\n\nif TYPE_CHECKING:\n    from collections.abc import Generator, MutableSequence\n    from socket import AddressFamily, SocketKind\n\n    from trio._socket import AddressFormat\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup, ExceptionGroup\n\n\n# Implementation of RFC 6555 \"Happy eyeballs\"\n# https://tools.ietf.org/html/rfc6555\n#\n# Basically, the problem here is that if we want to connect to some host, and\n# DNS returns multiple IP addresses, then we don't know which of them will\n# actually work -- it can happen that some of them are reachable, and some of\n# them are not. One particularly common situation where this happens is on a\n# host that thinks it has ipv6 connectivity, but really doesn't. But in\n# principle this could happen for any kind of multi-home situation (e.g. the\n# route to one mirror is down but another is up).\n#\n# The naive algorithm (e.g. the stdlib's socket.create_connection) would be to\n# pick one of the IP addresses and try to connect; if that fails, try the\n# next; etc. The problem with this is that TCP is stubborn, and if the first\n# address is a blackhole then it might take a very long time (tens of seconds)\n# before that connection attempt fails.\n#\n# That's where RFC 6555 comes in. It tells us that what we do is:\n# - get the list of IPs from getaddrinfo, trusting the order it gives us (with\n#   one exception noted in section 5.4)\n# - start a connection attempt to the first IP\n# - when this fails OR if it's still going after DELAY seconds, then start a\n#   connection attempt to the second IP\n# - when this fails OR if it's still going after another DELAY seconds, then\n#   start a connection attempt to the third IP\n# - ... repeat until we run out of IPs.\n#\n# Our implementation is similarly straightforward: we spawn a chain of tasks,\n# where each one (a) waits until the previous connection has failed or DELAY\n# seconds have passed, (b) spawns the next task, (c) attempts to connect. As\n# soon as any task crashes or succeeds, we cancel all the tasks and return.\n#\n# Note: this currently doesn't attempt to cache any results, so if you make\n# multiple connections to the same host it'll re-run the happy-eyeballs\n# algorithm each time. RFC 6555 is pretty confusing about whether this is\n# allowed. Section 4 describes an algorithm that attempts ipv4 and ipv6\n# simultaneously, and then says \"The client MUST cache information regarding\n# the outcome of each connection attempt, and it uses that information to\n# avoid thrashing the network with subsequent attempts.\" Then section 4.2 says\n# \"implementations MUST prefer the first IP address family returned by the\n# host's address preference policy, unless implementing a stateful\n# algorithm\". Here \"stateful\" means \"one that caches information about\n# previous attempts\". So my reading of this is that IF you're starting ipv4\n# and ipv6 at the same time then you MUST cache the result for ~ten minutes,\n# but IF you're \"preferring\" one protocol by trying it first (like we are),\n# then you don't need to cache.\n#\n# Caching is quite tricky: to get it right you need to do things like detect\n# when the network interfaces are reconfigured, and if you get it wrong then\n# connection attempts basically just don't work. So we don't even try.\n\n# \"Firefox and Chrome use 300 ms\"\n# https://tools.ietf.org/html/rfc6555#section-6\n# Though\n#   https://www.researchgate.net/profile/Vaibhav_Bajpai3/publication/304568993_Measuring_the_Effects_of_Happy_Eyeballs/links/5773848e08ae6f328f6c284c/Measuring-the-Effects-of-Happy-Eyeballs.pdf\n# claims that Firefox actually uses 0 ms, unless an about:config option is\n# toggled and then it uses 250 ms.\nDEFAULT_DELAY = 0.250\n\n# How should we call getaddrinfo? In particular, should we use AI_ADDRCONFIG?\n#\n# The idea of AI_ADDRCONFIG is that it only returns addresses that might\n# work. E.g., if getaddrinfo knows that you don't have any IPv6 connectivity,\n# then it doesn't return any IPv6 addresses. And this is kinda nice, because\n# it means maybe you can skip sending AAAA requests entirely. But in practice,\n# it doesn't really work right.\n#\n# - on Linux/glibc, empirically, the default is to return all addresses, and\n# with AI_ADDRCONFIG then it only returns IPv6 addresses if there is at least\n# one non-loopback IPv6 address configured... but this can be a link-local\n# address, so in practice I guess this is basically always configured if IPv6\n# is enabled at all. OTOH if you pass in \"::1\" as the target address with\n# AI_ADDRCONFIG and there's no *external* IPv6 address configured, you get an\n# error. So AI_ADDRCONFIG mostly doesn't do anything, even when you would want\n# it to, and when it does do something it might break things that would have\n# worked.\n#\n# - on Windows 10, empirically, if no IPv6 address is configured then by\n# default they are also suppressed from getaddrinfo (flags=0 and\n# flags=AI_ADDRCONFIG seem to do the same thing). If you pass AI_ALL, then you\n# get the full list.\n# ...except for localhost! getaddrinfo(\"localhost\", \"80\") gives me ::1, even\n# though there's no ipv6 and other queries only return ipv4.\n# If you pass in and IPv6 IP address as the target address, then that's always\n# returned OK, even with AI_ADDRCONFIG set and no IPv6 configured.\n#\n# But I guess other versions of windows messed this up, judging from these bug\n# reports:\n# https://bugs.chromium.org/p/chromium/issues/detail?id=5234\n# https://bugs.chromium.org/p/chromium/issues/detail?id=32522#c50\n#\n# So basically the options are either to use AI_ADDRCONFIG and then add some\n# complicated special cases to work around its brokenness, or else don't use\n# AI_ADDRCONFIG and accept that sometimes on legacy/misconfigured networks\n# we'll waste 300 ms trying to connect to a blackholed destination.\n#\n# Twisted and Tornado always uses default flags. I think we'll do the same.\n\n\n@contextmanager\ndef close_all() -> Generator[set[SocketType], None, None]:\n    sockets_to_close: set[SocketType] = set()\n    try:\n        yield sockets_to_close\n    finally:\n        errs = []\n        for sock in sockets_to_close:\n            try:\n                sock.close()\n            except BaseException as exc:\n                errs.append(exc)\n        if len(errs) == 1:\n            raise errs[0]\n        elif errs:\n            raise BaseExceptionGroup(\"\", errs)\n\n\ndef reorder_for_rfc_6555_section_5_4(  # type: ignore[explicit-any]\n    targets: MutableSequence[tuple[AddressFamily, SocketKind, int, str, Any]],\n) -> None:\n    # RFC 6555 section 5.4 says that if getaddrinfo returns multiple address\n    # families (e.g. IPv4 and IPv6), then you should make sure that your first\n    # and second attempts use different families:\n    #\n    #    https://tools.ietf.org/html/rfc6555#section-5.4\n    #\n    # This function post-processes the results from getaddrinfo, in-place, to\n    # satisfy this requirement.\n    for i in range(1, len(targets)):\n        if targets[i][0] != targets[0][0]:\n            # Found the first entry with a different address family; move it\n            # so that it becomes the second item on the list.\n            if i != 1:\n                targets.insert(1, targets.pop(i))\n            break\n\n\ndef format_host_port(host: str | bytes, port: int | str) -> str:\n    host = host.decode(\"ascii\") if isinstance(host, bytes) else host\n    if \":\" in host:\n        return f\"[{host}]:{port}\"\n    else:\n        return f\"{host}:{port}\"\n\n\n# Twisted's HostnameEndpoint has a good set of configurations:\n#   https://twistedmatrix.com/documents/current/api/twisted.internet.endpoints.HostnameEndpoint.html\n#\n# - per-connection timeout\n#   this doesn't seem useful -- we let you set a timeout on the whole thing\n#   using Trio's normal mechanisms, and that seems like enough\n# - delay between attempts\n# - bind address (but not port!)\n#   they *don't* support multiple address bindings, like giving the ipv4 and\n#   ipv6 addresses of the host.\n#   I think maybe our semantics should be: we accept a list of bind addresses,\n#   and we bind to the first one that is compatible with the\n#   connection attempt we want to make, and if none are compatible then we\n#   don't try to connect to that target.\n#\n# XX TODO: implement bind address support\n#\n# Actually, the best option is probably to be explicit: {AF_INET: \"...\",\n#   AF_INET6: \"...\"}\n# this might be simpler after\nasync def open_tcp_stream(\n    host: str | bytes,\n    port: int,\n    *,\n    happy_eyeballs_delay: float | None = DEFAULT_DELAY,\n    local_address: str | None = None,\n) -> trio.SocketStream:\n    \"\"\"Connect to the given host and port over TCP.\n\n    If the given ``host`` has multiple IP addresses associated with it, then\n    we have a problem: which one do we use?\n\n    One approach would be to attempt to connect to the first one, and then if\n    that fails, attempt to connect to the second one ... until we've tried all\n    of them. But the problem with this is that if the first IP address is\n    unreachable (for example, because it's an IPv6 address and our network\n    discards IPv6 packets), then we might end up waiting tens of seconds for\n    the first connection attempt to timeout before we try the second address.\n\n    Another approach would be to attempt to connect to all of the addresses at\n    the same time, in parallel, and then use whichever connection succeeds\n    first, abandoning the others. This would be fast, but create a lot of\n    unnecessary load on the network and the remote server.\n\n    This function strikes a balance between these two extremes: it works its\n    way through the available addresses one at a time, like the first\n    approach; but, if ``happy_eyeballs_delay`` seconds have passed and it's\n    still waiting for an attempt to succeed or fail, then it gets impatient\n    and starts the next connection attempt in parallel. As soon as any one\n    connection attempt succeeds, all the other attempts are cancelled. This\n    avoids unnecessary load because most connections will succeed after just\n    one or two attempts, but if one of the addresses is unreachable then it\n    doesn't slow us down too much.\n\n    This is known as a \"happy eyeballs\" algorithm, and our particular variant\n    is modelled after how Chrome connects to webservers; see `RFC 6555\n    <https://tools.ietf.org/html/rfc6555>`__ for more details.\n\n    Args:\n      host (str or bytes): The host to connect to. Can be an IPv4 address,\n          IPv6 address, or a hostname.\n\n      port (int): The port to connect to.\n\n      happy_eyeballs_delay (float or None): How many seconds to wait for each\n          connection attempt to succeed or fail before getting impatient and\n          starting another one in parallel. Set to `None` if you want\n          to limit to only one connection attempt at a time (like\n          :func:`socket.create_connection`). Default: 0.25 (250 ms).\n\n      local_address (None or str): The local IP address or hostname to use as\n          the source for outgoing connections. If ``None``, we let the OS pick\n          the source IP.\n\n          This is useful in some exotic networking configurations where your\n          host has multiple IP addresses, and you want to force the use of a\n          specific one.\n\n          Note that if you pass an IPv4 ``local_address``, then you won't be\n          able to connect to IPv6 hosts, and vice-versa. If you want to take\n          advantage of this to force the use of IPv4 or IPv6 without\n          specifying an exact source address, you can use the IPv4 wildcard\n          address ``local_address=\"0.0.0.0\"``, or the IPv6 wildcard address\n          ``local_address=\"::\"``.\n\n    Returns:\n      SocketStream: a :class:`~trio.abc.Stream` connected to the given server.\n\n    Raises:\n      OSError: if the connection fails.\n\n    See also:\n      open_ssl_over_tcp_stream\n\n    \"\"\"\n\n    # To keep our public API surface smaller, rule out some cases that\n    # getaddrinfo will accept in some circumstances, but that act weird or\n    # have non-portable behavior or are just plain not useful.\n    if not isinstance(host, (str, bytes)):\n        raise ValueError(f\"host must be str or bytes, not {host!r}\")\n    if not isinstance(port, int):\n        raise TypeError(f\"port must be int, not {port!r}\")\n\n    if happy_eyeballs_delay is None:\n        happy_eyeballs_delay = DEFAULT_DELAY\n\n    targets = await getaddrinfo(host, port, type=SOCK_STREAM)\n\n    # I don't think this can actually happen -- if there are no results,\n    # getaddrinfo should have raised OSError instead of returning an empty\n    # list. But let's be paranoid and handle it anyway:\n    if not targets:\n        msg = f\"no results found for hostname lookup: {format_host_port(host, port)}\"\n        raise OSError(msg)\n\n    reorder_for_rfc_6555_section_5_4(targets)\n\n    # This list records all the connection failures that we ignored.\n    oserrors: list[OSError] = []\n\n    # Keeps track of the socket that we're going to complete with,\n    # need to make sure this isn't automatically closed\n    winning_socket: SocketType | None = None\n\n    # Try connecting to the specified address. Possible outcomes:\n    # - success: record connected socket in winning_socket and cancel\n    #   concurrent attempts\n    # - failure: record exception in oserrors, set attempt_failed allowing\n    #   the next connection attempt to start early\n    # code needs to ensure sockets can be closed appropriately in the\n    # face of crash or cancellation\n    async def attempt_connect(\n        socket_args: tuple[AddressFamily, SocketKind, int],\n        sockaddr: AddressFormat,\n        attempt_failed: trio.Event,\n    ) -> None:\n        nonlocal winning_socket\n\n        try:\n            sock = socket(*socket_args)\n            open_sockets.add(sock)\n\n            if local_address is not None:\n                # TCP connections are identified by a 4-tuple:\n                #\n                #   (local IP, local port, remote IP, remote port)\n                #\n                # So if a single local IP wants to make multiple connections\n                # to the same (remote IP, remote port) pair, then those\n                # connections have to use different local ports, or else TCP\n                # won't be able to tell them apart. OTOH, if you have multiple\n                # connections to different remote IP/ports, then those\n                # connections can share a local port.\n                #\n                # Normally, when you call bind(), the kernel will immediately\n                # assign a specific local port to your socket. At this point\n                # the kernel doesn't know which (remote IP, remote port)\n                # you're going to use, so it has to pick a local port that\n                # *no* other connection is using. That's the only way to\n                # guarantee that this local port will be usable later when we\n                # call connect(). (Alternatively, you can set SO_REUSEADDR to\n                # allow multiple nascent connections to share the same port,\n                # but then connect() might fail with EADDRNOTAVAIL if we get\n                # unlucky and our TCP 4-tuple ends up colliding with another\n                # unrelated connection.)\n                #\n                # So calling bind() before connect() works, but it disables\n                # sharing of local ports. This is inefficient: it makes you\n                # more likely to run out of local ports.\n                #\n                # But on some versions of Linux, we can re-enable sharing of\n                # local ports by setting a special flag. This flag tells\n                # bind() to only bind the IP, and not the port. That way,\n                # connect() is allowed to pick the the port, and it can do a\n                # better job of it because it knows the remote IP/port.\n                with suppress(OSError, AttributeError):\n                    sock.setsockopt(\n                        trio.socket.IPPROTO_IP,\n                        trio.socket.IP_BIND_ADDRESS_NO_PORT,\n                        1,\n                    )\n                try:\n                    await sock.bind((local_address, 0))\n                except OSError:\n                    raise OSError(\n                        f\"local_address={local_address!r} is incompatible \"\n                        f\"with remote address {sockaddr!r}\",\n                    ) from None\n\n            await sock.connect(sockaddr)\n\n            # Success! Save the winning socket and cancel all outstanding\n            # connection attempts.\n            winning_socket = sock\n            nursery.cancel_scope.cancel(reason=\"successfully found a socket\")\n        except OSError as exc:\n            # This connection attempt failed, but the next one might\n            # succeed. Save the error for later so we can report it if\n            # everything fails, and tell the next attempt that it should go\n            # ahead (if it hasn't already).\n            oserrors.append(exc)\n            attempt_failed.set()\n\n    with close_all() as open_sockets:\n        # nursery spawns a task for each connection attempt, will be\n        # cancelled by the task that gets a successful connection\n        async with trio.open_nursery() as nursery:\n            for address_family, socket_type, proto, _, addr in targets:\n                # create an event to indicate connection failure,\n                # allowing the next target to be tried early\n                attempt_failed = trio.Event()\n\n                nursery.start_soon(\n                    attempt_connect,\n                    (address_family, socket_type, proto),\n                    addr,\n                    attempt_failed,\n                )\n\n                # give this attempt at most this time before moving on\n                with trio.move_on_after(happy_eyeballs_delay):\n                    await attempt_failed.wait()\n\n        # nothing succeeded\n        if winning_socket is None:\n            assert len(oserrors) == len(targets)\n            msg = f\"all attempts to connect to {format_host_port(host, port)} failed\"\n            raise OSError(msg) from ExceptionGroup(msg, oserrors)\n        else:\n            stream = trio.SocketStream(winning_socket)\n            open_sockets.remove(winning_socket)\n            return stream\n"
  },
  {
    "path": "src/trio/_highlevel_open_unix_stream.py",
    "content": "from __future__ import annotations\n\nimport os\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING, Protocol, TypeVar\n\nimport trio\nfrom trio.socket import SOCK_STREAM, socket\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n\nclass Closable(Protocol):\n    def close(self) -> None: ...\n\n\nCloseT = TypeVar(\"CloseT\", bound=Closable)\n\n\ntry:\n    from trio.socket import AF_UNIX\n\n    has_unix = True\nexcept ImportError:\n    has_unix = False\n\n\n@contextmanager\ndef close_on_error(obj: CloseT) -> Generator[CloseT, None, None]:\n    try:\n        yield obj\n    except:\n        obj.close()\n        raise\n\n\nasync def open_unix_socket(\n    filename: str | bytes | os.PathLike[str] | os.PathLike[bytes],\n) -> trio.SocketStream:\n    \"\"\"Opens a connection to the specified\n    `Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`__.\n\n    You must have read/write permission on the specified file to connect.\n\n    Args:\n      filename (str or bytes): The filename to open the connection to.\n\n    Returns:\n      SocketStream: a :class:`~trio.abc.Stream` connected to the given file.\n\n    Raises:\n      OSError: If the socket file could not be connected to.\n      RuntimeError: If AF_UNIX sockets are not supported.\n    \"\"\"\n    if not has_unix:\n        raise RuntimeError(\"Unix sockets are not supported on this platform\")\n\n    # much more simplified logic vs tcp sockets - one socket type and only one\n    # possible location to connect to\n    sock = socket(AF_UNIX, SOCK_STREAM)\n    with close_on_error(sock):\n        await sock.connect(os.fspath(filename))\n\n    return trio.SocketStream(sock)\n"
  },
  {
    "path": "src/trio/_highlevel_serve_listeners.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport logging\nimport os\nfrom collections.abc import Awaitable, Callable\nfrom typing import Any, NoReturn, TypeVar\n\nimport trio\n\n# Errors that accept(2) can return, and which indicate that the system is\n# overloaded\nACCEPT_CAPACITY_ERRNOS = {\n    errno.EMFILE,\n    errno.ENFILE,\n    errno.ENOMEM,\n    errno.ENOBUFS,\n}\n\n# How long to sleep when we get one of those errors\nSLEEP_TIME = 0.100\n\n# The logger we use to complain when this happens\nLOGGER = logging.getLogger(\"trio.serve_listeners\")\n\n\nStreamT = TypeVar(\"StreamT\", bound=trio.abc.AsyncResource)\nListenerT = TypeVar(\"ListenerT\", bound=trio.abc.Listener[Any])  # type: ignore[explicit-any]\nHandler = Callable[[StreamT], Awaitable[object]]\n\n\nasync def _run_handler(stream: StreamT, handler: Handler[StreamT]) -> None:\n    try:\n        await handler(stream)\n    finally:\n        await trio.aclose_forcefully(stream)\n\n\nasync def _serve_one_listener(\n    listener: trio.abc.Listener[StreamT],\n    handler_nursery: trio.Nursery,\n    handler: Handler[StreamT],\n) -> NoReturn:\n    async with listener:\n        while True:\n            try:\n                stream = await listener.accept()\n            except OSError as exc:\n                if exc.errno in ACCEPT_CAPACITY_ERRNOS:\n                    LOGGER.error(\n                        \"accept returned %s (%s); retrying in %s seconds\",\n                        errno.errorcode[exc.errno],\n                        os.strerror(exc.errno),\n                        SLEEP_TIME,\n                        exc_info=True,\n                    )\n                    await trio.sleep(SLEEP_TIME)\n                else:\n                    raise\n            else:\n                handler_nursery.start_soon(_run_handler, stream, handler)\n\n\n# This cannot be typed correctly, we need generic typevar bounds / HKT to indicate the\n# relationship between StreamT & ListenerT.\n# https://github.com/python/typing/issues/1226\n# https://github.com/python/typing/issues/548\n\n\nasync def serve_listeners(  # type: ignore[explicit-any]\n    handler: Handler[StreamT],\n    listeners: list[ListenerT],\n    *,\n    handler_nursery: trio.Nursery | None = None,\n    task_status: trio.TaskStatus[list[ListenerT]] = trio.TASK_STATUS_IGNORED,\n) -> NoReturn:\n    r\"\"\"Listen for incoming connections on ``listeners``, and for each one\n    start a task running ``handler(stream)``.\n\n    .. warning::\n\n       If ``handler`` raises an exception, then this function doesn't do\n       anything special to catch it – so by default the exception will\n       propagate out and crash your server. If you don't want this, then catch\n       exceptions inside your ``handler``, or use a ``handler_nursery`` object\n       that responds to exceptions in some other way.\n\n    Args:\n\n      handler: An async callable, that will be invoked like\n          ``handler_nursery.start_soon(handler, stream)`` for each incoming\n          connection.\n\n      listeners: A list of :class:`~trio.abc.Listener` objects.\n          :func:`serve_listeners` takes responsibility for closing them.\n\n      handler_nursery: The nursery used to start handlers, or any object with\n          a ``start_soon`` method. If ``None`` (the default), then\n          :func:`serve_listeners` will create a new nursery internally and use\n          that.\n\n      task_status: This function can be used with ``nursery.start``, which\n          will return ``listeners``.\n\n    Returns:\n\n      This function never returns unless cancelled.\n\n    Resource handling:\n\n      If ``handler`` neglects to close the ``stream``, then it will be closed\n      using :func:`trio.aclose_forcefully`.\n\n    Error handling:\n\n      Most errors coming from :meth:`~trio.abc.Listener.accept` are allowed to\n      propagate out (crashing the server in the process). However, some errors –\n      those which indicate that the server is temporarily overloaded – are\n      handled specially. These are :class:`OSError`\\s with one of the following\n      errnos:\n\n      * ``EMFILE``: process is out of file descriptors\n      * ``ENFILE``: system is out of file descriptors\n      * ``ENOBUFS``, ``ENOMEM``: the kernel hit some sort of memory limitation\n        when trying to create a socket object\n\n      When :func:`serve_listeners` gets one of these errors, then it:\n\n      * Logs the error to the standard library logger ``trio.serve_listeners``\n        (level = ERROR, with exception information included). By default this\n        causes it to be printed to stderr.\n      * Waits 100 ms before calling ``accept`` again, in hopes that the\n        system will recover.\n\n    \"\"\"\n    async with trio.open_nursery() as nursery:\n        if handler_nursery is None:\n            handler_nursery = nursery\n        for listener in listeners:\n            nursery.start_soon(_serve_one_listener, listener, handler_nursery, handler)\n        # The listeners are already queueing connections when we're called,\n        # but we wait until the end to call started() just in case we get an\n        # error or whatever.\n        task_status.started(listeners)\n\n    raise AssertionError(\n        \"_serve_one_listener should never complete\",\n    )  # pragma: no cover\n"
  },
  {
    "path": "src/trio/_highlevel_socket.py",
    "content": "# \"High-level\" networking interface\nfrom __future__ import annotations\n\nimport errno\nfrom contextlib import contextmanager, suppress\nfrom typing import TYPE_CHECKING, overload\n\nimport trio\n\nfrom . import socket as tsocket\nfrom ._util import ConflictDetector, final\nfrom .abc import HalfCloseableStream, Listener\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n    from ._socket import SocketType\n\nimport sys\n\nif sys.version_info >= (3, 12):\n    # NOTE: this isn't in the `TYPE_CHECKING` since for some reason\n    # sphinx doesn't autoreload this module for SocketStream\n    # (hypothesis: it's our module renaming magic)\n    from collections.abc import Buffer\nelif TYPE_CHECKING:\n    from typing_extensions import Buffer\n\n# XX TODO: this number was picked arbitrarily. We should do experiments to\n# tune it. (Or make it dynamic -- one idea is to start small and increase it\n# if we observe single reads filling up the whole buffer, at least within some\n# limits.)\nDEFAULT_RECEIVE_SIZE = 65536\n\n_closed_stream_errnos = {\n    # Unix\n    errno.EBADF,\n    # Windows\n    errno.ENOTSOCK,\n}\n\n\n@contextmanager\ndef _translate_socket_errors_to_stream_errors() -> Generator[None, None, None]:\n    try:\n        yield\n    except OSError as exc:\n        if exc.errno in _closed_stream_errnos:\n            raise trio.ClosedResourceError(\"this socket was already closed\") from None\n        else:\n            raise trio.BrokenResourceError(f\"socket connection broken: {exc}\") from exc\n\n\n@final\nclass SocketStream(HalfCloseableStream):\n    \"\"\"An implementation of the :class:`trio.abc.HalfCloseableStream`\n    interface based on a raw network socket.\n\n    Args:\n      socket: The Trio socket object to wrap. Must have type ``SOCK_STREAM``,\n          and be connected.\n\n    By default for TCP sockets, :class:`SocketStream` enables ``TCP_NODELAY``,\n    and (on platforms where it's supported) enables ``TCP_NOTSENT_LOWAT`` with\n    a reasonable buffer size (currently 16 KiB) – see `issue #72\n    <https://github.com/python-trio/trio/issues/72>`__ for discussion. You can\n    of course override these defaults by calling :meth:`setsockopt`.\n\n    Once a :class:`SocketStream` object is constructed, it implements the full\n    :class:`trio.abc.HalfCloseableStream` interface. In addition, it provides\n    a few extra features:\n\n    .. attribute:: socket\n\n       The Trio socket object that this stream wraps.\n\n    \"\"\"\n\n    def __init__(self, socket: SocketType) -> None:\n        if not isinstance(socket, tsocket.SocketType):\n            raise TypeError(\"SocketStream requires a Trio socket object\")\n        if socket.type != tsocket.SOCK_STREAM:\n            raise ValueError(\"SocketStream requires a SOCK_STREAM socket\")\n\n        self.socket = socket\n        self._send_conflict_detector = ConflictDetector(\n            \"another task is currently sending data on this SocketStream\",\n        )\n\n        # Socket defaults:\n\n        # Not supported on e.g. unix domain sockets\n        with suppress(OSError):\n            self.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, True)\n\n        if hasattr(tsocket, \"TCP_NOTSENT_LOWAT\"):\n            # 16 KiB is pretty arbitrary and could probably do with some\n            # tuning. (Apple is also setting this by default in CFNetwork\n            # apparently -- I'm curious what value they're using, though I\n            # couldn't find it online trivially. CFNetwork-129.20 source\n            # has no mentions of TCP_NOTSENT_LOWAT. This presentation says\n            # \"typically 8 kilobytes\":\n            # http://devstreaming.apple.com/videos/wwdc/2015/719ui2k57m/719/719_your_app_and_next_generation_networks.pdf?dl=1\n            # ). The theory is that you want it to be bandwidth *\n            # rescheduling interval.\n            with suppress(OSError):\n                self.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NOTSENT_LOWAT, 2**14)\n\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        if self.socket.did_shutdown_SHUT_WR:\n            raise trio.ClosedResourceError(\"can't send data after sending EOF\")\n        with self._send_conflict_detector:\n            with _translate_socket_errors_to_stream_errors():\n                with memoryview(data) as data:\n                    if not data:\n                        if self.socket.fileno() == -1:\n                            raise trio.ClosedResourceError(\"socket was already closed\")\n                        await trio.lowlevel.checkpoint()\n                        return\n                    total_sent = 0\n                    while total_sent < len(data):\n                        with data[total_sent:] as remaining:\n                            sent = await self.socket.send(remaining)\n                        total_sent += sent\n\n    async def wait_send_all_might_not_block(self) -> None:\n        with self._send_conflict_detector:\n            if self.socket.fileno() == -1:\n                raise trio.ClosedResourceError\n            with _translate_socket_errors_to_stream_errors():\n                await self.socket.wait_writable()\n\n    async def send_eof(self) -> None:\n        with self._send_conflict_detector:\n            await trio.lowlevel.checkpoint()\n            # On macOS, calling shutdown a second time raises ENOTCONN, but\n            # send_eof needs to be idempotent.\n            if self.socket.did_shutdown_SHUT_WR:\n                return\n            with _translate_socket_errors_to_stream_errors():\n                self.socket.shutdown(tsocket.SHUT_WR)\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytes:\n        if max_bytes is None:\n            max_bytes = DEFAULT_RECEIVE_SIZE\n        if max_bytes < 1:\n            raise ValueError(\"max_bytes must be >= 1\")\n        with _translate_socket_errors_to_stream_errors():\n            return await self.socket.recv(max_bytes)\n\n    async def aclose(self) -> None:\n        self.socket.close()\n        await trio.lowlevel.checkpoint()\n\n    # __aenter__, __aexit__ inherited from HalfCloseableStream are OK\n\n    @overload\n    def setsockopt(self, level: int, option: int, value: int | Buffer) -> None: ...\n\n    @overload\n    def setsockopt(self, level: int, option: int, value: None, length: int) -> None: ...\n\n    # TODO: rename `length` to `optlen`\n    def setsockopt(\n        self,\n        level: int,\n        option: int,\n        value: int | Buffer | None,\n        length: int | None = None,\n    ) -> None:\n        \"\"\"Set an option on the underlying socket.\n\n        See :meth:`socket.socket.setsockopt` for details.\n\n        \"\"\"\n        if length is None:\n            if value is None:\n                raise TypeError(\n                    \"invalid value for argument 'value', must not be None when specifying length\",\n                )\n            return self.socket.setsockopt(level, option, value)\n        if value is not None:\n            raise TypeError(\n                f\"invalid value for argument 'value': {value!r}, must be None when specifying optlen\",\n            )\n        return self.socket.setsockopt(level, option, value, length)\n\n    @overload\n    def getsockopt(self, level: int, option: int) -> int: ...\n\n    @overload\n    def getsockopt(self, level: int, option: int, buffersize: int) -> bytes: ...\n\n    def getsockopt(self, level: int, option: int, buffersize: int = 0) -> int | bytes:\n        \"\"\"Check the current value of an option on the underlying socket.\n\n        See :meth:`socket.socket.getsockopt` for details.\n\n        \"\"\"\n        # This is to work around\n        #   https://bitbucket.org/pypy/pypy/issues/2561\n        # We should be able to drop it when the next PyPy3 beta is released.\n        if buffersize == 0:\n            return self.socket.getsockopt(level, option)\n        else:\n            return self.socket.getsockopt(level, option, buffersize)\n\n\n################################################################\n# SocketListener\n################################################################\n\n# Accept error handling\n# =====================\n#\n# Literature review\n# -----------------\n#\n# Here's a list of all the possible errors that accept() can return, according\n# to the POSIX spec or the Linux, FreeBSD, macOS, and Windows docs:\n#\n# Can't happen with a Trio socket:\n# - EAGAIN/(WSA)EWOULDBLOCK\n# - EINTR\n# - WSANOTINITIALISED\n# - WSAEINPROGRESS: a blocking call is already in progress\n# - WSAEINTR: someone called WSACancelBlockingCall, but we don't make blocking\n#   calls in the first place\n#\n# Something is wrong with our call:\n# - EBADF: not a file descriptor\n# - (WSA)EINVAL: socket isn't listening, or (Linux, BSD) bad flags\n# - (WSA)ENOTSOCK: not a socket\n# - (WSA)EOPNOTSUPP: this kind of socket doesn't support accept\n# - (Linux, FreeBSD, Windows) EFAULT: the sockaddr pointer points to readonly\n#   memory\n#\n# Something is wrong with the environment:\n# - (WSA)EMFILE: this process hit its fd limit\n# - ENFILE: the system hit its fd limit\n# - (WSA)ENOBUFS, ENOMEM: unspecified memory problems\n#\n# Something is wrong with the connection we were going to accept. There's a\n# ton of variability between systems here:\n# - ECONNABORTED: documented everywhere, but apparently only the BSDs do this\n#   (signals a connection was closed/reset before being accepted)\n# - EPROTO: unspecified protocol error\n# - (Linux) EPERM: firewall rule prevented connection\n# - (Linux) ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH,\n#   EOPNOTSUPP, ENETUNREACH, ENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT,\n#   ETIMEDOUT, ... or any other error that the socket could give, because\n#   apparently if an error happens on a connection before it's accept()ed,\n#   Linux will report that error from accept().\n# - (Windows) WSAECONNRESET, WSAENETDOWN\n#\n#\n# Code review\n# -----------\n#\n# What do other libraries do?\n#\n# Twisted on Unix or when using nonblocking I/O on Windows:\n# - ignores EPERM, with comment about Linux firewalls\n# - logs and ignores EMFILE, ENOBUFS, ENFILE, ENOMEM, ECONNABORTED\n#   Comment notes that ECONNABORTED is a BSDism and that Linux returns the\n#   socket before having it fail, and macOS just silently discards it.\n# - other errors are raised, which is logged + kills the socket\n# ref: src/twisted/internet/tcp.py, Port.doRead\n#\n# Twisted using IOCP on Windows:\n# - logs and ignores all errors\n# ref: src/twisted/internet/iocpreactor/tcp.py, Port.handleAccept\n#\n# Tornado:\n# - ignore ECONNABORTED (comments notes that it was observed on FreeBSD)\n# - everything else raised, but all this does (by default) is cause it to be\n#   logged and then ignored\n# (ref: tornado/netutil.py, tornado/ioloop.py)\n#\n# libuv on Unix:\n# - ignores ECONNABORTED\n# - does a \"trick\" for EMFILE or ENFILE\n# - all other errors passed to the connection_cb to be handled\n# (ref: src/unix/stream.c:uv__server_io, uv__emfile_trick)\n#\n# libuv on Windows:\n# src/win/tcp.c:uv_tcp_queue_accept\n#   this calls AcceptEx, and then arranges to call:\n# src/win/tcp.c:uv_process_tcp_accept_req\n#   this gets the result from AcceptEx. If the original AcceptEx call failed,\n#   then \"we stop accepting connections and report this error to the\n#   connection callback\". I think this is for things like ENOTSOCK. If\n#   AcceptEx successfully queues an overlapped operation, and then that\n#   reports an error, it's just discarded.\n#\n# asyncio, selector mode:\n# - ignores EWOULDBLOCK, EINTR, ECONNABORTED\n# - on EMFILE, ENFILE, ENOBUFS, ENOMEM, logs an error and then disables the\n#   listening loop for 1 second\n# - everything else raises, but then the event loop just logs and ignores it\n# (selector_events.py: BaseSelectorEventLoop._accept_connection)\n#\n#\n# What should we do?\n# ------------------\n#\n# When accept() returns an error, we can either ignore it or raise it.\n#\n# We have a long list of errors that should be ignored, and a long list of\n# errors that should be raised. The big question is what to do with an error\n# that isn't on either list. On Linux apparently you can get nearly arbitrary\n# errors from accept() and they should be ignored, because it just indicates a\n# socket that crashed before it began, and there isn't really anything to be\n# done about this, plus on other platforms you may not get any indication at\n# all, so programs have to tolerate not getting any indication too. OTOH if we\n# get an unexpected error then it could indicate something arbitrarily bad --\n# after all, it's unexpected.\n#\n# Given that we know that other libraries seem to be getting along fine with a\n# fairly minimal list of errors to ignore, I think we'll be OK if we write\n# down that list and then raise on everything else.\n#\n# The other question is what to do about the capacity problem errors: EMFILE,\n# ENFILE, ENOBUFS, ENOMEM. Just flat out ignoring these is clearly not optimal\n# -- at the very least you want to log them, and probably you want to take\n# some remedial action. And if we ignore them then it prevents higher levels\n# from doing anything clever with them. So we raise them.\n\n_ignorable_accept_errno_names = [\n    # Linux can do this when the a connection is denied by the firewall\n    \"EPERM\",\n    # BSDs with an early close/reset\n    \"ECONNABORTED\",\n    # All the other miscellany noted above -- may not happen in practice, but\n    # whatever.\n    \"EPROTO\",\n    \"ENETDOWN\",\n    \"ENOPROTOOPT\",\n    \"EHOSTDOWN\",\n    \"ENONET\",\n    \"EHOSTUNREACH\",\n    \"EOPNOTSUPP\",\n    \"ENETUNREACH\",\n    \"ENOSR\",\n    \"ESOCKTNOSUPPORT\",\n    \"EPROTONOSUPPORT\",\n    \"ETIMEDOUT\",\n    \"ECONNRESET\",\n]\n\n# Not all errnos are defined on all platforms\n_ignorable_accept_errnos: set[int] = set()\nfor name in _ignorable_accept_errno_names:\n    with suppress(AttributeError):\n        _ignorable_accept_errnos.add(getattr(errno, name))\n\n\n@final\nclass SocketListener(Listener[SocketStream]):\n    \"\"\"A :class:`~trio.abc.Listener` that uses a listening socket to accept\n    incoming connections as :class:`SocketStream` objects.\n\n    Args:\n      socket: The Trio socket object to wrap. Must have type ``SOCK_STREAM``,\n          and be listening.\n\n    Note that the :class:`SocketListener` \"takes ownership\" of the given\n    socket; closing the :class:`SocketListener` will also close the socket.\n\n    .. attribute:: socket\n\n       The Trio socket object that this stream wraps.\n\n    \"\"\"\n\n    def __init__(self, socket: SocketType) -> None:\n        if not isinstance(socket, tsocket.SocketType):\n            raise TypeError(\"SocketListener requires a Trio socket object\")\n        if socket.type != tsocket.SOCK_STREAM:\n            raise ValueError(\"SocketListener requires a SOCK_STREAM socket\")\n        try:\n            listening = socket.getsockopt(tsocket.SOL_SOCKET, tsocket.SO_ACCEPTCONN)\n        except OSError:\n            # SO_ACCEPTCONN fails on macOS; we just have to trust the user.\n            pass\n        else:\n            if not listening:\n                raise ValueError(\"SocketListener requires a listening socket\")\n\n        self.socket = socket\n\n    async def accept(self) -> SocketStream:\n        \"\"\"Accept an incoming connection.\n\n        Returns:\n          :class:`SocketStream`\n\n        Raises:\n          OSError: if the underlying call to ``accept`` raises an unexpected\n              error.\n          ClosedResourceError: if you already closed the socket.\n\n        This method handles routine errors like ``ECONNABORTED``, but passes\n        other errors on to its caller. In particular, it does *not* make any\n        special effort to handle resource exhaustion errors like ``EMFILE``,\n        ``ENFILE``, ``ENOBUFS``, ``ENOMEM``.\n\n        \"\"\"\n        while True:\n            try:\n                sock, _ = await self.socket.accept()\n            except OSError as exc:\n                if exc.errno in _closed_stream_errnos:\n                    raise trio.ClosedResourceError from None\n                if exc.errno not in _ignorable_accept_errnos:\n                    raise\n            else:\n                return SocketStream(sock)\n\n    async def aclose(self) -> None:\n        \"\"\"Close this listener and its underlying socket.\"\"\"\n        self.socket.close()\n        await trio.lowlevel.checkpoint()\n"
  },
  {
    "path": "src/trio/_highlevel_ssl_helpers.py",
    "content": "from __future__ import annotations\n\nimport ssl\nfrom typing import TYPE_CHECKING, NoReturn, TypeVar\n\nimport trio\n\nfrom ._highlevel_open_tcp_stream import DEFAULT_DELAY\n\nT = TypeVar(\"T\")\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n    from ._highlevel_socket import SocketStream\n\n\n# It might have been nice to take a ssl_protocols= argument here to set up\n# NPN/ALPN, but to do this we have to mutate the context object, which is OK\n# if it's one we created, but not OK if it's one that was passed in... and\n# the one major protocol using NPN/ALPN is HTTP/2, which mandates that you use\n# a specially configured SSLContext anyway! I also thought maybe we could copy\n# the given SSLContext and then mutate the copy, but it's no good as SSLContext\n# objects can't be copied: https://bugs.python.org/issue33023.\n# So... let's punt on that for now. Hopefully we'll be getting a new Python\n# TLS API soon and can revisit this then.\nasync def open_ssl_over_tcp_stream(\n    host: str | bytes,\n    port: int,\n    *,\n    https_compatible: bool = False,\n    ssl_context: ssl.SSLContext | None = None,\n    happy_eyeballs_delay: float | None = DEFAULT_DELAY,\n) -> trio.SSLStream[SocketStream]:\n    \"\"\"Make a TLS-encrypted Connection to the given host and port over TCP.\n\n    This is a convenience wrapper that calls :func:`open_tcp_stream` and\n    wraps the result in an :class:`~trio.SSLStream`.\n\n    This function does not perform the TLS handshake; you can do it\n    manually by calling :meth:`~trio.SSLStream.do_handshake`, or else\n    it will be performed automatically the first time you send or receive\n    data.\n\n    Args:\n      host (bytes or str): The host to connect to. We require the server\n          to have a TLS certificate valid for this hostname.\n      port (int): The port to connect to.\n      https_compatible (bool): Set this to True if you're connecting to a web\n          server. See :class:`~trio.SSLStream` for details. Default:\n          False.\n      ssl_context (:class:`~ssl.SSLContext` or None): The SSL context to\n          use. If None (the default), :func:`ssl.create_default_context`\n          will be called to create a context.\n      happy_eyeballs_delay (float): See :func:`open_tcp_stream`.\n\n    Returns:\n      trio.SSLStream: the encrypted connection to the server.\n\n    \"\"\"\n    tcp_stream = await trio.open_tcp_stream(\n        host,\n        port,\n        happy_eyeballs_delay=happy_eyeballs_delay,\n    )\n    if ssl_context is None:\n        ssl_context = ssl.create_default_context()\n\n        if hasattr(ssl, \"OP_IGNORE_UNEXPECTED_EOF\"):\n            ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF\n\n    return trio.SSLStream(\n        tcp_stream,\n        ssl_context,\n        server_hostname=host,\n        https_compatible=https_compatible,\n    )\n\n\nasync def open_ssl_over_tcp_listeners(\n    port: int,\n    ssl_context: ssl.SSLContext,\n    *,\n    host: str | bytes | None = None,\n    https_compatible: bool = False,\n    backlog: int | None = None,\n) -> list[trio.SSLListener[SocketStream]]:\n    \"\"\"Start listening for SSL/TLS-encrypted TCP connections to the given port.\n\n    Args:\n      port (int): The port to listen on. See :func:`open_tcp_listeners`.\n      ssl_context (~ssl.SSLContext): The SSL context to use for all incoming\n          connections.\n      host (str, bytes, or None): The address to bind to; use ``None`` to bind\n          to the wildcard address. See :func:`open_tcp_listeners`.\n      https_compatible (bool): See :class:`~trio.SSLStream` for details.\n      backlog (int or None): See :func:`open_tcp_listeners` for details.\n\n    \"\"\"\n    tcp_listeners = await trio.open_tcp_listeners(port, host=host, backlog=backlog)\n    ssl_listeners = [\n        trio.SSLListener(tcp_listener, ssl_context, https_compatible=https_compatible)\n        for tcp_listener in tcp_listeners\n    ]\n    return ssl_listeners\n\n\nasync def serve_ssl_over_tcp(\n    handler: Callable[[trio.SSLStream[SocketStream]], Awaitable[object]],\n    port: int,\n    ssl_context: ssl.SSLContext,\n    *,\n    host: str | bytes | None = None,\n    https_compatible: bool = False,\n    backlog: int | None = None,\n    handler_nursery: trio.Nursery | None = None,\n    task_status: trio.TaskStatus[\n        list[trio.SSLListener[SocketStream]]\n    ] = trio.TASK_STATUS_IGNORED,\n) -> NoReturn:\n    \"\"\"Listen for incoming TCP connections, and for each one start a task\n    running ``handler(stream)``.\n\n    This is a thin convenience wrapper around\n    :func:`open_ssl_over_tcp_listeners` and :func:`serve_listeners` – see them\n    for full details.\n\n    .. warning::\n\n       If ``handler`` raises an exception, then this function doesn't do\n       anything special to catch it – so by default the exception will\n       propagate out and crash your server. If you don't want this, then catch\n       exceptions inside your ``handler``, or use a ``handler_nursery`` object\n       that responds to exceptions in some other way.\n\n    When used with ``nursery.start`` you get back the newly opened listeners.\n    See the documentation for :func:`serve_tcp` for an example where this is\n    useful.\n\n    Args:\n      handler: The handler to start for each incoming connection. Passed to\n          :func:`serve_listeners`.\n\n      port (int): The port to listen on. Use 0 to let the kernel pick\n          an open port. Ultimately passed to :func:`open_tcp_listeners`.\n\n      ssl_context (~ssl.SSLContext): The SSL context to use for all incoming\n          connections. Passed to :func:`open_ssl_over_tcp_listeners`.\n\n      host (str, bytes, or None): The address to bind to; use ``None`` to bind\n          to the wildcard address. Ultimately passed to\n          :func:`open_tcp_listeners`.\n\n      https_compatible (bool): Set this to True if you want to use\n          \"HTTPS-style\" TLS. See :class:`~trio.SSLStream` for details.\n\n      backlog (int or None): See :class:`~trio.SSLStream` for details.\n\n      handler_nursery: The nursery to start handlers in, or None to use an\n          internal nursery. Passed to :func:`serve_listeners`.\n\n      task_status: This function can be used with ``nursery.start``.\n\n    Returns:\n      This function only returns when cancelled.\n\n    \"\"\"\n    listeners = await trio.open_ssl_over_tcp_listeners(\n        port,\n        ssl_context,\n        host=host,\n        https_compatible=https_compatible,\n        backlog=backlog,\n    )\n    await trio.serve_listeners(\n        handler,\n        listeners,\n        handler_nursery=handler_nursery,\n        task_status=task_status,\n    )\n"
  },
  {
    "path": "src/trio/_path.py",
    "content": "from __future__ import annotations\n\nimport os\nimport pathlib\nimport sys\nfrom functools import partial, update_wrapper\nfrom inspect import cleandoc\nfrom typing import (\n    IO,\n    TYPE_CHECKING,\n    Any,\n    BinaryIO,\n    ClassVar,\n    Concatenate,\n    Literal,\n    TypeVar,\n    overload,\n)\n\nfrom trio._file_io import AsyncIOWrapper, wrap_file\nfrom trio._util import final\nfrom trio.to_thread import run_sync\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable, Iterable\n    from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper\n\n    from _typeshed import (\n        OpenBinaryMode,\n        OpenBinaryModeReading,\n        OpenBinaryModeUpdating,\n        OpenBinaryModeWriting,\n        OpenTextMode,\n    )\n    from typing_extensions import ParamSpec, Self\n\n    P = ParamSpec(\"P\")\n\n    PathT = TypeVar(\"PathT\", bound=\"Path\")\n    T = TypeVar(\"T\")\n\n\ndef _wraps_async(  # type: ignore[explicit-any]\n    wrapped: Callable[..., object],\n) -> Callable[[Callable[P, T]], Callable[P, Awaitable[T]]]:\n    def decorator(fn: Callable[P, T]) -> Callable[P, Awaitable[T]]:\n        async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:\n            return await run_sync(partial(fn, *args, **kwargs))\n\n        update_wrapper(wrapper, wrapped)\n        if wrapped.__doc__:\n            module = wrapped.__module__\n            # these are exported specially from CPython's intersphinx inventory\n            module = module.replace(\"pathlib._local\", \"pathlib\")\n            module = module.replace(\"pathlib._abc\", \"pathlib\")\n\n            name = wrapped.__qualname__\n            name = name.replace(\n                \"PathBase\", \"Path\"\n            )  # I'm not sure why this is necessary\n\n            wrapper.__doc__ = (\n                f\"Like :meth:`~{module}.{name}`, but async.\\n\"\n                f\"\\n\"\n                f\"{cleandoc(wrapped.__doc__)}\\n\"\n            )\n        return wrapper\n\n    return decorator\n\n\ndef _wrap_method(\n    fn: Callable[Concatenate[pathlib.Path, P], T],\n) -> Callable[Concatenate[Path, P], Awaitable[T]]:\n    @_wraps_async(fn)\n    def wrapper(self: Path, /, *args: P.args, **kwargs: P.kwargs) -> T:\n        return fn(self._wrapped_cls(self), *args, **kwargs)\n\n    return wrapper\n\n\ndef _wrap_method_path(\n    fn: Callable[Concatenate[pathlib.Path, P], pathlib.Path],\n) -> Callable[Concatenate[PathT, P], Awaitable[PathT]]:\n    @_wraps_async(fn)\n    def wrapper(self: PathT, /, *args: P.args, **kwargs: P.kwargs) -> PathT:\n        return self.__class__(fn(self._wrapped_cls(self), *args, **kwargs))\n\n    return wrapper\n\n\ndef _wrap_method_path_iterable(\n    fn: Callable[Concatenate[pathlib.Path, P], Iterable[pathlib.Path]],\n) -> Callable[Concatenate[PathT, P], Awaitable[Iterable[PathT]]]:\n    @_wraps_async(fn)\n    def wrapper(self: PathT, /, *args: P.args, **kwargs: P.kwargs) -> Iterable[PathT]:\n        return map(self.__class__, [*fn(self._wrapped_cls(self), *args, **kwargs)])\n\n    if wrapper.__doc__:\n        wrapper.__doc__ += (\n            f\"\\n\"\n            f\"This is an async method that returns a synchronous iterator, so you\\n\"\n            f\"use it like:\\n\"\n            f\"\\n\"\n            f\".. code:: python\\n\"\n            f\"\\n\"\n            f\"    for subpath in await mypath.{fn.__name__}():\\n\"\n            f\"        ...\\n\"\n            f\"\\n\"\n            f\".. note::\\n\"\n            f\"\\n\"\n            f\"    The iterator is loaded into memory immediately during the initial\\n\"\n            f\"    call (see `issue #501\\n\"\n            f\"    <https://github.com/python-trio/trio/issues/501>`__ for discussion).\\n\"\n        )\n    return wrapper\n\n\nclass Path(pathlib.PurePath):\n    \"\"\"An async :class:`pathlib.Path` that executes blocking methods in :meth:`trio.to_thread.run_sync`.\n\n    Instantiating :class:`Path` returns a concrete platform-specific subclass, one of :class:`PosixPath` or\n    :class:`WindowsPath`.\n    \"\"\"\n\n    __slots__ = ()\n\n    _wrapped_cls: ClassVar[type[pathlib.Path]]\n\n    def __new__(cls, *args: str | os.PathLike[str]) -> Self:\n        if cls is Path:\n            cls = WindowsPath if os.name == \"nt\" else PosixPath  # type: ignore[assignment]\n        return super().__new__(cls, *args)\n\n    @classmethod\n    @_wraps_async(pathlib.Path.cwd)\n    def cwd(cls) -> Self:\n        return cls(pathlib.Path.cwd())\n\n    @classmethod\n    @_wraps_async(pathlib.Path.home)\n    def home(cls) -> Self:\n        return cls(pathlib.Path.home())\n\n    @overload\n    async def open(\n        self,\n        mode: OpenTextMode = \"r\",\n        buffering: int = -1,\n        encoding: str | None = None,\n        errors: str | None = None,\n        newline: str | None = None,\n    ) -> AsyncIOWrapper[TextIOWrapper]: ...\n\n    @overload\n    async def open(\n        self,\n        mode: OpenBinaryMode,\n        buffering: Literal[0],\n        encoding: None = None,\n        errors: None = None,\n        newline: None = None,\n    ) -> AsyncIOWrapper[FileIO]: ...\n\n    @overload\n    async def open(\n        self,\n        mode: OpenBinaryModeUpdating,\n        buffering: Literal[-1, 1] = -1,\n        encoding: None = None,\n        errors: None = None,\n        newline: None = None,\n    ) -> AsyncIOWrapper[BufferedRandom]: ...\n\n    @overload\n    async def open(\n        self,\n        mode: OpenBinaryModeWriting,\n        buffering: Literal[-1, 1] = -1,\n        encoding: None = None,\n        errors: None = None,\n        newline: None = None,\n    ) -> AsyncIOWrapper[BufferedWriter]: ...\n\n    @overload\n    async def open(\n        self,\n        mode: OpenBinaryModeReading,\n        buffering: Literal[-1, 1] = -1,\n        encoding: None = None,\n        errors: None = None,\n        newline: None = None,\n    ) -> AsyncIOWrapper[BufferedReader]: ...\n\n    @overload\n    async def open(\n        self,\n        mode: OpenBinaryMode,\n        buffering: int = -1,\n        encoding: None = None,\n        errors: None = None,\n        newline: None = None,\n    ) -> AsyncIOWrapper[BinaryIO]: ...\n\n    @overload\n    async def open(  # type: ignore[explicit-any]  # Any usage matches builtins.open().\n        self,\n        mode: str,\n        buffering: int = -1,\n        encoding: str | None = None,\n        errors: str | None = None,\n        newline: str | None = None,\n    ) -> AsyncIOWrapper[IO[Any]]: ...\n\n    @_wraps_async(pathlib.Path.open)\n    def open(self, *args: Any, **kwargs: Any) -> AsyncIOWrapper[IO[Any]]:  # type: ignore[misc, explicit-any]  # Overload return mismatch.\n        return wrap_file(self._wrapped_cls(self).open(*args, **kwargs))\n\n    def __repr__(self) -> str:\n        return f\"trio.Path({str(self)!r})\"\n\n    stat = _wrap_method(pathlib.Path.stat)\n    chmod = _wrap_method(pathlib.Path.chmod)\n    exists = _wrap_method(pathlib.Path.exists)\n    glob = _wrap_method_path_iterable(pathlib.Path.glob)\n    rglob = _wrap_method_path_iterable(pathlib.Path.rglob)\n    is_dir = _wrap_method(pathlib.Path.is_dir)\n    is_file = _wrap_method(pathlib.Path.is_file)\n    is_symlink = _wrap_method(pathlib.Path.is_symlink)\n    is_socket = _wrap_method(pathlib.Path.is_socket)\n    is_fifo = _wrap_method(pathlib.Path.is_fifo)\n    is_block_device = _wrap_method(pathlib.Path.is_block_device)\n    is_char_device = _wrap_method(pathlib.Path.is_char_device)\n    if sys.version_info >= (3, 12):\n        is_junction = _wrap_method(pathlib.Path.is_junction)\n    iterdir = _wrap_method_path_iterable(pathlib.Path.iterdir)\n    lchmod = _wrap_method(pathlib.Path.lchmod)\n    lstat = _wrap_method(pathlib.Path.lstat)\n    mkdir = _wrap_method(pathlib.Path.mkdir)\n    if sys.platform != \"win32\":\n        owner = _wrap_method(pathlib.Path.owner)\n        group = _wrap_method(pathlib.Path.group)\n    if sys.platform != \"win32\" or sys.version_info >= (3, 12):\n        is_mount = _wrap_method(pathlib.Path.is_mount)\n    readlink = _wrap_method_path(pathlib.Path.readlink)\n    rename = _wrap_method_path(pathlib.Path.rename)\n    replace = _wrap_method_path(pathlib.Path.replace)\n    resolve = _wrap_method_path(pathlib.Path.resolve)\n    rmdir = _wrap_method(pathlib.Path.rmdir)\n    symlink_to = _wrap_method(pathlib.Path.symlink_to)\n    hardlink_to = _wrap_method(pathlib.Path.hardlink_to)\n    touch = _wrap_method(pathlib.Path.touch)\n    unlink = _wrap_method(pathlib.Path.unlink)\n    absolute = _wrap_method_path(pathlib.Path.absolute)\n    expanduser = _wrap_method_path(pathlib.Path.expanduser)\n    read_bytes = _wrap_method(pathlib.Path.read_bytes)\n    read_text = _wrap_method(pathlib.Path.read_text)\n    samefile = _wrap_method(pathlib.Path.samefile)\n    write_bytes = _wrap_method(pathlib.Path.write_bytes)\n    write_text = _wrap_method(pathlib.Path.write_text)\n    if sys.version_info < (3, 12):\n        link_to = _wrap_method(pathlib.Path.link_to)\n    if sys.version_info >= (3, 13):\n        full_match = _wrap_method(pathlib.Path.full_match)\n\n    def as_uri(self) -> str:\n        return pathlib.Path.as_uri(self)\n\n\nif Path.relative_to.__doc__:  # pragma: no branch\n    Path.relative_to.__doc__ = Path.relative_to.__doc__.replace(\" `..` \", \" ``..`` \")\n\n\n@final\nclass PosixPath(Path, pathlib.PurePosixPath):\n    \"\"\"An async :class:`pathlib.PosixPath` that executes blocking methods in :meth:`trio.to_thread.run_sync`.\"\"\"\n\n    __slots__ = ()\n\n    _wrapped_cls: ClassVar[type[pathlib.Path]] = pathlib.PosixPath\n\n\n@final\nclass WindowsPath(Path, pathlib.PureWindowsPath):\n    \"\"\"An async :class:`pathlib.WindowsPath` that executes blocking methods in :meth:`trio.to_thread.run_sync`.\"\"\"\n\n    __slots__ = ()\n\n    _wrapped_cls: ClassVar[type[pathlib.Path]] = pathlib.WindowsPath\n"
  },
  {
    "path": "src/trio/_repl.py",
    "content": "from __future__ import annotations\n\nimport ast\nimport contextlib\nimport inspect\nimport sys\nimport warnings\nfrom code import InteractiveConsole\nfrom types import CodeType, FrameType, FunctionType\nfrom typing import TYPE_CHECKING\n\nimport outcome\n\nimport trio\nimport trio.lowlevel\nfrom trio._util import final\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n\nclass SuppressDecorator(contextlib.ContextDecorator, contextlib.suppress):\n    pass\n\n\n@SuppressDecorator(KeyboardInterrupt)\n@trio.lowlevel.disable_ki_protection\ndef terminal_newline() -> None:  # TODO: test this line\n    import fcntl\n    import termios\n\n    # Fake up a newline char as if user had typed it at the terminal\n    try:\n        fcntl.ioctl(sys.stdin, termios.TIOCSTI, b\"\\n\")  # type: ignore[attr-defined, unused-ignore]\n    except OSError as e:\n        print(f\"\\nPress enter! Newline injection failed: {e}\", end=\"\", flush=True)\n\n\n@final\nclass TrioInteractiveConsole(InteractiveConsole):\n    def __init__(self, repl_locals: dict[str, object] | None = None) -> None:\n        super().__init__(locals=repl_locals)\n        self.token: trio.lowlevel.TrioToken | None = None\n        self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT\n        self.interrupted = False\n\n    def runcode(self, code: CodeType) -> None:\n        func = FunctionType(code, self.locals)\n        if inspect.iscoroutinefunction(func):\n            result = trio.from_thread.run(outcome.acapture, func)\n        else:\n            result = trio.from_thread.run_sync(outcome.capture, func)\n        if isinstance(result, outcome.Error):\n            # If it is SystemExit, quit the repl. Otherwise, print the traceback.\n            # If there is a SystemExit inside a BaseExceptionGroup, it probably isn't\n            # the user trying to quit the repl, but rather an error in the code. So, we\n            # don't try to inspect groups for SystemExit. Instead, we just print and\n            # return to the REPL.\n            if isinstance(result.error, SystemExit):\n                raise result.error\n            else:\n                # Inline our own version of self.showtraceback that can use\n                # outcome.Error.error directly to print clean tracebacks.\n                # This also means overriding self.showtraceback does nothing.\n                sys.last_type, sys.last_value = type(result.error), result.error\n                sys.last_traceback = result.error.__traceback__\n                # see https://docs.python.org/3/library/sys.html#sys.last_exc\n                if sys.version_info >= (3, 12):\n                    sys.last_exc = result.error\n\n                # We always use sys.excepthook, unlike other implementations.\n                # This means that overriding self.write also does nothing to tbs.\n                sys.excepthook(sys.last_type, sys.last_value, sys.last_traceback)\n        # clear any residual KI\n        trio.from_thread.run(trio.lowlevel.checkpoint_if_cancelled)\n        # trio.from_thread.check_cancelled() has too long of a memory\n\n    if sys.platform == \"win32\":  # TODO: test this line\n\n        def raw_input(self, prompt: str = \"\") -> str:\n            try:\n                return input(prompt)\n            except EOFError:\n                # check if trio has a pending KI\n                trio.from_thread.run(trio.lowlevel.checkpoint_if_cancelled)\n                raise\n\n    else:\n\n        def raw_input(self, prompt: str = \"\") -> str:\n            from signal import SIGINT, signal\n\n            assert not self.interrupted\n\n            def install_handler() -> (\n                Callable[[int, FrameType | None], None] | int | None\n            ):\n                def handler(\n                    sig: int, frame: FrameType | None\n                ) -> None:  # TODO: test this line\n                    self.interrupted = True\n                    token.run_sync_soon(terminal_newline, idempotent=True)\n\n                token = trio.lowlevel.current_trio_token()\n\n                return signal(SIGINT, handler)\n\n            prev_handler = trio.from_thread.run_sync(install_handler)\n            try:\n                return input(prompt)\n            finally:\n                trio.from_thread.run_sync(signal, SIGINT, prev_handler)\n                if self.interrupted:  # TODO: test this line\n                    raise KeyboardInterrupt\n\n        def write(self, output: str) -> None:\n            if self.interrupted:  # TODO: test this line\n                assert output == \"\\nKeyboardInterrupt\\n\"\n                sys.stderr.write(output[1:])\n                self.interrupted = False\n            else:\n                sys.stderr.write(output)\n\n\nasync def run_repl(console: TrioInteractiveConsole) -> None:\n    banner = (\n        f\"trio REPL {sys.version} on {sys.platform}\\n\"\n        f'Use \"await\" directly instead of \"trio.run()\".\\n'\n        f'Type \"help\", \"copyright\", \"credits\" or \"license\" '\n        f\"for more information.\\n\"\n        f'{getattr(sys, \"ps1\", \">>> \")}import trio'\n    )\n    try:\n        await trio.to_thread.run_sync(console.interact, banner)\n    finally:\n        warnings.filterwarnings(\n            \"ignore\",\n            message=r\"^coroutine .* was never awaited$\",\n            category=RuntimeWarning,\n        )\n\n\ndef main(original_locals: dict[str, object]) -> None:\n    with contextlib.suppress(ImportError):\n        import readline  # noqa: F401\n\n    repl_locals: dict[str, object] = {\"trio\": trio}\n    for key in {\n        \"__name__\",\n        \"__package__\",\n        \"__loader__\",\n        \"__spec__\",\n        \"__builtins__\",\n        \"__file__\",\n    }:\n        repl_locals[key] = original_locals[key]\n\n    console = TrioInteractiveConsole(repl_locals)\n    trio.run(run_repl, console)\n"
  },
  {
    "path": "src/trio/_signals.py",
    "content": "from __future__ import annotations\n\nimport signal\nfrom collections import OrderedDict\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\n\nimport trio\n\nfrom ._util import ConflictDetector, is_main_thread\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncIterator, Callable, Generator, Iterable\n    from types import FrameType\n\n    from typing_extensions import Self\n\n# Discussion of signal handling strategies:\n#\n# - On Windows signals barely exist. There are no options; signal handlers are\n#   the only available API.\n#\n# - On Linux signalfd is arguably the natural way. Semantics: signalfd acts as\n#   an *alternative* signal delivery mechanism. The way you use it is to mask\n#   out the relevant signals process-wide (so that they don't get delivered\n#   the normal way), and then when you read from signalfd that actually counts\n#   as delivering it (despite the mask). The problem with this is that we\n#   don't have any reliable way to mask out signals process-wide -- the only\n#   way to do that in Python is to call pthread_sigmask from the main thread\n#   *before starting any other threads*, and as a library we can't really\n#   impose that, and the failure mode is annoying (signals get delivered via\n#   signal handlers whether we want them to or not).\n#\n# - on macOS/*BSD, kqueue is the natural way. Semantics: kqueue acts as an\n#   *extra* signal delivery mechanism. Signals are delivered the normal\n#   way, *and* are delivered to kqueue. So you want to set them to SIG_IGN so\n#   that they don't end up pending forever (I guess?). I can't find any actual\n#   docs on how masking and EVFILT_SIGNAL interact. I did see someone note\n#   that if a signal is pending when the kqueue filter is added then you\n#   *don't* get notified of that, which makes sense. But still, we have to\n#   manipulate signal state (e.g. setting SIG_IGN) which as far as Python is\n#   concerned means we have to do this from the main thread.\n#\n# So in summary, there don't seem to be any compelling advantages to using the\n# platform-native signal notification systems; they're kinda nice, but it's\n# simpler to implement the naive signal-handler-based system once and be\n# done. (The big advantage would be if there were a reliable way to monitor\n# for SIGCHLD from outside the main thread and without interfering with other\n# libraries that also want to monitor for SIGCHLD. But there isn't. I guess\n# kqueue might give us that, but in kqueue we don't need it, because kqueue\n# can directly monitor for child process state changes.)\n\n\n@contextmanager\ndef _signal_handler(\n    signals: Iterable[int],\n    handler: Callable[[int, FrameType | None], object] | int | signal.Handlers | None,\n) -> Generator[None, None, None]:\n    original_handlers = {}\n    try:\n        for signum in set(signals):\n            original_handlers[signum] = signal.signal(signum, handler)\n        yield\n    finally:\n        for signum, original_handler in original_handlers.items():\n            signal.signal(signum, original_handler)\n\n\nclass SignalReceiver:\n    def __init__(self) -> None:\n        # {signal num: None}\n        self._pending: OrderedDict[int, None] = OrderedDict()\n        self._lot = trio.lowlevel.ParkingLot()\n        self._conflict_detector = ConflictDetector(\n            \"only one task can iterate on a signal receiver at a time\",\n        )\n        self._closed = False\n\n    def _add(self, signum: int) -> None:\n        if self._closed:\n            signal.raise_signal(signum)\n        else:\n            self._pending[signum] = None\n            self._lot.unpark()\n\n    def _redeliver_remaining(self) -> None:\n        # First make sure that any signals still in the delivery pipeline will\n        # get redelivered\n        self._closed = True\n\n        # And then redeliver any that are sitting in pending. This is done\n        # using a weird recursive construct to make sure we process everything\n        # even if some of the handlers raise exceptions.\n        def deliver_next() -> None:\n            if self._pending:\n                signum, _ = self._pending.popitem(last=False)\n                try:\n                    signal.raise_signal(signum)\n                finally:\n                    deliver_next()\n\n        deliver_next()\n\n    def __aiter__(self) -> Self:\n        return self\n\n    async def __anext__(self) -> int:\n        if self._closed:\n            raise RuntimeError(\"open_signal_receiver block already exited\")\n        # In principle it would be possible to support multiple concurrent\n        # calls to __anext__, but doing it without race conditions is quite\n        # tricky, and there doesn't seem to be any point in trying.\n        with self._conflict_detector:\n            if not self._pending:\n                await self._lot.park()\n            else:\n                await trio.lowlevel.checkpoint()\n            signum, _ = self._pending.popitem(last=False)\n            return signum\n\n\ndef get_pending_signal_count(rec: AsyncIterator[int]) -> int:\n    \"\"\"Helper for tests, not public or otherwise used.\"\"\"\n    # open_signal_receiver() always produces SignalReceiver, this should not fail.\n    assert isinstance(rec, SignalReceiver)\n    return len(rec._pending)\n\n\n@contextmanager\ndef open_signal_receiver(\n    *signals: signal.Signals | int,\n) -> Generator[AsyncIterator[int], None, None]:\n    \"\"\"A context manager for catching signals.\n\n    Entering this context manager starts listening for the given signals and\n    returns an async iterator; exiting the context manager stops listening.\n\n    The async iterator blocks until a signal arrives, and then yields it.\n\n    Note that if you leave the ``with`` block while the iterator has\n    unextracted signals still pending inside it, then they will be\n    re-delivered using Python's regular signal handling logic. This avoids a\n    race condition when signals arrives just before we exit the ``with``\n    block.\n\n    Args:\n      signals: the signals to listen for.\n\n    Raises:\n      TypeError: if no signals were provided.\n\n      RuntimeError: if you try to use this anywhere except Python's main\n          thread. (This is a Python limitation.)\n\n    Example:\n\n      A common convention for Unix daemons is that they should reload their\n      configuration when they receive a ``SIGHUP``. Here's a sketch of what\n      that might look like using :func:`open_signal_receiver`::\n\n         with trio.open_signal_receiver(signal.SIGHUP) as signal_aiter:\n             async for signum in signal_aiter:\n                 assert signum == signal.SIGHUP\n                 reload_configuration()\n\n    \"\"\"\n    if not signals:\n        raise TypeError(\"No signals were provided\")\n\n    if not is_main_thread():\n        raise RuntimeError(\n            \"Sorry, open_signal_receiver is only possible when running in \"\n            \"Python interpreter's main thread\",\n        )\n    token = trio.lowlevel.current_trio_token()\n    queue = SignalReceiver()\n\n    def handler(signum: int, frame: FrameType | None) -> None:\n        token.run_sync_soon(queue._add, signum, idempotent=True)\n\n    try:\n        with _signal_handler(signals, handler):\n            yield queue\n    finally:\n        queue._redeliver_remaining()\n"
  },
  {
    "path": "src/trio/_socket.py",
    "content": "from __future__ import annotations\n\nimport os\nimport select\nimport socket as _stdlib_socket\nimport sys\nfrom operator import index\nfrom socket import AddressFamily, SocketKind\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Concatenate,\n    SupportsIndex,\n    TypeAlias,\n    TypeVar,\n    overload,\n)\n\nimport idna as _idna\n\nimport trio\nfrom trio._util import wraps as _wraps\n\nfrom . import _core\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable, Iterable\n    from types import TracebackType\n\n    from typing_extensions import Buffer, ParamSpec, Self\n\n    from ._abc import HostnameResolver, SocketFactory\n\n    P = ParamSpec(\"P\")\n\n\nT = TypeVar(\"T\")\n\n# _stdlib_socket.socket supports 13 different socket families, see\n# https://docs.python.org/3/library/socket.html#socket-families\n# and the return type of several methods in SocketType will depend on those. Typeshed\n# has ended up typing those return types as `Any` in most cases, but for users that\n# know which family/families they're working in we could make SocketType a generic type,\n# where you specify the return values you expect from those methods depending on the\n# protocol the socket will be handling.\n# But without the ability to default the value to `Any` it will be overly cumbersome for\n# most users, so currently we just specify it as `Any`. Otherwise we would write:\n# `AddressFormat = TypeVar(\"AddressFormat\")`\n# but instead we simply do:\nAddressFormat: TypeAlias = Any  # type: ignore[explicit-any]\n\n\n# Usage:\n#\n#   async with _try_sync():\n#       return sync_call_that_might_fail_with_exception()\n#   # we only get here if the sync call in fact did fail with a\n#   # BlockingIOError\n#   return await do_it_properly_with_a_check_point()\n#\nclass _try_sync:\n    def __init__(\n        self,\n        blocking_exc_override: Callable[[BaseException], bool] | None = None,\n    ) -> None:\n        self._blocking_exc_override = blocking_exc_override\n\n    def _is_blocking_io_error(self, exc: BaseException) -> bool:\n        if self._blocking_exc_override is None:\n            return isinstance(exc, BlockingIOError)\n        else:\n            return self._blocking_exc_override(exc)\n\n    async def __aenter__(self) -> None:\n        await trio.lowlevel.checkpoint_if_cancelled()\n\n    async def __aexit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> bool:\n        if exc_value is not None and self._is_blocking_io_error(exc_value):\n            # Discard the exception and fall through to the code below the\n            # block\n            return True\n        else:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n            # Let the return or exception propagate\n            return False\n\n\n################################################################\n# Overrides\n################################################################\n\n_resolver: _core.RunVar[HostnameResolver | None] = _core.RunVar(\"hostname_resolver\")\n_socket_factory: _core.RunVar[SocketFactory | None] = _core.RunVar(\"socket_factory\")\n\n\ndef set_custom_hostname_resolver(\n    hostname_resolver: HostnameResolver | None,\n) -> HostnameResolver | None:\n    \"\"\"Set a custom hostname resolver.\n\n    By default, Trio's :func:`getaddrinfo` and :func:`getnameinfo` functions\n    use the standard system resolver functions. This function allows you to\n    customize that behavior. The main intended use case is for testing, but it\n    might also be useful for using third-party resolvers like `c-ares\n    <https://c-ares.haxx.se/>`__ (though be warned that these rarely make\n    perfect drop-in replacements for the system resolver). See\n    :class:`trio.abc.HostnameResolver` for more details.\n\n    Setting a custom hostname resolver affects all future calls to\n    :func:`getaddrinfo` and :func:`getnameinfo` within the enclosing call to\n    :func:`trio.run`. All other hostname resolution in Trio is implemented in\n    terms of these functions.\n\n    Generally you should call this function just once, right at the beginning\n    of your program.\n\n    Args:\n      hostname_resolver (trio.abc.HostnameResolver or None): The new custom\n          hostname resolver, or None to restore the default behavior.\n\n    Returns:\n      The previous hostname resolver (which may be None).\n\n    \"\"\"\n    old = _resolver.get(None)\n    _resolver.set(hostname_resolver)\n    return old\n\n\ndef set_custom_socket_factory(\n    socket_factory: SocketFactory | None,\n) -> SocketFactory | None:\n    \"\"\"Set a custom socket object factory.\n\n    This function allows you to replace Trio's normal socket class with a\n    custom class. This is very useful for testing, and probably a bad idea in\n    any other circumstance. See :class:`trio.abc.HostnameResolver` for more\n    details.\n\n    Setting a custom socket factory affects all future calls to :func:`socket`\n    within the enclosing call to :func:`trio.run`.\n\n    Generally you should call this function just once, right at the beginning\n    of your program.\n\n    Args:\n      socket_factory (trio.abc.SocketFactory or None): The new custom\n          socket factory, or None to restore the default behavior.\n\n    Returns:\n      The previous socket factory (which may be None).\n\n    \"\"\"\n    old = _socket_factory.get(None)\n    _socket_factory.set(socket_factory)\n    return old\n\n\n################################################################\n# getaddrinfo and friends\n################################################################\n\n# AI_NUMERICSERV may be missing on some older platforms, so use it when available.\n# See: https://github.com/python-trio/trio/issues/3133\n_NUMERIC_ONLY = _stdlib_socket.AI_NUMERICHOST\n_NUMERIC_ONLY |= getattr(_stdlib_socket, \"AI_NUMERICSERV\", 0)\n\n\n# It would be possible to @overload the return value depending on Literal[AddressFamily.INET/6], but should probably be added in typeshed first\nasync def getaddrinfo(\n    host: bytes | str | None,\n    port: bytes | str | int | None,\n    family: int = 0,\n    type: int = 0,\n    proto: int = 0,\n    flags: int = 0,\n) -> list[\n    tuple[\n        AddressFamily,\n        SocketKind,\n        int,\n        str,\n        tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n    ]\n]:\n    \"\"\"Look up a numeric address given a name.\n\n    Arguments and return values are identical to :func:`socket.getaddrinfo`,\n    except that this version is async.\n\n    Also, :func:`trio.socket.getaddrinfo` correctly uses IDNA 2008 to process\n    non-ASCII domain names. (:func:`socket.getaddrinfo` uses IDNA 2003, which\n    can give the wrong result in some cases and cause you to connect to a\n    different host than the one you intended; see `bpo-17305\n    <https://bugs.python.org/issue17305>`__.)\n\n    This function's behavior can be customized using\n    :func:`set_custom_hostname_resolver`.\n\n    \"\"\"\n\n    # If host and port are numeric, then getaddrinfo doesn't block and we can\n    # skip the whole thread thing, which seems worthwhile. So we try first\n    # with the _NUMERIC_ONLY flags set, and then only spawn a thread if that\n    # fails with EAI_NONAME:\n    def numeric_only_failure(exc: BaseException) -> bool:\n        return (\n            isinstance(exc, _stdlib_socket.gaierror)\n            and exc.errno == _stdlib_socket.EAI_NONAME\n        )\n\n    async with _try_sync(numeric_only_failure):\n        return _stdlib_socket.getaddrinfo(\n            host,\n            port,\n            family,\n            type,\n            proto,\n            flags | _NUMERIC_ONLY,\n        )\n    # That failed; it's a real hostname. We better use a thread.\n    #\n    # Also, it might be a unicode hostname, in which case we want to do our\n    # own encoding using the idna module, rather than letting Python do\n    # it. (Python will use the old IDNA 2003 standard, and possibly get the\n    # wrong answer - see bpo-17305). However, the idna module is picky, and\n    # will refuse to process some valid hostname strings, like \"::1\". So if\n    # it's already ascii, we pass it through; otherwise, we encode it to.\n    if isinstance(host, str):\n        try:\n            host = host.encode(\"ascii\")\n        except UnicodeEncodeError:\n            # UTS-46 defines various normalizations; in particular, by default\n            # idna.encode will error out if the hostname has Capital Letters\n            # in it; with uts46=True it will lowercase them instead.\n            host = _idna.encode(host, uts46=True)\n    hr = _resolver.get(None)\n    if hr is not None:\n        return await hr.getaddrinfo(host, port, family, type, proto, flags)\n    else:\n        return await trio.to_thread.run_sync(\n            _stdlib_socket.getaddrinfo,\n            host,\n            port,\n            family,\n            type,\n            proto,\n            flags,\n            abandon_on_cancel=True,\n        )\n\n\nasync def getnameinfo(\n    sockaddr: tuple[str, int] | tuple[str, int, int, int],\n    flags: int,\n) -> tuple[str, str]:\n    \"\"\"Look up a name given a numeric address.\n\n    Arguments and return values are identical to :func:`socket.getnameinfo`,\n    except that this version is async.\n\n    This function's behavior can be customized using\n    :func:`set_custom_hostname_resolver`.\n\n    \"\"\"\n    hr = _resolver.get(None)\n    if hr is not None:\n        return await hr.getnameinfo(sockaddr, flags)\n    else:\n        return await trio.to_thread.run_sync(\n            _stdlib_socket.getnameinfo,\n            sockaddr,\n            flags,\n            abandon_on_cancel=True,\n        )\n\n\nasync def getprotobyname(name: str) -> int:\n    \"\"\"Look up a protocol number by name. (Rarely used.)\n\n    Like :func:`socket.getprotobyname`, but async.\n\n    \"\"\"\n    return await trio.to_thread.run_sync(\n        _stdlib_socket.getprotobyname,\n        name,\n        abandon_on_cancel=True,\n    )\n\n\n# obsolete gethostbyname etc. intentionally omitted\n# likewise for create_connection (use open_tcp_stream instead)\n\n################################################################\n# Socket \"constructors\"\n################################################################\n\n\ndef from_stdlib_socket(sock: _stdlib_socket.socket) -> SocketType:\n    \"\"\"Convert a standard library :class:`socket.socket` object into a Trio\n    socket object.\n\n    \"\"\"\n    return _SocketType(sock)\n\n\n@_wraps(_stdlib_socket.fromfd, assigned=(), updated=())\ndef fromfd(\n    fd: SupportsIndex,\n    family: AddressFamily | int = _stdlib_socket.AF_INET,\n    type: SocketKind | int = _stdlib_socket.SOCK_STREAM,\n    proto: int = 0,\n) -> SocketType:\n    \"\"\"Like :func:`socket.fromfd`, but returns a Trio socket object.\"\"\"\n    family, type_, proto = _sniff_sockopts_for_fileno(family, type, proto, index(fd))\n    return from_stdlib_socket(_stdlib_socket.fromfd(fd, family, type_, proto))\n\n\nif sys.platform == \"win32\" or (\n    not TYPE_CHECKING and hasattr(_stdlib_socket, \"fromshare\")\n):\n\n    @_wraps(_stdlib_socket.fromshare, assigned=(), updated=())\n    def fromshare(info: bytes) -> SocketType:\n        \"\"\"Like :func:`socket.fromshare`, but returns a Trio socket object.\"\"\"\n        return from_stdlib_socket(_stdlib_socket.fromshare(info))\n\n\nif sys.platform == \"win32\":\n    FamilyT: TypeAlias = int\n    TypeT: TypeAlias = int\n    FamilyDefault = _stdlib_socket.AF_INET\nelse:\n    FamilyDefault: None = None\n    FamilyT: TypeAlias = int | AddressFamily | None\n    TypeT: TypeAlias = _stdlib_socket.socket | int\n\n\n@_wraps(_stdlib_socket.socketpair, assigned=(), updated=())\ndef socketpair(\n    family: FamilyT = FamilyDefault,\n    type: TypeT = SocketKind.SOCK_STREAM,\n    proto: int = 0,\n) -> tuple[SocketType, SocketType]:\n    \"\"\"Like :func:`socket.socketpair`, but returns a pair of Trio socket\n    objects.\n\n    \"\"\"\n    left, right = _stdlib_socket.socketpair(family, type, proto)\n    return (from_stdlib_socket(left), from_stdlib_socket(right))\n\n\n@_wraps(_stdlib_socket.socket, assigned=(), updated=())\ndef socket(\n    family: AddressFamily | int = _stdlib_socket.AF_INET,\n    type: SocketKind | int = _stdlib_socket.SOCK_STREAM,\n    proto: int = 0,\n    fileno: int | None = None,\n) -> SocketType:\n    \"\"\"Create a new Trio socket, like :class:`socket.socket`.\n\n    This function's behavior can be customized using\n    :func:`set_custom_socket_factory`.\n\n    \"\"\"\n    if fileno is None:\n        sf = _socket_factory.get(None)\n        if sf is not None:\n            return sf.socket(family, type, proto)\n    else:\n        family, type, proto = _sniff_sockopts_for_fileno(  # noqa: A001\n            family,\n            type,\n            proto,\n            fileno,\n        )\n    stdlib_socket = _stdlib_socket.socket(family, type, proto, fileno)\n    return from_stdlib_socket(stdlib_socket)\n\n\ndef _sniff_sockopts_for_fileno(\n    family: AddressFamily | int,\n    type_: SocketKind | int,\n    proto: int,\n    fileno: int | None,\n) -> tuple[AddressFamily | int, SocketKind | int, int]:\n    \"\"\"Correct SOCKOPTS for given fileno, falling back to provided values.\"\"\"\n    # Wrap the raw fileno into a Python socket object\n    # This object might have the wrong metadata, but it lets us easily call getsockopt\n    # and then we'll throw it away and construct a new one with the correct metadata.\n    if sys.platform != \"linux\":\n        return family, type_, proto\n    from socket import (  # type: ignore[attr-defined,unused-ignore]\n        SO_DOMAIN,\n        SO_PROTOCOL,\n        SO_TYPE,\n        SOL_SOCKET,\n    )\n\n    sockobj = _stdlib_socket.socket(family, type_, proto, fileno=fileno)\n    try:\n        family = sockobj.getsockopt(SOL_SOCKET, SO_DOMAIN)\n        proto = sockobj.getsockopt(SOL_SOCKET, SO_PROTOCOL)\n        type_ = sockobj.getsockopt(SOL_SOCKET, SO_TYPE)\n    finally:\n        # Unwrap it again, so that sockobj.__del__ doesn't try to close our socket\n        sockobj.detach()\n    return family, type_, proto\n\n\n################################################################\n# SocketType\n################################################################\n\n# sock.type gets weird stuff set in it, in particular on Linux:\n#\n#   https://bugs.python.org/issue21327\n#\n# But on other platforms (e.g. Windows) SOCK_NONBLOCK and SOCK_CLOEXEC aren't\n# even defined. To recover the actual socket type (e.g. SOCK_STREAM) from a\n# socket.type attribute, mask with this:\n_SOCK_TYPE_MASK = ~(\n    getattr(_stdlib_socket, \"SOCK_NONBLOCK\", 0)\n    | getattr(_stdlib_socket, \"SOCK_CLOEXEC\", 0)\n)\n\n\ndef _make_simple_sock_method_wrapper(\n    fn: Callable[Concatenate[_stdlib_socket.socket, P], T],\n    wait_fn: Callable[[_stdlib_socket.socket], Awaitable[None]],\n    maybe_avail: bool = False,\n) -> Callable[Concatenate[_SocketType, P], Awaitable[T]]:\n    @_wraps(fn, assigned=(\"__name__\",), updated=())\n    async def wrapper(self: _SocketType, *args: P.args, **kwargs: P.kwargs) -> T:\n        return await self._nonblocking_helper(wait_fn, fn, *args, **kwargs)\n\n    wrapper.__doc__ = f\"\"\"Like :meth:`socket.socket.{fn.__name__}`, but async.\n\n            \"\"\"\n    if maybe_avail:\n        wrapper.__doc__ += (\n            f\"Only available on platforms where :meth:`socket.socket.{fn.__name__}` is \"\n            \"available.\"\n        )\n    return wrapper\n\n\n# Helpers to work with the (hostname, port) language that Python uses for socket\n# addresses everywhere. Split out into a standalone function so it can be reused by\n# FakeNet.\n\n\n# Take an address in Python's representation, and returns a new address in\n# the same representation, but with names resolved to numbers,\n# etc.\n#\n# local=True means that the address is being used with bind() or similar\n# local=False means that the address is being used with connect() or sendto() or\n# similar.\n#\n\n\n# Using a TypeVar to indicate we return the same type of address appears to give errors\n# when passed a union of address types.\n# @overload likely works, but is extremely verbose.\n# NOTE: this function does not always checkpoint\nasync def _resolve_address_nocp(\n    type_: int,\n    family: AddressFamily,\n    proto: int,\n    *,\n    ipv6_v6only: bool | int,\n    address: AddressFormat,\n    local: bool,\n) -> AddressFormat:\n    # Do some pre-checking (or exit early for non-IP sockets)\n    if family == _stdlib_socket.AF_INET:\n        if not isinstance(address, tuple) or not len(address) == 2:\n            raise ValueError(\"address should be a (host, port) tuple\")\n    elif family == _stdlib_socket.AF_INET6:\n        if not isinstance(address, tuple) or not 2 <= len(address) <= 4:\n            raise ValueError(\n                \"address should be a (host, port, [flowinfo, [scopeid]]) tuple\",\n            )\n    elif hasattr(_stdlib_socket, \"AF_UNIX\") and family == _stdlib_socket.AF_UNIX:\n        # unwrap path-likes\n        assert isinstance(address, (str, bytes, os.PathLike))\n        return os.fspath(address)\n    else:\n        return address\n\n    # -- From here on we know we have IPv4 or IPV6 --\n    host: str | None\n    host, port, *_ = address\n    # Fast path for the simple case: already-resolved IP address,\n    # already-resolved port. This is particularly important for UDP, since\n    # every sendto call goes through here.\n    if isinstance(port, int) and host is not None:\n        try:\n            _stdlib_socket.inet_pton(family, host)\n        except (OSError, TypeError):\n            pass\n        else:\n            return address\n    # Special cases to match the stdlib, see gh-277\n    if host == \"\":\n        host = None\n    if host == \"<broadcast>\":\n        host = \"255.255.255.255\"\n    flags = 0\n    if local:\n        flags |= _stdlib_socket.AI_PASSIVE\n    # Since we always pass in an explicit family here, AI_ADDRCONFIG\n    # doesn't add any value -- if we have no ipv6 connectivity and are\n    # working with an ipv6 socket, then things will break soon enough! And\n    # if we do enable it, then it makes it impossible to even run tests\n    # for ipv6 address resolution on travis-ci, which as of 2017-03-07 has\n    # no ipv6.\n    # flags |= AI_ADDRCONFIG\n    if family == _stdlib_socket.AF_INET6 and not ipv6_v6only:\n        flags |= _stdlib_socket.AI_V4MAPPED\n    gai_res = await getaddrinfo(host, port, family, type_, proto, flags)\n    # AFAICT from the spec it's not possible for getaddrinfo to return an\n    # empty list.\n    assert len(gai_res) >= 1\n    # Address is the last item in the first entry\n    (*_, normed), *_ = gai_res\n    # The above ignored any flowid and scopeid in the passed-in address,\n    # so restore them if present:\n    if family == _stdlib_socket.AF_INET6:\n        list_normed = list(normed)\n        assert len(normed) == 4\n        if len(address) >= 3:\n            list_normed[2] = address[2]\n        if len(address) >= 4:\n            list_normed[3] = address[3]\n        return tuple(list_normed)\n    return normed\n\n\nclass SocketType:\n    def __init__(self) -> None:\n        # make sure this __init__ works with multiple inheritance\n        super().__init__()\n        # and only raises error if it's directly constructed\n        if type(self) is SocketType:\n            raise TypeError(\n                \"SocketType is an abstract class; use trio.socket.socket if you \"\n                \"want to construct a socket object\",\n            )\n\n    def detach(self) -> int:\n        raise NotImplementedError\n\n    def fileno(self) -> int:\n        raise NotImplementedError\n\n    def getpeername(self) -> AddressFormat:\n        raise NotImplementedError\n\n    def getsockname(self) -> AddressFormat:\n        raise NotImplementedError\n\n    @overload\n    def getsockopt(self, level: int, optname: int) -> int: ...\n\n    @overload\n    def getsockopt(self, level: int, optname: int, buflen: int) -> bytes: ...\n\n    def getsockopt(\n        self,\n        level: int,\n        optname: int,\n        buflen: int | None = None,\n    ) -> int | bytes:\n        raise NotImplementedError\n\n    @overload\n    def setsockopt(self, level: int, optname: int, value: int | Buffer) -> None: ...\n\n    @overload\n    def setsockopt(\n        self,\n        level: int,\n        optname: int,\n        value: None,\n        optlen: int,\n    ) -> None: ...\n\n    def setsockopt(\n        self,\n        level: int,\n        optname: int,\n        value: int | Buffer | None,\n        optlen: int | None = None,\n    ) -> None:\n        raise NotImplementedError\n\n    def listen(self, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None:\n        raise NotImplementedError\n\n    def get_inheritable(self) -> bool:\n        raise NotImplementedError\n\n    def set_inheritable(self, inheritable: bool) -> None:\n        raise NotImplementedError\n\n    if sys.platform == \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"share\")\n    ):\n\n        def share(self, process_id: int) -> bytes:\n            raise NotImplementedError\n\n    def __enter__(self) -> Self:\n        raise NotImplementedError\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        raise NotImplementedError\n\n    @property\n    def family(self) -> AddressFamily:\n        raise NotImplementedError\n\n    @property\n    def type(self) -> SocketKind:\n        raise NotImplementedError\n\n    @property\n    def proto(self) -> int:\n        raise NotImplementedError\n\n    @property\n    def did_shutdown_SHUT_WR(self) -> bool:\n        \"\"\"Return True if the socket has been shut down with the SHUT_WR flag\"\"\"\n        raise NotImplementedError\n\n    def __repr__(self) -> str:\n        raise NotImplementedError\n\n    def dup(self) -> SocketType:\n        raise NotImplementedError\n\n    def close(self) -> None:\n        raise NotImplementedError\n\n    async def bind(self, address: AddressFormat) -> None:\n        raise NotImplementedError\n\n    def shutdown(self, flag: int) -> None:\n        raise NotImplementedError\n\n    def is_readable(self) -> bool:\n        \"\"\"Return True if the socket is readable. This is checked with `select.select` on Windows, otherwise `select.poll`.\"\"\"\n        raise NotImplementedError\n\n    async def wait_writable(self) -> None:\n        \"\"\"Convenience method that calls trio.lowlevel.wait_writable for the object.\"\"\"\n        raise NotImplementedError\n\n    async def accept(self) -> tuple[SocketType, AddressFormat]:\n        raise NotImplementedError\n\n    async def connect(self, address: AddressFormat) -> None:\n        raise NotImplementedError\n\n    def recv(self, buflen: int, flags: int = 0, /) -> Awaitable[bytes]:\n        raise NotImplementedError\n\n    def recv_into(\n        self,\n        buffer: Buffer,\n        nbytes: int = 0,\n        flags: int = 0,\n    ) -> Awaitable[int]:\n        raise NotImplementedError\n\n    # return type of socket.socket.recvfrom in typeshed is tuple[bytes, Any]\n    def recvfrom(\n        self,\n        bufsize: int,\n        flags: int = 0,\n        /,\n    ) -> Awaitable[tuple[bytes, AddressFormat]]:\n        raise NotImplementedError\n\n    # return type of socket.socket.recvfrom_into in typeshed is tuple[bytes, Any]\n    def recvfrom_into(\n        self,\n        buffer: Buffer,\n        nbytes: int = 0,\n        flags: int = 0,\n    ) -> Awaitable[tuple[int, AddressFormat]]:\n        raise NotImplementedError\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"recvmsg\")\n    ):\n\n        def recvmsg(\n            self,\n            bufsize: int,\n            ancbufsize: int = 0,\n            flags: int = 0,\n            /,\n        ) -> Awaitable[tuple[bytes, list[tuple[int, int, bytes]], int, object]]:\n            raise NotImplementedError\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"recvmsg_into\")\n    ):\n\n        def recvmsg_into(\n            self,\n            buffers: Iterable[Buffer],\n            ancbufsize: int = 0,\n            flags: int = 0,\n            /,\n        ) -> Awaitable[tuple[int, list[tuple[int, int, bytes]], int, object]]:\n            raise NotImplementedError\n\n    def send(self, bytes: Buffer, flags: int = 0, /) -> Awaitable[int]:\n        raise NotImplementedError\n\n    @overload\n    async def sendto(\n        self,\n        data: Buffer,\n        address: tuple[object, ...] | str | Buffer,\n        /,\n    ) -> int: ...\n\n    @overload\n    async def sendto(\n        self,\n        data: Buffer,\n        flags: int,\n        address: tuple[object, ...] | str | Buffer,\n        /,\n    ) -> int: ...\n\n    async def sendto(self, *args: object) -> int:\n        raise NotImplementedError\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"sendmsg\")\n    ):\n\n        @_wraps(_stdlib_socket.socket.sendmsg, assigned=(), updated=())\n        async def sendmsg(\n            self,\n            buffers: Iterable[Buffer],\n            ancdata: Iterable[tuple[int, int, Buffer]] = (),\n            flags: int = 0,\n            address: AddressFormat | None = None,\n            /,\n        ) -> int:\n            raise NotImplementedError\n\n\n# copy docstrings from socket.SocketType / socket.socket\nfor name, obj in SocketType.__dict__.items():\n    # skip dunders and already defined docstrings\n    if name.startswith(\"__\") or obj.__doc__:\n        continue\n    # try both socket.socket and socket.SocketType\n    for stdlib_type in _stdlib_socket.socket, _stdlib_socket.SocketType:\n        stdlib_obj = getattr(stdlib_type, name, None)\n        if stdlib_obj and stdlib_obj.__doc__:\n            break\n    else:\n        continue\n    obj.__doc__ = stdlib_obj.__doc__\n\n\nclass _SocketType(SocketType):\n    def __init__(self, sock: _stdlib_socket.socket) -> None:\n        if type(sock) is not _stdlib_socket.socket:\n            # For example, ssl.SSLSocket subclasses socket.socket, but we\n            # certainly don't want to blindly wrap one of those.\n            raise TypeError(\n                f\"expected object of type 'socket.socket', not '{type(sock).__name__}'\",\n            )\n        self._sock = sock\n        self._sock.setblocking(False)\n        self._did_shutdown_SHUT_WR = False\n\n    ################################################################\n    # Simple + portable methods and attributes\n    ################################################################\n\n    # forwarded methods\n    def detach(self) -> int:\n        return self._sock.detach()\n\n    def fileno(self) -> int:\n        return self._sock.fileno()\n\n    def getpeername(self) -> AddressFormat:\n        return self._sock.getpeername()\n\n    def getsockname(self) -> AddressFormat:\n        return self._sock.getsockname()\n\n    @overload\n    def getsockopt(self, level: int, optname: int) -> int: ...\n\n    @overload\n    def getsockopt(self, level: int, optname: int, buflen: int) -> bytes: ...\n\n    def getsockopt(\n        self,\n        level: int,\n        optname: int,\n        buflen: int | None = None,\n    ) -> int | bytes:\n        if buflen is None:\n            return self._sock.getsockopt(level, optname)\n        return self._sock.getsockopt(level, optname, buflen)\n\n    @overload\n    def setsockopt(self, level: int, optname: int, value: int | Buffer) -> None: ...\n\n    @overload\n    def setsockopt(\n        self,\n        level: int,\n        optname: int,\n        value: None,\n        optlen: int,\n    ) -> None: ...\n\n    def setsockopt(\n        self,\n        level: int,\n        optname: int,\n        value: int | Buffer | None,\n        optlen: int | None = None,\n    ) -> None:\n        if optlen is None:\n            if value is None:\n                raise TypeError(\n                    \"invalid value for argument 'value', must not be None when specifying optlen\",\n                )\n            return self._sock.setsockopt(level, optname, value)\n        if value is not None:\n            raise TypeError(\n                f\"invalid value for argument 'value': {value!r}, must be None when specifying optlen\",\n            )\n\n        # Note: PyPy may crash here due to setsockopt only supporting\n        # four parameters.\n        return self._sock.setsockopt(level, optname, value, optlen)\n\n    def listen(self, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None:\n        return self._sock.listen(backlog)\n\n    def get_inheritable(self) -> bool:\n        return self._sock.get_inheritable()\n\n    def set_inheritable(self, inheritable: bool) -> None:\n        return self._sock.set_inheritable(inheritable)\n\n    if sys.platform == \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"share\")\n    ):\n\n        def share(self, process_id: int) -> bytes:\n            return self._sock.share(process_id)\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        return self._sock.__exit__(exc_type, exc_value, traceback)\n\n    @property\n    def family(self) -> AddressFamily:\n        return self._sock.family\n\n    @property\n    def type(self) -> SocketKind:\n        return self._sock.type\n\n    @property\n    def proto(self) -> int:\n        return self._sock.proto\n\n    @property\n    def did_shutdown_SHUT_WR(self) -> bool:\n        return self._did_shutdown_SHUT_WR\n\n    def __repr__(self) -> str:\n        return repr(self._sock).replace(\"socket.socket\", \"trio.socket.socket\")\n\n    def dup(self) -> SocketType:\n        \"\"\"Same as :meth:`socket.socket.dup`.\"\"\"\n        return _SocketType(self._sock.dup())\n\n    def close(self) -> None:\n        if self._sock.fileno() != -1:\n            trio.lowlevel.notify_closing(self._sock)\n            self._sock.close()\n\n    async def bind(self, address: AddressFormat) -> None:\n        address = await self._resolve_address_nocp(address, local=True)\n        if (\n            hasattr(_stdlib_socket, \"AF_UNIX\")\n            and self.family == _stdlib_socket.AF_UNIX\n            and address[0]\n        ):\n            # Use a thread for the filesystem traversal (unless it's an\n            # abstract domain socket)\n            return await trio.to_thread.run_sync(self._sock.bind, address)\n        else:\n            # POSIX actually says that bind can return EWOULDBLOCK and\n            # complete asynchronously, like connect. But in practice AFAICT\n            # there aren't yet any real systems that do this, so we'll worry\n            # about it when it happens.\n            await trio.lowlevel.checkpoint()\n            return self._sock.bind(address)\n\n    def shutdown(self, flag: int) -> None:\n        # no need to worry about return value b/c always returns None:\n        self._sock.shutdown(flag)\n        # only do this if the call succeeded:\n        if flag in [_stdlib_socket.SHUT_WR, _stdlib_socket.SHUT_RDWR]:\n            self._did_shutdown_SHUT_WR = True\n\n    def is_readable(self) -> bool:\n        # use select.select on Windows, and select.poll everywhere else\n        if sys.platform == \"win32\":\n            rready, _, _ = select.select([self._sock], [], [], 0)\n            return bool(rready)\n        p = select.poll()\n        p.register(self._sock, select.POLLIN)\n        return bool(p.poll(0))\n\n    async def wait_writable(self) -> None:\n        await _core.wait_writable(self._sock)\n\n    async def _resolve_address_nocp(\n        self,\n        address: AddressFormat,\n        *,\n        local: bool,\n    ) -> AddressFormat:\n        if self.family == _stdlib_socket.AF_INET6:\n            ipv6_v6only = self._sock.getsockopt(\n                _stdlib_socket.IPPROTO_IPV6,\n                _stdlib_socket.IPV6_V6ONLY,\n            )\n        else:\n            ipv6_v6only = False\n        return await _resolve_address_nocp(\n            self.type,\n            self.family,\n            self.proto,\n            ipv6_v6only=ipv6_v6only,\n            address=address,\n            local=local,\n        )\n\n    # args and kwargs must be starred, otherwise pyright complains:\n    # '\"args\" member of ParamSpec is valid only when used with *args parameter'\n    # '\"kwargs\" member of ParamSpec is valid only when used with **kwargs parameter'\n    # wait_fn and fn must also be first in the signature\n    # 'Keyword parameter cannot appear in signature after ParamSpec args parameter'\n\n    async def _nonblocking_helper(\n        self,\n        wait_fn: Callable[[_stdlib_socket.socket], Awaitable[None]],\n        fn: Callable[Concatenate[_stdlib_socket.socket, P], T],\n        *args: P.args,\n        **kwargs: P.kwargs,\n    ) -> T:\n        # We have to reconcile two conflicting goals:\n        # - We want to make it look like we always blocked in doing these\n        #   operations. The obvious way is to always do an IO wait before\n        #   calling the function.\n        # - But, we also want to provide the correct semantics, and part\n        #   of that means giving correct errors. So, for example, if you\n        #   haven't called .listen(), then .accept() raises an error\n        #   immediately. But in this same circumstance, then on macOS, the\n        #   socket does not register as readable. So if we block waiting\n        #   for read *before* we call accept, then we'll be waiting\n        #   forever instead of properly raising an error. (On Linux,\n        #   interestingly, AFAICT a socket that can't possible read/write\n        #   *does* count as readable/writable for select() purposes. But\n        #   not on macOS.)\n        #\n        # So, we have to call the function once, with the appropriate\n        # cancellation/yielding sandwich if it succeeds, and if it gives\n        # BlockingIOError *then* we fall back to IO wait.\n        #\n        # XX think if this can be combined with the similar logic for IOCP\n        # submission...\n        async with _try_sync():\n            return fn(self._sock, *args, **kwargs)\n        # First attempt raised BlockingIOError:\n        while True:\n            await wait_fn(self._sock)\n            try:\n                return fn(self._sock, *args, **kwargs)\n            except BlockingIOError:\n                pass\n\n    ################################################################\n    # accept\n    ################################################################\n\n    _accept = _make_simple_sock_method_wrapper(\n        _stdlib_socket.socket.accept,\n        _core.wait_readable,\n    )\n\n    async def accept(self) -> tuple[SocketType, AddressFormat]:\n        \"\"\"Like :meth:`socket.socket.accept`, but async.\"\"\"\n        sock, addr = await self._accept()\n        return from_stdlib_socket(sock), addr\n\n    ################################################################\n    # connect\n    ################################################################\n\n    async def connect(self, address: AddressFormat) -> None:\n        # nonblocking connect is weird -- you call it to start things\n        # off, then the socket becomes writable as a completion\n        # notification. This means it isn't really cancellable... we close the\n        # socket if cancelled, to avoid confusion.\n        try:\n            address = await self._resolve_address_nocp(address, local=False)\n            async with _try_sync():\n                # An interesting puzzle: can a non-blocking connect() return EINTR\n                # (= raise InterruptedError)? PEP 475 specifically left this as\n                # the one place where it lets an InterruptedError escape instead\n                # of automatically retrying. This is based on the idea that EINTR\n                # from connect means that the connection was already started, and\n                # will continue in the background. For a blocking connect, this\n                # sort of makes sense: if it returns EINTR then the connection\n                # attempt is continuing in the background, and on many system you\n                # can't then call connect() again because there is already a\n                # connect happening. See:\n                #\n                #   http://www.madore.org/~david/computers/connect-intr.html\n                #\n                # For a non-blocking connect, it doesn't make as much sense --\n                # surely the interrupt didn't happen after we successfully\n                # initiated the connect and are just waiting for it to complete,\n                # because a non-blocking connect does not wait! And the spec\n                # describes the interaction between EINTR/blocking connect, but\n                # doesn't have anything useful to say about non-blocking connect:\n                #\n                #   http://pubs.opengroup.org/onlinepubs/007904975/functions/connect.html\n                #\n                # So we have a conundrum: if EINTR means that the connect() hasn't\n                # happened (like it does for essentially every other syscall),\n                # then InterruptedError should be caught and retried. If EINTR\n                # means that the connect() has successfully started, then\n                # InterruptedError should be caught and ignored. Which should we\n                # do?\n                #\n                # In practice, the resolution is probably that non-blocking\n                # connect simply never returns EINTR, so the question of how to\n                # handle it is moot.  Someone spelunked macOS/FreeBSD and\n                # confirmed this is true there:\n                #\n                #   https://stackoverflow.com/questions/14134440/eintr-and-non-blocking-calls\n                #\n                # and exarkun seems to think it's true in general of non-blocking\n                # calls:\n                #\n                #   https://twistedmatrix.com/pipermail/twisted-python/2010-September/022864.html\n                # (and indeed, AFAICT twisted doesn't try to handle\n                # InterruptedError).\n                #\n                # So we don't try to catch InterruptedError. This way if it\n                # happens, someone will hopefully tell us, and then hopefully we\n                # can investigate their system to figure out what its semantics\n                # are.\n                return self._sock.connect(address)\n            # It raised BlockingIOError, meaning that it's started the\n            # connection attempt. We wait for it to complete:\n            await _core.wait_writable(self._sock)\n        except trio.Cancelled:\n            # We can't really cancel a connect, and the socket is in an\n            # indeterminate state. Better to close it so we don't get\n            # confused.\n            self._sock.close()\n            raise\n        # Okay, the connect finished, but it might have failed:\n        err = self._sock.getsockopt(_stdlib_socket.SOL_SOCKET, _stdlib_socket.SO_ERROR)\n        if err != 0:\n            raise OSError(err, f\"Error connecting to {address!r}: {os.strerror(err)}\")\n\n    ################################################################\n    # recv\n    ################################################################\n\n    # Not possible to typecheck with a Callable (due to DefaultArg), nor with a\n    # callback Protocol (https://github.com/python/typing/discussions/1040)\n    # but this seems to work. If not explicitly defined then pyright --verifytypes will\n    # complain about AmbiguousType\n    if TYPE_CHECKING:\n\n        def recv(self, buflen: int, flags: int = 0, /) -> Awaitable[bytes]: ...\n\n    recv = _make_simple_sock_method_wrapper(\n        _stdlib_socket.socket.recv,\n        _core.wait_readable,\n    )\n\n    ################################################################\n    # recv_into\n    ################################################################\n\n    if TYPE_CHECKING:\n\n        def recv_into(\n            self,\n            /,\n            buffer: Buffer,\n            nbytes: int = 0,\n            flags: int = 0,\n        ) -> Awaitable[int]: ...\n\n    recv_into = _make_simple_sock_method_wrapper(\n        _stdlib_socket.socket.recv_into,\n        _core.wait_readable,\n    )\n\n    ################################################################\n    # recvfrom\n    ################################################################\n\n    if TYPE_CHECKING:\n        # return type of socket.socket.recvfrom in typeshed is tuple[bytes, Any]\n        def recvfrom(\n            self,\n            bufsize: int,\n            flags: int = 0,\n            /,\n        ) -> Awaitable[tuple[bytes, AddressFormat]]: ...\n\n    recvfrom = _make_simple_sock_method_wrapper(\n        _stdlib_socket.socket.recvfrom,\n        _core.wait_readable,\n    )\n\n    ################################################################\n    # recvfrom_into\n    ################################################################\n\n    if TYPE_CHECKING:\n        # return type of socket.socket.recvfrom_into in typeshed is tuple[bytes, Any]\n        def recvfrom_into(\n            self,\n            /,\n            buffer: Buffer,\n            nbytes: int = 0,\n            flags: int = 0,\n        ) -> Awaitable[tuple[int, AddressFormat]]: ...\n\n    recvfrom_into = _make_simple_sock_method_wrapper(\n        _stdlib_socket.socket.recvfrom_into,\n        _core.wait_readable,\n    )\n\n    ################################################################\n    # recvmsg\n    ################################################################\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"recvmsg\")\n    ):\n        if TYPE_CHECKING:\n\n            def recvmsg(\n                self,\n                bufsize: int,\n                ancbufsize: int = 0,\n                flags: int = 0,\n                /,\n            ) -> Awaitable[tuple[bytes, list[tuple[int, int, bytes]], int, object]]: ...\n\n        recvmsg = _make_simple_sock_method_wrapper(\n            _stdlib_socket.socket.recvmsg,\n            _core.wait_readable,\n            maybe_avail=True,\n        )\n\n    ################################################################\n    # recvmsg_into\n    ################################################################\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"recvmsg_into\")\n    ):\n        if TYPE_CHECKING:\n\n            def recvmsg_into(\n                self,\n                buffers: Iterable[Buffer],\n                ancbufsize: int = 0,\n                flags: int = 0,\n                /,\n            ) -> Awaitable[tuple[int, list[tuple[int, int, bytes]], int, object]]: ...\n\n        recvmsg_into = _make_simple_sock_method_wrapper(\n            _stdlib_socket.socket.recvmsg_into,\n            _core.wait_readable,\n            maybe_avail=True,\n        )\n\n    ################################################################\n    # send\n    ################################################################\n\n    if TYPE_CHECKING:\n\n        def send(self, bytes: Buffer, flags: int = 0, /) -> Awaitable[int]: ...\n\n    send = _make_simple_sock_method_wrapper(\n        _stdlib_socket.socket.send,\n        _core.wait_writable,\n    )\n\n    ################################################################\n    # sendto\n    ################################################################\n\n    @overload\n    async def sendto(\n        self,\n        data: Buffer,\n        address: tuple[object, ...] | str | Buffer,\n        /,\n    ) -> int: ...\n\n    @overload\n    async def sendto(\n        self,\n        data: Buffer,\n        flags: int,\n        address: tuple[object, ...] | str | Buffer,\n        /,\n    ) -> int: ...\n\n    @_wraps(_stdlib_socket.socket.sendto, assigned=(), updated=())\n    async def sendto(self, *args: object) -> int:\n        \"\"\"Similar to :meth:`socket.socket.sendto`, but async.\"\"\"\n        # args is: data[, flags], address\n        # and kwargs are not accepted\n        args_list = list(args)\n        args_list[-1] = await self._resolve_address_nocp(args[-1], local=False)\n        # args_list is Any, which isn't the signature of sendto().\n        # We don't care about invalid types, sendto() will do the checking.\n        return await self._nonblocking_helper(\n            _core.wait_writable,\n            _stdlib_socket.socket.sendto,  # type: ignore[arg-type]\n            *args_list,\n        )\n\n    ################################################################\n    # sendmsg\n    ################################################################\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(_stdlib_socket.socket, \"sendmsg\")\n    ):\n\n        @_wraps(_stdlib_socket.socket.sendmsg, assigned=(), updated=())\n        async def sendmsg(\n            self,\n            buffers: Iterable[Buffer],\n            ancdata: Iterable[tuple[int, int, Buffer]] = (),\n            flags: int = 0,\n            address: AddressFormat | None = None,\n            /,\n        ) -> int:\n            \"\"\"Similar to :meth:`socket.socket.sendmsg`, but async.\n\n            Only available on platforms where :meth:`socket.socket.sendmsg` is\n            available.\n\n            \"\"\"\n            if address is not None:\n                address = await self._resolve_address_nocp(address, local=False)\n            return await self._nonblocking_helper(\n                _core.wait_writable,\n                _stdlib_socket.socket.sendmsg,\n                buffers,\n                ancdata,\n                flags,\n                address,\n            )\n\n    ################################################################\n    # sendfile\n    ################################################################\n\n    # Not implemented yet:\n    # async def sendfile(self, file, offset=0, count=None):\n    #     XX\n\n    # Intentionally omitted:\n    #   sendall\n    #   makefile\n    #   setblocking/getblocking\n    #   settimeout/gettimeout\n    #   timeout\n"
  },
  {
    "path": "src/trio/_ssl.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport operator as _operator\nimport ssl as _stdlib_ssl\nfrom enum import Enum as _Enum\nfrom typing import TYPE_CHECKING, Any, ClassVar, Final as TFinal, Generic, TypeVar\n\nimport trio\n\nfrom . import _sync\nfrom ._highlevel_generic import aclose_forcefully\nfrom ._util import ConflictDetector, final\nfrom .abc import Listener, Stream\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n    from typing_extensions import TypeVarTuple, Unpack\n\n    Ts = TypeVarTuple(\"Ts\")\n\n# General theory of operation:\n#\n# We implement an API that closely mirrors the stdlib ssl module's blocking\n# API, and we do it using the stdlib ssl module's non-blocking in-memory API.\n# The stdlib non-blocking in-memory API is barely documented, and acts as a\n# thin wrapper around openssl, whose documentation also leaves something to be\n# desired. So here's the main things you need to know to understand the code\n# in this file:\n#\n# We use an ssl.SSLObject, which exposes the four main I/O operations:\n#\n# - do_handshake: performs the initial handshake. Must be called once at the\n#   beginning of each connection; is a no-op once it's completed once.\n#\n# - write: takes some unencrypted data and attempts to send it to the remote\n#   peer.\n\n# - read: attempts to decrypt and return some data from the remote peer.\n#\n# - unwrap: this is weirdly named; maybe it helps to realize that the thing it\n#   wraps is called SSL_shutdown. It sends a cryptographically signed message\n#   saying \"I'm closing this connection now\", and then waits to receive the\n#   same from the remote peer (unless we already received one, in which case\n#   it returns immediately).\n#\n# All of these operations read and write from some in-memory buffers called\n# \"BIOs\", which are an opaque OpenSSL-specific object that's basically\n# semantically equivalent to a Python bytearray. When they want to send some\n# bytes to the remote peer, they append them to the outgoing BIO, and when\n# they want to receive some bytes from the remote peer, they try to pull them\n# out of the incoming BIO. \"Sending\" always succeeds, because the outgoing BIO\n# can always be extended to hold more data. \"Receiving\" acts sort of like a\n# non-blocking socket: it might manage to get some data immediately, or it\n# might fail and need to be tried again later. We can also directly add or\n# remove data from the BIOs whenever we want.\n#\n# Now the problem is that while these I/O operations are opaque atomic\n# operations from the point of view of us calling them, under the hood they\n# might require some arbitrary sequence of sends and receives from the remote\n# peer. This is particularly true for do_handshake, which generally requires a\n# few round trips, but it's also true for write and read, due to an evil thing\n# called \"renegotiation\".\n#\n# Renegotiation is the process by which one of the peers might arbitrarily\n# decide to redo the handshake at any time. Did I mention it's evil? It's\n# pretty evil, and almost universally hated. The HTTP/2 spec forbids the use\n# of TLS renegotiation for HTTP/2 connections. TLS 1.3 removes it from the\n# protocol entirely. It's impossible to trigger a renegotiation if using\n# Python's ssl module. OpenSSL's renegotiation support is pretty buggy [1].\n# Nonetheless, it does get used in real life, mostly in two cases:\n#\n# 1) Normally in TLS 1.2 and below, when the client side of a connection wants\n# to present a certificate to prove their identity, that certificate gets sent\n# in plaintext. This is bad, because it means that anyone eavesdropping can\n# see who's connecting – it's like sending your username in plain text. Not as\n# bad as sending your password in plain text, but still, pretty bad. However,\n# renegotiations *are* encrypted. So as a workaround, it's not uncommon for\n# systems that want to use client certificates to first do an anonymous\n# handshake, and then to turn around and do a second handshake (=\n# renegotiation) and this time ask for a client cert. Or sometimes this is\n# done on a case-by-case basis, e.g. a web server might accept a connection,\n# read the request, and then once it sees the page you're asking for it might\n# stop and ask you for a certificate.\n#\n# 2) In principle the same TLS connection can be used for an arbitrarily long\n# time, and might transmit arbitrarily large amounts of data. But this creates\n# a cryptographic problem: an attacker who has access to arbitrarily large\n# amounts of data that's all encrypted using the same key may eventually be\n# able to use this to figure out the key. Is this a real practical problem? I\n# have no idea, I'm not a cryptographer. In any case, some people worry that\n# it's a problem, so their TLS libraries are designed to automatically trigger\n# a renegotiation every once in a while on some sort of timer.\n#\n# The end result is that you might be going along, minding your own business,\n# and then *bam*! a wild renegotiation appears! And you just have to cope.\n#\n# The reason that coping with renegotiations is difficult is that some\n# unassuming \"read\" or \"write\" call might find itself unable to progress until\n# it does a handshake, which remember is a process with multiple round\n# trips. So read might have to send data, and write might have to receive\n# data, and this might happen multiple times. And some of those attempts might\n# fail because there isn't any data yet, and need to be retried. Managing all\n# this is pretty complicated.\n#\n# Here's how openssl (and thus the stdlib ssl module) handle this. All of the\n# I/O operations above follow the same rules. When you call one of them:\n#\n# - it might write some data to the outgoing BIO\n# - it might read some data from the incoming BIO\n# - it might raise SSLWantReadError if it can't complete without reading more\n#   data from the incoming BIO. This is important: the \"read\" in ReadError\n#   refers to reading from the *underlying* stream.\n# - (and in principle it might raise SSLWantWriteError too, but that never\n#   happens when using memory BIOs, so never mind)\n#\n# If it doesn't raise an error, then the operation completed successfully\n# (though we still need to take any outgoing data out of the memory buffer and\n# put it onto the wire). If it *does* raise an error, then we need to retry\n# *exactly that method call* later – in particular, if a 'write' failed, we\n# need to try again later *with the same data*, because openssl might have\n# already committed some of the initial parts of our data to its output even\n# though it didn't tell us that, and has remembered that the next time we call\n# write it needs to skip the first 1024 bytes or whatever it is. (Well,\n# technically, we're actually allowed to call 'write' again with a data buffer\n# which is the same as our old one PLUS some extra stuff added onto the end,\n# but in Trio that never comes up so never mind.)\n#\n# There are some people online who claim that once you've gotten a Want*Error\n# then the *very next call* you make to openssl *must* be the same as the\n# previous one. I'm pretty sure those people are wrong. In particular, it's\n# okay to call write, get a WantReadError, and then call read a few times;\n# it's just that *the next time you call write*, it has to be with the same\n# data.\n#\n# One final wrinkle: we want our SSLStream to support full-duplex operation,\n# i.e. it should be possible for one task to be calling send_all while another\n# task is calling receive_some. But renegotiation makes this a big hassle, because\n# even if SSLStream's restricts themselves to one task calling send_all and one\n# task calling receive_some, those two tasks might end up both wanting to call\n# send_all, or both to call receive_some at the same time *on the underlying\n# stream*. So we have to do some careful locking to hide this problem from our\n# users.\n#\n# (Renegotiation is evil.)\n#\n# So our basic strategy is to define a single helper method called \"_retry\",\n# which has generic logic for dealing with SSLWantReadError, pushing data from\n# the outgoing BIO to the wire, reading data from the wire to the incoming\n# BIO, retrying an I/O call until it works, and synchronizing with other tasks\n# that might be calling _retry concurrently. Basically it takes an SSLObject\n# non-blocking in-memory method and converts it into a Trio async blocking\n# method. _retry is only about 30 lines of code, but all these cases\n# multiplied by concurrent calls make it extremely tricky, so there are lots\n# of comments down below on the details, and a really extensive test suite in\n# test_ssl.py. And now you know *why* it's so tricky, and can probably\n# understand how it works.\n#\n# [1] https://rt.openssl.org/Ticket/Display.html?id=3712\n\n# XX how closely should we match the stdlib API?\n# - maybe suppress_ragged_eofs=False is a better default?\n# - maybe check crypto folks for advice?\n# - this is also interesting: https://bugs.python.org/issue8108#msg102867\n\n# Definitely keep an eye on Cory's TLS API ideas on security-sig etc.\n\n# XX document behavior on cancellation/error (i.e.: all is lost abandon\n# stream)\n# docs will need to make very clear that this is different from all the other\n# cancellations in core Trio\n\n\nT = TypeVar(\"T\")\n\n################################################################\n# SSLStream\n################################################################\n\n# Ideally, when the user calls SSLStream.receive_some() with no argument, then\n# we should do exactly one call to self.transport_stream.receive_some(),\n# decrypt everything we got, and return it. Unfortunately, the way openssl's\n# API works, we have to pick how much data we want to allow when we call\n# read(), and then it (potentially) triggers a call to\n# transport_stream.receive_some(). So at the time we pick the amount of data\n# to decrypt, we don't know how much data we've read. As a simple heuristic,\n# we record the max amount of data returned by previous calls to\n# transport_stream.receive_some(), and we use that for future calls to read().\n# But what do we use for the very first call? That's what this constant sets.\n#\n# Note that the value passed to read() is a limit on the amount of\n# *decrypted* data, but we can only see the size of the *encrypted* data\n# returned by transport_stream.receive_some(). TLS adds a small amount of\n# framing overhead, and TLS compression is rarely used these days because it's\n# insecure. So the size of the encrypted data should be a slight over-estimate\n# of the size of the decrypted data, which is exactly what we want.\n#\n# The specific value is not really based on anything; it might be worth tuning\n# at some point. But, if you have an TCP connection with the typical 1500 byte\n# MTU and an initial window of 10 (see RFC 6928), then the initial burst of\n# data will be limited to ~15000 bytes (or a bit less due to IP-level framing\n# overhead), so this is chosen to be larger than that.\nSTARTING_RECEIVE_SIZE: TFinal = 16384\n\n\ndef _is_eof(exc: BaseException | None) -> bool:\n    # There appears to be a bug on Python 3.10, where SSLErrors\n    # aren't properly translated into SSLEOFErrors.\n    # This stringly-typed error check is borrowed from the AnyIO\n    # project.\n    return isinstance(exc, _stdlib_ssl.SSLEOFError) or (\n        \"UNEXPECTED_EOF_WHILE_READING\" in getattr(exc, \"strerror\", ())\n    )\n\n\nclass NeedHandshakeError(Exception):\n    \"\"\"Some :class:`SSLStream` methods can't return any meaningful data until\n    after the handshake. If you call them before the handshake, they raise\n    this error.\n\n    \"\"\"\n\n\nclass _Once:\n    __slots__ = (\"_afn\", \"_args\", \"_done\", \"started\")\n\n    def __init__(\n        self,\n        afn: Callable[[*Ts], Awaitable[object]],\n        *args: Unpack[Ts],\n    ) -> None:\n        self._afn = afn\n        self._args = args\n        self.started = False\n        self._done = _sync.Event()\n\n    async def ensure(self, *, checkpoint: bool) -> None:\n        if not self.started:\n            self.started = True\n            await self._afn(*self._args)\n            self._done.set()\n        elif not checkpoint and self._done.is_set():\n            return\n        else:\n            await self._done.wait()\n\n    @property\n    def done(self) -> bool:\n        return bool(self._done.is_set())\n\n\n_State = _Enum(\"_State\", [\"OK\", \"BROKEN\", \"CLOSED\"])\n\n# invariant\nT_Stream = TypeVar(\"T_Stream\", bound=Stream)\n\n\n@final\nclass SSLStream(Stream, Generic[T_Stream]):\n    r\"\"\"Encrypted communication using SSL/TLS.\n\n    :class:`SSLStream` wraps an arbitrary :class:`~trio.abc.Stream`, and\n    allows you to perform encrypted communication over it using the usual\n    :class:`~trio.abc.Stream` interface. You pass regular data to\n    :meth:`send_all`, then it encrypts it and sends the encrypted data on the\n    underlying :class:`~trio.abc.Stream`; :meth:`receive_some` takes encrypted\n    data out of the underlying :class:`~trio.abc.Stream` and decrypts it\n    before returning it.\n\n    You should read the standard library's :mod:`ssl` documentation carefully\n    before attempting to use this class, and probably other general\n    documentation on SSL/TLS as well. SSL/TLS is subtle and quick to\n    anger. Really. I'm not kidding.\n\n    Args:\n      transport_stream (~trio.abc.Stream): The stream used to transport\n          encrypted data. Required.\n\n      ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` used for\n          this connection. Required. Usually created by calling\n          :func:`ssl.create_default_context`.\n\n      server_hostname (str, bytes, or None): The name of the server being\n          connected to. Used for `SNI\n          <https://en.wikipedia.org/wiki/Server_Name_Indication>`__ and for\n          validating the server's certificate (if hostname checking is\n          enabled). This is effectively mandatory for clients, and actually\n          mandatory if ``ssl_context.check_hostname`` is ``True``.\n\n      server_side (bool): Whether this stream is acting as a client or\n          server. Defaults to False, i.e. client mode.\n\n      https_compatible (bool): There are two versions of SSL/TLS commonly\n          encountered in the wild: the standard version, and the version used\n          for HTTPS (HTTP-over-SSL/TLS).\n\n          Standard-compliant SSL/TLS implementations always send a\n          cryptographically signed ``close_notify`` message before closing the\n          connection. This is important because if the underlying transport\n          were simply closed, then there wouldn't be any way for the other\n          side to know whether the connection was intentionally closed by the\n          peer that they negotiated a cryptographic connection to, or by some\n          `man-in-the-middle\n          <https://en.wikipedia.org/wiki/Man-in-the-middle_attack>`__ attacker\n          who can't manipulate the cryptographic stream, but can manipulate\n          the transport layer (a so-called \"truncation attack\").\n\n          However, this part of the standard is widely ignored by real-world\n          HTTPS implementations, which means that if you want to interoperate\n          with them, then you NEED to ignore it too.\n\n          Fortunately this isn't as bad as it sounds, because the HTTP\n          protocol already includes its own equivalent of ``close_notify``, so\n          doing this again at the SSL/TLS level is redundant. But not all\n          protocols do! Therefore, by default Trio implements the safer\n          standard-compliant version (``https_compatible=False``). But if\n          you're speaking HTTPS or some other protocol where\n          ``close_notify``\\s are commonly skipped, then you should set\n          ``https_compatible=True``; with this setting, Trio will neither\n          expect nor send ``close_notify`` messages.\n\n          If you have code that was written to use :class:`ssl.SSLSocket` and\n          now you're porting it to Trio, then it may be useful to know that a\n          difference between :class:`SSLStream` and :class:`ssl.SSLSocket` is\n          that :class:`~ssl.SSLSocket` implements the\n          ``https_compatible=True`` behavior by default.\n\n    Attributes:\n      transport_stream (trio.abc.Stream): The underlying transport stream\n          that was passed to ``__init__``. An example of when this would be\n          useful is if you're using :class:`SSLStream` over a\n          :class:`~trio.SocketStream` and want to call the\n          :class:`~trio.SocketStream`'s :meth:`~trio.SocketStream.setsockopt`\n          method.\n\n    Internally, this class is implemented using an instance of\n    :class:`ssl.SSLObject`, and all of :class:`~ssl.SSLObject`'s methods and\n    attributes are re-exported as methods and attributes on this class.\n    However, there is one difference: :class:`~ssl.SSLObject` has several\n    methods that return information about the encrypted connection, like\n    :meth:`~ssl.SSLSocket.cipher` or\n    :meth:`~ssl.SSLSocket.selected_alpn_protocol`. If you call them before the\n    handshake, when they can't possibly return useful data, then\n    :class:`ssl.SSLObject` returns None, but :class:`trio.SSLStream`\n    raises :exc:`NeedHandshakeError`.\n\n    This also means that if you register a SNI callback using\n    `~ssl.SSLContext.sni_callback`, then the first argument your callback\n    receives will be a :class:`ssl.SSLObject`.\n\n    \"\"\"\n\n    # Note: any new arguments here should likely also be added to\n    # SSLListener.__init__, and maybe the open_ssl_over_tcp_* helpers.\n    def __init__(\n        self,\n        transport_stream: T_Stream,\n        ssl_context: _stdlib_ssl.SSLContext,\n        *,\n        server_hostname: str | bytes | None = None,\n        server_side: bool = False,\n        https_compatible: bool = False,\n    ) -> None:\n        self.transport_stream: T_Stream = transport_stream\n        self._state = _State.OK\n        self._https_compatible = https_compatible\n        self._outgoing = _stdlib_ssl.MemoryBIO()\n        self._delayed_outgoing: bytes | None = None\n        self._incoming = _stdlib_ssl.MemoryBIO()\n        self._ssl_object = ssl_context.wrap_bio(\n            self._incoming,\n            self._outgoing,\n            server_side=server_side,\n            server_hostname=server_hostname,\n        )\n        # Tracks whether we've already done the initial handshake\n        self._handshook = _Once(self._do_handshake)\n\n        # These are used to synchronize access to self.transport_stream\n        self._inner_send_lock = _sync.StrictFIFOLock()\n        self._inner_recv_count = 0\n        self._inner_recv_lock = _sync.Lock()\n\n        # These are used to make sure that our caller doesn't attempt to make\n        # multiple concurrent calls to send_all/wait_send_all_might_not_block\n        # or to receive_some.\n        self._outer_send_conflict_detector = ConflictDetector(\n            \"another task is currently sending data on this SSLStream\",\n        )\n        self._outer_recv_conflict_detector = ConflictDetector(\n            \"another task is currently receiving data on this SSLStream\",\n        )\n\n        self._estimated_receive_size = STARTING_RECEIVE_SIZE\n\n    _forwarded: ClassVar = {\n        \"context\",\n        \"server_side\",\n        \"server_hostname\",\n        \"session\",\n        \"session_reused\",\n        \"getpeercert\",\n        \"selected_npn_protocol\",\n        \"cipher\",\n        \"shared_ciphers\",\n        \"compression\",\n        \"pending\",\n        \"get_channel_binding\",\n        \"selected_alpn_protocol\",\n        \"version\",\n    }\n\n    _after_handshake: ClassVar = {\n        \"session_reused\",\n        \"getpeercert\",\n        \"selected_npn_protocol\",\n        \"cipher\",\n        \"shared_ciphers\",\n        \"compression\",\n        \"get_channel_binding\",\n        \"selected_alpn_protocol\",\n        \"version\",\n    }\n\n    def __getattr__(  # type: ignore[explicit-any]\n        self,\n        name: str,\n    ) -> Any:\n        if name in self._forwarded:\n            if name in self._after_handshake and not self._handshook.done:\n                raise NeedHandshakeError(f\"call do_handshake() before calling {name!r}\")\n\n            return getattr(self._ssl_object, name)\n        else:\n            raise AttributeError(name)\n\n    def __setattr__(self, name: str, value: object) -> None:\n        if name in self._forwarded:\n            setattr(self._ssl_object, name, value)\n        else:\n            super().__setattr__(name, value)\n\n    def __dir__(self) -> list[str]:\n        return list(super().__dir__()) + list(self._forwarded)\n\n    def _check_status(self) -> None:\n        if self._state is _State.OK:\n            return\n        elif self._state is _State.BROKEN:\n            raise trio.BrokenResourceError\n        elif self._state is _State.CLOSED:\n            raise trio.ClosedResourceError\n        else:  # pragma: no cover\n            raise AssertionError()\n\n    # This is probably the single trickiest function in Trio. It has lots of\n    # comments, though, just make sure to think carefully if you ever have to\n    # touch it. The big comment at the top of this file will help explain\n    # too.\n    async def _retry(\n        self,\n        fn: Callable[[*Ts], T],\n        *args: Unpack[Ts],\n        ignore_want_read: bool = False,\n        is_handshake: bool = False,\n    ) -> T | None:\n        await trio.lowlevel.checkpoint_if_cancelled()\n        yielded = False\n        finished = False\n        while not finished:\n            # WARNING: this code needs to be very careful with when it\n            # calls 'await'! There might be multiple tasks calling this\n            # function at the same time trying to do different operations,\n            # so we need to be careful to:\n            #\n            # 1) interact with the SSLObject, then\n            # 2) await on exactly one thing that lets us make forward\n            # progress, then\n            # 3) loop or exit\n            #\n            # In particular we don't want to yield while interacting with\n            # the SSLObject (because it's shared state, so someone else\n            # might come in and mess with it while we're suspended), and\n            # we don't want to yield *before* starting the operation that\n            # will help us make progress, because then someone else might\n            # come in and leapfrog us.\n\n            # Call the SSLObject method, and get its result.\n            #\n            # NB: despite what the docs say, SSLWantWriteError can't\n            # happen – \"Writes to memory BIOs will always succeed if\n            # memory is available: that is their size can grow\n            # indefinitely.\"\n            # https://wiki.openssl.org/index.php/Manual:BIO_s_mem(3)\n            want_read = False\n            ret = None\n            try:\n                ret = fn(*args)\n            except _stdlib_ssl.SSLWantReadError:\n                want_read = True\n            except (_stdlib_ssl.SSLError, _stdlib_ssl.CertificateError) as exc:\n                self._state = _State.BROKEN\n                raise trio.BrokenResourceError from exc\n            else:\n                finished = True\n            if ignore_want_read:\n                want_read = False\n                finished = True\n            to_send = self._outgoing.read()\n\n            # Some versions of SSL_do_handshake have a bug in how they handle\n            # the TLS 1.3 handshake on the server side: after the handshake\n            # finishes, they automatically send session tickets, even though\n            # the client may not be expecting data to arrive at this point and\n            # sending it could cause a deadlock or lost data. This applies at\n            # least to OpenSSL 1.1.1c and earlier, and the OpenSSL devs\n            # currently have no plans to fix it:\n            #\n            #   https://github.com/openssl/openssl/issues/7948\n            #   https://github.com/openssl/openssl/issues/7967\n            #\n            # The correct behavior is to wait to send session tickets on the\n            # first call to SSL_write. (This is what BoringSSL does.) So, we\n            # use a heuristic to detect when OpenSSL has tried to send session\n            # tickets, and we manually delay sending them until the\n            # appropriate moment. For more discussion see:\n            #\n            #   https://github.com/python-trio/trio/issues/819#issuecomment-517529763\n            if (\n                is_handshake\n                and not want_read\n                and self._ssl_object.server_side\n                and self._ssl_object.version() == \"TLSv1.3\"\n            ):\n                assert self._delayed_outgoing is None\n                self._delayed_outgoing = to_send\n                to_send = b\"\"\n\n            # Outputs from the above code block are:\n            #\n            # - to_send: bytestring; if non-empty then we need to send\n            #   this data to make forward progress\n            #\n            # - want_read: True if we need to receive_some some data to make\n            #   forward progress\n            #\n            # - finished: False means that we need to retry the call to\n            #   fn(*args) again, after having pushed things forward. True\n            #   means we still need to do whatever was said (in particular\n            #   send any data in to_send), but once we do then we're\n            #   done.\n            #\n            # - ret: the operation's return value. (Meaningless unless\n            #   finished is True.)\n            #\n            # Invariant: want_read and finished can't both be True at the\n            # same time.\n            #\n            # Now we need to move things forward. There are two things we\n            # might have to do, and any given operation might require\n            # either, both, or neither to proceed:\n            #\n            # - send the data in to_send\n            #\n            # - receive_some some data and put it into the incoming BIO\n            #\n            # Our strategy is: if there's data to send, send it;\n            # *otherwise* if there's data to receive_some, receive_some it.\n            #\n            # If both need to happen, then we only send. Why? Well, we\n            # know that *right now* we have to both send and receive_some\n            # before the operation can complete. But as soon as we yield,\n            # that information becomes potentially stale – e.g. while\n            # we're sending, some other task might go and receive_some the\n            # data we need and put it into the incoming BIO. And if it\n            # does, then we *definitely don't* want to do a receive_some –\n            # there might not be any more data coming, and we'd deadlock!\n            # We could do something tricky to keep track of whether a\n            # receive_some happens while we're sending, but the case where\n            # we have to do both is very unusual (only during a\n            # renegotiation), so it's better to keep things simple. So we\n            # do just one potentially-blocking operation, then check again\n            # for fresh information.\n            #\n            # And we prioritize sending over receiving because, if there\n            # are multiple tasks that want to receive_some, then it\n            # doesn't matter what order they go in. But if there are\n            # multiple tasks that want to send, then they each have\n            # different data, and the data needs to get put onto the wire\n            # in the same order that it was retrieved from the outgoing\n            # BIO. So if we have data to send, that *needs* to be the\n            # *very* *next* *thing* we do, to make sure no-one else sneaks\n            # in before us. Or if we can't send immediately because\n            # someone else is, then we at least need to get in line\n            # immediately.\n            if to_send:\n                # NOTE: This relies on the lock being strict FIFO fair!\n                async with self._inner_send_lock:\n                    yielded = True\n                    try:\n                        if self._delayed_outgoing is not None:\n                            to_send = self._delayed_outgoing + to_send\n                            self._delayed_outgoing = None\n                        await self.transport_stream.send_all(to_send)\n                    except:\n                        # Some unknown amount of our data got sent, and we\n                        # don't know how much. This stream is doomed.\n                        self._state = _State.BROKEN\n                        raise\n            elif want_read:\n                # It's possible that someone else is already blocked in\n                # transport_stream.receive_some. If so then we want to\n                # wait for them to finish, but we don't want to call\n                # transport_stream.receive_some again ourselves; we just\n                # want to loop around and check if their contribution\n                # helped anything. So we make a note of how many times\n                # some task has been through here before taking the lock,\n                # and if it's changed by the time we get the lock, then we\n                # skip calling transport_stream.receive_some and loop\n                # around immediately.\n                recv_count = self._inner_recv_count\n                async with self._inner_recv_lock:\n                    yielded = True\n                    if recv_count == self._inner_recv_count:\n                        data = await self.transport_stream.receive_some()\n                        if not data:\n                            self._incoming.write_eof()\n                        else:\n                            self._estimated_receive_size = max(\n                                self._estimated_receive_size,\n                                len(data),\n                            )\n                            self._incoming.write(data)\n                        self._inner_recv_count += 1\n        if not yielded:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n        return ret\n\n    async def _do_handshake(self) -> None:\n        try:\n            await self._retry(self._ssl_object.do_handshake, is_handshake=True)\n        except:\n            self._state = _State.BROKEN\n            raise\n\n    async def do_handshake(self) -> None:\n        \"\"\"Ensure that the initial handshake has completed.\n\n        The SSL protocol requires an initial handshake to exchange\n        certificates, select cryptographic keys, and so forth, before any\n        actual data can be sent or received. You don't have to call this\n        method; if you don't, then :class:`SSLStream` will automatically\n        perform the handshake as needed, the first time you try to send or\n        receive data. But if you want to trigger it manually – for example,\n        because you want to look at the peer's certificate before you start\n        talking to them – then you can call this method.\n\n        If the initial handshake is already in progress in another task, this\n        waits for it to complete and then returns.\n\n        If the initial handshake has already completed, this returns\n        immediately without doing anything (except executing a checkpoint).\n\n        .. warning:: If this method is cancelled, then it may leave the\n           :class:`SSLStream` in an unusable state. If this happens then any\n           future attempt to use the object will raise\n           :exc:`trio.BrokenResourceError`.\n\n        \"\"\"\n        self._check_status()\n        await self._handshook.ensure(checkpoint=True)\n\n    # Most things work if we don't explicitly force do_handshake to be called\n    # before calling receive_some or send_all, because openssl will\n    # automatically perform the handshake on the first SSL_{read,write}\n    # call. BUT, allowing openssl to do this will disable Python's hostname\n    # checking!!! See:\n    #   https://bugs.python.org/issue30141\n    # So we *definitely* have to make sure that do_handshake is called\n    # before doing anything else.\n    async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:\n        \"\"\"Read some data from the underlying transport, decrypt it, and\n        return it.\n\n        See :meth:`trio.abc.ReceiveStream.receive_some` for details.\n\n        .. warning:: If this method is cancelled while the initial handshake\n           or a renegotiation are in progress, then it may leave the\n           :class:`SSLStream` in an unusable state. If this happens then any\n           future attempt to use the object will raise\n           :exc:`trio.BrokenResourceError`.\n\n        \"\"\"\n        with self._outer_recv_conflict_detector:\n            self._check_status()\n            try:\n                await self._handshook.ensure(checkpoint=False)\n            except trio.BrokenResourceError as exc:\n                # For some reason, EOF before handshake sometimes raises\n                # SSLSyscallError instead of SSLEOFError (e.g. on my linux\n                # laptop, but not on appveyor). Thanks openssl.\n                if self._https_compatible and (\n                    isinstance(exc.__cause__, _stdlib_ssl.SSLSyscallError)\n                    or _is_eof(exc.__cause__)\n                ):\n                    await trio.lowlevel.checkpoint()\n                    return b\"\"\n                else:\n                    raise\n            if max_bytes is None:\n                # If we somehow have more data already in our pending buffer\n                # than the estimate receive size, bump up our size a bit for\n                # this read only.\n                max_bytes = max(self._estimated_receive_size, self._incoming.pending)\n            else:\n                max_bytes = _operator.index(max_bytes)\n                if max_bytes < 1:\n                    raise ValueError(\"max_bytes must be >= 1\")\n            try:\n                received = await self._retry(self._ssl_object.read, max_bytes)\n                assert received is not None\n                return received\n            except trio.BrokenResourceError as exc:\n                # This isn't quite equivalent to just returning b\"\" in the\n                # first place, because we still end up with self._state set to\n                # BROKEN. But that's actually fine, because after getting an\n                # EOF on TLS then the only thing you can do is close the\n                # stream, and closing doesn't care about the state.\n\n                if self._https_compatible and _is_eof(exc.__cause__):\n                    await trio.lowlevel.checkpoint()\n                    return b\"\"\n                else:\n                    raise\n\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        \"\"\"Encrypt some data and then send it on the underlying transport.\n\n        See :meth:`trio.abc.SendStream.send_all` for details.\n\n        .. warning:: If this method is cancelled, then it may leave the\n           :class:`SSLStream` in an unusable state. If this happens then any\n           attempt to use the object will raise\n           :exc:`trio.BrokenResourceError`.\n\n        \"\"\"\n        with self._outer_send_conflict_detector:\n            self._check_status()\n            await self._handshook.ensure(checkpoint=False)\n            # SSLObject interprets write(b\"\") as an EOF for some reason, which\n            # is not what we want.\n            if not data:\n                await trio.lowlevel.checkpoint()\n                return\n            await self._retry(self._ssl_object.write, data)\n\n    async def unwrap(self) -> tuple[Stream, bytes | bytearray]:\n        \"\"\"Cleanly close down the SSL/TLS encryption layer, allowing the\n        underlying stream to be used for unencrypted communication.\n\n        You almost certainly don't need this.\n\n        Returns:\n          A pair ``(transport_stream, trailing_bytes)``, where\n          ``transport_stream`` is the underlying transport stream, and\n          ``trailing_bytes`` is a byte string. Since :class:`SSLStream`\n          doesn't necessarily know where the end of the encrypted data will\n          be, it can happen that it accidentally reads too much from the\n          underlying stream. ``trailing_bytes`` contains this extra data; you\n          should process it as if it was returned from a call to\n          ``transport_stream.receive_some(...)``.\n\n        \"\"\"\n        with self._outer_recv_conflict_detector, self._outer_send_conflict_detector:\n            self._check_status()\n            await self._handshook.ensure(checkpoint=False)\n            await self._retry(self._ssl_object.unwrap)\n            transport_stream = self.transport_stream\n            self._state = _State.CLOSED\n            self.transport_stream = None  # type: ignore[assignment]  # State is CLOSED now, nothing should use\n            return (transport_stream, self._incoming.read())\n\n    async def aclose(self) -> None:\n        \"\"\"Gracefully shut down this connection, and close the underlying\n        transport.\n\n        If ``https_compatible`` is False (the default), then this attempts to\n        first send a ``close_notify`` and then close the underlying stream by\n        calling its :meth:`~trio.abc.AsyncResource.aclose` method.\n\n        If ``https_compatible`` is set to True, then this simply closes the\n        underlying stream and marks this stream as closed.\n\n        \"\"\"\n        if self._state is _State.CLOSED:\n            await trio.lowlevel.checkpoint()\n            return\n        if self._state is _State.BROKEN or self._https_compatible:\n            self._state = _State.CLOSED\n            await self.transport_stream.aclose()\n            return\n        try:\n            # https_compatible=False, so we're in spec-compliant mode and have\n            # to send close_notify so that the other side gets a cryptographic\n            # assurance that we've called aclose. Of course, we can't do\n            # anything cryptographic until after we've completed the\n            # handshake:\n            await self._handshook.ensure(checkpoint=False)\n            # Then, we call SSL_shutdown *once*, because we want to send a\n            # close_notify but *not* wait for the other side to send back a\n            # response. In principle it would be more polite to wait for the\n            # other side to reply with their own close_notify. However, if\n            # they aren't paying attention (e.g., if they're just sending\n            # data and not receiving) then we will never notice our\n            # close_notify and we'll be waiting forever. Eventually we'll time\n            # out (hopefully), but it's still kind of nasty. And we can't\n            # require the other side to always be receiving, because (a)\n            # backpressure is kind of important, and (b) I bet there are\n            # broken TLS implementations out there that don't receive all the\n            # time. (Like e.g. anyone using Python ssl in synchronous mode.)\n            #\n            # The send-then-immediately-close behavior is explicitly allowed\n            # by the TLS specs, so we're ok on that.\n            #\n            # Subtlety: SSLObject.unwrap will immediately call it a second\n            # time, and the second time will raise SSLWantReadError because\n            # there hasn't been time for the other side to respond\n            # yet. (Unless they spontaneously sent a close_notify before we\n            # called this, and it's either already been processed or gets\n            # pulled out of the buffer by Python's second call.) So the way to\n            # do what we want is to ignore SSLWantReadError on this call.\n            #\n            # Also, because the other side might have already sent\n            # close_notify and closed their connection then it's possible that\n            # our attempt to send close_notify will raise\n            # BrokenResourceError. This is totally legal, and in fact can happen\n            # with two well-behaved Trio programs talking to each other, so we\n            # don't want to raise an error. So we suppress BrokenResourceError\n            # here. (This is safe, because literally the only thing this call\n            # to _retry will do is send the close_notify alert, so that's\n            # surely where the error comes from.)\n            #\n            # FYI in some cases this could also raise SSLSyscallError which I\n            # think is because SSL_shutdown is terrible. (Check out that note\n            # at the bottom of the man page saying that it sometimes gets\n            # raised spuriously.) I haven't seen this since we switched to\n            # immediately closing the socket, and I don't know exactly what\n            # conditions cause it and how to respond, so for now we're just\n            # letting that happen. But if you start seeing it, then hopefully\n            # this will give you a little head start on tracking it down,\n            # because whoa did this puzzle us at the 2017 PyCon sprints.\n            #\n            # Also, if someone else is blocked in send/receive, then we aren't\n            # going to be able to do a clean shutdown. If that happens, we'll\n            # just do an unclean shutdown.\n            with contextlib.suppress(trio.BrokenResourceError, trio.BusyResourceError):\n                await self._retry(self._ssl_object.unwrap, ignore_want_read=True)\n        except:\n            # Failure! Kill the stream and move on.\n            await aclose_forcefully(self.transport_stream)\n            raise\n        else:\n            # Success! Gracefully close the underlying stream.\n            await self.transport_stream.aclose()\n        finally:\n            self._state = _State.CLOSED\n\n    async def wait_send_all_might_not_block(self) -> None:\n        \"\"\"See :meth:`trio.abc.SendStream.wait_send_all_might_not_block`.\"\"\"\n        # This method's implementation is deceptively simple.\n        #\n        # First, we take the outer send lock, because of Trio's standard\n        # semantics that wait_send_all_might_not_block and send_all\n        # conflict.\n        with self._outer_send_conflict_detector:\n            self._check_status()\n            # Then we take the inner send lock. We know that no other tasks\n            # are calling self.send_all or self.wait_send_all_might_not_block,\n            # because we have the outer_send_lock. But! There might be another\n            # task calling self.receive_some -> transport_stream.send_all, in\n            # which case if we were to call\n            # transport_stream.wait_send_all_might_not_block directly we'd\n            # have two tasks doing write-related operations on\n            # transport_stream simultaneously, which is not allowed. We\n            # *don't* want to raise this conflict to our caller, because it's\n            # purely an internal affair – all they did was call\n            # wait_send_all_might_not_block and receive_some at the same time,\n            # which is totally valid. And waiting for the lock is OK, because\n            # a call to send_all certainly wouldn't complete while the other\n            # task holds the lock.\n            async with self._inner_send_lock:\n                # Now we have the lock, which creates another potential\n                # problem: what if a call to self.receive_some attempts to do\n                # transport_stream.send_all now? It'll have to wait for us to\n                # finish! But that's OK, because we release the lock as soon\n                # as the underlying stream becomes writable, and the\n                # self.receive_some call wasn't going to make any progress\n                # until then anyway.\n                #\n                # Of course, this does mean we might return *before* the\n                # stream is logically writable, because immediately after we\n                # return self.receive_some might write some data and make it\n                # non-writable again. But that's OK too,\n                # wait_send_all_might_not_block only guarantees that it\n                # doesn't return late.\n                await self.transport_stream.wait_send_all_might_not_block()\n\n\n# this is necessary for Sphinx, see also `_abc.py`\nSSLStream.__module__ = SSLStream.__module__.replace(\"._ssl\", \"\")\n\n\n@final\nclass SSLListener(Listener[SSLStream[T_Stream]]):\n    \"\"\"A :class:`~trio.abc.Listener` for SSL/TLS-encrypted servers.\n\n    :class:`SSLListener` wraps around another Listener, and converts\n    all incoming connections to encrypted connections by wrapping them\n    in a :class:`SSLStream`.\n\n    Args:\n      transport_listener (~trio.abc.Listener): The listener whose incoming\n          connections will be wrapped in :class:`SSLStream`.\n\n      ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` that will be\n          used for incoming connections.\n\n      https_compatible (bool): Passed on to :class:`SSLStream`.\n\n    Attributes:\n      transport_listener (trio.abc.Listener): The underlying listener that was\n          passed to ``__init__``.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        transport_listener: Listener[T_Stream],\n        ssl_context: _stdlib_ssl.SSLContext,\n        *,\n        https_compatible: bool = False,\n    ) -> None:\n        self.transport_listener = transport_listener\n        self._ssl_context = ssl_context\n        self._https_compatible = https_compatible\n\n    async def accept(self) -> SSLStream[T_Stream]:\n        \"\"\"Accept the next connection and wrap it in an :class:`SSLStream`.\n\n        See :meth:`trio.abc.Listener.accept` for details.\n\n        \"\"\"\n        transport_stream = await self.transport_listener.accept()\n        return SSLStream(\n            transport_stream,\n            self._ssl_context,\n            server_side=True,\n            https_compatible=self._https_compatible,\n        )\n\n    async def aclose(self) -> None:\n        \"\"\"Close the transport listener.\"\"\"\n        await self.transport_listener.aclose()\n"
  },
  {
    "path": "src/trio/_subprocess.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport subprocess\nimport sys\nimport warnings\nfrom contextlib import ExitStack\nfrom functools import partial\nfrom typing import (\n    TYPE_CHECKING,\n    Final,\n    Literal,\n    Protocol,\n    TypeAlias,\n    TypedDict,\n    overload,\n)\n\nimport trio\n\nfrom ._core import ClosedResourceError, TaskStatus\nfrom ._highlevel_generic import StapledStream\nfrom ._subprocess_platform import (\n    create_pipe_from_child_output,\n    create_pipe_to_child_stdin,\n    wait_child_exiting,\n)\nfrom ._sync import Lock\nfrom ._util import NoPublicConstructor, final\n\nif TYPE_CHECKING:\n    import signal\n    from collections.abc import Awaitable, Callable, Iterable, Mapping, Sequence\n    from io import TextIOWrapper\n\n    from typing_extensions import Unpack\n\n    from ._abc import ReceiveStream, SendStream\n\n\n# Sphinx cannot parse the stringified version\nStrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes]\n\n\n# Linux-specific, but has complex lifetime management stuff so we hard-code it\n# here instead of hiding it behind the _subprocess_platform abstraction\ncan_try_pidfd_open: bool\nif TYPE_CHECKING:\n\n    def pidfd_open(fd: int, flags: int) -> int: ...\n\n    from ._subprocess_platform import ClosableReceiveStream, ClosableSendStream\n\nelse:\n    can_try_pidfd_open = True\n    try:\n        from os import pidfd_open\n    except ImportError:\n        if sys.platform == \"linux\":\n            # this workaround is needed on:\n            #  - CPython <= 3.8\n            #  - non-CPython (maybe?)\n            #  - Anaconda's interpreter (as it is built to assume an older\n            #    than current linux kernel)\n            #\n            # The last point implies that other custom builds might not work;\n            # therefore, no assertion should be here.\n            import ctypes\n\n            _cdll_for_pidfd_open = ctypes.CDLL(None, use_errno=True)\n            _cdll_for_pidfd_open.syscall.restype = ctypes.c_long\n            # pid and flags are actually int-sized, but the syscall() function\n            # always takes longs. (Except on x32 where long is 32-bits and syscall\n            # takes 64-bit arguments. But in the unlikely case that anyone is\n            # using x32, this will still work, b/c we only need to pass in 32 bits\n            # of data, and the C ABI doesn't distinguish between passing 32-bit vs\n            # 64-bit integers; our 32-bit values will get loaded into 64-bit\n            # registers where syscall() will find them.)\n            _cdll_for_pidfd_open.syscall.argtypes = [\n                ctypes.c_long,  # syscall number\n                ctypes.c_long,  # pid\n                ctypes.c_long,  # flags\n            ]\n            __NR_pidfd_open = 434\n\n            def pidfd_open(fd: int, flags: int) -> int:\n                result = _cdll_for_pidfd_open.syscall(__NR_pidfd_open, fd, flags)\n                if result < 0:  # pragma: no cover\n                    err = ctypes.get_errno()\n                    raise OSError(err, os.strerror(err))\n                return result\n\n        else:\n            can_try_pidfd_open = False\n\n\nclass HasFileno(Protocol):\n    \"\"\"Represents any file-like object that has a file descriptor.\"\"\"\n\n    def fileno(self) -> int: ...\n\n\n@final\nclass Process(metaclass=NoPublicConstructor):\n    r\"\"\"A child process. Like :class:`subprocess.Popen`, but async.\n\n    This class has no public constructor. The most common way to get a\n    `Process` object is to combine `Nursery.start` with `run_process`::\n\n       process_object = await nursery.start(run_process, ...)\n\n    This way, `run_process` supervises the process and makes sure that it is\n    cleaned up properly, while optionally checking the return value, feeding\n    it input, and so on.\n\n    If you need more control – for example, because you want to spawn a child\n    process that outlives your program – then another option is to use\n    `trio.lowlevel.open_process`::\n\n       process_object = await trio.lowlevel.open_process(...)\n\n    Attributes:\n      args (str or list): The ``command`` passed at construction time,\n          specifying the process to execute and its arguments.\n      pid (int): The process ID of the child process managed by this object.\n      stdin (trio.abc.SendStream or None): A stream connected to the child's\n          standard input stream: when you write bytes here, they become available\n          for the child to read. Only available if the :class:`Process`\n          was constructed using ``stdin=PIPE``; otherwise this will be None.\n      stdout (trio.abc.ReceiveStream or None): A stream connected to\n          the child's standard output stream: when the child writes to\n          standard output, the written bytes become available for you\n          to read here. Only available if the :class:`Process` was\n          constructed using ``stdout=PIPE``; otherwise this will be None.\n      stderr (trio.abc.ReceiveStream or None): A stream connected to\n          the child's standard error stream: when the child writes to\n          standard error, the written bytes become available for you\n          to read here. Only available if the :class:`Process` was\n          constructed using ``stderr=PIPE``; otherwise this will be None.\n      stdio (trio.StapledStream or None): A stream that sends data to\n          the child's standard input and receives from the child's standard\n          output. Only available if both :attr:`stdin` and :attr:`stdout` are\n          available; otherwise this will be None.\n\n    \"\"\"\n\n    # We're always in binary mode.\n    universal_newlines: Final = False\n    encoding: Final = None\n    errors: Final = None\n\n    # Available for the per-platform wait_child_exiting() implementations\n    # to stash some state; waitid platforms use this to avoid spawning\n    # arbitrarily many threads if wait() keeps getting cancelled.\n    _wait_for_exit_data: object = None\n\n    def __init__(\n        self,\n        popen: subprocess.Popen[bytes],\n        stdin: SendStream | None,\n        stdout: ReceiveStream | None,\n        stderr: ReceiveStream | None,\n    ) -> None:\n        self._proc = popen\n        self.stdin = stdin\n        self.stdout = stdout\n        self.stderr = stderr\n\n        self.stdio: StapledStream[SendStream, ReceiveStream] | None = None\n        if self.stdin is not None and self.stdout is not None:\n            self.stdio = StapledStream(self.stdin, self.stdout)\n\n        self._wait_lock: Lock = Lock()\n\n        self._pidfd: TextIOWrapper | None = None\n        if can_try_pidfd_open:\n            try:\n                fd: int = pidfd_open(self._proc.pid, 0)\n            except OSError:  # pragma: no cover\n                # Well, we tried, but it didn't work (probably because we're\n                # running on an older kernel, or in an older sandbox, that\n                # hasn't been updated to support pidfd_open). We'll fall back\n                # on waitid instead.\n                pass\n            else:\n                # It worked! Wrap the raw fd up in a Python file object to\n                # make sure it'll get closed.\n                # SIM115: open-file-with-context-handler\n                self._pidfd = open(fd)  # noqa: SIM115\n\n        self.args: StrOrBytesPath | Sequence[StrOrBytesPath] = self._proc.args\n        self.pid: int = self._proc.pid\n\n    def __repr__(self) -> str:\n        returncode = self.returncode\n        if returncode is None:\n            status = f\"running with PID {self.pid}\"\n        else:\n            if returncode < 0:\n                status = f\"exited with signal {-returncode}\"\n            else:\n                status = f\"exited with status {returncode}\"\n        return f\"<trio.Process {self.args!r}: {status}>\"\n\n    @property\n    def returncode(self) -> int | None:\n        \"\"\"The exit status of the process (an integer), or ``None`` if it's\n        still running.\n\n        By convention, a return code of zero indicates success.  On\n        UNIX, negative values indicate termination due to a signal,\n        e.g., -11 if terminated by signal 11 (``SIGSEGV``).  On\n        Windows, a process that exits due to a call to\n        :meth:`Process.terminate` will have an exit status of 1.\n\n        Unlike the standard library `subprocess.Popen.returncode`, you don't\n        have to call `poll` or `wait` to update this attribute; it's\n        automatically updated as needed, and will always give you the latest\n        information.\n\n        \"\"\"\n        result = self._proc.poll()\n        if result is not None:\n            self._close_pidfd()\n        return result\n\n    def _close_pidfd(self) -> None:\n        if self._pidfd is not None:\n            trio.lowlevel.notify_closing(self._pidfd.fileno())\n            self._pidfd.close()\n            self._pidfd = None\n\n    async def wait(self) -> int:\n        \"\"\"Block until the process exits.\n\n        Returns:\n          The exit status of the process; see :attr:`returncode`.\n        \"\"\"\n        async with self._wait_lock:\n            if self.poll() is None:\n                if self._pidfd is not None:\n                    with contextlib.suppress(\n                        ClosedResourceError,\n                    ):  # something else (probably a call to poll) already closed the pidfd\n                        await trio.lowlevel.wait_readable(self._pidfd.fileno())\n                else:\n                    await wait_child_exiting(self)\n                # We have to use .wait() here, not .poll(), because on macOS\n                # (and maybe other systems, who knows), there's a race\n                # condition inside the kernel that creates a tiny window where\n                # kqueue reports that the process has exited, but\n                # waitpid(WNOHANG) can't yet reap it. So this .wait() may\n                # actually block for a tiny fraction of a second.\n                self._proc.wait()\n                self._close_pidfd()\n        assert self._proc.returncode is not None\n        return self._proc.returncode\n\n    def poll(self) -> int | None:\n        \"\"\"Returns the exit status of the process (an integer), or ``None`` if\n        it's still running.\n\n        Note that on Trio (unlike the standard library `subprocess.Popen`),\n        ``process.poll()`` and ``process.returncode`` always give the same\n        result. See `returncode` for more details. This method is only\n        included to make it easier to port code from `subprocess`.\n\n        \"\"\"\n        return self.returncode\n\n    def send_signal(self, sig: signal.Signals | int) -> None:\n        \"\"\"Send signal ``sig`` to the process.\n\n        On UNIX, ``sig`` may be any signal defined in the\n        :mod:`signal` module, such as ``signal.SIGINT`` or\n        ``signal.SIGTERM``. On Windows, it may be anything accepted by\n        the standard library :meth:`subprocess.Popen.send_signal`.\n        \"\"\"\n        self._proc.send_signal(sig)\n\n    def terminate(self) -> None:\n        \"\"\"Terminate the process, politely if possible.\n\n        On UNIX, this is equivalent to\n        ``send_signal(signal.SIGTERM)``; by convention this requests\n        graceful termination, but a misbehaving or buggy process might\n        ignore it. On Windows, :meth:`terminate` forcibly terminates the\n        process in the same manner as :meth:`kill`.\n        \"\"\"\n        self._proc.terminate()\n\n    def kill(self) -> None:\n        \"\"\"Immediately terminate the process.\n\n        On UNIX, this is equivalent to\n        ``send_signal(signal.SIGKILL)``.  On Windows, it calls\n        ``TerminateProcess``. In both cases, the process cannot\n        prevent itself from being killed, but the termination will be\n        delivered asynchronously; use :meth:`wait` if you want to\n        ensure the process is actually dead before proceeding.\n        \"\"\"\n        self._proc.kill()\n\n\nasync def _open_process(\n    command: StrOrBytesPath | Sequence[StrOrBytesPath],\n    *,\n    stdin: int | HasFileno | None = None,\n    stdout: int | HasFileno | None = None,\n    stderr: int | HasFileno | None = None,\n    **options: object,\n) -> Process:\n    r\"\"\"Execute a child program in a new process.\n\n    After construction, you can interact with the child process by writing data to its\n    `~trio.Process.stdin` stream (a `~trio.abc.SendStream`), reading data from its\n    `~trio.Process.stdout` and/or `~trio.Process.stderr` streams (both\n    `~trio.abc.ReceiveStream`\\s), sending it signals using `~trio.Process.terminate`,\n    `~trio.Process.kill`, or `~trio.Process.send_signal`, and waiting for it to exit\n    using `~trio.Process.wait`. See `trio.Process` for details.\n\n    Each standard stream is only available if you specify that a pipe should be created\n    for it. For example, if you pass ``stdin=subprocess.PIPE``, you can write to the\n    `~trio.Process.stdin` stream, else `~trio.Process.stdin` will be ``None``.\n\n    Unlike `trio.run_process`, this function doesn't do any kind of automatic\n    management of the child process. It's up to you to implement whatever semantics you\n    want.\n\n    Args:\n      command: The command to run. Typically this is a sequence of strings or\n          bytes such as ``['ls', '-l', 'directory with spaces']``, where the\n          first element names the executable to invoke and the other elements\n          specify its arguments. With ``shell=True`` in the ``**options``, or on\n          Windows, ``command`` can be a string or bytes, which will be parsed\n          following platform-dependent :ref:`quoting rules\n          <subprocess-quoting>`. In all cases ``command`` can be a path or a\n          sequence of paths.\n      stdin: Specifies what the child process's standard input\n          stream should connect to: output written by the parent\n          (``subprocess.PIPE``), nothing (``subprocess.DEVNULL``),\n          or an open file (pass a file descriptor or something whose\n          ``fileno`` method returns one). If ``stdin`` is unspecified,\n          the child process will have the same standard input stream\n          as its parent.\n      stdout: Like ``stdin``, but for the child process's standard output\n          stream.\n      stderr: Like ``stdin``, but for the child process's standard error\n          stream. An additional value ``subprocess.STDOUT`` is supported,\n          which causes the child's standard output and standard error\n          messages to be intermixed on a single standard output stream,\n          attached to whatever the ``stdout`` option says to attach it to.\n      **options: Other :ref:`general subprocess options <subprocess-options>`\n          are also accepted.\n\n    Returns:\n      A new `trio.Process` object.\n\n    Raises:\n      OSError: if the process spawning fails, for example because the\n         specified command could not be found.\n\n    \"\"\"\n    for key in (\"universal_newlines\", \"text\", \"encoding\", \"errors\", \"bufsize\"):\n        if options.get(key):\n            raise TypeError(\n                \"trio.Process only supports communicating over \"\n                f\"unbuffered byte streams; the '{key}' option is not supported\",\n            )\n\n    if os.name == \"posix\":\n        # TODO: how do paths and sequences thereof play with `shell=True`?\n        if isinstance(command, (str, bytes)) and not options.get(\"shell\"):\n            raise TypeError(\n                \"command must be a sequence (not a string or bytes) if \"\n                \"shell=False on UNIX systems\",\n            )\n        if not isinstance(command, (str, bytes)) and options.get(\"shell\"):\n            raise TypeError(\n                \"command must be a string or bytes (not a sequence) if \"\n                \"shell=True on UNIX systems\",\n            )\n\n    trio_stdin: ClosableSendStream | None = None\n    trio_stdout: ClosableReceiveStream | None = None\n    trio_stderr: ClosableReceiveStream | None = None\n    # Close the parent's handle for each child side of a pipe; we want the child to\n    # have the only copy, so that when it exits we can read EOF on our side. The\n    # trio ends of pipes will be transferred to the Process object, which will be\n    # responsible for their lifetime. If process spawning fails, though, we still\n    # want to close them before letting the failure bubble out\n    with ExitStack() as always_cleanup, ExitStack() as cleanup_on_fail:\n        if stdin == subprocess.PIPE:\n            trio_stdin, stdin = create_pipe_to_child_stdin()\n            always_cleanup.callback(os.close, stdin)\n            cleanup_on_fail.callback(trio_stdin.close)\n        if stdout == subprocess.PIPE:\n            trio_stdout, stdout = create_pipe_from_child_output()\n            always_cleanup.callback(os.close, stdout)\n            cleanup_on_fail.callback(trio_stdout.close)\n        if stderr == subprocess.STDOUT:\n            # If we created a pipe for stdout, pass the same pipe for\n            # stderr.  If stdout was some non-pipe thing (DEVNULL or a\n            # given FD), pass the same thing. If stdout was passed as\n            # None, keep stderr as STDOUT to allow subprocess to dup\n            # our stdout. Regardless of which of these is applicable,\n            # don't create a new Trio stream for stderr -- if stdout\n            # is piped, stderr will be intermixed on the stdout stream.\n            if stdout is not None:\n                stderr = stdout\n        elif stderr == subprocess.PIPE:\n            trio_stderr, stderr = create_pipe_from_child_output()\n            always_cleanup.callback(os.close, stderr)\n            cleanup_on_fail.callback(trio_stderr.close)\n\n        popen = await trio.to_thread.run_sync(\n            partial(\n                subprocess.Popen,\n                command,\n                stdin=stdin,\n                stdout=stdout,\n                stderr=stderr,\n                **options,\n            ),\n        )\n        # We did not fail, so dismiss the stack for the trio ends\n        cleanup_on_fail.pop_all()\n\n    return Process._create(popen, trio_stdin, trio_stdout, trio_stderr)\n\n\n# async function missing await\nasync def _windows_deliver_cancel(p: Process) -> None:  # noqa: RUF029\n    try:\n        p.terminate()\n    except OSError as exc:\n        warnings.warn(\n            RuntimeWarning(f\"TerminateProcess on {p!r} failed with: {exc!r}\"),\n            stacklevel=1,\n        )\n\n\nasync def _posix_deliver_cancel(p: Process) -> None:\n    try:\n        p.terminate()\n        await trio.sleep(5)\n        warnings.warn(\n            RuntimeWarning(\n                f\"process {p!r} ignored SIGTERM for 5 seconds. \"\n                \"(Maybe you should pass a custom deliver_cancel?) \"\n                \"Trying SIGKILL.\",\n            ),\n            stacklevel=1,\n        )\n        p.kill()\n    except OSError as exc:\n        warnings.warn(\n            RuntimeWarning(f\"tried to kill process {p!r}, but failed with: {exc!r}\"),\n            stacklevel=1,\n        )\n\n\n# Use a private name, so we can declare platform-specific stubs below.\n# This is also the signature read by Sphinx\nasync def _run_process(\n    command: StrOrBytesPath | Sequence[StrOrBytesPath],\n    *,\n    stdin: bytes | bytearray | memoryview | int | HasFileno | None = b\"\",\n    capture_stdout: bool = False,\n    capture_stderr: bool = False,\n    check: bool = True,\n    deliver_cancel: Callable[[Process], Awaitable[object]] | None = None,\n    task_status: TaskStatus[Process] = trio.TASK_STATUS_IGNORED,\n    **options: object,\n) -> subprocess.CompletedProcess[bytes]:\n    \"\"\"Run ``command`` in a subprocess and wait for it to complete.\n\n    This function can be called in two different ways.\n\n    One option is a direct call, like::\n\n        completed_process_info = await trio.run_process(...)\n\n    In this case, it returns a :class:`subprocess.CompletedProcess` instance\n    describing the results. Use this if you want to treat a process like a\n    function call.\n\n    The other option is to run it as a task using `Nursery.start` – the enhanced version\n    of `~Nursery.start_soon` that lets a task pass back a value during startup::\n\n        process = await nursery.start(trio.run_process, ...)\n\n    In this case, `~Nursery.start` returns a `Process` object that you can use\n    to interact with the process while it's running. Use this if you want to\n    treat a process like a background task.\n\n    Either way, `run_process` makes sure that the process has exited before\n    returning, handles cancellation, optionally checks for errors, and\n    provides some convenient shorthands for dealing with the child's\n    input/output.\n\n    **Input:** `run_process` supports all the same ``stdin=`` arguments as\n    `subprocess.Popen`. In addition, if you simply want to pass in some fixed\n    data, you can pass a plain `bytes` object, and `run_process` will take\n    care of setting up a pipe, feeding in the data you gave, and then sending\n    end-of-file. The default is ``b\"\"``, which means that the child will receive\n    an empty stdin. If you want the child to instead read from the parent's\n    stdin, use ``stdin=None``.\n\n    **Output:** By default, any output produced by the subprocess is\n    passed through to the standard output and error streams of the\n    parent Trio process.\n\n    When calling `run_process` directly, you can capture the subprocess's output by\n    passing ``capture_stdout=True`` to capture the subprocess's standard output, and/or\n    ``capture_stderr=True`` to capture its standard error. Captured data is collected up\n    by Trio into an in-memory buffer, and then provided as the\n    :attr:`~subprocess.CompletedProcess.stdout` and/or\n    :attr:`~subprocess.CompletedProcess.stderr` attributes of the returned\n    :class:`~subprocess.CompletedProcess` object. The value for any stream that was not\n    captured will be ``None``.\n\n    If you want to capture both stdout and stderr while keeping them\n    separate, pass ``capture_stdout=True, capture_stderr=True``.\n\n    If you want to capture both stdout and stderr but mixed together\n    in the order they were printed, use: ``capture_stdout=True, stderr=subprocess.STDOUT``.\n    This directs the child's stderr into its stdout, so the combined\n    output will be available in the `~subprocess.CompletedProcess.stdout`\n    attribute.\n\n    If you're using ``await nursery.start(trio.run_process, ...)`` and want to capture\n    the subprocess's output for further processing, then use ``stdout=subprocess.PIPE``\n    and then make sure to read the data out of the `Process.stdout` stream. If you want\n    to capture stderr separately, use ``stderr=subprocess.PIPE``. If you want to capture\n    both, but mixed together in the correct order, use ``stdout=subprocess.PIPE,\n    stderr=subprocess.STDOUT``.\n\n    **Error checking:** If the subprocess exits with a nonzero status\n    code, indicating failure, :func:`run_process` raises a\n    :exc:`subprocess.CalledProcessError` exception rather than\n    returning normally. The captured outputs are still available as\n    the :attr:`~subprocess.CalledProcessError.stdout` and\n    :attr:`~subprocess.CalledProcessError.stderr` attributes of that\n    exception.  To disable this behavior, so that :func:`run_process`\n    returns normally even if the subprocess exits abnormally, pass ``check=False``.\n\n    Note that this can make the ``capture_stdout`` and ``capture_stderr``\n    arguments useful even when starting `run_process` as a task: if you only\n    care about the output if the process fails, then you can enable capturing\n    and then read the output off of the `~subprocess.CalledProcessError`.\n\n    **Cancellation:** If cancelled, `run_process` sends a termination\n    request to the subprocess, then waits for it to fully exit. The\n    ``deliver_cancel`` argument lets you control how the process is terminated.\n\n    .. note:: `run_process` is intentionally similar to the standard library\n       `subprocess.run`, but some of the defaults are different. Specifically, we\n       default to:\n\n       - ``check=True``, because `\"errors should never pass silently / unless\n         explicitly silenced\" <https://www.python.org/dev/peps/pep-0020/>`__.\n\n       - ``stdin=b\"\"``, because it produces less-confusing results if a subprocess\n         unexpectedly tries to read from stdin.\n\n       To get the `subprocess.run` semantics, use ``check=False, stdin=None``.\n\n    Args:\n      command (list or str): The command to run. Typically this is a\n          sequence of strings such as ``['ls', '-l', 'directory with spaces']``,\n          where the first element names the executable to invoke and the other\n          elements specify its arguments. With ``shell=True`` in the\n          ``**options``, or on Windows, ``command`` may alternatively\n          be a string, which will be parsed following platform-dependent\n          :ref:`quoting rules <subprocess-quoting>`.\n\n      stdin (:obj:`bytes`, subprocess.PIPE, file descriptor, or None): The\n          bytes to provide to the subprocess on its standard input stream, or\n          ``None`` if the subprocess's standard input should come from the\n          same place as the parent Trio process's standard input. As is the\n          case with the :mod:`subprocess` module, you can also pass a file\n          descriptor or an object with a ``fileno()`` method, in which case\n          the subprocess's standard input will come from that file.\n\n          When starting `run_process` as a background task, you can also use\n          ``stdin=subprocess.PIPE``, in which case `Process.stdin` will be a\n          `~trio.abc.SendStream` that you can use to send data to the child.\n\n      capture_stdout (bool): If true, capture the bytes that the subprocess\n          writes to its standard output stream and return them in the\n          `~subprocess.CompletedProcess.stdout` attribute of the returned\n          `subprocess.CompletedProcess` or `subprocess.CalledProcessError`.\n\n      capture_stderr (bool): If true, capture the bytes that the subprocess\n          writes to its standard error stream and return them in the\n          `~subprocess.CompletedProcess.stderr` attribute of the returned\n          `~subprocess.CompletedProcess` or `subprocess.CalledProcessError`.\n\n      check (bool): If false, don't validate that the subprocess exits\n          successfully. You should be sure to check the\n          ``returncode`` attribute of the returned object if you pass\n          ``check=False``, so that errors don't pass silently.\n\n      deliver_cancel (async function or None): If `run_process` is cancelled,\n          then it needs to kill the child process. There are multiple ways to\n          do this, so we let you customize it.\n\n          If you pass None (the default), then the behavior depends on the\n          platform:\n\n          - On Windows, Trio calls ``TerminateProcess``, which should kill the\n            process immediately.\n\n          - On Unix-likes, the default behavior is to send a ``SIGTERM``, wait\n            5 seconds, and send a ``SIGKILL``.\n\n          Alternatively, you can customize this behavior by passing in an\n          arbitrary async function, which will be called with the `Process`\n          object as an argument. For example, the default Unix behavior could\n          be implemented like this::\n\n             async def my_deliver_cancel(process):\n                 process.send_signal(signal.SIGTERM)\n                 await trio.sleep(5)\n                 process.send_signal(signal.SIGKILL)\n\n          When the process actually exits, the ``deliver_cancel`` function\n          will automatically be cancelled – so if the process exits after\n          ``SIGTERM``, then we'll never reach the ``SIGKILL``.\n\n          In any case, `run_process` will always wait for the child process to\n          exit before raising `Cancelled`.\n\n      **options: :func:`run_process` also accepts any :ref:`general subprocess\n          options <subprocess-options>` and passes them on to the\n          :class:`~trio.Process` constructor. This includes the\n          ``stdout`` and ``stderr`` options, which provide additional\n          redirection possibilities such as ``stderr=subprocess.STDOUT``,\n          ``stdout=subprocess.DEVNULL``, or file descriptors.\n\n    Returns:\n\n      When called normally – a `subprocess.CompletedProcess` instance\n      describing the return code and outputs.\n\n      When called via `Nursery.start` – a `trio.Process` instance.\n\n    Raises:\n      UnicodeError: if ``stdin`` is specified as a Unicode string, rather\n          than bytes\n      ValueError: if multiple redirections are specified for the same\n          stream, e.g., both ``capture_stdout=True`` and\n          ``stdout=subprocess.DEVNULL``\n      subprocess.CalledProcessError: if ``check=False`` is not passed\n          and the process exits with a nonzero exit status\n      OSError: if an error is encountered starting or communicating with\n          the process\n      ExceptionGroup: if exceptions occur in ``deliver_cancel``,\n          or when exceptions occur when communicating with the subprocess.\n          If strict_exception_groups is set to false in the global context,\n          which is deprecated, then single exceptions will be collapsed.\n\n    .. note:: The child process runs in the same process group as the parent\n       Trio process, so a Ctrl+C will be delivered simultaneously to both\n       parent and child. If you don't want this behavior, consult your\n       platform's documentation for starting child processes in a different\n       process group.\n\n    \"\"\"\n\n    if isinstance(stdin, str):\n        raise UnicodeError(\"process stdin must be bytes, not str\")\n    if task_status is trio.TASK_STATUS_IGNORED:\n        if stdin is subprocess.PIPE:\n            raise ValueError(\n                \"stdout=subprocess.PIPE is only valid with nursery.start, \"\n                \"since that's the only way to access the pipe; use nursery.start \"\n                \"or pass the data you want to write directly\",\n            )\n        if options.get(\"stdout\") is subprocess.PIPE:\n            raise ValueError(\n                \"stdout=subprocess.PIPE is only valid with nursery.start, \"\n                \"since that's the only way to access the pipe\",\n            )\n        if options.get(\"stderr\") is subprocess.PIPE:\n            raise ValueError(\n                \"stderr=subprocess.PIPE is only valid with nursery.start, \"\n                \"since that's the only way to access the pipe\",\n            )\n    if isinstance(stdin, (bytes, bytearray, memoryview)):\n        input_ = stdin\n        options[\"stdin\"] = subprocess.PIPE\n    else:\n        # stdin should be something acceptable to Process\n        # (None, DEVNULL, a file descriptor, etc) and Process\n        # will raise if it's not\n        input_ = None\n        options[\"stdin\"] = stdin\n\n    if capture_stdout:\n        if \"stdout\" in options:\n            raise ValueError(\"can't specify both stdout and capture_stdout\")\n        options[\"stdout\"] = subprocess.PIPE\n    if capture_stderr:\n        if \"stderr\" in options:\n            raise ValueError(\"can't specify both stderr and capture_stderr\")\n        options[\"stderr\"] = subprocess.PIPE\n\n    if deliver_cancel is None:\n        if os.name == \"nt\":\n            deliver_cancel = _windows_deliver_cancel\n        else:\n            assert os.name == \"posix\"\n            deliver_cancel = _posix_deliver_cancel\n\n    stdout_chunks: list[bytes | bytearray] = []\n    stderr_chunks: list[bytes | bytearray] = []\n\n    async def feed_input(stream: SendStream) -> None:\n        async with stream:\n            try:\n                assert input_ is not None\n                await stream.send_all(input_)\n            except trio.BrokenResourceError:\n                pass\n\n    async def read_output(\n        stream: ReceiveStream,\n        chunks: list[bytes | bytearray],\n    ) -> None:\n        async with stream:\n            async for chunk in stream:\n                chunks.append(chunk)  # noqa: PERF401\n\n    # Opening the process does not need to be inside the nursery, so we put it outside\n    # so any exceptions get directly seen by users.\n    proc = await _open_process(command, **options)  # type: ignore[arg-type]\n    async with trio.open_nursery() as nursery:\n        try:\n            if input_ is not None:\n                assert proc.stdin is not None\n                nursery.start_soon(feed_input, proc.stdin)\n                proc.stdin = None\n                proc.stdio = None\n            if capture_stdout:\n                assert proc.stdout is not None\n                nursery.start_soon(read_output, proc.stdout, stdout_chunks)\n                proc.stdout = None\n                proc.stdio = None\n            if capture_stderr:\n                assert proc.stderr is not None\n                nursery.start_soon(read_output, proc.stderr, stderr_chunks)\n                proc.stderr = None\n            task_status.started(proc)\n            await proc.wait()\n        except BaseException:\n            with trio.CancelScope(shield=True):\n                killer_cscope = trio.CancelScope(shield=True)\n\n                async def killer() -> None:\n                    with killer_cscope:\n                        await deliver_cancel(proc)\n\n                nursery.start_soon(killer)\n                await proc.wait()\n                killer_cscope.cancel(reason=\"trio internal implementation detail\")\n                raise\n\n    stdout = b\"\".join(stdout_chunks) if capture_stdout else None\n    stderr = b\"\".join(stderr_chunks) if capture_stderr else None\n\n    if proc.returncode and check:\n        raise subprocess.CalledProcessError(\n            proc.returncode,\n            proc.args,\n            output=stdout,\n            stderr=stderr,\n        )\n    else:\n        assert proc.returncode is not None\n        return subprocess.CompletedProcess(proc.args, proc.returncode, stdout, stderr)\n\n\n# There's a lot of duplication here because type checkers don't\n# have a good way to represent overloads that differ only\n# slightly. A cheat sheet:\n#\n# - on Windows, command is Union[str, Sequence[str]];\n#   on Unix, command is str if shell=True and Sequence[str] otherwise\n#\n# - on Windows, there are startupinfo and creationflags options;\n#   on Unix, there are preexec_fn, restore_signals, start_new_session,\n#            pass_fds, group (3.9+), extra_groups (3.9+), user (3.9+),\n#            umask (3.9+), pipesize (3.10+), process_group (3.11+)\n#\n# - run_process() has the signature of open_process() plus arguments\n#   capture_stdout, capture_stderr, check, deliver_cancel, the ability\n#   to pass bytes as stdin, and the ability to run in `nursery.start`\n\n\nclass GeneralProcessArgs(TypedDict, total=False):\n    \"\"\"Arguments shared between all runs.\"\"\"\n\n    stdout: int | HasFileno | None\n    stderr: int | HasFileno | None\n    close_fds: bool\n    cwd: StrOrBytesPath | None\n    env: Mapping[str, str] | None\n    executable: StrOrBytesPath | None\n\n\nif TYPE_CHECKING:\n    if sys.platform == \"win32\":\n\n        class WindowsProcessArgs(GeneralProcessArgs, total=False):\n            \"\"\"Arguments shared between all Windows runs.\"\"\"\n\n            shell: bool\n            startupinfo: subprocess.STARTUPINFO | None\n            creationflags: int\n\n        async def open_process(\n            command: StrOrBytesPath | Sequence[StrOrBytesPath],\n            *,\n            stdin: int | HasFileno | None = None,\n            **kwargs: Unpack[WindowsProcessArgs],\n        ) -> trio.Process:\n            r\"\"\"Execute a child program in a new process.\n\n            After construction, you can interact with the child process by writing data to its\n            `~trio.Process.stdin` stream (a `~trio.abc.SendStream`), reading data from its\n            `~trio.Process.stdout` and/or `~trio.Process.stderr` streams (both\n            `~trio.abc.ReceiveStream`\\s), sending it signals using `~trio.Process.terminate`,\n            `~trio.Process.kill`, or `~trio.Process.send_signal`, and waiting for it to exit\n            using `~trio.Process.wait`. See `trio.Process` for details.\n\n            Each standard stream is only available if you specify that a pipe should be created\n            for it. For example, if you pass ``stdin=subprocess.PIPE``, you can write to the\n            `~trio.Process.stdin` stream, else `~trio.Process.stdin` will be ``None``.\n\n            Unlike `trio.run_process`, this function doesn't do any kind of automatic\n            management of the child process. It's up to you to implement whatever semantics you\n            want.\n\n            Args:\n              command (list or str): The command to run. Typically this is a\n                  sequence of strings such as ``['ls', '-l', 'directory with spaces']``,\n                  where the first element names the executable to invoke and the other\n                  elements specify its arguments. With ``shell=True`` in the\n                  ``**options``, or on Windows, ``command`` may alternatively\n                  be a string, which will be parsed following platform-dependent\n                  :ref:`quoting rules <subprocess-quoting>`.\n              stdin: Specifies what the child process's standard input\n                  stream should connect to: output written by the parent\n                  (``subprocess.PIPE``), nothing (``subprocess.DEVNULL``),\n                  or an open file (pass a file descriptor or something whose\n                  ``fileno`` method returns one). If ``stdin`` is unspecified,\n                  the child process will have the same standard input stream\n                  as its parent.\n              stdout: Like ``stdin``, but for the child process's standard output\n                  stream.\n              stderr: Like ``stdin``, but for the child process's standard error\n                  stream. An additional value ``subprocess.STDOUT`` is supported,\n                  which causes the child's standard output and standard error\n                  messages to be intermixed on a single standard output stream,\n                  attached to whatever the ``stdout`` option says to attach it to.\n              **options: Other :ref:`general subprocess options <subprocess-options>`\n                  are also accepted.\n\n            Returns:\n              A new `trio.Process` object.\n\n            Raises:\n              OSError: if the process spawning fails, for example because the\n                 specified command could not be found.\n\n            \"\"\"\n            ...\n\n        async def run_process(\n            command: StrOrBytesPath | Sequence[StrOrBytesPath],\n            *,\n            task_status: TaskStatus[Process] = trio.TASK_STATUS_IGNORED,\n            stdin: bytes | bytearray | memoryview | int | HasFileno | None = None,\n            capture_stdout: bool = False,\n            capture_stderr: bool = False,\n            check: bool = True,\n            deliver_cancel: Callable[[Process], Awaitable[object]] | None = None,\n            **kwargs: Unpack[WindowsProcessArgs],\n        ) -> subprocess.CompletedProcess[bytes]:\n            \"\"\"Run ``command`` in a subprocess and wait for it to complete.\n\n            This function can be called in two different ways.\n\n            One option is a direct call, like::\n\n                completed_process_info = await trio.run_process(...)\n\n            In this case, it returns a :class:`subprocess.CompletedProcess` instance\n            describing the results. Use this if you want to treat a process like a\n            function call.\n\n            The other option is to run it as a task using `Nursery.start` – the enhanced version\n            of `~Nursery.start_soon` that lets a task pass back a value during startup::\n\n                process = await nursery.start(trio.run_process, ...)\n\n            In this case, `~Nursery.start` returns a `Process` object that you can use\n            to interact with the process while it's running. Use this if you want to\n            treat a process like a background task.\n\n            Either way, `run_process` makes sure that the process has exited before\n            returning, handles cancellation, optionally checks for errors, and\n            provides some convenient shorthands for dealing with the child's\n            input/output.\n\n            **Input:** `run_process` supports all the same ``stdin=`` arguments as\n            `subprocess.Popen`. In addition, if you simply want to pass in some fixed\n            data, you can pass a plain `bytes` object, and `run_process` will take\n            care of setting up a pipe, feeding in the data you gave, and then sending\n            end-of-file. The default is ``b\"\"``, which means that the child will receive\n            an empty stdin. If you want the child to instead read from the parent's\n            stdin, use ``stdin=None``.\n\n            **Output:** By default, any output produced by the subprocess is\n            passed through to the standard output and error streams of the\n            parent Trio process.\n\n            When calling `run_process` directly, you can capture the subprocess's output by\n            passing ``capture_stdout=True`` to capture the subprocess's standard output, and/or\n            ``capture_stderr=True`` to capture its standard error. Captured data is collected up\n            by Trio into an in-memory buffer, and then provided as the\n            :attr:`~subprocess.CompletedProcess.stdout` and/or\n            :attr:`~subprocess.CompletedProcess.stderr` attributes of the returned\n            :class:`~subprocess.CompletedProcess` object. The value for any stream that was not\n            captured will be ``None``.\n\n            If you want to capture both stdout and stderr while keeping them\n            separate, pass ``capture_stdout=True, capture_stderr=True``.\n\n            If you want to capture both stdout and stderr but mixed together\n            in the order they were printed, use: ``capture_stdout=True, stderr=subprocess.STDOUT``.\n            This directs the child's stderr into its stdout, so the combined\n            output will be available in the `~subprocess.CompletedProcess.stdout`\n            attribute.\n\n            If you're using ``await nursery.start(trio.run_process, ...)`` and want to capture\n            the subprocess's output for further processing, then use ``stdout=subprocess.PIPE``\n            and then make sure to read the data out of the `Process.stdout` stream. If you want\n            to capture stderr separately, use ``stderr=subprocess.PIPE``. If you want to capture\n            both, but mixed together in the correct order, use ``stdout=subprocess.PIPE,\n            stderr=subprocess.STDOUT``.\n\n            **Error checking:** If the subprocess exits with a nonzero status\n            code, indicating failure, :func:`run_process` raises a\n            :exc:`subprocess.CalledProcessError` exception rather than\n            returning normally. The captured outputs are still available as\n            the :attr:`~subprocess.CalledProcessError.stdout` and\n            :attr:`~subprocess.CalledProcessError.stderr` attributes of that\n            exception.  To disable this behavior, so that :func:`run_process`\n            returns normally even if the subprocess exits abnormally, pass ``check=False``.\n\n            Note that this can make the ``capture_stdout`` and ``capture_stderr``\n            arguments useful even when starting `run_process` as a task: if you only\n            care about the output if the process fails, then you can enable capturing\n            and then read the output off of the `~subprocess.CalledProcessError`.\n\n            **Cancellation:** If cancelled, `run_process` sends a termination\n            request to the subprocess, then waits for it to fully exit. The\n            ``deliver_cancel`` argument lets you control how the process is terminated.\n\n            .. note:: `run_process` is intentionally similar to the standard library\n               `subprocess.run`, but some of the defaults are different. Specifically, we\n               default to:\n\n               - ``check=True``, because `\"errors should never pass silently / unless\n                 explicitly silenced\" <https://www.python.org/dev/peps/pep-0020/>`__.\n\n               - ``stdin=b\"\"``, because it produces less-confusing results if a subprocess\n                 unexpectedly tries to read from stdin.\n\n               To get the `subprocess.run` semantics, use ``check=False, stdin=None``.\n\n            Args:\n              command (list or str): The command to run. Typically this is a\n                  sequence of strings such as ``['ls', '-l', 'directory with spaces']``,\n                  where the first element names the executable to invoke and the other\n                  elements specify its arguments. With ``shell=True`` in the\n                  ``**options``, or on Windows, ``command`` may alternatively\n                  be a string, which will be parsed following platform-dependent\n                  :ref:`quoting rules <subprocess-quoting>`.\n\n              stdin (:obj:`bytes`, subprocess.PIPE, file descriptor, or None): The\n                  bytes to provide to the subprocess on its standard input stream, or\n                  ``None`` if the subprocess's standard input should come from the\n                  same place as the parent Trio process's standard input. As is the\n                  case with the :mod:`subprocess` module, you can also pass a file\n                  descriptor or an object with a ``fileno()`` method, in which case\n                  the subprocess's standard input will come from that file.\n\n                  When starting `run_process` as a background task, you can also use\n                  ``stdin=subprocess.PIPE``, in which case `Process.stdin` will be a\n                  `~trio.abc.SendStream` that you can use to send data to the child.\n\n              capture_stdout (bool): If true, capture the bytes that the subprocess\n                  writes to its standard output stream and return them in the\n                  `~subprocess.CompletedProcess.stdout` attribute of the returned\n                  `subprocess.CompletedProcess` or `subprocess.CalledProcessError`.\n\n              capture_stderr (bool): If true, capture the bytes that the subprocess\n                  writes to its standard error stream and return them in the\n                  `~subprocess.CompletedProcess.stderr` attribute of the returned\n                  `~subprocess.CompletedProcess` or `subprocess.CalledProcessError`.\n\n              check (bool): If false, don't validate that the subprocess exits\n                  successfully. You should be sure to check the\n                  ``returncode`` attribute of the returned object if you pass\n                  ``check=False``, so that errors don't pass silently.\n\n              deliver_cancel (async function or None): If `run_process` is cancelled,\n                  then it needs to kill the child process. There are multiple ways to\n                  do this, so we let you customize it.\n\n                  If you pass None (the default), then the behavior depends on the\n                  platform:\n\n                  - On Windows, Trio calls ``TerminateProcess``, which should kill the\n                    process immediately.\n\n                  - On Unix-likes, the default behavior is to send a ``SIGTERM``, wait\n                    5 seconds, and send a ``SIGKILL``.\n\n                  Alternatively, you can customize this behavior by passing in an\n                  arbitrary async function, which will be called with the `Process`\n                  object as an argument. For example, the default Unix behavior could\n                  be implemented like this::\n\n                     async def my_deliver_cancel(process):\n                         process.send_signal(signal.SIGTERM)\n                         await trio.sleep(5)\n                         process.send_signal(signal.SIGKILL)\n\n                  When the process actually exits, the ``deliver_cancel`` function\n                  will automatically be cancelled – so if the process exits after\n                  ``SIGTERM``, then we'll never reach the ``SIGKILL``.\n\n                  In any case, `run_process` will always wait for the child process to\n                  exit before raising `Cancelled`.\n\n              **options: :func:`run_process` also accepts any :ref:`general subprocess\n                  options <subprocess-options>` and passes them on to the\n                  :class:`~trio.Process` constructor. This includes the\n                  ``stdout`` and ``stderr`` options, which provide additional\n                  redirection possibilities such as ``stderr=subprocess.STDOUT``,\n                  ``stdout=subprocess.DEVNULL``, or file descriptors.\n\n            Returns:\n\n              When called normally – a `subprocess.CompletedProcess` instance\n              describing the return code and outputs.\n\n              When called via `Nursery.start` – a `trio.Process` instance.\n\n            Raises:\n              UnicodeError: if ``stdin`` is specified as a Unicode string, rather\n                  than bytes\n              ValueError: if multiple redirections are specified for the same\n                  stream, e.g., both ``capture_stdout=True`` and\n                  ``stdout=subprocess.DEVNULL``\n              subprocess.CalledProcessError: if ``check=False`` is not passed\n                  and the process exits with a nonzero exit status\n              OSError: if an error is encountered starting or communicating with\n                  the process\n\n            .. note:: The child process runs in the same process group as the parent\n               Trio process, so a Ctrl+C will be delivered simultaneously to both\n               parent and child. If you don't want this behavior, consult your\n               platform's documentation for starting child processes in a different\n               process group.\n\n            \"\"\"\n            ...\n\n    else:  # Unix\n        # pyright doesn't give any error about overloads missing docstrings as they're\n        # overloads. But might still be a problem for other static analyzers / docstring\n        # readers (?)\n\n        class UnixProcessArgs3_10(GeneralProcessArgs, total=False):\n            \"\"\"Arguments shared between all Unix runs.\"\"\"\n\n            preexec_fn: Callable[[], object] | None\n            restore_signals: bool\n            start_new_session: bool\n            pass_fds: Sequence[int]\n\n            # 3.9+\n            group: str | int | None\n            extra_groups: Iterable[str | int] | None\n            user: str | int | None\n            umask: int\n\n            # 3.10+\n            pipesize: int\n\n        class UnixProcessArgs3_11(UnixProcessArgs3_10, total=False):\n            \"\"\"Arguments shared between all Unix runs on 3.11+.\"\"\"\n\n            process_group: int | None\n\n        class UnixRunProcessMixin(TypedDict, total=False):\n            \"\"\"Arguments unique to run_process on Unix.\"\"\"\n\n            task_status: TaskStatus[Process]\n            capture_stdout: bool\n            capture_stderr: bool\n            check: bool\n            deliver_cancel: Callable[[Process], Awaitable[None]] | None\n\n        # TODO: once https://github.com/python/mypy/issues/18692 is\n        #       fixed, move the `UnixRunProcessArgs` definition down.\n        if sys.version_info >= (3, 11):\n            UnixProcessArgs = UnixProcessArgs3_11\n\n            class UnixRunProcessArgs(UnixProcessArgs3_11, UnixRunProcessMixin):\n                \"\"\"Arguments for run_process on Unix with 3.11+\"\"\"\n\n        else:\n            UnixProcessArgs = UnixProcessArgs3_10\n\n            class UnixRunProcessArgs(UnixProcessArgs3_10, UnixRunProcessMixin):\n                \"\"\"Arguments for run_process on Unix with 3.10+\"\"\"\n\n        @overload  # type: ignore[no-overload-impl]\n        async def open_process(\n            command: StrOrBytesPath,\n            *,\n            stdin: int | HasFileno | None = None,\n            shell: Literal[True],\n            **kwargs: Unpack[UnixProcessArgs],\n        ) -> trio.Process: ...\n\n        @overload\n        async def open_process(\n            command: Sequence[StrOrBytesPath],\n            *,\n            stdin: int | HasFileno | None = None,\n            shell: bool = False,\n            **kwargs: Unpack[UnixProcessArgs],\n        ) -> trio.Process: ...\n\n        @overload  # type: ignore[no-overload-impl]\n        async def run_process(\n            command: StrOrBytesPath,\n            *,\n            stdin: bytes | bytearray | memoryview | int | HasFileno | None = b\"\",\n            shell: Literal[True],\n            **kwargs: Unpack[UnixRunProcessArgs],\n        ) -> subprocess.CompletedProcess[bytes]: ...\n\n        @overload\n        async def run_process(\n            command: Sequence[StrOrBytesPath],\n            *,\n            stdin: bytes | bytearray | memoryview | int | HasFileno | None = b\"\",\n            shell: bool = False,\n            **kwargs: Unpack[UnixRunProcessArgs],\n        ) -> subprocess.CompletedProcess[bytes]: ...\n\nelse:\n    # At runtime, use the actual implementations.\n    open_process = _open_process\n    open_process.__name__ = open_process.__qualname__ = \"open_process\"\n\n    run_process = _run_process\n    run_process.__name__ = run_process.__qualname__ = \"run_process\"\n"
  },
  {
    "path": "src/trio/_subprocess_platform/__init__.py",
    "content": "# Platform-specific subprocess bits'n'pieces.\nfrom __future__ import annotations\n\nimport os\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport trio\n\nfrom .. import _core, _subprocess\nfrom .._abc import ReceiveStream, SendStream  # noqa: TC001\n\n_wait_child_exiting_error: ImportError | None = None\n_create_child_pipe_error: ImportError | None = None\n\n\nif TYPE_CHECKING:\n    # internal types for the pipe representations used in type checking only\n    class ClosableSendStream(SendStream):\n        def close(self) -> None: ...\n\n    class ClosableReceiveStream(ReceiveStream):\n        def close(self) -> None: ...\n\n\n# Fallback versions of the functions provided -- implementations\n# per OS are imported atop these at the bottom of the module.\nasync def wait_child_exiting(process: _subprocess.Process) -> None:\n    \"\"\"Block until the child process managed by ``process`` is exiting.\n\n    It is invalid to call this function if the process has already\n    been waited on; that is, ``process.returncode`` must be None.\n\n    When this function returns, it indicates that a call to\n    :meth:`subprocess.Popen.wait` will immediately be able to\n    return the process's exit status. The actual exit status is not\n    consumed by this call, since :class:`~subprocess.Popen` wants\n    to be able to do that itself.\n    \"\"\"\n    raise NotImplementedError from _wait_child_exiting_error  # pragma: no cover\n\n\ndef create_pipe_to_child_stdin() -> tuple[ClosableSendStream, int]:\n    \"\"\"Create a new pipe suitable for sending data from this\n    process to the standard input of a child we're about to spawn.\n\n    Returns:\n      A pair ``(trio_end, subprocess_end)`` where ``trio_end`` is a\n      :class:`~trio.abc.SendStream` and ``subprocess_end`` is\n      something suitable for passing as the ``stdin`` argument of\n      :class:`subprocess.Popen`.\n    \"\"\"\n    raise NotImplementedError from _create_child_pipe_error  # pragma: no cover\n\n\ndef create_pipe_from_child_output() -> tuple[ClosableReceiveStream, int]:\n    \"\"\"Create a new pipe suitable for receiving data into this\n    process from the standard output or error stream of a child\n    we're about to spawn.\n\n    Returns:\n      A pair ``(trio_end, subprocess_end)`` where ``trio_end`` is a\n      :class:`~trio.abc.ReceiveStream` and ``subprocess_end`` is\n      something suitable for passing as the ``stdin`` argument of\n      :class:`subprocess.Popen`.\n    \"\"\"\n    raise NotImplementedError from _create_child_pipe_error  # pragma: no cover\n\n\ntry:\n    if sys.platform == \"win32\":\n        from .windows import wait_child_exiting\n    elif sys.platform != \"linux\" and (TYPE_CHECKING or hasattr(_core, \"wait_kevent\")):\n        from .kqueue import wait_child_exiting\n    else:\n        # as it's an exported symbol, noqa'd\n        from .waitid import wait_child_exiting  # noqa: F401\nexcept ImportError as ex:  # pragma: no cover\n    _wait_child_exiting_error = ex\n\ntry:\n    if TYPE_CHECKING:\n        # Not worth type checking these definitions\n        pass\n\n    elif os.name == \"posix\":\n\n        def create_pipe_to_child_stdin() -> tuple[trio.lowlevel.FdStream, int]:\n            rfd, wfd = os.pipe()\n            return trio.lowlevel.FdStream(wfd), rfd\n\n        def create_pipe_from_child_output() -> tuple[trio.lowlevel.FdStream, int]:\n            rfd, wfd = os.pipe()\n            return trio.lowlevel.FdStream(rfd), wfd\n\n    elif os.name == \"nt\":\n        import msvcrt\n\n        # This isn't exported or documented, but it's also not\n        # underscore-prefixed, and seems kosher to use. The asyncio docs\n        # for 3.5 included an example that imported socketpair from\n        # windows_utils (before socket.socketpair existed on Windows), and\n        # when asyncio.windows_utils.socketpair was removed in 3.7, the\n        # removal was mentioned in the release notes.\n        from asyncio.windows_utils import pipe as windows_pipe\n\n        from .._windows_pipes import PipeReceiveStream, PipeSendStream\n\n        def create_pipe_to_child_stdin() -> tuple[PipeSendStream, int]:\n            # for stdin, we want the write end (our end) to use overlapped I/O\n            rh, wh = windows_pipe(overlapped=(False, True))\n            return PipeSendStream(wh), msvcrt.open_osfhandle(rh, os.O_RDONLY)\n\n        def create_pipe_from_child_output() -> tuple[PipeReceiveStream, int]:\n            # for stdout/err, it's the read end that's overlapped\n            rh, wh = windows_pipe(overlapped=(True, False))\n            return PipeReceiveStream(rh), msvcrt.open_osfhandle(wh, 0)\n\n    else:  # pragma: no cover\n        raise ImportError(\"pipes not implemented on this platform\")\n\nexcept ImportError as ex:  # pragma: no cover\n    _create_child_pipe_error = ex\n"
  },
  {
    "path": "src/trio/_subprocess_platform/kqueue.py",
    "content": "from __future__ import annotations\n\nimport select\nimport sys\nfrom typing import TYPE_CHECKING\n\nfrom .. import _core, _subprocess\n\nassert (sys.platform != \"win32\" and sys.platform != \"linux\") or not TYPE_CHECKING\n\n\nasync def wait_child_exiting(process: _subprocess.Process) -> None:\n    kqueue = _core.current_kqueue()\n    try:\n        from select import KQ_NOTE_EXIT\n    except ImportError:  # pragma: no cover\n        # pypy doesn't define KQ_NOTE_EXIT:\n        # https://bitbucket.org/pypy/pypy/issues/2921/\n        # I verified this value against both Darwin and FreeBSD\n        KQ_NOTE_EXIT = 0x80000000  # type: ignore[misc]\n\n    def make_event(flags: int) -> select.kevent:\n        return select.kevent(\n            process.pid,\n            filter=select.KQ_FILTER_PROC,\n            flags=flags,\n            fflags=KQ_NOTE_EXIT,\n        )\n\n    try:\n        kqueue.control([make_event(select.KQ_EV_ADD | select.KQ_EV_ONESHOT)], 0)\n    except ProcessLookupError:  # pragma: no cover\n        # This can supposedly happen if the process is in the process\n        # of exiting, and it can even be the case that kqueue says the\n        # process doesn't exist before waitpid(WNOHANG) says it hasn't\n        # exited yet. See the discussion in https://chromium.googlesource.com/\n        # chromium/src/base/+/master/process/kill_mac.cc .\n        # We haven't actually seen this error occur since we added\n        # locking to prevent multiple calls to wait_child_exiting()\n        # for the same process simultaneously, but given the explanation\n        # in Chromium it seems we should still keep the check.\n        return\n\n    def abort(_: _core.RaiseCancelT) -> _core.Abort:\n        kqueue.control([make_event(select.KQ_EV_DELETE)], 0)\n        return _core.Abort.SUCCEEDED\n\n    await _core.wait_kevent(process.pid, select.KQ_FILTER_PROC, abort)\n"
  },
  {
    "path": "src/trio/_subprocess_platform/waitid.py",
    "content": "import errno\nimport math\nimport os\nimport sys\nfrom typing import TYPE_CHECKING\n\nfrom .. import _core, _subprocess\nfrom .._sync import CapacityLimiter, Event\nfrom .._threads import to_thread_run_sync\n\nassert (sys.platform != \"win32\" and sys.platform != \"darwin\") or not TYPE_CHECKING\n\ntry:\n    from os import waitid\n\n    def sync_wait_reapable(pid: int) -> None:\n        waitid(os.P_PID, pid, os.WEXITED | os.WNOWAIT)\n\nexcept ImportError:\n    # pypy doesn't define os.waitid so we need to pull it out ourselves\n    # using cffi: https://bitbucket.org/pypy/pypy/issues/2922/\n    import cffi\n\n    waitid_ffi = cffi.FFI()\n\n    # Believe it or not, siginfo_t starts with fields in the\n    # same layout on both Linux and Darwin. The Linux structure\n    # is bigger so that's what we use to size `pad`; while\n    # there are a few extra fields in there, most of it is\n    # true padding which would not be written by the syscall.\n    waitid_ffi.cdef(\n        \"\"\"\ntypedef struct siginfo_s {\n    int si_signo;\n    int si_errno;\n    int si_code;\n    int si_pid;\n    int si_uid;\n    int si_status;\n    int pad[26];\n} siginfo_t;\nint waitid(int idtype, int id, siginfo_t* result, int options);\n\"\"\",\n    )\n    waitid_cffi = waitid_ffi.dlopen(None).waitid  # type: ignore[attr-defined]\n\n    def sync_wait_reapable(pid: int) -> None:\n        P_PID = 1\n        WEXITED = 0x00000004\n        if sys.platform == \"darwin\":  # pragma: no cover\n            # waitid() is not exposed on Python on Darwin but does\n            # work through CFFI; note that we typically won't get\n            # here since Darwin also defines kqueue\n            WNOWAIT = 0x00000020\n        else:\n            WNOWAIT = 0x01000000\n        result = waitid_ffi.new(\"siginfo_t *\")\n        while waitid_cffi(P_PID, pid, result, WEXITED | WNOWAIT) < 0:\n            got_errno = waitid_ffi.errno\n            if got_errno == errno.EINTR:\n                continue\n            raise OSError(got_errno, os.strerror(got_errno))\n\n\n# adapted from\n# https://github.com/python-trio/trio/issues/4#issuecomment-398967572\n\nwaitid_limiter = CapacityLimiter(math.inf)\n\n\nasync def _waitid_system_task(pid: int, event: Event) -> None:\n    \"\"\"Spawn a thread that waits for ``pid`` to exit, then wake any tasks\n    that were waiting on it.\n    \"\"\"\n    # abandon_on_cancel=True: if this task is cancelled, then we abandon the\n    # thread to keep running waitpid in the background. Since this is\n    # always run as a system task, this will only happen if the whole\n    # call to trio.run is shutting down.\n\n    try:\n        await to_thread_run_sync(\n            sync_wait_reapable,\n            pid,\n            abandon_on_cancel=True,\n            limiter=waitid_limiter,\n        )\n    except OSError:\n        # If waitid fails, waitpid will fail too, so it still makes\n        # sense to wake up the callers of wait_process_exiting(). The\n        # most likely reason for this error in practice is a child\n        # exiting when wait() is not possible because SIGCHLD is\n        # ignored.\n        pass\n    finally:\n        event.set()\n\n\nasync def wait_child_exiting(process: \"_subprocess.Process\") -> None:\n    # Logic of this function:\n    # - The first time we get called, we create an Event and start\n    #   an instance of _waitid_system_task that will set the Event\n    #   when waitid() completes. If that Event is set before\n    #   we get cancelled, we're good.\n    # - Otherwise, a following call after the cancellation must\n    #   reuse the Event created during the first call, lest we\n    #   create an arbitrary number of threads waiting on the same\n    #   process.\n\n    if process._wait_for_exit_data is None:\n        process._wait_for_exit_data = event = Event()\n        _core.spawn_system_task(_waitid_system_task, process.pid, event)\n    assert isinstance(process._wait_for_exit_data, Event)\n    await process._wait_for_exit_data.wait()\n"
  },
  {
    "path": "src/trio/_subprocess_platform/windows.py",
    "content": "from typing import TYPE_CHECKING\n\nfrom .._wait_for_object import WaitForSingleObject\n\nif TYPE_CHECKING:\n    from .. import _subprocess\n\n\nasync def wait_child_exiting(process: \"_subprocess.Process\") -> None:\n    # _handle is not in Popen stubs, though it is present on Windows.\n    await WaitForSingleObject(int(process._proc._handle))  # type: ignore[attr-defined]\n"
  },
  {
    "path": "src/trio/_sync.py",
    "content": "from __future__ import annotations\n\nimport math\nfrom typing import TYPE_CHECKING, Literal, Protocol, TypeVar\n\nimport attrs\n\nimport trio\n\nfrom . import _core\nfrom ._core import (\n    Abort,\n    ParkingLot,\n    RaiseCancelT,\n    add_parking_lot_breaker,\n    enable_ki_protection,\n    remove_parking_lot_breaker,\n)\nfrom ._deprecate import warn_deprecated\nfrom ._util import final\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from types import TracebackType\n\n    from typing_extensions import deprecated\n\n    from ._core import Task\n    from ._core._parking_lot import ParkingLotStatistics\nelse:\n    T = TypeVar(\"T\")\n\n    def deprecated(\n        message: str,\n        /,\n        *,\n        category: type[Warning] | None = DeprecationWarning,\n        stacklevel: int = 1,\n    ) -> Callable[[T], T]:\n        def wrapper(f: T) -> T:\n            return f\n\n        return wrapper\n\n\n@attrs.frozen\nclass EventStatistics:\n    \"\"\"An object containing debugging information.\n\n    Currently the following fields are defined:\n\n    * ``tasks_waiting``: The number of tasks blocked on this event's\n      :meth:`trio.Event.wait` method.\n\n    \"\"\"\n\n    tasks_waiting: int\n\n\n@final\n@attrs.define(repr=False, eq=False)\nclass Event:\n    \"\"\"A waitable boolean value useful for inter-task synchronization,\n    inspired by :class:`threading.Event`.\n\n    An event object has an internal boolean flag, representing whether\n    the event has happened yet. The flag is initially False, and the\n    :meth:`wait` method waits until the flag is True. If the flag is\n    already True, then :meth:`wait` returns immediately. (If the event has\n    already happened, there's nothing to wait for.) The :meth:`set` method\n    sets the flag to True, and wakes up any waiters.\n\n    This behavior is useful because it helps avoid race conditions and\n    lost wakeups: it doesn't matter whether :meth:`set` gets called just\n    before or after :meth:`wait`. If you want a lower-level wakeup\n    primitive that doesn't have this protection, consider :class:`Condition`\n    or :class:`trio.lowlevel.ParkingLot`.\n\n    .. note:: Unlike `threading.Event`, `trio.Event` has no\n       `~threading.Event.clear` method. In Trio, once an `Event` has happened,\n       it cannot un-happen. If you need to represent a series of events,\n       consider creating a new `Event` object for each one (they're cheap!),\n       or other synchronization methods like :ref:`channels <channels>` or\n       `trio.lowlevel.ParkingLot`.\n\n    \"\"\"\n\n    _tasks: set[Task] = attrs.field(factory=set, init=False)\n    _flag: bool = attrs.field(default=False, init=False)\n\n    def is_set(self) -> bool:\n        \"\"\"Return the current value of the internal flag.\"\"\"\n        return self._flag\n\n    @enable_ki_protection\n    def set(self) -> None:\n        \"\"\"Set the internal flag value to True, and wake any waiting tasks.\"\"\"\n        if not self._flag:\n            self._flag = True\n            for task in self._tasks:\n                _core.reschedule(task)\n            self._tasks.clear()\n\n    async def wait(self) -> None:\n        \"\"\"Block until the internal flag value becomes True.\n\n        If it's already True, then this method returns immediately.\n\n        \"\"\"\n        if self._flag:\n            await trio.lowlevel.checkpoint()\n        else:\n            task = _core.current_task()\n            self._tasks.add(task)\n\n            def abort_fn(_: RaiseCancelT) -> Abort:\n                self._tasks.remove(task)\n                return _core.Abort.SUCCEEDED\n\n            await _core.wait_task_rescheduled(abort_fn)\n\n    def statistics(self) -> EventStatistics:\n        \"\"\"Return an object containing debugging information.\n\n        Currently the following fields are defined:\n\n        * ``tasks_waiting``: The number of tasks blocked on this event's\n          :meth:`wait` method.\n\n        \"\"\"\n        return EventStatistics(tasks_waiting=len(self._tasks))\n\n    @deprecated(\n        \"trio.Event.__bool__ is deprecated since Trio 0.31.0; use trio.Event.is_set instead (https://github.com/python-trio/trio/issues/3238)\",\n        stacklevel=2,\n    )\n    def __bool__(self) -> Literal[True]:\n        \"\"\"Return True and raise warning.\"\"\"\n        warn_deprecated(\n            self.__bool__,\n            \"0.31.0\",\n            issue=3238,\n            instead=self.is_set,\n        )\n        return True\n\n\nclass _HasAcquireRelease(Protocol):\n    \"\"\"Only classes with acquire() and release() can use the mixin's implementations.\"\"\"\n\n    async def acquire(self) -> object: ...\n\n    def release(self) -> object: ...\n\n\nclass AsyncContextManagerMixin:\n    @enable_ki_protection\n    async def __aenter__(self: _HasAcquireRelease) -> None:\n        await self.acquire()\n\n    @enable_ki_protection\n    async def __aexit__(\n        self: _HasAcquireRelease,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self.release()\n\n\n@attrs.frozen\nclass CapacityLimiterStatistics:\n    \"\"\"An object containing debugging information.\n\n    Currently the following fields are defined:\n\n    * ``borrowed_tokens``: The number of tokens currently borrowed from\n      the sack.\n    * ``total_tokens``: The total number of tokens in the sack. Usually\n      this will be larger than ``borrowed_tokens``, but it's possibly for\n      it to be smaller if :attr:`trio.CapacityLimiter.total_tokens` was recently decreased.\n    * ``borrowers``: A list of all tasks or other entities that currently\n      hold a token.\n    * ``tasks_waiting``: The number of tasks blocked on this\n      :class:`CapacityLimiter`\\'s :meth:`trio.CapacityLimiter.acquire` or\n      :meth:`trio.CapacityLimiter.acquire_on_behalf_of` methods.\n\n    \"\"\"\n\n    borrowed_tokens: int\n    total_tokens: int | float\n    borrowers: list[Task | object]\n    tasks_waiting: int\n\n\n# Can be a generic type with a default of Task if/when PEP 696 is released\n# and implemented in type checkers. Making it fully generic would currently\n# introduce a lot of unnecessary hassle.\n@final\nclass CapacityLimiter(AsyncContextManagerMixin):\n    \"\"\"An object for controlling access to a resource with limited capacity.\n\n    Sometimes you need to put a limit on how many tasks can do something at\n    the same time. For example, you might want to use some threads to run\n    multiple blocking I/O operations in parallel... but if you use too many\n    threads at once, then your system can become overloaded and it'll actually\n    make things slower. One popular solution is to impose a policy like \"run\n    up to 40 threads at the same time, but no more\". But how do you implement\n    a policy like this?\n\n    That's what :class:`CapacityLimiter` is for. You can think of a\n    :class:`CapacityLimiter` object as a sack that starts out holding some fixed\n    number of tokens::\n\n       limit = trio.CapacityLimiter(40)\n\n    Then tasks can come along and borrow a token out of the sack::\n\n       # Borrow a token:\n       async with limit:\n           # We are holding a token!\n           await perform_expensive_operation()\n       # Exiting the 'async with' block puts the token back into the sack\n\n    And crucially, if you try to borrow a token but the sack is empty, then\n    you have to wait for another task to finish what it's doing and put its\n    token back first before you can take it and continue.\n\n    Another way to think of it: a :class:`CapacityLimiter` is like a sofa with a\n    fixed number of seats, and if they're all taken then you have to wait for\n    someone to get up before you can sit down.\n\n    By default, :func:`trio.to_thread.run_sync` uses a\n    :class:`CapacityLimiter` to limit the number of threads running at once;\n    see `trio.to_thread.current_default_thread_limiter` for details.\n\n    If you're familiar with semaphores, then you can think of this as a\n    restricted semaphore that's specialized for one common use case, with\n    additional error checking. For a more traditional semaphore, see\n    :class:`Semaphore`.\n\n    .. note::\n\n       Don't confuse this with the `\"leaky bucket\"\n       <https://en.wikipedia.org/wiki/Leaky_bucket>`__ or `\"token bucket\"\n       <https://en.wikipedia.org/wiki/Token_bucket>`__ algorithms used to\n       limit bandwidth usage on networks. The basic idea of using tokens to\n       track a resource limit is similar, but this is a very simple sack where\n       tokens aren't automatically created or destroyed over time; they're\n       just borrowed and then put back.\n\n    \"\"\"\n\n    # total_tokens would ideally be int|Literal[math.inf] - but that's not valid typing\n    def __init__(self, total_tokens: int | float) -> None:  # noqa: PYI041\n        self._lot = ParkingLot()\n        self._borrowers: set[Task | object] = set()\n        # Maps tasks attempting to acquire -> borrower, to handle on-behalf-of\n        self._pending_borrowers: dict[Task, Task | object] = {}\n        # invoke the property setter for validation\n        self.total_tokens: int | float = total_tokens\n        assert self._total_tokens == total_tokens\n\n    def __repr__(self) -> str:\n        return f\"<trio.CapacityLimiter at {id(self):#x}, {len(self._borrowers)}/{self._total_tokens} with {len(self._lot)} waiting>\"\n\n    @property\n    def total_tokens(self) -> int | float:\n        \"\"\"The total capacity available.\n\n        You can change :attr:`total_tokens` by assigning to this attribute. If\n        you make it larger, then the appropriate number of waiting tasks will\n        be woken immediately to take the new tokens. If you decrease\n        total_tokens below the number of tasks that are currently using the\n        resource, then all current tasks will be allowed to finish as normal,\n        but no new tasks will be allowed in until the total number of tasks\n        drops below the new total_tokens.\n\n        \"\"\"\n        return self._total_tokens\n\n    @total_tokens.setter\n    def total_tokens(self, new_total_tokens: int | float) -> None:  # noqa: PYI041\n        if not isinstance(new_total_tokens, int) and new_total_tokens != math.inf:\n            raise TypeError(\"total_tokens must be an int or math.inf\")\n        if new_total_tokens < 0:\n            raise ValueError(\"total_tokens must be >= 0\")\n        self._total_tokens = new_total_tokens\n        self._wake_waiters()\n\n    def _wake_waiters(self) -> None:\n        available = self._total_tokens - len(self._borrowers)\n        for woken in self._lot.unpark(count=available):\n            self._borrowers.add(self._pending_borrowers.pop(woken))\n\n    @property\n    def borrowed_tokens(self) -> int:\n        \"\"\"The amount of capacity that's currently in use.\"\"\"\n        return len(self._borrowers)\n\n    @property\n    def available_tokens(self) -> int | float:\n        \"\"\"The amount of capacity that's available to use.\"\"\"\n        return self.total_tokens - self.borrowed_tokens\n\n    @enable_ki_protection\n    def acquire_nowait(self) -> None:\n        \"\"\"Borrow a token from the sack, without blocking.\n\n        Raises:\n          WouldBlock: if no tokens are available.\n          RuntimeError: if the current task already holds one of this sack's\n              tokens.\n\n        \"\"\"\n        self.acquire_on_behalf_of_nowait(trio.lowlevel.current_task())\n\n    @enable_ki_protection\n    def acquire_on_behalf_of_nowait(self, borrower: Task | object) -> None:\n        \"\"\"Borrow a token from the sack on behalf of ``borrower``, without\n        blocking.\n\n        Args:\n          borrower: A :class:`trio.lowlevel.Task` or arbitrary opaque object\n             used to record who is borrowing this token. This is used by\n             :func:`trio.to_thread.run_sync` to allow threads to \"hold\n             tokens\", with the intention in the future of using it to `allow\n             deadlock detection and other useful things\n             <https://github.com/python-trio/trio/issues/182>`__\n\n        Raises:\n          WouldBlock: if no tokens are available.\n          RuntimeError: if ``borrower`` already holds one of this sack's\n              tokens.\n\n        \"\"\"\n        if borrower in self._borrowers:\n            raise RuntimeError(\n                \"this borrower is already holding one of this CapacityLimiter's tokens\",\n            )\n        if len(self._borrowers) < self._total_tokens and not self._lot:\n            self._borrowers.add(borrower)\n        else:\n            raise trio.WouldBlock\n\n    @enable_ki_protection\n    async def acquire(self) -> None:\n        \"\"\"Borrow a token from the sack, blocking if necessary.\n\n        Raises:\n          RuntimeError: if the current task already holds one of this sack's\n              tokens.\n\n        \"\"\"\n        await self.acquire_on_behalf_of(trio.lowlevel.current_task())\n\n    @enable_ki_protection\n    async def acquire_on_behalf_of(self, borrower: Task | object) -> None:\n        \"\"\"Borrow a token from the sack on behalf of ``borrower``, blocking if\n        necessary.\n\n        Args:\n          borrower: A :class:`trio.lowlevel.Task` or arbitrary opaque object\n             used to record who is borrowing this token; see\n             :meth:`acquire_on_behalf_of_nowait` for details.\n\n        Raises:\n          RuntimeError: if ``borrower`` task already holds one of this sack's\n             tokens.\n\n        \"\"\"\n        await trio.lowlevel.checkpoint_if_cancelled()\n        try:\n            self.acquire_on_behalf_of_nowait(borrower)\n        except trio.WouldBlock:\n            task = trio.lowlevel.current_task()\n            self._pending_borrowers[task] = borrower\n            try:\n                await self._lot.park()\n            except trio.Cancelled:\n                self._pending_borrowers.pop(task)\n                raise\n        else:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n\n    @enable_ki_protection\n    def release(self) -> None:\n        \"\"\"Put a token back into the sack.\n\n        Raises:\n          RuntimeError: if the current task has not acquired one of this\n              sack's tokens.\n\n        \"\"\"\n        self.release_on_behalf_of(trio.lowlevel.current_task())\n\n    @enable_ki_protection\n    def release_on_behalf_of(self, borrower: Task | object) -> None:\n        \"\"\"Put a token back into the sack on behalf of ``borrower``.\n\n        Raises:\n          RuntimeError: if the given borrower has not acquired one of this\n              sack's tokens.\n\n        \"\"\"\n        if borrower not in self._borrowers:\n            raise RuntimeError(\n                \"this borrower isn't holding any of this CapacityLimiter's tokens\",\n            )\n        self._borrowers.remove(borrower)\n        self._wake_waiters()\n\n    def statistics(self) -> CapacityLimiterStatistics:\n        \"\"\"Return an object containing debugging information.\n\n        Currently the following fields are defined:\n\n        * ``borrowed_tokens``: The number of tokens currently borrowed from\n          the sack.\n        * ``total_tokens``: The total number of tokens in the sack. Usually\n          this will be larger than ``borrowed_tokens``, but it's possibly for\n          it to be smaller if :attr:`total_tokens` was recently decreased.\n        * ``borrowers``: A list of all tasks or other entities that currently\n          hold a token.\n        * ``tasks_waiting``: The number of tasks blocked on this\n          :class:`CapacityLimiter`\\'s :meth:`acquire` or\n          :meth:`acquire_on_behalf_of` methods.\n\n        \"\"\"\n        return CapacityLimiterStatistics(\n            borrowed_tokens=len(self._borrowers),\n            total_tokens=self._total_tokens,\n            # Use a list instead of a frozenset just in case we start to allow\n            # one borrower to hold multiple tokens in the future\n            borrowers=list(self._borrowers),\n            tasks_waiting=len(self._lot),\n        )\n\n\n@final\nclass Semaphore(AsyncContextManagerMixin):\n    \"\"\"A `semaphore <https://en.wikipedia.org/wiki/Semaphore_(programming)>`__.\n\n    A semaphore holds an integer value, which can be incremented by\n    calling :meth:`release` and decremented by calling :meth:`acquire` – but\n    the value is never allowed to drop below zero. If the value is zero, then\n    :meth:`acquire` will block until someone calls :meth:`release`.\n\n    If you're looking for a :class:`Semaphore` to limit the number of tasks\n    that can access some resource simultaneously, then consider using a\n    :class:`CapacityLimiter` instead.\n\n    This object's interface is similar to, but different from, that of\n    :class:`threading.Semaphore`.\n\n    A :class:`Semaphore` object can be used as an async context manager; it\n    blocks on entry but not on exit.\n\n    Args:\n      initial_value (int): A non-negative integer giving semaphore's initial\n        value.\n      max_value (int or None): If given, makes this a \"bounded\" semaphore that\n        raises an error if the value is about to exceed the given\n        ``max_value``.\n\n    \"\"\"\n\n    def __init__(self, initial_value: int, *, max_value: int | None = None) -> None:\n        if not isinstance(initial_value, int):\n            raise TypeError(\"initial_value must be an int\")\n        if initial_value < 0:\n            raise ValueError(\"initial value must be >= 0\")\n        if max_value is not None:\n            if not isinstance(max_value, int):\n                raise TypeError(\"max_value must be None or an int\")\n            if max_value < initial_value:\n                raise ValueError(\"max_values must be >= initial_value\")\n\n        # Invariants:\n        # bool(self._lot) implies self._value == 0\n        # (or equivalently: self._value > 0 implies not self._lot)\n        self._lot = trio.lowlevel.ParkingLot()\n        self._value = initial_value\n        self._max_value = max_value\n\n    def __repr__(self) -> str:\n        if self._max_value is None:\n            max_value_str = \"\"\n        else:\n            max_value_str = f\", max_value={self._max_value}\"\n        return f\"<trio.Semaphore({self._value}{max_value_str}) at {id(self):#x}>\"\n\n    @property\n    def value(self) -> int:\n        \"\"\"The current value of the semaphore.\"\"\"\n        return self._value\n\n    @property\n    def max_value(self) -> int | None:\n        \"\"\"The maximum allowed value. May be None to indicate no limit.\"\"\"\n        return self._max_value\n\n    @enable_ki_protection\n    def acquire_nowait(self) -> None:\n        \"\"\"Attempt to decrement the semaphore value, without blocking.\n\n        Raises:\n          WouldBlock: if the value is zero.\n\n        \"\"\"\n        if self._value > 0:\n            assert not self._lot\n            self._value -= 1\n        else:\n            raise trio.WouldBlock\n\n    @enable_ki_protection\n    async def acquire(self) -> None:\n        \"\"\"Decrement the semaphore value, blocking if necessary to avoid\n        letting it drop below zero.\n\n        \"\"\"\n        await trio.lowlevel.checkpoint_if_cancelled()\n        try:\n            self.acquire_nowait()\n        except trio.WouldBlock:\n            await self._lot.park()\n        else:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n\n    @enable_ki_protection\n    def release(self) -> None:\n        \"\"\"Increment the semaphore value, possibly waking a task blocked in\n        :meth:`acquire`.\n\n        Raises:\n          ValueError: if incrementing the value would cause it to exceed\n              :attr:`max_value`.\n\n        \"\"\"\n        if self._lot:\n            assert self._value == 0\n            self._lot.unpark(count=1)\n        else:\n            if self._max_value is not None and self._value == self._max_value:\n                raise ValueError(\"semaphore released too many times\")\n            self._value += 1\n\n    def statistics(self) -> ParkingLotStatistics:\n        \"\"\"Return an object containing debugging information.\n\n        Currently the following fields are defined:\n\n        * ``tasks_waiting``: The number of tasks blocked on this semaphore's\n          :meth:`acquire` method.\n\n        \"\"\"\n        return self._lot.statistics()\n\n\n@attrs.frozen\nclass LockStatistics:\n    \"\"\"An object containing debugging information for a Lock.\n\n    Currently the following fields are defined:\n\n    * ``locked`` (boolean): indicating whether the lock is held.\n    * ``owner``: the :class:`trio.lowlevel.Task` currently holding the lock,\n      or None if the lock is not held.\n    * ``tasks_waiting`` (int): The number of tasks blocked on this lock's\n      :meth:`trio.Lock.acquire` method.\n\n    \"\"\"\n\n    locked: bool\n    owner: Task | None\n    tasks_waiting: int\n\n\n@attrs.define(eq=False, repr=False, slots=False)\nclass _LockImpl(AsyncContextManagerMixin):\n    _lot: ParkingLot = attrs.field(factory=ParkingLot, init=False)\n    _owner: Task | None = attrs.field(default=None, init=False)\n\n    def __repr__(self) -> str:\n        if self.locked():\n            s1 = \"locked\"\n            s2 = f\" with {len(self._lot)} waiters\"\n        else:\n            s1 = \"unlocked\"\n            s2 = \"\"\n        return f\"<{s1} {self.__class__.__name__} object at {id(self):#x}{s2}>\"\n\n    def locked(self) -> bool:\n        \"\"\"Check whether the lock is currently held.\n\n        Returns:\n          bool: True if the lock is held, False otherwise.\n\n        \"\"\"\n        return self._owner is not None\n\n    @enable_ki_protection\n    def acquire_nowait(self) -> None:\n        \"\"\"Attempt to acquire the lock, without blocking.\n\n        Raises:\n          WouldBlock: if the lock is held.\n\n        \"\"\"\n\n        task = trio.lowlevel.current_task()\n        if self._owner is task:\n            raise RuntimeError(\"attempt to re-acquire an already held Lock\")\n        elif self._owner is None and not self._lot:\n            # No-one owns it\n            self._owner = task\n            add_parking_lot_breaker(task, self._lot)\n        else:\n            raise trio.WouldBlock\n\n    @enable_ki_protection\n    async def acquire(self) -> None:\n        \"\"\"Acquire the lock, blocking if necessary.\n\n        Raises:\n          BrokenResourceError: if the owner of the lock exits without releasing.\n        \"\"\"\n        await trio.lowlevel.checkpoint_if_cancelled()\n        try:\n            self.acquire_nowait()\n        except trio.WouldBlock:\n            try:\n                # NOTE: it's important that the contended acquire path is just\n                # \"_lot.park()\", because that's how Condition.wait() acquires the\n                # lock as well.\n                await self._lot.park()\n            except trio.BrokenResourceError:\n                raise trio.BrokenResourceError(\n                    f\"Owner of this lock exited without releasing: {self._owner}\",\n                ) from None\n        else:\n            await trio.lowlevel.cancel_shielded_checkpoint()\n\n    @enable_ki_protection\n    def release(self) -> None:\n        \"\"\"Release the lock.\n\n        Raises:\n          RuntimeError: if the calling task does not hold the lock.\n\n        \"\"\"\n        task = trio.lowlevel.current_task()\n        if task is not self._owner:\n            raise RuntimeError(\"can't release a Lock you don't own\")\n        remove_parking_lot_breaker(self._owner, self._lot)\n        if self._lot:\n            (self._owner,) = self._lot.unpark(count=1)\n            add_parking_lot_breaker(self._owner, self._lot)\n        else:\n            self._owner = None\n\n    def statistics(self) -> LockStatistics:\n        \"\"\"Return an object containing debugging information.\n\n        Currently the following fields are defined:\n\n        * ``locked``: boolean indicating whether the lock is held.\n        * ``owner``: the :class:`trio.lowlevel.Task` currently holding the lock,\n          or None if the lock is not held.\n        * ``tasks_waiting``: The number of tasks blocked on this lock's\n          :meth:`acquire` method.\n\n        \"\"\"\n        return LockStatistics(\n            locked=self.locked(),\n            owner=self._owner,\n            tasks_waiting=len(self._lot),\n        )\n\n\n@final\nclass Lock(_LockImpl):\n    \"\"\"A classic `mutex\n    <https://en.wikipedia.org/wiki/Lock_(computer_science)>`__.\n\n    This is a non-reentrant, single-owner lock. Unlike\n    :class:`threading.Lock`, only the owner of the lock is allowed to release\n    it.\n\n    A :class:`Lock` object can be used as an async context manager; it\n    blocks on entry but not on exit.\n\n    \"\"\"\n\n\n@final\nclass StrictFIFOLock(_LockImpl):\n    r\"\"\"A variant of :class:`Lock` where tasks are guaranteed to acquire the\n    lock in strict first-come-first-served order.\n\n    An example of when this is useful is if you're implementing something like\n    :class:`trio.SSLStream` or an HTTP/2 server using `h2\n    <https://hyper-h2.readthedocs.io/>`__, where you have multiple concurrent\n    tasks that are interacting with a shared state machine, and at\n    unpredictable moments the state machine requests that a chunk of data be\n    sent over the network. (For example, when using h2 simply reading incoming\n    data can occasionally `create outgoing data to send\n    <https://http2.github.io/http2-spec/#PING>`__.) The challenge is to make\n    sure that these chunks are sent in the correct order, without being\n    garbled.\n\n    One option would be to use a regular :class:`Lock`, and wrap it around\n    every interaction with the state machine::\n\n        # This approach is sometimes workable but often sub-optimal; see below\n        async with lock:\n            state_machine.do_something()\n            if state_machine.has_data_to_send():\n                await conn.sendall(state_machine.get_data_to_send())\n\n    But this can be problematic. If you're using h2 then *usually* reading\n    incoming data doesn't create the need to send any data, so we don't want\n    to force every task that tries to read from the network to sit and wait\n    a potentially long time for ``sendall`` to finish. And in some situations\n    this could even potentially cause a deadlock, if the remote peer is\n    waiting for you to read some data before it accepts the data you're\n    sending.\n\n    :class:`StrictFIFOLock` provides an alternative. We can rewrite our\n    example like::\n\n        # Note: no awaits between when we start using the state machine and\n        # when we block to take the lock!\n        state_machine.do_something()\n        if state_machine.has_data_to_send():\n            # Notice that we fetch the data to send out of the state machine\n            # *before* sleeping, so that other tasks won't see it.\n            chunk = state_machine.get_data_to_send()\n            async with strict_fifo_lock:\n                await conn.sendall(chunk)\n\n    First we do all our interaction with the state machine in a single\n    scheduling quantum (notice there are no ``await``\\s in there), so it's\n    automatically atomic with respect to other tasks. And then if and only if\n    we have data to send, we get in line to send it – and\n    :class:`StrictFIFOLock` guarantees that each task will send its data in\n    the same order that the state machine generated it.\n\n    Currently, :class:`StrictFIFOLock` is identical to :class:`Lock`,\n    but (a) this may not always be true in the future, especially if Trio ever\n    implements `more sophisticated scheduling policies\n    <https://github.com/python-trio/trio/issues/32>`__, and (b) the above code\n    is relying on a pretty subtle property of its lock. Using a\n    :class:`StrictFIFOLock` acts as an executable reminder that you're relying\n    on this property.\n\n    \"\"\"\n\n\n@attrs.frozen\nclass ConditionStatistics:\n    r\"\"\"An object containing debugging information for a Condition.\n\n    Currently the following fields are defined:\n\n    * ``tasks_waiting`` (int): The number of tasks blocked on this condition's\n      :meth:`trio.Condition.wait` method.\n    * ``lock_statistics``: The result of calling the underlying\n      :class:`Lock`\\s  :meth:`~Lock.statistics` method.\n\n    \"\"\"\n\n    tasks_waiting: int\n    lock_statistics: LockStatistics\n\n\n@final\nclass Condition(AsyncContextManagerMixin):\n    \"\"\"A classic `condition variable\n    <https://en.wikipedia.org/wiki/Monitor_(synchronization)>`__, similar to\n    :class:`threading.Condition`.\n\n    A :class:`Condition` object can be used as an async context manager to\n    acquire the underlying lock; it blocks on entry but not on exit.\n\n    Args:\n      lock (Lock): the lock object to use. If given, must be a\n          :class:`trio.Lock`. If None, a new :class:`Lock` will be allocated\n          and used.\n\n    \"\"\"\n\n    def __init__(self, lock: Lock | None = None) -> None:\n        if lock is None:\n            lock = Lock()\n        if type(lock) is not Lock:\n            raise TypeError(\"lock must be a trio.Lock\")\n        self._lock = lock\n        self._lot = trio.lowlevel.ParkingLot()\n\n    def locked(self) -> bool:\n        \"\"\"Check whether the underlying lock is currently held.\n\n        Returns:\n          bool: True if the lock is held, False otherwise.\n\n        \"\"\"\n        return self._lock.locked()\n\n    def acquire_nowait(self) -> None:\n        \"\"\"Attempt to acquire the underlying lock, without blocking.\n\n        Raises:\n          WouldBlock: if the lock is currently held.\n\n        \"\"\"\n        return self._lock.acquire_nowait()\n\n    async def acquire(self) -> None:\n        \"\"\"Acquire the underlying lock, blocking if necessary.\n\n        Raises:\n          BrokenResourceError: if the owner of the underlying lock exits without releasing.\n        \"\"\"\n        await self._lock.acquire()\n\n    def release(self) -> None:\n        \"\"\"Release the underlying lock.\"\"\"\n        self._lock.release()\n\n    @enable_ki_protection\n    async def wait(self) -> None:\n        \"\"\"Wait for another task to call :meth:`notify` or\n        :meth:`notify_all`.\n\n        When calling this method, you must hold the lock. It releases the lock\n        while waiting, and then re-acquires it before waking up.\n\n        There is a subtlety with how this method interacts with cancellation:\n        when cancelled it will block to re-acquire the lock before raising\n        :exc:`Cancelled`. This may cause cancellation to be less prompt than\n        expected. The advantage is that it makes code like this work::\n\n           async with condition:\n               await condition.wait()\n\n        If we didn't re-acquire the lock before waking up, and :meth:`wait`\n        were cancelled here, then we'd crash in ``condition.__aexit__`` when\n        we tried to release the lock we no longer held.\n\n        Raises:\n          RuntimeError: if the calling task does not hold the lock.\n          BrokenResourceError: if the owner of the lock exits without releasing, when attempting to re-acquire.\n\n        \"\"\"\n        if trio.lowlevel.current_task() is not self._lock._owner:\n            raise RuntimeError(\"must hold the lock to wait\")\n        self.release()\n        # NOTE: we go to sleep on self._lot, but we'll wake up on\n        # self._lock._lot. That's all that's required to acquire a Lock.\n        try:\n            await self._lot.park()\n        except:\n            with trio.CancelScope(shield=True):\n                await self.acquire()\n            raise\n\n    def notify(self, n: int = 1) -> None:\n        \"\"\"Wake one or more tasks that are blocked in :meth:`wait`.\n\n        Args:\n          n (int): The number of tasks to wake.\n\n        Raises:\n          RuntimeError: if the calling task does not hold the lock.\n\n        \"\"\"\n        if trio.lowlevel.current_task() is not self._lock._owner:\n            raise RuntimeError(\"must hold the lock to notify\")\n        self._lot.repark(self._lock._lot, count=n)\n\n    def notify_all(self) -> None:\n        \"\"\"Wake all tasks that are currently blocked in :meth:`wait`.\n\n        Raises:\n          RuntimeError: if the calling task does not hold the lock.\n\n        \"\"\"\n        if trio.lowlevel.current_task() is not self._lock._owner:\n            raise RuntimeError(\"must hold the lock to notify\")\n        self._lot.repark_all(self._lock._lot)\n\n    def statistics(self) -> ConditionStatistics:\n        r\"\"\"Return an object containing debugging information.\n\n        Currently the following fields are defined:\n\n        * ``tasks_waiting``: The number of tasks blocked on this condition's\n          :meth:`wait` method.\n        * ``lock_statistics``: The result of calling the underlying\n          :class:`Lock`\\s  :meth:`~Lock.statistics` method.\n\n        \"\"\"\n        return ConditionStatistics(\n            tasks_waiting=len(self._lot),\n            lock_statistics=self._lock.statistics(),\n        )\n"
  },
  {
    "path": "src/trio/_tests/__init__.py",
    "content": ""
  },
  {
    "path": "src/trio/_tests/_check_type_completeness.json",
    "content": "{\n    \"Darwin\": [\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.send_all\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.wait_send_all_might_not_block\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.receive_some\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.close\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.aclose\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.fileno\\\"\"\n    ],\n    \"Linux\": [\n        \"No docstring found for class \\\"trio._core._io_epoll._EpollStatistics\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.send_all\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.wait_send_all_might_not_block\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.receive_some\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.close\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.aclose\\\"\",\n        \"No docstring found for function \\\"trio._unix_pipes.FdStream.fileno\\\"\"\n    ],\n    \"Windows\": [],\n    \"all\": [\n        \"No docstring found for class \\\"trio.MemoryReceiveChannel\\\"\",\n        \"No docstring found for class \\\"trio._channel.MemoryReceiveChannel\\\"\",\n        \"No docstring found for class \\\"trio.MemoryChannelStatistics\\\"\",\n        \"No docstring found for class \\\"trio._channel.MemoryChannelStatistics\\\"\",\n        \"No docstring found for class \\\"trio.MemorySendChannel\\\"\",\n        \"No docstring found for class \\\"trio._channel.MemorySendChannel\\\"\",\n        \"No docstring found for class \\\"trio._core._run.Task\\\"\",\n        \"No docstring found for class \\\"trio._socket.SocketType\\\"\",\n        \"No docstring found for function \\\"trio._highlevel_socket.SocketStream.send_all\\\"\",\n        \"No docstring found for function \\\"trio._highlevel_socket.SocketStream.wait_send_all_might_not_block\\\"\",\n        \"No docstring found for function \\\"trio._highlevel_socket.SocketStream.send_eof\\\"\",\n        \"No docstring found for function \\\"trio._highlevel_socket.SocketStream.receive_some\\\"\",\n        \"No docstring found for function \\\"trio._highlevel_socket.SocketStream.aclose\\\"\",\n        \"No docstring found for function \\\"trio._subprocess.HasFileno.fileno\\\"\",\n        \"No docstring found for class \\\"trio._sync.AsyncContextManagerMixin\\\"\",\n        \"No docstring found for function \\\"trio._sync._HasAcquireRelease.acquire\\\"\",\n        \"No docstring found for function \\\"trio._sync._HasAcquireRelease.release\\\"\",\n        \"No docstring found for class \\\"trio._sync._LockImpl\\\"\",\n        \"No docstring found for class \\\"trio._core._local._NoValue\\\"\",\n        \"No docstring found for class \\\"trio._core._local.RunVarToken\\\"\",\n        \"No docstring found for class \\\"trio.lowlevel.RunVarToken\\\"\",\n        \"No docstring found for class \\\"trio.lowlevel.Task\\\"\",\n        \"No docstring found for class \\\"trio.socket.SocketType\\\"\",\n        \"No docstring found for class \\\"trio.socket.gaierror\\\"\",\n        \"No docstring found for class \\\"trio.socket.herror\\\"\",\n        \"No docstring found for function \\\"trio._core._mock_clock.MockClock.start_clock\\\"\",\n        \"No docstring found for function \\\"trio._core._mock_clock.MockClock.current_time\\\"\",\n        \"No docstring found for function \\\"trio._core._mock_clock.MockClock.deadline_to_sleep_time\\\"\"\n    ]\n}\n"
  },
  {
    "path": "src/trio/_tests/check_type_completeness.py",
    "content": "#!/usr/bin/env python3\n\"\"\"This is a file that wraps calls to `pyright --verifytypes`, achieving two things:\n1. give an error if docstrings are missing.\n    pyright will give a number of missing docstrings, and error messages, but not exit with a non-zero value.\n2. filter out specific errors we don't care about.\n    this is largely due to 1, but also because Trio does some very complex stuff and --verifytypes has few to no ways of ignoring specific errors.\n\nIf this check is giving you false alarms, you can ignore them by adding logic to `has_docstring_at_runtime`, in the main loop in `check_type`, or by updating the json file.\n\"\"\"\n\nfrom __future__ import annotations\n\n# this file is not run as part of the tests, instead it's run standalone from check.sh\nimport argparse\nimport json\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nimport trio\nimport trio.testing\n\n# not needed if everything is working, but if somebody does something to generate\n# tons of errors, we can be nice and stop them from getting 3*tons of output\nprinted_diagnostics: set[str] = set()\n\n\n# TODO: consider checking manually without `--ignoreexternal`, and/or\n# removing it from the below call later on.\ndef run_pyright(platform: str) -> subprocess.CompletedProcess[bytes]:\n    return subprocess.run(\n        [\n            \"pyright\",\n            # Specify a platform and version to keep imported modules consistent.\n            f\"--pythonplatform={platform}\",\n            \"--pythonversion=3.10\",\n            \"--verifytypes=trio\",\n            \"--outputjson\",\n            \"--ignoreexternal\",\n        ],\n        capture_output=True,\n    )\n\n\ndef has_docstring_at_runtime(name: str) -> bool:\n    \"\"\"Pyright gives us an object identifier of xx.yy.zz\n    This function tries to decompose that into its constituent parts, such that we\n    can resolve it, in order to check whether it has a `__doc__` at runtime and\n    verifytypes misses it because we're doing overly fancy stuff.\n    \"\"\"\n    # This assert is solely for stopping isort from removing our imports of trio & trio.testing\n    # It could also be done with isort:skip, but that'd also disable import sorting and the like.\n    assert trio.testing is not None\n\n    # figure out what part of the name is the module, so we can \"import\" it\n    name_parts = name.split(\".\")\n    assert name_parts[0] == \"trio\"\n    if name_parts[1] == \"tests\":\n        return True\n\n    # traverse down the remaining identifiers with getattr\n    obj = trio\n    try:\n        for obj_name in name_parts[1:]:\n            obj = getattr(obj, obj_name)\n    except AttributeError as exc:\n        # asynciowrapper does funky getattr stuff\n        if \"AsyncIOWrapper\" in str(exc) or name in (\n            # Symbols not existing on all platforms, so we can't dynamically inspect them.\n            # Manually confirmed to have docstrings but pyright doesn't see them due to\n            # export shenanigans. TODO: actually manually confirm that.\n            # In theory we could verify these at runtime, probably by running the script separately\n            # on separate platforms. It might also be a decent idea to work the other way around,\n            # a la test_static_tool_sees_class_members\n            # darwin\n            \"trio.lowlevel.current_kqueue\",\n            \"trio.lowlevel.monitor_kevent\",\n            \"trio.lowlevel.wait_kevent\",\n            \"trio._core._io_kqueue._KqueueStatistics\",\n            # windows\n            \"trio._socket.SocketType.share\",\n            \"trio._core._io_windows._WindowsStatistics\",\n            \"trio._core._windows_cffi.Handle\",\n            \"trio.lowlevel.current_iocp\",\n            \"trio.lowlevel.monitor_completion_key\",\n            \"trio.lowlevel.readinto_overlapped\",\n            \"trio.lowlevel.register_with_iocp\",\n            \"trio.lowlevel.wait_overlapped\",\n            \"trio.lowlevel.write_overlapped\",\n            \"trio.lowlevel.WaitForSingleObject\",\n            \"trio.socket.fromshare\",\n            # linux\n            # this test will fail on linux, but I don't develop on linux. So the next\n            # person to do so is very welcome to open a pull request and populate with\n            # objects\n            # TODO: these are erroring on all platforms, why?\n            \"trio._highlevel_generic.StapledStream.send_stream\",\n            \"trio._highlevel_generic.StapledStream.receive_stream\",\n            \"trio._ssl.SSLStream.transport_stream\",\n            \"trio._file_io._HasFileNo\",\n            \"trio._file_io._HasFileNo.fileno\",\n        ):\n            return True\n\n        else:\n            print(\n                f\"Pyright sees {name} at runtime, but unable to getattr({obj.__name__}, {obj_name}).\",\n                file=sys.stderr,\n            )\n            return False\n    return bool(obj.__doc__)\n\n\ndef check_type(\n    platform: str,\n    full_diagnostics_file: Path | None,\n    expected_errors: list[object],\n) -> list[object]:\n    # convince isort we use the trio import\n    assert trio is not None\n\n    # run pyright, load output into json\n    res = run_pyright(platform)\n    current_result = json.loads(res.stdout)\n\n    if res.stderr:\n        print(res.stderr, file=sys.stderr)\n\n    if full_diagnostics_file:\n        with open(full_diagnostics_file, \"a\") as f:\n            json.dump(current_result, f, sort_keys=True, indent=4)\n\n    errors = []\n\n    for symbol in current_result[\"typeCompleteness\"][\"symbols\"]:\n        diagnostics = symbol[\"diagnostics\"]\n        name = symbol[\"name\"]\n        for diagnostic in diagnostics:\n            message = diagnostic[\"message\"]\n            if name in (\n                \"trio._path.PosixPath\",\n                \"trio._path.WindowsPath\",\n            ) and message.startswith(\"Type of base class \"):\n                continue\n\n            if name.startswith(\"trio._path.Path\"):\n                if message.startswith(\"No docstring found for\"):\n                    continue\n                if message.startswith(\n                    \"Type is missing type annotation and could be inferred differently by type checkers\",\n                ):\n                    continue\n\n            # ignore errors about missing docstrings if they're available at runtime\n            if message.startswith(\"No docstring found for\"):\n                if has_docstring_at_runtime(symbol[\"name\"]):\n                    continue\n            else:\n                # Missing docstring messages include the name of the object.\n                # Other errors don't, so we add it.\n                message = f\"{name}: {message}\"\n            if message not in expected_errors and message not in printed_diagnostics:\n                print(f\"new error: {message}\", file=sys.stderr)\n            errors.append(message)\n            printed_diagnostics.add(message)\n\n        continue\n\n    return errors\n\n\ndef main(args: argparse.Namespace) -> int:\n    if args.full_diagnostics_file:\n        full_diagnostics_file = Path(args.full_diagnostics_file)\n        full_diagnostics_file.write_text(\"\")\n    else:\n        full_diagnostics_file = None\n\n    errors_by_platform_file = Path(__file__).parent / \"_check_type_completeness.json\"\n    if errors_by_platform_file.exists():\n        with open(errors_by_platform_file) as f:\n            errors_by_platform = json.load(f)\n    else:\n        errors_by_platform = {\"Linux\": [], \"Windows\": [], \"Darwin\": [], \"all\": []}\n\n    changed = False\n    for platform in \"Linux\", \"Windows\", \"Darwin\":\n        platform_errors = errors_by_platform[platform] + errors_by_platform[\"all\"]\n        print(\"*\" * 20, f\"\\nChecking {platform}...\")\n        errors = check_type(platform, full_diagnostics_file, platform_errors)\n\n        new_errors = [e for e in errors if e not in platform_errors]\n        missing_errors = [e for e in platform_errors if e not in errors]\n\n        if new_errors:\n            print(\n                f\"New errors introduced in `pyright --verifytypes`. Fix them, or ignore them by modifying {errors_by_platform_file}, either manually or with '--overwrite-file'.\",\n                file=sys.stderr,\n            )\n            changed = True\n        if missing_errors:\n            print(\n                f\"Congratulations, you have resolved existing errors! Please remove them from {errors_by_platform_file}, either manually or with '--overwrite-file'.\",\n                file=sys.stderr,\n            )\n            changed = True\n            print(missing_errors, file=sys.stderr)\n\n        errors_by_platform[platform] = errors\n    print(\"*\" * 20)\n\n    # cut down the size of the json file by a lot, and make it easier to parse for\n    # humans, by moving errors that appear on all platforms to a separate category\n    errors_by_platform[\"all\"] = []\n    for e in errors_by_platform[\"Linux\"].copy():\n        if e in errors_by_platform[\"Darwin\"] and e in errors_by_platform[\"Windows\"]:\n            for platform in \"Linux\", \"Windows\", \"Darwin\":\n                errors_by_platform[platform].remove(e)\n            errors_by_platform[\"all\"].append(e)\n\n    if changed and args.overwrite_file:\n        with open(errors_by_platform_file, \"w\") as f:\n            json.dump(errors_by_platform, f, indent=4, sort_keys=True)\n            # newline at end of file\n            f.write(\"\\n\")\n\n    # True -> 1 -> non-zero exit value -> error\n    return changed\n\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\n    \"--overwrite-file\",\n    action=\"store_true\",\n    default=False,\n    help=\"Use this flag to overwrite the current stored results. Either in CI together with a diff check, or to avoid having to manually correct it.\",\n)\nparser.add_argument(\n    \"--full-diagnostics-file\",\n    type=Path,\n    default=None,\n    help=\"Use this for debugging, it will dump the output of all three pyright runs by platform into this file.\",\n)\nargs = parser.parse_args()\n\nassert __name__ == \"__main__\", \"This script should be run standalone\"\nsys.exit(main(args))\n"
  },
  {
    "path": "src/trio/_tests/module_with_deprecations.py",
    "content": "regular = \"hi\"\n\nimport sys\n\nfrom .. import _deprecate\n\n_deprecate.deprecate_attributes(\n    __name__,\n    {\n        \"dep1\": _deprecate.DeprecatedAttribute(\"value1\", \"1.1\", issue=1),\n        \"dep2\": _deprecate.DeprecatedAttribute(\n            \"value2\",\n            \"1.2\",\n            issue=1,\n            instead=\"instead-string\",\n        ),\n    },\n)\n\nthis_mod = sys.modules[__name__]\nassert this_mod.regular == \"hi\"\nassert \"dep1\" not in globals()\n"
  },
  {
    "path": "src/trio/_tests/pytest_plugin.py",
    "content": "from __future__ import annotations\n\nimport inspect\nfrom typing import NoReturn\n\nimport pytest\n\nfrom ..testing import MockClock, trio_test\n\nRUN_SLOW = True\nSKIP_OPTIONAL_IMPORTS = False\n\n\ndef pytest_addoption(parser: pytest.Parser) -> None:\n    parser.addoption(\"--run-slow\", action=\"store_true\", help=\"run slow tests\")\n    parser.addoption(\n        \"--skip-optional-imports\",\n        action=\"store_true\",\n        help=\"skip tests that rely on libraries not required by trio itself\",\n    )\n\n\ndef pytest_configure(config: pytest.Config) -> None:\n    global RUN_SLOW\n    RUN_SLOW = config.getoption(\"--run-slow\", default=True)\n    global SKIP_OPTIONAL_IMPORTS\n    SKIP_OPTIONAL_IMPORTS = config.getoption(\"--skip-optional-imports\", default=False)\n\n\n@pytest.fixture\ndef mock_clock() -> MockClock:\n    return MockClock()\n\n\n@pytest.fixture\ndef autojump_clock() -> MockClock:\n    return MockClock(autojump_threshold=0)\n\n\n# FIXME: split off into a package (or just make part of Trio's public\n# interface?), with config file to enable? and I guess a mark option too; I\n# guess it's useful with the class- and file-level marking machinery (where\n# the raw @trio_test decorator isn't enough).\n@pytest.hookimpl(tryfirst=True)\ndef pytest_pyfunc_call(pyfuncitem: pytest.Function) -> None:\n    if inspect.iscoroutinefunction(pyfuncitem.obj):\n        pyfuncitem.obj = trio_test(pyfuncitem.obj)\n\n\ndef skip_if_optional_else_raise(error: ImportError) -> NoReturn:\n    if SKIP_OPTIONAL_IMPORTS:\n        pytest.skip(error.msg, allow_module_level=True)\n    else:  # pragma: no cover\n        raise error\n"
  },
  {
    "path": "src/trio/_tests/test_abc.py",
    "content": "from __future__ import annotations\n\nimport attrs\nimport pytest\n\nfrom .. import abc as tabc\nfrom ..lowlevel import Task\n\n\ndef test_instrument_implements_hook_methods() -> None:\n    attrs = {\n        \"before_run\": (),\n        \"after_run\": (),\n        \"task_spawned\": (Task,),\n        \"task_scheduled\": (Task,),\n        \"before_task_step\": (Task,),\n        \"after_task_step\": (Task,),\n        \"task_exited\": (Task,),\n        \"before_io_wait\": (3.3,),\n        \"after_io_wait\": (3.3,),\n    }\n\n    mayonnaise = tabc.Instrument()\n\n    for method_name, args in attrs.items():\n        assert hasattr(mayonnaise, method_name)\n        method = getattr(mayonnaise, method_name)\n        assert callable(method)\n        method(*args)\n\n\nasync def test_AsyncResource_defaults() -> None:\n    @attrs.define(slots=False)\n    class MyAR(tabc.AsyncResource):\n        record: list[str] = attrs.Factory(list)\n\n        async def aclose(self) -> None:\n            self.record.append(\"ac\")\n\n    async with MyAR() as myar:\n        assert isinstance(myar, MyAR)\n        assert myar.record == []\n\n    assert myar.record == [\"ac\"]\n\n\ndef test_abc_generics() -> None:\n    # Pythons below 3.5.2 had a typing.Generic that would throw\n    # errors when instantiating or subclassing a parameterized\n    # version of a class with any __slots__. This is why RunVar\n    # (which has slots) is not generic. This tests that\n    # the generic ABCs are fine, because while they are slotted\n    # they don't actually define any slots.\n\n    class SlottedChannel(tabc.SendChannel[tabc.Stream]):\n        __slots__ = (\"x\",)\n\n        def send_nowait(self, value: object) -> None:\n            raise RuntimeError\n\n        async def send(self, value: object) -> None:\n            raise RuntimeError  # pragma: no cover\n\n        def clone(self) -> None:\n            raise RuntimeError  # pragma: no cover\n\n        async def aclose(self) -> None:\n            pass  # pragma: no cover\n\n    channel = SlottedChannel()\n    with pytest.raises(RuntimeError):\n        channel.send_nowait(None)\n"
  },
  {
    "path": "src/trio/_tests/test_channel.py",
    "content": "from __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nimport trio\nfrom trio import EndOfChannel, as_safe_channel, open_memory_channel\n\nfrom ..testing import assert_checkpoints, wait_all_tasks_blocked\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import ExceptionGroup\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator\n\n\nasync def test_channel() -> None:\n    with pytest.raises(TypeError):\n        open_memory_channel(1.0)\n    with pytest.raises(ValueError, match=r\"^max_buffer_size must be >= 0$\"):\n        open_memory_channel(-1)\n\n    s, r = open_memory_channel[int | str | None](2)\n    repr(s)  # smoke test\n    repr(r)  # smoke test\n\n    s.send_nowait(1)\n    with assert_checkpoints():\n        await s.send(2)\n    with pytest.raises(trio.WouldBlock):\n        s.send_nowait(None)\n\n    with assert_checkpoints():\n        assert await r.receive() == 1\n    assert r.receive_nowait() == 2\n    with pytest.raises(trio.WouldBlock):\n        r.receive_nowait()\n\n    s.send_nowait(\"last\")\n    await s.aclose()\n    with pytest.raises(trio.ClosedResourceError):\n        await s.send(\"too late\")\n    with pytest.raises(trio.ClosedResourceError):\n        s.send_nowait(\"too late\")\n    with pytest.raises(trio.ClosedResourceError):\n        s.clone()\n    await s.aclose()\n\n    assert r.receive_nowait() == \"last\"\n    with pytest.raises(EndOfChannel):\n        await r.receive()\n    await r.aclose()\n    with pytest.raises(trio.ClosedResourceError):\n        await r.receive()\n    with pytest.raises(trio.ClosedResourceError):\n        r.receive_nowait()\n    await r.aclose()\n\n\nasync def test_553(autojump_clock: trio.abc.Clock) -> None:\n    s, r = open_memory_channel[str](1)\n    with trio.move_on_after(10) as timeout_scope:\n        await r.receive()\n    assert timeout_scope.cancelled_caught\n    await s.send(\"Test for PR #553\")\n\n\nasync def test_channel_multiple_producers() -> None:\n    async def producer(send_channel: trio.MemorySendChannel[int], i: int) -> None:\n        # We close our handle when we're done with it\n        async with send_channel:\n            for j in range(3 * i, 3 * (i + 1)):\n                await send_channel.send(j)\n\n    send_channel, receive_channel = open_memory_channel[int](0)\n    async with trio.open_nursery() as nursery:\n        # We hand out clones to all the new producers, and then close the\n        # original.\n        async with send_channel:\n            for i in range(10):\n                nursery.start_soon(producer, send_channel.clone(), i)\n\n        got = [value async for value in receive_channel]\n\n        got.sort()\n        assert got == list(range(30))\n\n\nasync def test_channel_multiple_consumers() -> None:\n    successful_receivers = set()\n    received = []\n\n    async def consumer(receive_channel: trio.MemoryReceiveChannel[int], i: int) -> None:\n        async for value in receive_channel:\n            successful_receivers.add(i)\n            received.append(value)\n\n    async with trio.open_nursery() as nursery:\n        send_channel, receive_channel = trio.open_memory_channel[int](1)\n        async with send_channel:\n            for i in range(5):\n                nursery.start_soon(consumer, receive_channel, i)\n            await wait_all_tasks_blocked()\n            for i in range(10):\n                await send_channel.send(i)\n\n    assert successful_receivers == set(range(5))\n    assert len(received) == 10\n    assert set(received) == set(range(10))\n\n\nasync def test_close_basics() -> None:\n    async def send_block(\n        s: trio.MemorySendChannel[None],\n        expect: type[BaseException],\n    ) -> None:\n        with pytest.raises(expect):\n            await s.send(None)\n\n    # closing send -> other send gets ClosedResourceError\n    s, r = open_memory_channel[None](0)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(send_block, s, trio.ClosedResourceError)\n        await wait_all_tasks_blocked()\n        await s.aclose()\n\n    # and it's persistent\n    with pytest.raises(trio.ClosedResourceError):\n        s.send_nowait(None)\n    with pytest.raises(trio.ClosedResourceError):\n        await s.send(None)\n\n    # and receive gets EndOfChannel\n    with pytest.raises(EndOfChannel):\n        r.receive_nowait()\n    with pytest.raises(EndOfChannel):\n        await r.receive()\n\n    # closing receive -> send gets BrokenResourceError\n    s, r = open_memory_channel[None](0)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(send_block, s, trio.BrokenResourceError)\n        await wait_all_tasks_blocked()\n        await r.aclose()\n\n    # and it's persistent\n    with pytest.raises(trio.BrokenResourceError):\n        s.send_nowait(None)\n    with pytest.raises(trio.BrokenResourceError):\n        await s.send(None)\n\n    # closing receive -> other receive gets ClosedResourceError\n    async def receive_block(r: trio.MemoryReceiveChannel[int]) -> None:\n        with pytest.raises(trio.ClosedResourceError):\n            await r.receive()\n\n    _s2, r2 = open_memory_channel[int](0)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(receive_block, r2)\n        await wait_all_tasks_blocked()\n        await r2.aclose()\n\n    # and it's persistent\n    with pytest.raises(trio.ClosedResourceError):\n        r2.receive_nowait()\n    with pytest.raises(trio.ClosedResourceError):\n        await r2.receive()\n\n\nasync def test_close_sync() -> None:\n    async def send_block(\n        s: trio.MemorySendChannel[None],\n        expect: type[BaseException],\n    ) -> None:\n        with pytest.raises(expect):\n            await s.send(None)\n\n    # closing send -> other send gets ClosedResourceError\n    s, r = open_memory_channel[None](0)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(send_block, s, trio.ClosedResourceError)\n        await wait_all_tasks_blocked()\n        s.close()\n\n    # and it's persistent\n    with pytest.raises(trio.ClosedResourceError):\n        s.send_nowait(None)\n    with pytest.raises(trio.ClosedResourceError):\n        await s.send(None)\n\n    # and receive gets EndOfChannel\n    with pytest.raises(EndOfChannel):\n        r.receive_nowait()\n    with pytest.raises(EndOfChannel):\n        await r.receive()\n\n    # closing receive -> send gets BrokenResourceError\n    s, r = open_memory_channel[None](0)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(send_block, s, trio.BrokenResourceError)\n        await wait_all_tasks_blocked()\n        r.close()\n\n    # and it's persistent\n    with pytest.raises(trio.BrokenResourceError):\n        s.send_nowait(None)\n    with pytest.raises(trio.BrokenResourceError):\n        await s.send(None)\n\n    # closing receive -> other receive gets ClosedResourceError\n    async def receive_block(r: trio.MemoryReceiveChannel[None]) -> None:\n        with pytest.raises(trio.ClosedResourceError):\n            await r.receive()\n\n    s, r = open_memory_channel[None](0)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(receive_block, r)\n        await wait_all_tasks_blocked()\n        r.close()\n\n    # and it's persistent\n    with pytest.raises(trio.ClosedResourceError):\n        r.receive_nowait()\n    with pytest.raises(trio.ClosedResourceError):\n        await r.receive()\n\n\nasync def test_receive_channel_clone_and_close() -> None:\n    s, r = open_memory_channel[None](10)\n\n    r2 = r.clone()\n    r3 = r.clone()\n\n    s.send_nowait(None)\n    await r.aclose()\n    with r2:\n        pass\n\n    with pytest.raises(trio.ClosedResourceError):\n        r.clone()\n\n    with pytest.raises(trio.ClosedResourceError):\n        r2.clone()\n\n    # Can still send, r3 is still open\n    s.send_nowait(None)\n\n    await r3.aclose()\n\n    # But now the receiver is really closed\n    with pytest.raises(trio.BrokenResourceError):\n        s.send_nowait(None)\n\n\nasync def test_close_multiple_send_handles() -> None:\n    # With multiple send handles, closing one handle only wakes senders on\n    # that handle, but others can continue just fine\n    s1, r = open_memory_channel[str](0)\n    s2 = s1.clone()\n\n    async def send_will_close() -> None:\n        with pytest.raises(trio.ClosedResourceError):\n            await s1.send(\"nope\")\n\n    async def send_will_succeed() -> None:\n        await s2.send(\"ok\")\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(send_will_close)\n        nursery.start_soon(send_will_succeed)\n        await wait_all_tasks_blocked()\n        await s1.aclose()\n        assert await r.receive() == \"ok\"\n\n\nasync def test_close_multiple_receive_handles() -> None:\n    # With multiple receive handles, closing one handle only wakes receivers on\n    # that handle, but others can continue just fine\n    s, r1 = open_memory_channel[str](0)\n    r2 = r1.clone()\n\n    async def receive_will_close() -> None:\n        with pytest.raises(trio.ClosedResourceError):\n            await r1.receive()\n\n    async def receive_will_succeed() -> None:\n        assert await r2.receive() == \"ok\"\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(receive_will_close)\n        nursery.start_soon(receive_will_succeed)\n        await wait_all_tasks_blocked()\n        await r1.aclose()\n        await s.send(\"ok\")\n\n\nasync def test_inf_capacity() -> None:\n    send, receive = open_memory_channel[int](float(\"inf\"))\n\n    # It's accepted, and we can send all day without blocking\n    with send:\n        for i in range(10):\n            send.send_nowait(i)\n\n    got = [i async for i in receive]\n    assert got == list(range(10))\n\n\nasync def test_statistics() -> None:\n    s, r = open_memory_channel[None](2)\n\n    assert s.statistics() == r.statistics()\n    stats = s.statistics()\n    assert stats.current_buffer_used == 0\n    assert stats.max_buffer_size == 2\n    assert stats.open_send_channels == 1\n    assert stats.open_receive_channels == 1\n    assert stats.tasks_waiting_send == 0\n    assert stats.tasks_waiting_receive == 0\n\n    s.send_nowait(None)\n    assert s.statistics().current_buffer_used == 1\n\n    s2 = s.clone()\n    assert s.statistics().open_send_channels == 2\n    await s.aclose()\n    assert s2.statistics().open_send_channels == 1\n\n    r2 = r.clone()\n    assert s2.statistics().open_receive_channels == 2\n    await r2.aclose()\n    assert s2.statistics().open_receive_channels == 1\n\n    async with trio.open_nursery() as nursery:\n        s2.send_nowait(None)  # fill up the buffer\n        assert s.statistics().current_buffer_used == 2\n        nursery.start_soon(s2.send, None)\n        nursery.start_soon(s2.send, None)\n        await wait_all_tasks_blocked()\n        assert s.statistics().tasks_waiting_send == 2\n        nursery.cancel_scope.cancel()\n    assert s.statistics().tasks_waiting_send == 0\n\n    # empty out the buffer again\n    try:\n        while True:\n            r.receive_nowait()\n    except trio.WouldBlock:\n        pass\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(r.receive)\n        await wait_all_tasks_blocked()\n        assert s.statistics().tasks_waiting_receive == 1\n        nursery.cancel_scope.cancel()\n    assert s.statistics().tasks_waiting_receive == 0\n\n\nasync def test_channel_fairness() -> None:\n    # We can remove an item we just sent, and send an item back in after, if\n    # no-one else is waiting.\n    s, r = open_memory_channel[int | None](1)\n    s.send_nowait(1)\n    assert r.receive_nowait() == 1\n    s.send_nowait(2)\n    assert r.receive_nowait() == 2\n\n    # But if someone else is waiting to receive, then they \"own\" the item we\n    # send, so we can't receive it (even though we run first):\n\n    result: int | None = None\n\n    async def do_receive(r: trio.MemoryReceiveChannel[int | None]) -> None:\n        nonlocal result\n        result = await r.receive()\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(do_receive, r)\n        await wait_all_tasks_blocked()\n        s.send_nowait(2)\n        with pytest.raises(trio.WouldBlock):\n            r.receive_nowait()\n    assert result == 2\n\n    # And the analogous situation for send: if we free up a space, we can't\n    # immediately send something in it if someone is already waiting to do\n    # that\n    s, r = open_memory_channel[int | None](1)\n    s.send_nowait(1)\n    with pytest.raises(trio.WouldBlock):\n        s.send_nowait(None)\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(s.send, 2)\n        await wait_all_tasks_blocked()\n        assert r.receive_nowait() == 1\n        with pytest.raises(trio.WouldBlock):\n            s.send_nowait(3)\n        assert (await r.receive()) == 2\n\n\nasync def test_unbuffered() -> None:\n    s, r = open_memory_channel[int](0)\n    with pytest.raises(trio.WouldBlock):\n        r.receive_nowait()\n    with pytest.raises(trio.WouldBlock):\n        s.send_nowait(1)\n\n    async def do_send(s: trio.MemorySendChannel[int], v: int) -> None:\n        with assert_checkpoints():\n            await s.send(v)\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(do_send, s, 1)\n        with assert_checkpoints():\n            assert await r.receive() == 1\n    with pytest.raises(trio.WouldBlock):\n        r.receive_nowait()\n\n\nasync def test_as_safe_channel_exhaust() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        yield 1\n\n    async with agen() as recv_chan:\n        async for x in recv_chan:\n            assert x == 1\n\n\nasync def test_as_safe_channel_broken_resource() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        yield 1\n        yield 2  # pragma: no cover\n\n    async with agen() as recv_chan:\n        assert await recv_chan.__anext__() == 1\n\n        # close the receiving channel\n        await recv_chan.aclose()\n\n        # trying to get the next element errors\n        with pytest.raises(trio.ClosedResourceError):\n            await recv_chan.__anext__()\n\n        # but we don't get an error on exit of the cm\n\n\nasync def test_as_safe_channel_cancelled() -> None:\n    with trio.CancelScope() as cs:\n\n        @as_safe_channel\n        async def agen() -> AsyncGenerator[None]:  # pragma: no cover\n            raise AssertionError(\n                \"cancel before consumption means generator should not be iterated\"\n            )\n            yield  # indicate that we're an iterator\n\n        async with agen():\n            cs.cancel()\n\n\nasync def test_as_safe_channel_no_race() -> None:\n    # this previously led to a race condition due to\n    # https://github.com/python-trio/trio/issues/1559\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        yield 1\n        raise ValueError(\"oae\")\n\n    with pytest.raises(ValueError, match=r\"^oae$\"):\n        async with agen() as recv_chan:\n            async for x in recv_chan:\n                assert x == 1\n\n\nasync def test_as_safe_channel_buffer_size_too_small(\n    autojump_clock: trio.testing.MockClock,\n) -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        yield 1\n        raise AssertionError(\n            \"buffer size 0 means we shouldn't be asked for another value\"\n        )  # pragma: no cover\n\n    with trio.move_on_after(5):\n        async with agen() as recv_chan:\n            async for x in recv_chan:  # pragma: no branch\n                assert x == 1\n                await trio.sleep_forever()\n\n\nasync def test_as_safe_channel_no_interleave() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        yield 1\n        raise AssertionError  # pragma: no cover\n\n    async with agen() as recv_chan:\n        assert await recv_chan.__anext__() == 1\n        await trio.lowlevel.checkpoint()\n\n\nasync def test_as_safe_channel_genexit_finally() -> None:\n    @as_safe_channel\n    async def agen(events: list[str]) -> AsyncGenerator[int]:\n        try:\n            yield 1\n        except BaseException as e:\n            events.append(repr(e))\n            raise\n        finally:\n            events.append(\"finally\")\n            raise ValueError(\"agen\")\n\n    events: list[str] = []\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(ValueError, match=\"^agen$\"),\n        pytest.RaisesExc(TypeError, match=\"^iterator$\"),\n    ) as g:\n        async with agen(events) as recv_chan:\n            async for i in recv_chan:  # pragma: no branch\n                assert i == 1\n                raise TypeError(\"iterator\")\n\n    if sys.version_info >= (3, 11):\n        assert g.value.__notes__ == [\n            \"Encountered exception during cleanup of generator object, as \"\n            \"well as exception in the contextmanager body - unable to unwrap.\"\n        ]\n\n    assert events == [\"GeneratorExit()\", \"finally\"]\n\n\nasync def test_as_safe_channel_nested_loop() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        for i in range(2):\n            yield i\n\n    ii = 0\n    async with agen() as recv_chan1:\n        async for i in recv_chan1:\n            async with agen() as recv_chan:\n                jj = 0\n                async for j in recv_chan:\n                    assert (i, j) == (ii, jj)\n                    jj += 1\n            ii += 1\n\n\nasync def test_as_safe_channel_doesnt_leak_cancellation() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        yield\n        with trio.CancelScope() as cscope:\n            cscope.cancel()\n            yield\n\n    with pytest.raises(AssertionError):\n        async with agen() as recv_chan:\n            async for _ in recv_chan:\n                pass\n        raise AssertionError(\"should be reachable\")\n\n\nasync def test_as_safe_channel_dont_unwrap_user_exceptiongroup() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        raise NotImplementedError(\"not entered\")\n        yield  # pragma: no cover\n\n    with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match=\"bar\"), match=\"foo\"):\n        async with agen() as _:\n            raise ExceptionGroup(\"foo\", [ValueError(\"bar\")])\n\n\nasync def test_as_safe_channel_multiple_receiver() -> None:\n    event = trio.Event()\n\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[int]:\n        await event.wait()\n        yield 0\n        yield 1\n\n    async def handle_value(\n        recv_chan: trio.abc.ReceiveChannel[int],\n        value: int,\n        task_status: trio.TaskStatus,\n    ) -> None:\n        task_status.started()\n        assert await recv_chan.receive() == value\n\n    async with agen() as recv_chan:\n        async with trio.open_nursery() as nursery:\n            await nursery.start(handle_value, recv_chan, 0)\n            await nursery.start(handle_value, recv_chan, 1)\n            event.set()\n\n\nasync def test_as_safe_channel_multi_cancel() -> None:\n    @as_safe_channel\n    async def agen(events: list[str]) -> AsyncGenerator[None]:\n        try:\n            yield\n        finally:\n            # this will give a warning of ASYNC120, although it's not technically a\n            # problem of swallowing existing exceptions\n            try:\n                await trio.lowlevel.checkpoint()\n            except trio.Cancelled:\n                events.append(\"agen cancel\")\n                raise\n\n    events: list[str] = []\n    with trio.CancelScope() as cs:\n        with pytest.raises(trio.Cancelled):\n            async with agen(events) as recv_chan:\n                async for _ in recv_chan:  # pragma: no branch\n                    cs.cancel()\n                    try:\n                        await trio.lowlevel.checkpoint()\n                    except trio.Cancelled:\n                        events.append(\"body cancel\")\n                        raise\n    assert events == [\"body cancel\", \"agen cancel\"]\n\n\nasync def test_as_safe_channel_genexit_exception_group() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        try:\n            async with trio.open_nursery():\n                yield\n        except BaseException as e:\n            assert pytest.RaisesGroup(GeneratorExit).matches(e)  # noqa: PT017\n            raise\n\n    async with agen() as g:\n        async for _ in g:\n            break\n\n\nasync def test_as_safe_channel_does_not_suppress_nested_genexit() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        yield\n\n    with pytest.RaisesGroup(GeneratorExit):\n        async with agen() as g, trio.open_nursery():\n            await g.receive()  # this is for coverage reasons\n            raise GeneratorExit\n\n\nasync def test_as_safe_channel_genexit_filter() -> None:\n    async def wait_then_raise() -> None:\n        try:\n            await trio.sleep_forever()\n        except trio.Cancelled:\n            raise ValueError from None\n\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(wait_then_raise)\n            yield\n\n    with pytest.RaisesGroup(ValueError):\n        async with agen() as g:\n            async for _ in g:\n                break\n\n\nasync def test_as_safe_channel_swallowing_extra_exceptions() -> None:\n    async def wait_then_raise(ex: type[BaseException]) -> None:\n        try:\n            await trio.sleep_forever()\n        except trio.Cancelled:\n            raise ex from None\n\n    @as_safe_channel\n    async def agen(ex: type[BaseException]) -> AsyncGenerator[None]:\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(wait_then_raise, ex)\n            nursery.start_soon(wait_then_raise, GeneratorExit)\n            yield\n\n    with pytest.RaisesGroup(AssertionError):\n        async with agen(GeneratorExit) as g:\n            async for _ in g:\n                break\n\n    with pytest.RaisesGroup(ValueError, AssertionError):\n        async with agen(ValueError) as g:\n            async for _ in g:\n                break\n\n\nasync def test_as_safe_channel_close_between_iteration() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        while True:\n            yield\n\n    async with agen() as chan, trio.open_nursery() as nursery:\n\n        async def close_channel() -> None:\n            await trio.lowlevel.checkpoint()\n            await chan.aclose()\n\n        nursery.start_soon(close_channel)\n        with pytest.raises(trio.ClosedResourceError):\n            async for _ in chan:\n                pass\n\n\nasync def test_as_safe_channel_close_before_iteration() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        raise AssertionError(\"should be unreachable\")  # pragma: no cover\n        yield  # pragma: no cover\n\n    async with agen() as chan:\n        await chan.aclose()\n        with pytest.raises(trio.ClosedResourceError):\n            await chan.receive()\n\n\nasync def test_as_safe_channel_close_during_iteration() -> None:\n    @as_safe_channel\n    async def agen() -> AsyncGenerator[None]:\n        yield\n        await chan.aclose()\n        while True:\n            yield\n\n    async with agen() as chan:\n        with pytest.raises(trio.ClosedResourceError):\n            async for _ in chan:\n                pass\n\n        # This is necessary to ensure that `chan` has been sent\n        # to. Otherwise, this test sometimes passes on a broken\n        # version of trio.\n        await trio.testing.wait_all_tasks_blocked()\n"
  },
  {
    "path": "src/trio/_tests/test_contextvars.py",
    "content": "from __future__ import annotations\n\nimport contextvars\n\nfrom .. import _core\n\ntrio_testing_contextvar: contextvars.ContextVar[str] = contextvars.ContextVar(\n    \"trio_testing_contextvar\",\n)\n\n\nasync def test_contextvars_default() -> None:\n    trio_testing_contextvar.set(\"main\")\n    record: list[str] = []\n\n    async def child() -> None:\n        value = trio_testing_contextvar.get()\n        record.append(value)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child)\n    assert record == [\"main\"]\n\n\nasync def test_contextvars_set() -> None:\n    trio_testing_contextvar.set(\"main\")\n    record: list[str] = []\n\n    async def child() -> None:\n        trio_testing_contextvar.set(\"child\")\n        value = trio_testing_contextvar.get()\n        record.append(value)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child)\n    value = trio_testing_contextvar.get()\n    assert record == [\"child\"]\n    assert value == \"main\"\n\n\nasync def test_contextvars_copy() -> None:\n    trio_testing_contextvar.set(\"main\")\n    context = contextvars.copy_context()\n    trio_testing_contextvar.set(\"second_main\")\n    record: list[str] = []\n\n    async def child() -> None:\n        value = trio_testing_contextvar.get()\n        record.append(value)\n\n    async with _core.open_nursery() as nursery:\n        context.run(nursery.start_soon, child)\n        nursery.start_soon(child)\n    value = trio_testing_contextvar.get()\n    assert set(record) == {\"main\", \"second_main\"}\n    assert value == \"second_main\"\n"
  },
  {
    "path": "src/trio/_tests/test_deprecate.py",
    "content": "from __future__ import annotations\n\nimport inspect\nimport warnings\nfrom types import ModuleType\n\nimport pytest\n\nfrom .._deprecate import (\n    TrioDeprecationWarning,\n    deprecated,\n    deprecated_alias,\n    warn_deprecated,\n)\nfrom . import module_with_deprecations\n\n\n@pytest.fixture\ndef recwarn_always(recwarn: pytest.WarningsRecorder) -> pytest.WarningsRecorder:\n    warnings.simplefilter(\"always\")\n    # ResourceWarnings about unclosed sockets can occur nondeterministically\n    # (during GC) which throws off the tests in this file\n    warnings.simplefilter(\"ignore\", ResourceWarning)\n    return recwarn\n\n\ndef _here() -> tuple[str, int]:\n    frame = inspect.currentframe()\n    assert frame is not None\n    assert frame.f_back is not None\n    info = inspect.getframeinfo(frame.f_back)\n    return (info.filename, info.lineno)\n\n\ndef test_warn_deprecated(recwarn_always: pytest.WarningsRecorder) -> None:\n    def deprecated_thing() -> None:\n        warn_deprecated(\"ice\", \"1.2\", issue=1, instead=\"water\")\n\n    deprecated_thing()\n    filename, lineno = _here()\n    assert len(recwarn_always) == 1\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"ice is deprecated\" in got.message.args[0]\n    assert \"Trio 1.2\" in got.message.args[0]\n    assert \"water instead\" in got.message.args[0]\n    assert \"/issues/1\" in got.message.args[0]\n    assert got.filename == filename\n    assert got.lineno == lineno - 1\n\n\ndef test_warn_deprecated_no_instead_or_issue(\n    recwarn_always: pytest.WarningsRecorder,\n) -> None:\n    # Explicitly no instead or issue\n    warn_deprecated(\"water\", \"1.3\", issue=None, instead=None)\n    assert len(recwarn_always) == 1\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"water is deprecated\" in got.message.args[0]\n    assert \"no replacement\" in got.message.args[0]\n    assert \"Trio 1.3\" in got.message.args[0]\n\n\ndef test_warn_deprecated_stacklevel(recwarn_always: pytest.WarningsRecorder) -> None:\n    def nested1() -> None:\n        nested2()\n\n    def nested2() -> None:\n        warn_deprecated(\"x\", \"1.3\", issue=7, instead=\"y\", stacklevel=3)\n\n    filename, lineno = _here()\n    nested1()\n    got = recwarn_always.pop(DeprecationWarning)\n    assert got.filename == filename\n    assert got.lineno == lineno + 1\n\n\ndef old() -> None:  # pragma: no cover\n    pass\n\n\ndef new() -> None:  # pragma: no cover\n    pass\n\n\ndef test_warn_deprecated_formatting(recwarn_always: pytest.WarningsRecorder) -> None:\n    warn_deprecated(old, \"1.0\", issue=1, instead=new)\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"test_deprecate.old is deprecated\" in got.message.args[0]\n    assert \"test_deprecate.new instead\" in got.message.args[0]\n\n\n@deprecated(\"1.5\", issue=123, instead=new)\ndef deprecated_old() -> int:\n    return 3\n\n\ndef test_deprecated_decorator(recwarn_always: pytest.WarningsRecorder) -> None:\n    assert deprecated_old() == 3\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"test_deprecate.deprecated_old is deprecated\" in got.message.args[0]\n    assert \"1.5\" in got.message.args[0]\n    assert \"test_deprecate.new\" in got.message.args[0]\n    assert \"issues/123\" in got.message.args[0]\n\n\nclass Foo:\n    @deprecated(\"1.0\", issue=123, instead=\"crying\")\n    def method(self) -> int:\n        return 7\n\n\ndef test_deprecated_decorator_method(recwarn_always: pytest.WarningsRecorder) -> None:\n    f = Foo()\n    assert f.method() == 7\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"test_deprecate.Foo.method is deprecated\" in got.message.args[0]\n\n\n@deprecated(\"1.2\", thing=\"the thing\", issue=None, instead=None)\ndef deprecated_with_thing() -> int:\n    return 72\n\n\ndef test_deprecated_decorator_with_explicit_thing(\n    recwarn_always: pytest.WarningsRecorder,\n) -> None:\n    assert deprecated_with_thing() == 72\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"the thing is deprecated\" in got.message.args[0]\n\n\ndef new_hotness() -> str:\n    return \"new hotness\"\n\n\nold_hotness = deprecated_alias(\"old_hotness\", new_hotness, \"1.23\", issue=1)\n\n\ndef test_deprecated_alias(recwarn_always: pytest.WarningsRecorder) -> None:\n    assert old_hotness() == \"new hotness\"\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"test_deprecate.old_hotness is deprecated\" in got.message.args[0]\n    assert \"1.23\" in got.message.args[0]\n    assert \"test_deprecate.new_hotness instead\" in got.message.args[0]\n    assert \"issues/1\" in got.message.args[0]\n\n    assert isinstance(old_hotness.__doc__, str)\n    assert \".. deprecated:: 1.23\" in old_hotness.__doc__\n    assert \"test_deprecate.new_hotness instead\" in old_hotness.__doc__\n    assert \"issues/1>`__\" in old_hotness.__doc__\n\n\nclass Alias:\n    def new_hotness_method(self) -> str:\n        return \"new hotness method\"\n\n    old_hotness_method = deprecated_alias(\n        \"Alias.old_hotness_method\",\n        new_hotness_method,\n        \"3.21\",\n        issue=1,\n    )\n\n\ndef test_deprecated_alias_method(recwarn_always: pytest.WarningsRecorder) -> None:\n    obj = Alias()\n    assert obj.old_hotness_method() == \"new hotness method\"\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    msg = got.message.args[0]\n    assert \"test_deprecate.Alias.old_hotness_method is deprecated\" in msg\n    assert \"test_deprecate.Alias.new_hotness_method instead\" in msg\n\n\n@deprecated(\"2.1\", issue=1, instead=\"hi\")\ndef docstring_test1() -> None:  # pragma: no cover\n    \"\"\"Hello!\"\"\"\n\n\n@deprecated(\"2.1\", issue=None, instead=\"hi\")\ndef docstring_test2() -> None:  # pragma: no cover\n    \"\"\"Hello!\"\"\"\n\n\n@deprecated(\"2.1\", issue=1, instead=None)\ndef docstring_test3() -> None:  # pragma: no cover\n    \"\"\"Hello!\"\"\"\n\n\n@deprecated(\"2.1\", issue=None, instead=None)\ndef docstring_test4() -> None:  # pragma: no cover\n    \"\"\"Hello!\"\"\"\n\n\ndef test_deprecated_docstring_munging() -> None:\n    assert docstring_test1.__doc__ == \"\"\"Hello!\n\n.. deprecated:: 2.1\n   Use hi instead.\n   For details, see `issue #1 <https://github.com/python-trio/trio/issues/1>`__.\n\n\"\"\"\n\n    assert docstring_test2.__doc__ == \"\"\"Hello!\n\n.. deprecated:: 2.1\n   Use hi instead.\n\n\"\"\"\n\n    assert docstring_test3.__doc__ == \"\"\"Hello!\n\n.. deprecated:: 2.1\n   For details, see `issue #1 <https://github.com/python-trio/trio/issues/1>`__.\n\n\"\"\"\n\n    assert docstring_test4.__doc__ == \"\"\"Hello!\n\n.. deprecated:: 2.1\n\n\"\"\"\n\n\ndef test_module_with_deprecations(recwarn_always: pytest.WarningsRecorder) -> None:\n    assert module_with_deprecations.regular == \"hi\"\n    assert len(recwarn_always) == 0\n\n    assert type(module_with_deprecations) is ModuleType\n\n    filename, lineno = _here()\n    assert module_with_deprecations.dep1 == \"value1\"  # type: ignore[attr-defined]\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert got.filename == filename\n    assert got.lineno == lineno + 1\n\n    assert \"module_with_deprecations.dep1\" in got.message.args[0]\n    assert \"Trio 1.1\" in got.message.args[0]\n    assert \"/issues/1\" in got.message.args[0]\n    assert \"value1 instead\" in got.message.args[0]\n\n    assert module_with_deprecations.dep2 == \"value2\"  # type: ignore[attr-defined]\n    got = recwarn_always.pop(DeprecationWarning)\n    assert isinstance(got.message, Warning)\n    assert \"instead-string instead\" in got.message.args[0]\n\n    with pytest.raises(AttributeError):\n        module_with_deprecations.asdf  # type: ignore[attr-defined]  # noqa: B018  # \"useless expression\"\n\n\ndef test_warning_class() -> None:\n    with pytest.deprecated_call():\n        warn_deprecated(\"foo\", \"bar\", issue=None, instead=None)\n\n    # essentially the same as the above check\n    with pytest.warns(\n        DeprecationWarning,\n        match=\"^foo is deprecated since Trio bar with no replacement$\",\n    ):\n        warn_deprecated(\"foo\", \"bar\", issue=None, instead=None)\n\n    with pytest.warns(TrioDeprecationWarning):\n        warn_deprecated(\n            \"foo\",\n            \"bar\",\n            issue=None,\n            instead=None,\n            use_triodeprecationwarning=True,\n        )\n"
  },
  {
    "path": "src/trio/_tests/test_deprecate_strict_exception_groups_false.py",
    "content": "from collections.abc import Awaitable, Callable\n\nimport pytest\n\nimport trio\n\n\nasync def test_deprecation_warning_open_nursery() -> None:\n    with pytest.warns(\n        trio.TrioDeprecationWarning,\n        match=\"strict_exception_groups=False\",\n    ) as record:\n        async with trio.open_nursery(strict_exception_groups=False):\n            ...\n    assert len(record) == 1\n    async with trio.open_nursery(strict_exception_groups=True):\n        ...\n    async with trio.open_nursery():\n        ...\n\n\ndef test_deprecation_warning_run() -> None:\n    async def foo() -> None: ...\n\n    async def foo_nursery() -> None:\n        # this should not raise a warning, even if it's implied loose\n        async with trio.open_nursery():\n            ...\n\n    async def foo_loose_nursery() -> None:\n        # this should raise a warning, even if specifying the parameter is redundant\n        async with trio.open_nursery(strict_exception_groups=False):\n            ...\n\n    def helper(fun: Callable[[], Awaitable[None]], num: int) -> None:\n        with pytest.warns(\n            trio.TrioDeprecationWarning,\n            match=\"strict_exception_groups=False\",\n        ) as record:\n            trio.run(fun, strict_exception_groups=False)\n        assert len(record) == num\n\n    helper(foo, 1)\n    helper(foo_nursery, 1)\n    helper(foo_loose_nursery, 2)\n\n\ndef test_deprecation_warning_start_guest_run() -> None:\n    # \"The simplest possible \"host\" loop.\"\n    from .._core._tests.test_guest_mode import trivial_guest_run\n\n    async def trio_return(in_host: object) -> str:\n        await trio.lowlevel.checkpoint()\n        return \"ok\"\n\n    with pytest.warns(\n        trio.TrioDeprecationWarning,\n        match=\"strict_exception_groups=False\",\n    ) as record:\n        trivial_guest_run(\n            trio_return,\n            strict_exception_groups=False,\n        )\n    assert len(record) == 1\n"
  },
  {
    "path": "src/trio/_tests/test_dtls.py",
    "content": "from __future__ import annotations\n\nimport random\nfrom contextlib import asynccontextmanager\nfrom itertools import count\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport attrs\nimport pytest\n\nfrom trio._tests.pytest_plugin import skip_if_optional_else_raise\n\ntry:\n    import trustme\n    from OpenSSL import SSL\nexcept ImportError as error:\n    skip_if_optional_else_raise(error)\n\n\nimport trio\nimport trio.testing\nfrom trio import DTLSChannel, DTLSEndpoint\nfrom trio.testing._fake_net import FakeNet, UDPPacket\n\nfrom .._core._tests.tutil import binds_ipv6, gc_collect_harder, slow\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator\n\nca = trustme.CA()\nserver_cert = ca.issue_cert(\"example.com\")\n\n\n@pytest.fixture\ndef server_ctx() -> SSL.Context:\n    ctx = SSL.Context(SSL.DTLS_METHOD)\n    server_cert.configure_cert(ctx)\n    return ctx\n\n\ndef client_ctx_fn() -> SSL.Context:\n    ctx = SSL.Context(SSL.DTLS_METHOD)\n    ca.configure_trust(ctx)\n    return ctx\n\n\n@pytest.fixture\ndef client_ctx() -> SSL.Context:\n    return client_ctx_fn()\n\n\nparametrize_ipv6 = pytest.mark.parametrize(\n    \"ipv6\",\n    [False, pytest.param(True, marks=binds_ipv6)],\n    ids=[\"ipv4\", \"ipv6\"],\n)\n\n\ndef endpoint(**kwargs: int | bool) -> DTLSEndpoint:\n    ipv6 = kwargs.pop(\"ipv6\", False)\n    family = trio.socket.AF_INET6 if ipv6 else trio.socket.AF_INET\n    sock = trio.socket.socket(type=trio.socket.SOCK_DGRAM, family=family)\n    return DTLSEndpoint(sock, **kwargs)\n\n\n@asynccontextmanager\nasync def dtls_echo_server(\n    *,\n    server_ctx: SSL.Context,\n    autocancel: bool = True,\n    mtu: int | None = None,\n    ipv6: bool = False,\n) -> AsyncGenerator[tuple[DTLSEndpoint, tuple[str, int]], None]:\n    with endpoint(ipv6=ipv6) as server:\n        localhost = \"::1\" if ipv6 else \"127.0.0.1\"\n        await server.socket.bind((localhost, 0))\n        async with trio.open_nursery() as nursery:\n\n            async def echo_handler(dtls_channel: DTLSChannel) -> None:\n                print(\n                    \"echo handler started: \"\n                    f\"server {dtls_channel.endpoint.socket.getsockname()!r} \"\n                    f\"client {dtls_channel.peer_address!r}\",\n                )\n                if mtu is not None:\n                    dtls_channel.set_ciphertext_mtu(mtu)\n                try:\n                    print(\"server starting do_handshake\")\n                    await dtls_channel.do_handshake()\n                    print(\"server finished do_handshake\")\n                    # no branch for leaving this for loop because we only leave\n                    # a channel by cancellation.\n                    async for packet in dtls_channel:  # pragma: no branch\n                        print(f\"echoing {packet!r} -> {dtls_channel.peer_address!r}\")\n                        await dtls_channel.send(packet)\n                except trio.BrokenResourceError:  # pragma: no cover\n                    print(\"echo handler channel broken\")\n\n            await nursery.start(server.serve, server_ctx, echo_handler)\n\n            yield server, server.socket.getsockname()\n\n            if autocancel:\n                nursery.cancel_scope.cancel()\n\n\n@parametrize_ipv6\nasync def test_smoke(\n    ipv6: bool, server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    async with dtls_echo_server(ipv6=ipv6, server_ctx=server_ctx) as (\n        _server_endpoint,\n        address,\n    ):\n        with endpoint(ipv6=ipv6) as client_endpoint:\n            client_channel = client_endpoint.connect(address, client_ctx)\n            with pytest.raises(trio.NeedHandshakeError):\n                client_channel.get_cleartext_mtu()\n\n            await client_channel.do_handshake()\n            await client_channel.send(b\"hello\")\n            assert await client_channel.receive() == b\"hello\"\n            await client_channel.send(b\"goodbye\")\n            assert await client_channel.receive() == b\"goodbye\"\n\n            with pytest.raises(\n                ValueError,\n                match=r\"^openssl doesn't support sending empty DTLS packets$\",\n            ):\n                await client_channel.send(b\"\")\n\n            client_channel.set_ciphertext_mtu(1234)\n            cleartext_mtu_1234 = client_channel.get_cleartext_mtu()\n            client_channel.set_ciphertext_mtu(4321)\n            assert client_channel.get_cleartext_mtu() > cleartext_mtu_1234\n            client_channel.set_ciphertext_mtu(1234)\n            assert client_channel.get_cleartext_mtu() == cleartext_mtu_1234\n\n\n@slow\nasync def test_handshake_over_terrible_network(\n    autojump_clock: trio.testing.MockClock,\n    server_ctx: SSL.Context,\n) -> None:\n    HANDSHAKES = 100\n    r = random.Random(0)\n    fn = FakeNet()\n    fn.enable()\n    # avoid spurious timeouts on slow machines\n    autojump_clock.autojump_threshold = 0.001\n\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        async with trio.open_nursery() as nursery:\n\n            async def route_packet(packet: UDPPacket) -> None:\n                while True:\n                    op = r.choices(\n                        [\"deliver\", \"drop\", \"dupe\", \"delay\"],\n                        weights=[0.7, 0.1, 0.1, 0.1],\n                    )[0]\n                    print(f\"{packet.source} -> {packet.destination}: {op}\")\n                    if op == \"drop\":\n                        return\n                    elif op == \"dupe\":\n                        fn.send_packet(packet)\n                    elif op == \"delay\":\n                        await trio.sleep(r.random() * 3)\n                    # I wanted to test random packet corruption too, but it turns out\n                    # openssl has a bug in the following scenario:\n                    #\n                    # - client sends ClientHello\n                    # - server sends HelloVerifyRequest with cookie -- but cookie is\n                    #   invalid b/c either the ClientHello or HelloVerifyRequest was\n                    #   corrupted\n                    # - client re-sends ClientHello with invalid cookie\n                    # - server replies with new HelloVerifyRequest and correct cookie\n                    #\n                    # At this point, the client *should* switch to the new, valid\n                    # cookie. But OpenSSL doesn't; it stubbornly insists on re-sending\n                    # the original, invalid cookie over and over. In theory we could\n                    # work around this by detecting cookie changes and starting over\n                    # with a whole new SSL object, but (a) it doesn't seem worth it, (b)\n                    # when I tried then I ran into another issue where OpenSSL got stuck\n                    # in an infinite loop sending alerts over and over, which I didn't\n                    # dig into because see (a).\n                    #\n                    # elif op == \"distort\":\n                    #     payload = bytearray(packet.payload)\n                    #     payload[r.randrange(len(payload))] ^= 1 << r.randrange(8)\n                    #     packet = attrs.evolve(packet, payload=payload)\n                    else:\n                        assert op == \"deliver\"\n                        print(\n                            f\"{packet.source} -> {packet.destination}: delivered\"\n                            f\" {packet.payload.hex()}\",\n                        )\n                        fn.deliver_packet(packet)\n                        break\n\n            def route_packet_wrapper(packet: UDPPacket) -> None:\n                try:  # noqa: SIM105  # suppressible-exception\n                    nursery.start_soon(route_packet, packet)\n                except RuntimeError:  # pragma: no cover\n                    # We're exiting the nursery, so any remaining packets can just get\n                    # dropped\n                    pass\n\n            fn.route_packet = route_packet_wrapper  # type: ignore[assignment]  # TODO: Fix FakeNet typing\n\n            for i in range(HANDSHAKES):\n                print(\"#\" * 80)\n                print(\"#\" * 80)\n                print(\"#\" * 80)\n                with endpoint() as client_endpoint:\n                    client = client_endpoint.connect(address, client_ctx_fn())\n                    print(\"client starting do_handshake\")\n                    await client.do_handshake()\n                    print(\"client finished do_handshake\")\n                    msg = str(i).encode()\n                    # Make multiple attempts to send data, because the network might\n                    # drop it\n                    while True:\n                        with trio.move_on_after(10) as cscope:\n                            await client.send(msg)\n                            assert await client.receive() == msg\n                        if not cscope.cancelled_caught:\n                            break\n\n\nasync def test_implicit_handshake(\n    server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with endpoint() as client_endpoint:\n            client = client_endpoint.connect(address, client_ctx)\n\n            # Implicit handshake\n            await client.send(b\"xyz\")\n            assert await client.receive() == b\"xyz\"\n\n\nasync def test_full_duplex(server_ctx: SSL.Context, client_ctx: SSL.Context) -> None:\n    # Tests simultaneous send/receive, and also multiple methods implicitly invoking\n    # do_handshake simultaneously.\n    with endpoint() as server_endpoint, endpoint() as client_endpoint:\n        await server_endpoint.socket.bind((\"127.0.0.1\", 0))\n        async with trio.open_nursery() as server_nursery:\n\n            async def handler(channel: DTLSChannel) -> None:\n                async with trio.open_nursery() as nursery:\n                    nursery.start_soon(channel.send, b\"from server\")\n                    nursery.start_soon(channel.receive)\n\n            await server_nursery.start(server_endpoint.serve, server_ctx, handler)\n\n            client = client_endpoint.connect(\n                server_endpoint.socket.getsockname(),\n                client_ctx,\n            )\n            async with trio.open_nursery() as nursery:\n                nursery.start_soon(client.send, b\"from client\")\n                nursery.start_soon(client.receive)\n\n            server_nursery.cancel_scope.cancel()\n\n\nasync def test_channel_closing(\n    server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with endpoint() as client_endpoint:\n            client = client_endpoint.connect(address, client_ctx)\n            await client.do_handshake()\n            client.close()\n\n            with pytest.raises(trio.ClosedResourceError):\n                await client.send(b\"abc\")\n            with pytest.raises(trio.ClosedResourceError):\n                await client.receive()\n\n            # close is idempotent\n            client.close()\n            # can also aclose\n            await client.aclose()\n\n\nasync def test_serve_exits_cleanly_on_close(server_ctx: SSL.Context) -> None:\n    async with dtls_echo_server(autocancel=False, server_ctx=server_ctx) as (\n        server_endpoint,\n        _address,\n    ):\n        server_endpoint.close()\n        # Testing that the nursery exits even without being cancelled\n    # close is idempotent\n    server_endpoint.close()\n\n\nasync def test_client_multiplex(server_ctx: SSL.Context) -> None:\n    async with (\n        dtls_echo_server(server_ctx=server_ctx) as (_, address1),\n        dtls_echo_server(server_ctx=server_ctx) as (_, address2),\n    ):\n        with endpoint() as client_endpoint:\n            client1 = client_endpoint.connect(address1, client_ctx_fn())\n            client2 = client_endpoint.connect(address2, client_ctx_fn())\n\n            await client1.send(b\"abc\")\n            await client2.send(b\"xyz\")\n            assert await client2.receive() == b\"xyz\"\n            assert await client1.receive() == b\"abc\"\n\n            client_endpoint.close()\n\n            with pytest.raises(trio.ClosedResourceError):\n                await client1.send(b\"xxx\")\n            with pytest.raises(trio.ClosedResourceError):\n                await client2.receive()\n            with pytest.raises(trio.ClosedResourceError):\n                client_endpoint.connect(address1, client_ctx_fn())\n\n            async def null_handler(_: object) -> None:  # pragma: no cover\n                pass\n\n            async with trio.open_nursery() as nursery:\n                with pytest.raises(trio.ClosedResourceError):\n                    await nursery.start(client_endpoint.serve, server_ctx, null_handler)\n\n\nasync def test_dtls_over_dgram_only() -> None:\n    with trio.socket.socket() as s:\n        with pytest.raises(ValueError, match=r\"^DTLS requires a SOCK_DGRAM socket$\"):\n            DTLSEndpoint(s)\n\n\nasync def test_double_serve(server_ctx: SSL.Context) -> None:\n    async def null_handler(_: object) -> None:  # pragma: no cover\n        pass\n\n    with endpoint() as server_endpoint:\n        await server_endpoint.socket.bind((\"127.0.0.1\", 0))\n        async with trio.open_nursery() as nursery:\n            await nursery.start(server_endpoint.serve, server_ctx, null_handler)\n            with pytest.raises(trio.BusyResourceError):\n                await nursery.start(server_endpoint.serve, server_ctx, null_handler)\n\n            nursery.cancel_scope.cancel()\n\n        async with trio.open_nursery() as nursery:\n            await nursery.start(server_endpoint.serve, server_ctx, null_handler)\n            nursery.cancel_scope.cancel()\n\n\nasync def test_connect_to_non_server(\n    autojump_clock: trio.abc.Clock, client_ctx: SSL.Context\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n    with endpoint() as client1, endpoint() as client2:\n        await client1.socket.bind((\"127.0.0.1\", 0))\n        # This should just time out\n        with trio.move_on_after(100) as cscope:\n            channel = client2.connect(client1.socket.getsockname(), client_ctx)\n            await channel.do_handshake()\n        assert cscope.cancelled_caught\n\n\n@pytest.mark.parametrize(\"buffer_size\", [10, 20])\nasync def test_incoming_buffer_overflow(\n    autojump_clock: trio.abc.Clock,\n    server_ctx: SSL.Context,\n    client_ctx: SSL.Context,\n    buffer_size: int,\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with endpoint(incoming_packets_buffer=buffer_size) as client_endpoint:\n            assert client_endpoint.incoming_packets_buffer == buffer_size\n            client = client_endpoint.connect(address, client_ctx)\n            for i in range(buffer_size + 15):\n                await client.send(str(i).encode())\n                await trio.sleep(1)\n            stats = client.statistics()\n            assert stats.incoming_packets_dropped_in_trio == 15\n            for i in range(buffer_size):\n                assert await client.receive() == str(i).encode()\n            await client.send(b\"buffer clear now\")\n            assert await client.receive() == b\"buffer clear now\"\n\n\nasync def test_server_socket_doesnt_crash_on_garbage(\n    autojump_clock: trio.abc.Clock, server_ctx: SSL.Context\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    from trio._dtls import (\n        ContentType,\n        HandshakeFragment,\n        HandshakeType,\n        ProtocolVersion,\n        Record,\n        encode_handshake_fragment,\n        encode_record,\n    )\n\n    client_hello = encode_record(\n        Record(\n            content_type=ContentType.handshake,\n            version=ProtocolVersion.DTLS10,\n            epoch_seqno=0,\n            payload=encode_handshake_fragment(\n                HandshakeFragment(\n                    msg_type=HandshakeType.client_hello,\n                    msg_len=10,\n                    msg_seq=0,\n                    frag_offset=0,\n                    frag_len=10,\n                    frag=bytes(10),\n                ),\n            ),\n        ),\n    )\n\n    client_hello_extended = client_hello + b\"\\x00\"\n    client_hello_short = client_hello[:-1]\n    # cuts off in middle of handshake message header\n    client_hello_really_short = client_hello[:14]\n    client_hello_corrupt_record_len = bytearray(client_hello)\n    client_hello_corrupt_record_len[11] = 0xFF\n\n    client_hello_fragmented = encode_record(\n        Record(\n            content_type=ContentType.handshake,\n            version=ProtocolVersion.DTLS10,\n            epoch_seqno=0,\n            payload=encode_handshake_fragment(\n                HandshakeFragment(\n                    msg_type=HandshakeType.client_hello,\n                    msg_len=20,\n                    msg_seq=0,\n                    frag_offset=0,\n                    frag_len=10,\n                    frag=bytes(10),\n                ),\n            ),\n        ),\n    )\n\n    client_hello_trailing_data_in_record = encode_record(\n        Record(\n            content_type=ContentType.handshake,\n            version=ProtocolVersion.DTLS10,\n            epoch_seqno=0,\n            payload=encode_handshake_fragment(\n                HandshakeFragment(\n                    msg_type=HandshakeType.client_hello,\n                    msg_len=20,\n                    msg_seq=0,\n                    frag_offset=0,\n                    frag_len=10,\n                    frag=bytes(10),\n                ),\n            )\n            + b\"\\x00\",\n        ),\n    )\n\n    handshake_empty = encode_record(\n        Record(\n            content_type=ContentType.handshake,\n            version=ProtocolVersion.DTLS10,\n            epoch_seqno=0,\n            payload=b\"\",\n        ),\n    )\n\n    client_hello_truncated_in_cookie = encode_record(\n        Record(\n            content_type=ContentType.handshake,\n            version=ProtocolVersion.DTLS10,\n            epoch_seqno=0,\n            payload=bytes(2 + 32 + 1) + b\"\\xff\",\n        ),\n    )\n\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with trio.socket.socket(type=trio.socket.SOCK_DGRAM) as sock:\n            for bad_packet in [\n                b\"\",\n                b\"xyz\",\n                client_hello_extended,\n                client_hello_short,\n                client_hello_really_short,\n                client_hello_corrupt_record_len,\n                client_hello_fragmented,\n                client_hello_trailing_data_in_record,\n                handshake_empty,\n                client_hello_truncated_in_cookie,\n            ]:\n                await sock.sendto(bad_packet, address)\n                await trio.sleep(1)\n\n\nasync def test_invalid_cookie_rejected(\n    autojump_clock: trio.abc.Clock, server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    from trio._dtls import BadPacket, decode_client_hello_untrusted\n\n    with trio.CancelScope() as cscope:\n        # the first 11 bytes of ClientHello aren't protected by the cookie, so only test\n        # corrupting bytes after that.\n        offset_to_corrupt = count(11)\n\n        def route_packet(packet: UDPPacket) -> None:\n            try:\n                _, cookie, _ = decode_client_hello_untrusted(packet.payload)\n            except BadPacket:\n                pass\n            else:\n                if len(cookie) != 0:\n                    # this is a challenge response packet\n                    # let's corrupt the next offset so the handshake should fail\n                    payload = bytearray(packet.payload)\n                    offset = next(offset_to_corrupt)\n                    if offset >= len(payload):\n                        # We've tried all offsets. Clamp offset to the end of the\n                        # payload, and terminate the test.\n                        offset = len(payload) - 1\n                        cscope.cancel()\n                    payload[offset] ^= 0x01\n                    packet = attrs.evolve(packet, payload=payload)\n\n            fn.deliver_packet(packet)\n\n        fn.route_packet = route_packet  # type: ignore[assignment]  # TODO: Fix FakeNet typing\n\n        async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n            while True:\n                with endpoint() as client:\n                    channel = client.connect(address, client_ctx)\n                    await channel.do_handshake()\n    assert cscope.cancelled_caught\n\n\nasync def test_client_cancels_handshake_and_starts_new_one(\n    autojump_clock: trio.abc.Clock, server_ctx: SSL.Context\n) -> None:\n    # if a client disappears during the handshake, and then starts a new handshake from\n    # scratch, then the first handler's channel should fail, and a new handler get\n    # started\n    fn = FakeNet()\n    fn.enable()\n\n    with endpoint() as server, endpoint() as client:\n        await server.socket.bind((\"127.0.0.1\", 0))\n        async with trio.open_nursery() as nursery:\n            first_time = True\n\n            async def handler(channel: DTLSChannel) -> None:\n                nonlocal first_time\n                if first_time:\n                    first_time = False\n                    print(\"handler: first time, cancelling connect\")\n                    connect_cscope.cancel()\n                    await trio.sleep(0.5)\n                    print(\"handler: handshake should fail now\")\n                    with pytest.raises(trio.BrokenResourceError):\n                        await channel.do_handshake()\n                else:\n                    print(\"handler: not first time, sending hello\")\n                    await channel.send(b\"hello\")\n\n            await nursery.start(server.serve, server_ctx, handler)\n\n            print(\"client: starting first connect\")\n            with trio.CancelScope() as connect_cscope:\n                channel = client.connect(server.socket.getsockname(), client_ctx_fn())\n                await channel.do_handshake()\n            assert connect_cscope.cancelled_caught\n\n            print(\"client: starting second connect\")\n            channel = client.connect(server.socket.getsockname(), client_ctx_fn())\n            assert await channel.receive() == b\"hello\"\n\n            # Give handlers a chance to finish\n            await trio.sleep(10)\n            nursery.cancel_scope.cancel()\n\n\nasync def test_swap_client_server(server_ctx: SSL.Context) -> None:\n    with endpoint() as a, endpoint() as b:\n        await a.socket.bind((\"127.0.0.1\", 0))\n        await b.socket.bind((\"127.0.0.1\", 0))\n\n        async def echo_handler(channel: DTLSChannel) -> None:\n            async for packet in channel:\n                await channel.send(packet)\n\n        async def crashing_echo_handler(channel: DTLSChannel) -> None:\n            with pytest.raises(trio.BrokenResourceError):\n                await echo_handler(channel)\n\n        async with trio.open_nursery() as nursery:\n            await nursery.start(a.serve, server_ctx, crashing_echo_handler)\n            await nursery.start(b.serve, server_ctx, echo_handler)\n\n            b_to_a = b.connect(a.socket.getsockname(), client_ctx_fn())\n            await b_to_a.send(b\"b as client\")\n            assert await b_to_a.receive() == b\"b as client\"\n\n            a_to_b = a.connect(b.socket.getsockname(), client_ctx_fn())\n            await a_to_b.do_handshake()\n            with pytest.raises(trio.BrokenResourceError):\n                await b_to_a.send(b\"association broken\")\n            await a_to_b.send(b\"a as client\")\n            assert await a_to_b.receive() == b\"a as client\"\n\n            nursery.cancel_scope.cancel()\n\n\n@slow\nasync def test_openssl_retransmit_doesnt_break_stuff(\n    server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    # can't use autojump_clock here, because the point of the test is to wait for\n    # openssl's built-in retransmit timer to expire, which is hard-coded to use\n    # wall-clock time.\n    fn = FakeNet()\n    fn.enable()\n\n    blackholed = True\n\n    def route_packet(packet: UDPPacket) -> None:\n        if blackholed:\n            print(\"dropped packet\", packet)\n            return\n        print(\"delivered packet\", packet)\n        # packets.append(\n        #     scapy.all.IP(\n        #         src=packet.source.ip.compressed, dst=packet.destination.ip.compressed\n        #     )\n        #     / scapy.all.UDP(sport=packet.source.port, dport=packet.destination.port)\n        #     / packet.payload\n        # )\n        fn.deliver_packet(packet)\n\n    fn.route_packet = route_packet  # type: ignore[assignment]  # TODO add type annotations for FakeNet\n\n    async with dtls_echo_server(server_ctx=server_ctx) as (server_endpoint, address):\n        with endpoint() as client_endpoint:\n            async with trio.open_nursery() as nursery:\n\n                async def connecter() -> None:\n                    client = client_endpoint.connect(address, client_ctx)\n                    await client.do_handshake(initial_retransmit_timeout=1.5)\n                    await client.send(b\"hi\")\n                    assert await client.receive() == b\"hi\"\n\n                nursery.start_soon(connecter)\n\n                # openssl's default timeout is 1 second, so this ensures that it thinks\n                # the timeout has expired\n                await trio.sleep(1.1)\n                # disable blackholing and send a garbage packet to wake up openssl so it\n                # notices the timeout has expired\n                blackholed = False\n                await server_endpoint.socket.sendto(\n                    b\"xxx\",\n                    client_endpoint.socket.getsockname(),\n                )\n                # now the client task should finish connecting and exit cleanly\n\n    # scapy.all.wrpcap(\"/tmp/trace.pcap\", packets)\n\n\nasync def test_initial_retransmit_timeout_configuration(\n    autojump_clock: trio.abc.Clock, server_ctx: SSL.Context\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    blackholed = True\n\n    def route_packet(packet: UDPPacket) -> None:\n        nonlocal blackholed\n        if blackholed:\n            blackholed = False\n        else:\n            fn.deliver_packet(packet)\n\n    fn.route_packet = route_packet  # type: ignore[assignment]  # TODO: add type annotations for FakeNet\n\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        for t in [1, 2, 4]:\n            with endpoint() as client:\n                before = trio.current_time()\n                blackholed = True\n                channel = client.connect(address, client_ctx_fn())\n                await channel.do_handshake(initial_retransmit_timeout=t)\n                after = trio.current_time()\n                assert after - before == t\n\n\nasync def test_explicit_tiny_mtu_is_respected(\n    server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    # ClientHello is ~240 bytes, and it can't be fragmented, so our mtu has to\n    # be larger than that. (300 is still smaller than any real network though.)\n    MTU = 300\n\n    fn = FakeNet()\n    fn.enable()\n\n    def route_packet(packet: UDPPacket) -> None:\n        print(f\"delivering {packet}\")\n        print(f\"payload size: {len(packet.payload)}\")\n        assert len(packet.payload) <= MTU\n        fn.deliver_packet(packet)\n\n    fn.route_packet = route_packet  # type: ignore[assignment]  # TODO add type annotations for FakeNet\n\n    async with dtls_echo_server(mtu=MTU, server_ctx=server_ctx) as (_server, address):\n        with endpoint() as client:\n            channel = client.connect(address, client_ctx)\n            channel.set_ciphertext_mtu(MTU)\n            await channel.do_handshake()\n            await channel.send(b\"hi\")\n            assert await channel.receive() == b\"hi\"\n\n\n@parametrize_ipv6\nasync def test_handshake_handles_minimum_network_mtu(\n    ipv6: bool,\n    autojump_clock: trio.abc.Clock,\n    server_ctx: SSL.Context,\n    client_ctx: SSL.Context,\n) -> None:\n    # Fake network that has the minimum allowable MTU for whatever protocol we're using.\n    fn = FakeNet()\n    fn.enable()\n\n    mtu = 1280 - 48 if ipv6 else 576 - 28\n\n    def route_packet(packet: UDPPacket) -> None:\n        if len(packet.payload) > mtu:\n            print(f\"dropping {packet}\")\n        else:\n            print(f\"delivering {packet}\")\n            fn.deliver_packet(packet)\n\n    fn.route_packet = route_packet  # type: ignore[assignment]  # TODO: add type annotations for FakeNet\n\n    # See if we can successfully do a handshake -- some of the volleys will get dropped,\n    # and the retransmit logic should detect this and back off the MTU to something\n    # smaller until it succeeds.\n    async with dtls_echo_server(ipv6=ipv6, server_ctx=server_ctx) as (_, address):\n        with endpoint(ipv6=ipv6) as client_endpoint:\n            client = client_endpoint.connect(address, client_ctx)\n            # the handshake mtu backoff shouldn't affect the return value from\n            # get_cleartext_mtu, b/c that's under the user's control via\n            # set_ciphertext_mtu\n            client.set_ciphertext_mtu(9999)\n            await client.send(b\"xyz\")\n            assert await client.receive() == b\"xyz\"\n            assert client.get_cleartext_mtu() > 9000  # as vegeta said\n\n\n@pytest.mark.filterwarnings(\"always:unclosed DTLS:ResourceWarning\")\nasync def test_system_task_cleaned_up_on_gc(client_ctx: SSL.Context) -> None:\n    before_tasks = trio.lowlevel.current_statistics().tasks_living\n\n    # We put this into a sub-function so that everything automatically becomes garbage\n    # when the frame exits. For some reason just doing 'del e' wasn't enough on pypy\n    # with coverage enabled -- I think we were hitting this bug:\n    #     https://foss.heptapod.net/pypy/pypy/-/issues/3656\n    async def start_and_forget_endpoint() -> int:\n        e = endpoint()\n\n        # This connection/handshake attempt can't succeed. The only purpose is to force\n        # the endpoint to set up a receive loop.\n        with trio.socket.socket(type=trio.socket.SOCK_DGRAM) as s:\n            await s.bind((\"127.0.0.1\", 0))\n            c = e.connect(s.getsockname(), client_ctx)\n            async with trio.open_nursery() as nursery:\n                nursery.start_soon(c.do_handshake)\n                await trio.testing.wait_all_tasks_blocked()\n                nursery.cancel_scope.cancel()\n\n        during_tasks = trio.lowlevel.current_statistics().tasks_living\n        return during_tasks\n\n    with pytest.warns(ResourceWarning):  # noqa: PT031\n        during_tasks = await start_and_forget_endpoint()\n        await trio.testing.wait_all_tasks_blocked()\n        gc_collect_harder()\n\n    await trio.testing.wait_all_tasks_blocked()\n\n    after_tasks = trio.lowlevel.current_statistics().tasks_living\n    assert before_tasks < during_tasks\n    assert before_tasks == after_tasks\n\n\n@pytest.mark.filterwarnings(\"always:unclosed DTLS:ResourceWarning\")\nasync def test_gc_before_system_task_starts() -> None:\n    e = endpoint()\n\n    with pytest.warns(ResourceWarning):  # noqa: PT031\n        del e\n        gc_collect_harder()\n\n    await trio.testing.wait_all_tasks_blocked()\n\n\n@pytest.mark.filterwarnings(\"always:unclosed DTLS:ResourceWarning\")\nasync def test_gc_as_packet_received() -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    e = endpoint()\n    await e.socket.bind((\"127.0.0.1\", 0))\n    e._ensure_receive_loop()\n\n    await trio.testing.wait_all_tasks_blocked()\n\n    with trio.socket.socket(type=trio.socket.SOCK_DGRAM) as s:\n        await s.sendto(b\"xxx\", e.socket.getsockname())\n    # At this point, the endpoint's receive loop has been marked runnable because it\n    # just received a packet; closing the endpoint socket won't interrupt that. But by\n    # the time it wakes up to process the packet, the endpoint will be gone.\n    with pytest.warns(ResourceWarning):  # noqa: PT031\n        del e\n        gc_collect_harder()\n\n\n@pytest.mark.filterwarnings(\"always:unclosed DTLS:ResourceWarning\")\ndef test_gc_after_trio_exits() -> None:\n    async def main() -> DTLSEndpoint:\n        # We use fakenet just to make sure no real sockets can leak out of the test\n        # case - on pypy somehow the socket was outliving the gc_collect_harder call\n        # below. Since the test is just making sure DTLSEndpoint.__del__ doesn't explode\n        # when called after trio exits, it doesn't need a real socket.\n        fn = FakeNet()\n        fn.enable()\n        return endpoint()\n\n    e = trio.run(main)\n    with pytest.warns(ResourceWarning):  # noqa: PT031\n        del e\n        gc_collect_harder()\n\n\nasync def test_already_closed_socket_doesnt_crash() -> None:\n    with endpoint() as e:\n        # We close the socket before checkpointing, so the socket will already be closed\n        # when the system task starts up\n        e.socket.close()\n        # Now give it a chance to start up, and hopefully not crash\n        await trio.testing.wait_all_tasks_blocked()\n\n\nasync def test_socket_closed_while_processing_clienthello(\n    autojump_clock: trio.abc.Clock, server_ctx: SSL.Context, client_ctx: SSL.Context\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    # Check what happens if the socket is discovered to be closed when sending a\n    # HelloVerifyRequest, since that has its own sending logic\n    async with dtls_echo_server(server_ctx=server_ctx) as (server, address):\n\n        def route_packet(packet: UDPPacket) -> None:\n            fn.deliver_packet(packet)\n            server.socket.close()\n\n        fn.route_packet = route_packet  # type: ignore[assignment]  # TODO add type annotations for FakeNet\n\n        with endpoint() as client_endpoint:\n            with trio.move_on_after(10):\n                client = client_endpoint.connect(address, client_ctx)\n                await client.do_handshake()\n\n\nasync def test_association_replaced_while_handshake_running(\n    autojump_clock: trio.abc.Clock, server_ctx: SSL.Context\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    def route_packet(packet: UDPPacket) -> None:\n        pass\n\n    fn.route_packet = route_packet  # type: ignore[assignment]  # TODO: add type annotations for FakeNet\n\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with endpoint() as client_endpoint:\n            # TODO: should this have the same exact client_ctx?\n            c1 = client_endpoint.connect(address, client_ctx_fn())\n            async with trio.open_nursery() as nursery:\n\n                async def doomed_handshake() -> None:\n                    with pytest.raises(trio.BrokenResourceError):\n                        await c1.do_handshake()\n\n                nursery.start_soon(doomed_handshake)\n\n                await trio.sleep(10)\n\n                client_endpoint.connect(address, client_ctx_fn())\n\n\nasync def test_association_replaced_before_handshake_starts(\n    server_ctx: SSL.Context,\n) -> None:\n    fn = FakeNet()\n    fn.enable()\n\n    # This test shouldn't send any packets\n    def route_packet(packet: UDPPacket) -> NoReturn:  # pragma: no cover\n        raise AssertionError()\n\n    fn.route_packet = route_packet  # type: ignore[assignment]  # TODO add type annotations for FakeNet\n\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with endpoint() as client_endpoint:\n            # TODO: should this use the same client_ctx?\n            c1 = client_endpoint.connect(address, client_ctx_fn())\n            client_endpoint.connect(address, client_ctx_fn())\n            with pytest.raises(trio.BrokenResourceError):\n                await c1.do_handshake()\n\n\nasync def test_send_to_closed_local_port(server_ctx: SSL.Context) -> None:\n    # On Windows, sending a UDP packet to a closed local port can cause a weird\n    # ECONNRESET error later, inside the receive task. Make sure we're handling it\n    # properly.\n    async with dtls_echo_server(server_ctx=server_ctx) as (_, address):\n        with endpoint() as client_endpoint:\n            async with trio.open_nursery() as nursery:\n                for i in range(1, 10):\n                    channel = client_endpoint.connect((\"127.0.0.1\", i), client_ctx_fn())\n                    nursery.start_soon(channel.do_handshake)\n                channel = client_endpoint.connect(address, client_ctx_fn())\n                await channel.send(b\"xxx\")\n                assert await channel.receive() == b\"xxx\"\n                nursery.cancel_scope.cancel()\n"
  },
  {
    "path": "src/trio/_tests/test_exports.py",
    "content": "from __future__ import annotations  # isort: split\n\nimport __future__  # Regular import, not special!\n\nimport enum\nimport functools\nimport importlib\nimport inspect\nimport json\nimport socket as stdlib_socket\nimport sys\nimport types\nfrom pathlib import Path, PurePath\nfrom types import ModuleType\nfrom typing import TYPE_CHECKING, Any, Protocol\n\nimport attrs\nimport pytest\n\nimport trio\nimport trio.testing\nfrom trio._tests.pytest_plugin import RUN_SLOW, skip_if_optional_else_raise\n\nfrom .. import _core, _util\nfrom .._core._tests.tutil import slow\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable, Iterator\n\nmypy_cache_updated = False\n\n\ntry:  # If installed, check both versions of this class.\n    from typing_extensions import Protocol as Protocol_ext\nexcept ImportError:  # pragma: no cover\n    Protocol_ext = Protocol\n\n\ndef _ensure_mypy_cache_updated() -> None:\n    # This pollutes the `empty` dir. Should this be changed?\n    try:\n        from mypy.api import run\n    except ImportError as error:\n        skip_if_optional_else_raise(error)\n\n    global mypy_cache_updated\n    if not mypy_cache_updated:\n        # mypy cache was *probably* already updated by the other tests,\n        # but `pytest -k ...` might run just this test on its own\n        result = run(\n            [\n                \"--config-file=\",\n                \"--cache-dir=./.mypy_cache\",\n                \"--no-error-summary\",\n                \"-c\",\n                \"import trio\",\n            ],\n        )\n        assert not result[1]  # stderr\n        assert not result[0]  # stdout\n        mypy_cache_updated = True\n\n\ndef test_core_is_properly_reexported() -> None:\n    # Each export from _core should be re-exported by exactly one of these\n    # three modules:\n    sources = [trio, trio.lowlevel, trio.testing]\n    for symbol in dir(_core):\n        if symbol.startswith(\"_\"):\n            continue\n        found = 0\n        for source in sources:\n            if symbol in dir(source) and getattr(source, symbol) is getattr(\n                _core,\n                symbol,\n            ):\n                found += 1\n        print(symbol, found)\n        assert found == 1\n\n\ndef class_is_final(cls: type) -> bool:\n    \"\"\"Check if a class cannot be subclassed.\"\"\"\n    try:\n        # new_class() handles metaclasses properly, type(...) does not.\n        types.new_class(\"SubclassTester\", (cls,))\n    except TypeError:\n        return True\n    else:\n        return False\n\n\ndef iter_modules(\n    module: types.ModuleType,\n    only_public: bool,\n) -> Iterator[types.ModuleType]:\n    yield module\n    for name, class_ in module.__dict__.items():\n        if name.startswith(\"_\") and only_public:\n            continue\n        if not isinstance(class_, ModuleType):\n            continue\n        if not class_.__name__.startswith(module.__name__):  # pragma: no cover\n            continue\n        if class_ is module:  # pragma: no cover\n            continue\n        yield from iter_modules(class_, only_public)\n\n\nPUBLIC_MODULES = list(iter_modules(trio, only_public=True))\nALL_MODULES = list(iter_modules(trio, only_public=False))\nPUBLIC_MODULE_NAMES = [m.__name__ for m in PUBLIC_MODULES]\n\n\n# It doesn't make sense for downstream redistributors to run this test, since\n# they might be using a newer version of Python with additional symbols which\n# won't be reflected in trio.socket, and this shouldn't cause downstream test\n# runs to start failing.\n@pytest.mark.redistributors_should_skip\n@pytest.mark.skipif(\n    sys.version_info[:4] == (3, 14, 0, \"beta\"),\n    # 12 pass, 16 fail\n    reason=\"several tools don't support 3.14\",\n)\n# Static analysis tools often have trouble with alpha releases, where Python's\n# internals are in flux, grammar may not have settled down, etc.\n@pytest.mark.skipif(\n    sys.version_info.releaselevel == \"alpha\",\n    reason=\"skip static introspection tools on Python dev/alpha releases\",\n)\n@pytest.mark.parametrize(\"modname\", PUBLIC_MODULE_NAMES)\n@pytest.mark.parametrize(\"tool\", [\"pylint\", \"jedi\", \"mypy\", \"pyright_verifytypes\"])\n@pytest.mark.filterwarnings(\n    # https://github.com/pypa/setuptools/issues/3274\n    \"ignore:module 'sre_constants' is deprecated:DeprecationWarning\",\n)\ndef test_static_tool_sees_all_symbols(tool: str, modname: str, tmp_path: Path) -> None:\n    module = importlib.import_module(modname)\n\n    def no_underscores(symbols: Iterable[str]) -> set[str]:\n        return {symbol for symbol in symbols if not symbol.startswith(\"_\")}\n\n    runtime_names = no_underscores(dir(module))\n\n    # ignore deprecated module `tests` being invisible\n    if modname == \"trio\":\n        runtime_names.discard(\"tests\")\n\n    # Ignore any __future__ feature objects, if imported under that name.\n    for name in __future__.all_feature_names:\n        if getattr(module, name, None) is getattr(__future__, name):\n            runtime_names.remove(name)\n\n    if tool == \"pylint\":\n        try:\n            from pylint.lint import PyLinter\n        except ImportError as error:\n            skip_if_optional_else_raise(error)\n\n        linter = PyLinter()\n        assert module.__file__ is not None\n        ast = linter.get_ast(module.__file__, modname)\n        static_names = no_underscores(ast)  # type: ignore[arg-type]\n    elif tool == \"jedi\":\n        if sys.implementation.name != \"cpython\":\n            pytest.skip(\"jedi does not support pypy\")\n\n        try:\n            import jedi\n        except ImportError as error:\n            skip_if_optional_else_raise(error)\n\n        # Simulate typing \"import trio; trio.<TAB>\"\n        script = jedi.Script(f\"import {modname}; {modname}.\")\n        completions = script.complete()\n        static_names = no_underscores(c.name for c in completions)\n    elif tool == \"mypy\":\n        if sys.implementation.name != \"cpython\":\n            # https://github.com/python/mypy/issues/20329\n            pytest.skip(\"mypy does not support pypy\")\n\n        if not RUN_SLOW:  # pragma: no cover\n            pytest.skip(\"use --run-slow to check against mypy\")\n\n        cache = Path.cwd() / \".mypy_cache\"\n\n        _ensure_mypy_cache_updated()\n\n        trio_cache = next(cache.glob(\"*/trio\"))\n        _, modname = (modname + \".\").split(\".\", 1)\n        modname = modname[:-1]\n        mod_cache = trio_cache / modname if modname else trio_cache\n        if mod_cache.is_dir():  # pragma: no coverage\n            mod_cache = mod_cache / \"__init__.data.json\"\n        else:\n            mod_cache = trio_cache / (modname + \".data.json\")\n\n        assert mod_cache.exists()\n        assert mod_cache.is_file()\n        with mod_cache.open() as cache_file:\n            cache_json = json.loads(cache_file.read())\n            static_names = no_underscores(\n                key\n                for key, value in cache_json[\"names\"].items()\n                if not key.startswith(\".\") and value[\"kind\"] == \"Gdef\"\n            )\n    elif tool == \"pyright_verifytypes\":\n        if not RUN_SLOW:  # pragma: no cover\n            pytest.skip(\"use --run-slow to check against pyright\")\n\n        try:\n            import pyright  # noqa: F401\n        except ImportError as error:\n            skip_if_optional_else_raise(error)\n        import subprocess\n\n        res = subprocess.run(\n            [\"pyright\", f\"--verifytypes={modname}\", \"--outputjson\"],\n            capture_output=True,\n        )\n        current_result = json.loads(res.stdout)\n\n        static_names = {\n            x[\"name\"][len(modname) + 1 :]\n            for x in current_result[\"typeCompleteness\"][\"symbols\"]\n            if x[\"name\"].startswith(modname)\n        }\n    else:  # pragma: no cover\n        raise AssertionError()\n\n    # It's expected that the static set will contain more names than the\n    # runtime set:\n    # - static tools are sometimes sloppy and include deleted names\n    # - some symbols are platform-specific at runtime, but always show up in\n    #   static analysis (e.g. in trio.socket or trio.lowlevel)\n    # So we check that the runtime names are a subset of the static names.\n    missing_names = runtime_names - static_names\n\n    # ignore warnings about deprecated module tests\n    missing_names -= {\"tests\"}\n\n    if missing_names:  # pragma: no cover\n        print(f\"{tool} can't see the following names in {modname}:\")\n        print()\n        for name in sorted(missing_names):\n            print(f\"    {name}\")\n        raise AssertionError()\n\n\n@slow\n# see comment on test_static_tool_sees_all_symbols\n@pytest.mark.redistributors_should_skip\n# Static analysis tools often have trouble with alpha releases, where Python's\n# internals are in flux, grammar may not have settled down, etc.\n@pytest.mark.skipif(\n    sys.version_info.releaselevel == \"alpha\",\n    reason=\"skip static introspection tools on Python dev/alpha releases\",\n)\n@pytest.mark.parametrize(\"module_name\", PUBLIC_MODULE_NAMES)\n@pytest.mark.parametrize(\"tool\", [\"jedi\", \"mypy\"])\ndef test_static_tool_sees_class_members(\n    tool: str,\n    module_name: str,\n    tmp_path: Path,\n) -> None:\n    module = PUBLIC_MODULES[PUBLIC_MODULE_NAMES.index(module_name)]\n\n    # ignore hidden, but not dunder, symbols\n    def no_hidden(symbols: Iterable[str]) -> set[str]:\n        return {\n            symbol\n            for symbol in symbols\n            if (not symbol.startswith(\"_\")) or symbol.startswith(\"__\")\n        }\n\n    if tool == \"jedi\" and sys.implementation.name != \"cpython\":\n        pytest.skip(\"jedi does not support pypy\")\n\n    if tool == \"mypy\" and sys.implementation.name != \"cpython\":\n        # https://github.com/python/mypy/issues/20329\n        pytest.skip(\"mypy does not support pypy\")\n\n    if tool == \"mypy\":\n        cache = Path.cwd() / \".mypy_cache\"\n\n        _ensure_mypy_cache_updated()\n\n        trio_cache = next(cache.glob(\"*/trio\"))\n        modname = module_name\n        _, modname = (modname + \".\").split(\".\", 1)\n        modname = modname[:-1]\n        mod_cache = trio_cache / modname if modname else trio_cache\n        if mod_cache.is_dir():\n            mod_cache = mod_cache / \"__init__.data.json\"\n        else:\n            mod_cache = trio_cache / (modname + \".data.json\")\n\n        assert mod_cache.exists()\n        assert mod_cache.is_file()\n        with mod_cache.open() as cache_file:\n            cache_json = json.loads(cache_file.read())\n\n        # skip a bunch of file-system activity (probably can un-memoize?)\n        @functools.lru_cache\n        def lookup_symbol(symbol: str) -> dict[str, Any]:  # type: ignore[misc, explicit-any]\n            topname, *modname, name = symbol.split(\".\")\n            version = next(cache.glob(\"3.*/\"))\n            mod_cache = version / topname\n            if not mod_cache.is_dir():\n                mod_cache = version / (topname + \".data.json\")\n\n            if modname:\n                for piece in modname[:-1]:\n                    mod_cache /= piece\n                next_cache = mod_cache / modname[-1]\n                if next_cache.is_dir():  # pragma: no coverage\n                    mod_cache = next_cache / \"__init__.data.json\"\n                else:\n                    mod_cache = mod_cache / (modname[-1] + \".data.json\")\n            elif mod_cache.is_dir():\n                mod_cache /= \"__init__.data.json\"\n            with mod_cache.open() as f:\n                return json.loads(f.read())[\"names\"][name]  # type: ignore[no-any-return]\n\n    errors: dict[str, object] = {}\n    for class_name, class_ in module.__dict__.items():\n        if not isinstance(class_, type):\n            continue\n        if module_name == \"trio.socket\" and class_name in dir(stdlib_socket):\n            continue\n\n        # Ignore classes that don't use attrs, they only define their members once\n        # __init__ is called (and reason they don't use attrs is because they're going\n        # to be reimplemented in pytest).\n        # Not 100% that's the case, and it works locally, so whatever /shrug\n        if module_name == \"trio.testing\" and class_name in (\"_RaisesGroup\", \"_Matcher\"):\n            continue\n\n        # dir() and inspect.getmembers doesn't display properties from the metaclass\n        # also ignore some dunder methods that tend to differ but are of no consequence\n        ignore_names = set(dir(type(class_))) | {\n            \"__annotations__\",\n            \"__attrs_attrs__\",\n            \"__attrs_own_setattr__\",\n            \"__callable_proto_members_only__\",\n            \"__class_getitem__\",\n            \"__final__\",\n            \"__getstate__\",\n            \"__match_args__\",\n            \"__order__\",\n            \"__orig_bases__\",\n            \"__parameters__\",\n            \"__protocol_attrs__\",\n            \"__setstate__\",\n            \"__slots__\",\n            \"__weakref__\",\n            # ignore errors about dunders inherited from stdlib that tools might\n            # not see\n            \"__copy__\",\n            \"__deepcopy__\",\n        }\n\n        if type(class_) is type:\n            # C extension classes don't have these dunders, but Python classes do\n            ignore_names.add(\"__firstlineno__\")\n            ignore_names.add(\"__static_attributes__\")\n\n        # inspect.getmembers sees `name` and `value` in Enums, otherwise\n        # it behaves the same way as `dir`\n        # runtime_names = no_underscores(dir(class_))\n        runtime_names = (\n            no_hidden(x[0] for x in inspect.getmembers(class_)) - ignore_names\n        )\n\n        if tool == \"jedi\":\n            try:\n                import jedi\n            except ImportError as error:\n                skip_if_optional_else_raise(error)\n\n            script = jedi.Script(\n                f\"from {module_name} import {class_name}; {class_name}.\",\n            )\n            completions = script.complete()\n            static_names = no_hidden(c.name for c in completions) - ignore_names\n\n        elif tool == \"mypy\":\n            # load the cached type information\n            cached_type_info = cache_json[\"names\"][class_name]\n            assert (\n                \"node\" not in cached_type_info\n            ), \"previously this was an 'if' but it seems it's no longer possible for this cache to contain 'node', if this assert raises for you please let us know!\"\n            cached_type_info = lookup_symbol(cached_type_info[\"cross_ref\"])\n\n            assert \"node\" in cached_type_info\n            node = cached_type_info[\"node\"]\n            static_names = no_hidden(\n                k for k in node.get(\"names\", ()) if not k.startswith(\".\")\n            )\n            for symbol in node[\"mro\"][1:]:\n                node = lookup_symbol(symbol)[\"node\"]\n                static_names |= no_hidden(\n                    k for k in node.get(\"names\", ()) if not k.startswith(\".\")\n                )\n            static_names -= ignore_names\n\n        else:  # pragma: no cover\n            raise AssertionError(\"unknown tool\")\n\n        missing = runtime_names - static_names\n        extra = static_names - runtime_names\n\n        # using .remove() instead of .delete() to get an error in case they start not\n        # being missing\n\n        if (\n            tool == \"jedi\"\n            and BaseException in class_.__mro__\n            and sys.version_info >= (3, 11)\n        ):\n            missing.remove(\"add_note\")\n\n        if (\n            tool == \"mypy\"\n            and BaseException in class_.__mro__\n            and sys.version_info >= (3, 11)\n        ):\n            extra.remove(\"__notes__\")\n\n        if tool == \"mypy\" and attrs.has(class_):\n            # e.g. __trio__core__run_CancelScope_AttrsAttributes__\n            before = len(extra)\n            extra = {e for e in extra if not e.endswith(\"AttrsAttributes__\")}\n            assert len(extra) == before - 1\n\n        if attrs.has(class_):\n            # dynamically created attribute by attrs?\n            missing.remove(\"__attrs_props__\")\n\n        # dir does not see `__signature__` on enums until 3.14\n        if (\n            tool == \"mypy\"\n            and enum.Enum in class_.__mro__\n            and sys.version_info >= (3, 12)\n            and sys.version_info < (3, 14)\n        ):\n            extra.remove(\"__signature__\")\n\n        # TODO: this *should* be visible via `dir`!!\n        if tool == \"mypy\" and class_ == trio.Nursery:\n            extra.remove(\"cancel_scope\")\n\n        # These are (mostly? solely?) *runtime* attributes, often set in\n        # __init__, which doesn't show up with dir() or inspect.getmembers,\n        # but we get them in the way we query mypy & jedi\n        EXTRAS = {\n            trio.DTLSChannel: {\"peer_address\", \"endpoint\"},\n            trio.DTLSEndpoint: {\"socket\", \"incoming_packets_buffer\"},\n            trio.Process: {\"args\", \"pid\", \"stderr\", \"stdin\", \"stdio\", \"stdout\"},\n            trio.SSLListener: {\"transport_listener\"},\n            trio.SSLStream: {\"transport_stream\"},\n            trio.SocketListener: {\"socket\"},\n            trio.SocketStream: {\"socket\"},\n            trio.testing.MemoryReceiveStream: {\"close_hook\", \"receive_some_hook\"},\n            trio.testing.MemorySendStream: {\n                \"close_hook\",\n                \"send_all_hook\",\n                \"wait_send_all_might_not_block_hook\",\n            },\n        }\n        if tool == \"mypy\" and class_ in EXTRAS:\n            before = len(extra)\n            extra -= EXTRAS[class_]\n            assert len(extra) == before - len(EXTRAS[class_])\n\n        # TODO: why is this? Is it a problem?\n        # see https://github.com/python-trio/trio/pull/2631#discussion_r1185615916\n        if class_ == trio.StapledStream:\n            extra.remove(\"receive_stream\")\n            extra.remove(\"send_stream\")\n\n        # I have not researched why these are missing, should maybe create an issue\n        # upstream with jedi\n        if tool == \"jedi\" and sys.version_info >= (3, 11):\n            if class_ in (\n                trio.DTLSChannel,\n                trio.MemoryReceiveChannel,\n                trio.MemorySendChannel,\n                trio.SSLListener,\n                trio.SocketListener,\n            ):\n                missing.remove(\"__aenter__\")\n                missing.remove(\"__aexit__\")\n            if class_ in (trio.DTLSChannel, trio.MemoryReceiveChannel):\n                missing.remove(\"__aiter__\")\n                missing.remove(\"__anext__\")\n\n        if class_ in (trio.Path, trio.WindowsPath, trio.PosixPath):\n            # These are from inherited subclasses.\n            missing -= PurePath.__dict__.keys()\n            # These are unix-only.\n            if tool == \"mypy\" and sys.platform == \"win32\":\n                missing -= {\"owner\", \"is_mount\", \"group\"}\n            if tool == \"jedi\" and sys.platform == \"win32\":\n                extra -= {\"owner\", \"is_mount\", \"group\"}\n\n        # not sure why jedi in particular ignores this (static?) method in 3.13\n        if (\n            tool == \"jedi\"\n            and sys.version_info[:2] == (3, 13)\n            and class_ in (trio.Path, trio.WindowsPath, trio.PosixPath)\n        ):\n            missing.remove(\"with_segments\")\n\n        # tuple subclasses are weird\n        if issubclass(class_, tuple):\n            extra.remove(\"__reversed__\")\n            missing.remove(\"__getnewargs__\")\n\n        if sys.version_info >= (3, 13) and attrs.has(class_):\n            missing.remove(\"__replace__\")\n\n        if sys.version_info >= (3, 14):\n            # these depend on whether a class has processed deferred annotations.\n            # (which might or might not happen and we don't know)\n            missing.discard(\"__annotate_func__\")\n            missing.discard(\"__annotations_cache__\")\n\n        if missing or extra:  # pragma: no cover\n            errors[f\"{module_name}.{class_name}\"] = {\n                \"missing\": missing,\n                \"extra\": extra,\n            }\n\n    # `assert not errors` will not print the full content of errors, even with\n    # `--verbose`, so we manually print it\n    if errors:  # pragma: no cover\n        from pprint import pprint\n\n        print(f\"\\n{tool} can't see the following symbols in {module_name}:\")\n        pprint(errors)\n    assert not errors\n\n\ndef test_nopublic_is_final() -> None:\n    \"\"\"Check all NoPublicConstructor classes are also @final.\"\"\"\n    assert class_is_final(_util.NoPublicConstructor)  # This is itself final.\n\n    for module in ALL_MODULES:\n        for class_ in module.__dict__.values():\n            if isinstance(class_, _util.NoPublicConstructor):\n                assert class_is_final(class_)\n\n\ndef test_classes_are_final() -> None:\n    # Sanity checks.\n    assert not class_is_final(object)\n    assert class_is_final(bool)\n\n    for module in PUBLIC_MODULES:\n        for name, class_ in module.__dict__.items():\n            if not isinstance(class_, type):\n                continue\n            # Deprecated classes are exported with a leading underscore\n            if name.startswith(\"_\"):  # pragma: no cover\n                continue\n\n            # Abstract classes can be subclassed, because that's the whole\n            # point of ABCs\n            if inspect.isabstract(class_):\n                continue\n            # Same with protocols, but only direct children.\n            if Protocol in class_.__bases__ or Protocol_ext in class_.__bases__:\n                continue\n            # Exceptions are allowed to be subclassed, because exception\n            # subclassing isn't used to inherit behavior.\n            if issubclass(class_, BaseException):\n                continue\n            # These are classes that are conceptually abstract, but\n            # inspect.isabstract returns False for boring reasons.\n            if class_ is trio.abc.Instrument or class_ is trio.socket.SocketType:\n                continue\n            # ... insert other special cases here ...\n\n            # The `Path` class needs to support inheritance to allow `WindowsPath` and `PosixPath`.\n            if class_ is trio.Path:\n                continue\n            # don't care about the *Statistics classes\n            if name.endswith(\"Statistics\"):\n                continue\n\n            assert class_is_final(class_)\n\n\n# Plugin might not be running, especially if running from an installed version.\n@pytest.mark.skipif(\n    not hasattr(attrs.field, \"trio_modded\"),\n    reason=\"Pytest plugin not installed.\",\n)\ndef test_pyright_recognizes_init_attributes() -> None:\n    \"\"\"Check whether we provide `alias` for all underscore prefixed attributes.\n\n    Attrs always sets the `alias` attribute on fields, so a pytest plugin is used\n    to monkeypatch `field()` to record whether an alias was defined in the metadata.\n    See `_trio_check_attrs_aliases`.\n    \"\"\"\n    for module in PUBLIC_MODULES:\n        for class_ in module.__dict__.values():\n            if not attrs.has(class_):\n                continue\n            if isinstance(class_, _util.NoPublicConstructor):\n                continue\n\n            attributes = [\n                attr\n                for attr in attrs.fields(class_)\n                if attr.init\n                if attr.alias\n                not in (\n                    attr.name,\n                    # trio_original_args may not be present in autoattribs\n                    attr.metadata.get(\"trio_original_args\", {}).get(\"alias\"),\n                )\n            ]\n\n            assert attributes == [], class_\n"
  },
  {
    "path": "src/trio/_tests/test_fakenet.py",
    "content": "import errno\nimport re\nimport socket\nimport sys\n\nimport pytest\n\nimport trio\nfrom trio.testing._fake_net import FakeNet\n\n# ENOTCONN gives different messages on different platforms\nif sys.platform == \"linux\":\n    ENOTCONN_MSG = r\"^\\[Errno 107\\] (Transport endpoint is|Socket) not connected$\"\nelif sys.platform == \"darwin\":\n    ENOTCONN_MSG = r\"^\\[Errno 57\\] Socket is not connected$\"\nelse:\n    ENOTCONN_MSG = r\"^\\[Errno 10057\\] Unknown error$\"\n\n\ndef fn() -> FakeNet:\n    fn = FakeNet()\n    fn.enable()\n    return fn\n\n\nasync def test_basic_udp() -> None:\n    fn()\n    s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n    s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n\n    await s1.bind((\"127.0.0.1\", 0))\n    ip, port = s1.getsockname()\n    assert ip == \"127.0.0.1\"\n    assert port != 0\n\n    with pytest.raises(\n        OSError,\n        match=r\"^\\[\\w+ \\d+\\] Invalid argument$\",\n    ) as exc:  # Cannot rebind.\n        await s1.bind((\"192.0.2.1\", 0))\n    assert exc.value.errno == errno.EINVAL\n\n    # Cannot bind multiple sockets to the same address\n    with pytest.raises(\n        OSError,\n        match=r\"^\\[\\w+ \\d+\\] (Address (already )?in use|Unknown error)$\",\n    ) as exc:\n        await s2.bind((\"127.0.0.1\", port))\n    assert exc.value.errno == errno.EADDRINUSE\n\n    await s2.sendto(b\"xyz\", s1.getsockname())\n    data, addr = await s1.recvfrom(10)\n    assert data == b\"xyz\"\n    assert addr == s2.getsockname()\n    await s1.sendto(b\"abc\", s2.getsockname())\n    data, addr = await s2.recvfrom(10)\n    assert data == b\"abc\"\n    assert addr == s1.getsockname()\n\n\nasync def test_msg_trunc() -> None:\n    fn()\n    s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n    s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n    await s1.bind((\"127.0.0.1\", 0))\n    await s2.sendto(b\"xyz\", s1.getsockname())\n    await s1.recvfrom(10)\n\n\nasync def test_recv_methods() -> None:\n    \"\"\"Test all recv methods for codecov\"\"\"\n    fn()\n    s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n    s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n\n    # receiving on an unbound socket is a bad idea (I think?)\n    with pytest.raises(NotImplementedError, match=\"code will most likely hang\"):\n        await s2.recv(10)\n\n    await s1.bind((\"127.0.0.1\", 0))\n    ip, port = s1.getsockname()\n    assert ip == \"127.0.0.1\"\n    assert port != 0\n\n    # recvfrom\n    await s2.sendto(b\"abc\", s1.getsockname())\n    data, addr = await s1.recvfrom(10)\n    assert data == b\"abc\"\n    assert addr == s2.getsockname()\n\n    # recv\n    await s1.sendto(b\"def\", s2.getsockname())\n    data = await s2.recv(10)\n    assert data == b\"def\"\n\n    # recvfrom_into\n    assert await s1.sendto(b\"ghi\", s2.getsockname()) == 3\n    buf = bytearray(10)\n\n    with pytest.raises(NotImplementedError, match=r\"^partial recvfrom_into$\"):\n        nbytes, addr = await s2.recvfrom_into(buf, nbytes=2)\n\n    nbytes, addr = await s2.recvfrom_into(buf)\n    assert nbytes == 3\n    assert buf == b\"ghi\" + b\"\\x00\" * 7\n    assert addr == s1.getsockname()\n\n    # recv_into\n    assert await s1.sendto(b\"jkl\", s2.getsockname()) == 3\n    buf2 = bytearray(10)\n    nbytes = await s2.recv_into(buf2)\n    assert nbytes == 3\n    assert buf2 == b\"jkl\" + b\"\\x00\" * 7\n\n    if sys.platform == \"linux\" and sys.implementation.name == \"cpython\":\n        flags: int = socket.MSG_MORE\n    else:\n        flags = 1\n\n    # Send seems explicitly non-functional\n    with pytest.raises(OSError, match=ENOTCONN_MSG) as exc:\n        await s2.send(b\"mno\")\n    assert exc.value.errno == errno.ENOTCONN\n    with pytest.raises(\n        NotImplementedError, match=r\"^FakeNet send flags must be 0, not\"\n    ):\n        await s2.send(b\"mno\", flags)\n\n    # sendto errors\n    # it's successfully used earlier\n    with pytest.raises(\n        NotImplementedError, match=r\"^FakeNet send flags must be 0, not\"\n    ):\n        await s2.sendto(b\"mno\", flags, s1.getsockname())\n    with pytest.raises(TypeError, match=r\"wrong number of arguments$\"):\n        await s2.sendto(b\"mno\", flags, s1.getsockname(), \"extra arg\")  # type: ignore[call-overload]\n\n\n@pytest.mark.skipif(\n    sys.platform == \"win32\",\n    reason=\"functions not in socket on windows\",\n)\nasync def test_nonwindows_functionality() -> None:\n    # mypy doesn't support a good way of aborting typechecking on different platforms\n    if sys.platform != \"win32\":  # pragma: no branch\n        fn()\n        s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n        s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n        await s2.bind((\"127.0.0.1\", 0))\n\n        # sendmsg\n        with pytest.raises(OSError, match=ENOTCONN_MSG) as exc:\n            await s2.sendmsg([b\"mno\"])\n        assert exc.value.errno == errno.ENOTCONN\n\n        assert await s1.sendmsg([b\"jkl\"], (), 0, s2.getsockname()) == 3\n        data, ancdata, msg_flags, addr = await s2.recvmsg(10)\n        assert data == b\"jkl\"\n        assert ancdata == []\n        assert msg_flags == 0\n        assert addr == s1.getsockname()\n\n        # TODO: recvmsg\n\n        # recvmsg_into\n        assert await s1.sendto(b\"xyzw\", s2.getsockname()) == 4\n        buf1 = bytearray(2)\n        buf2 = bytearray(3)\n        ret = await s2.recvmsg_into([buf1, buf2])\n        nbytes, ancdata, msg_flags, addr = ret\n        assert nbytes == 4\n        assert buf1 == b\"xy\"\n        assert buf2 == b\"zw\" + b\"\\x00\"\n        assert ancdata == []\n        assert msg_flags == 0\n        assert addr == s1.getsockname()\n\n        # recvmsg_into with MSG_TRUNC set\n        assert await s1.sendto(b\"xyzwv\", s2.getsockname()) == 5\n        buf1 = bytearray(2)\n        ret = await s2.recvmsg_into([buf1])\n        nbytes, ancdata, msg_flags, addr = ret\n        assert nbytes == 2\n        assert buf1 == b\"xy\"\n        assert ancdata == []\n        assert msg_flags == socket.MSG_TRUNC\n        assert addr == s1.getsockname()\n\n        with pytest.raises(\n            AttributeError,\n            match=r\"^'FakeSocket' object has no attribute 'share'$\",\n        ):\n            await s1.share(0)  # type: ignore[attr-defined]\n\n\n@pytest.mark.skipif(\n    sys.platform != \"win32\",\n    reason=\"windows-specific fakesocket testing\",\n)\nasync def test_windows_functionality() -> None:\n    # mypy doesn't support a good way of aborting typechecking on different platforms\n    if sys.platform == \"win32\":  # pragma: no branch\n        fn()\n        s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n        s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n        await s1.bind((\"127.0.0.1\", 0))\n        with pytest.raises(\n            AttributeError,\n            match=r\"^'FakeSocket' object has no attribute 'sendmsg'$\",\n        ):\n            await s1.sendmsg([b\"jkl\"], (), 0, s2.getsockname())  # type: ignore[attr-defined]\n        with pytest.raises(\n            AttributeError,\n            match=r\"^'FakeSocket' object has no attribute 'recvmsg'$\",\n        ):\n            s2.recvmsg(0)  # type: ignore[attr-defined]\n        with pytest.raises(\n            AttributeError,\n            match=r\"^'FakeSocket' object has no attribute 'recvmsg_into'$\",\n        ):\n            s2.recvmsg_into([])  # type: ignore[attr-defined]\n        with pytest.raises(NotImplementedError):\n            s1.share(0)\n\n\nasync def test_basic_tcp() -> None:\n    fn()\n    with pytest.raises(NotImplementedError):\n        trio.socket.socket()\n\n\nasync def test_not_implemented_functions() -> None:\n    fn()\n    s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n\n    # getsockopt\n    with pytest.raises(\n        OSError,\n        match=r\"^FakeNet doesn't implement getsockopt\\(\\d, \\d\\)$\",\n    ):\n        s1.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)\n\n    # setsockopt\n    with pytest.raises(\n        NotImplementedError,\n        match=r\"^FakeNet always has IPV6_V6ONLY=True$\",\n    ):\n        s1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)\n    with pytest.raises(\n        OSError,\n        match=r\"^FakeNet doesn't implement setsockopt\\(\\d+, \\d+, \\.\\.\\.\\)$\",\n    ):\n        s1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, True)\n    with pytest.raises(\n        OSError,\n        match=r\"^FakeNet doesn't implement setsockopt\\(\\d+, \\d+, \\.\\.\\.\\)$\",\n    ):\n        s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n\n    # set_inheritable\n    s1.set_inheritable(False)\n    with pytest.raises(\n        NotImplementedError,\n        match=r\"^FakeNet can't make inheritable sockets$\",\n    ):\n        s1.set_inheritable(True)\n\n    # get_inheritable\n    assert not s1.get_inheritable()\n\n\nasync def test_getpeername() -> None:\n    fn()\n    s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n    with pytest.raises(OSError, match=ENOTCONN_MSG) as exc:\n        s1.getpeername()\n    assert exc.value.errno == errno.ENOTCONN\n\n    await s1.bind((\"127.0.0.1\", 0))\n\n    with pytest.raises(\n        AssertionError,\n        match=r\"^This method seems to assume that self._binding has a remote UDPEndpoint$\",\n    ):\n        s1.getpeername()\n\n\nasync def test_init() -> None:\n    fn()\n    with pytest.raises(\n        NotImplementedError,\n        match=re.escape(\n            f\"FakeNet doesn't (yet) support type={trio.socket.SOCK_STREAM}\",\n        ),\n    ):\n        s1 = trio.socket.socket()\n\n    # getsockname on unbound ipv4 socket\n    s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)\n    assert s1.getsockname() == (\"0.0.0.0\", 0)\n\n    # getsockname on bound ipv4 socket\n    await s1.bind((\"0.0.0.0\", 0))\n    ip, port = s1.getsockname()\n    assert ip == \"127.0.0.1\"\n    assert port != 0\n\n    # getsockname on unbound ipv6 socket\n    s2 = trio.socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)\n    assert s2.getsockname() == (\"::\", 0)\n\n    # getsockname on bound ipv6 socket\n    await s2.bind((\"::\", 0))\n    ip, port, *_ = s2.getsockname()\n    assert ip == \"::1\"\n    assert port != 0\n    assert _ == [0, 0]\n"
  },
  {
    "path": "src/trio/_tests/test_file_io.py",
    "content": "from __future__ import annotations\n\nimport importlib\nimport io\nimport os\nimport re\nfrom typing import TYPE_CHECKING\nfrom unittest import mock\nfrom unittest.mock import sentinel\n\nimport pytest\n\nimport trio\nfrom trio import _core, _file_io\nfrom trio._file_io import _FILE_ASYNC_METHODS, _FILE_SYNC_ATTRS, AsyncIOWrapper\n\nif TYPE_CHECKING:\n    import pathlib\n\n\n@pytest.fixture\ndef path(tmp_path: pathlib.Path) -> str:\n    return os.fspath(tmp_path / \"test\")\n\n\n@pytest.fixture\ndef wrapped() -> mock.Mock:\n    return mock.Mock(spec_set=io.StringIO)\n\n\n@pytest.fixture\ndef async_file(wrapped: mock.Mock) -> AsyncIOWrapper[mock.Mock]:\n    return trio.wrap_file(wrapped)\n\n\ndef test_wrap_invalid() -> None:\n    with pytest.raises(TypeError):\n        trio.wrap_file(\"\")\n\n\ndef test_wrap_non_iobase() -> None:\n    class FakeFile:\n        def close(self) -> None:  # pragma: no cover\n            pass\n\n        def write(self) -> None:  # pragma: no cover\n            pass\n\n    wrapped = FakeFile()\n    assert not isinstance(wrapped, io.IOBase)\n\n    async_file = trio.wrap_file(wrapped)\n    assert isinstance(async_file, AsyncIOWrapper)\n\n    del FakeFile.write\n\n    with pytest.raises(TypeError):\n        trio.wrap_file(FakeFile())\n\n\ndef test_wrapped_property(\n    async_file: AsyncIOWrapper[mock.Mock],\n    wrapped: mock.Mock,\n) -> None:\n    assert async_file.wrapped is wrapped\n\n\ndef test_dir_matches_wrapped(\n    async_file: AsyncIOWrapper[mock.Mock],\n    wrapped: mock.Mock,\n) -> None:\n    attrs = _FILE_SYNC_ATTRS.union(_FILE_ASYNC_METHODS)\n\n    # all supported attrs in wrapped should be available in async_file\n    assert all(attr in dir(async_file) for attr in attrs if attr in dir(wrapped))\n    # all supported attrs not in wrapped should not be available in async_file\n    assert not any(\n        attr in dir(async_file) for attr in attrs if attr not in dir(wrapped)\n    )\n\n\ndef test_unsupported_not_forwarded() -> None:\n    class FakeFile(io.RawIOBase):\n        def unsupported_attr(self) -> None:  # pragma: no cover\n            pass\n\n    async_file = trio.wrap_file(FakeFile())\n\n    assert hasattr(async_file.wrapped, \"unsupported_attr\")\n\n    with pytest.raises(AttributeError):\n        # B018 \"useless expression\"\n        async_file.unsupported_attr  # type: ignore[attr-defined] # noqa: B018\n\n\ndef test_type_stubs_match_lists() -> None:\n    \"\"\"Check the manual stubs match the list of wrapped methods.\"\"\"\n    # Fetch the module's source code.\n    assert _file_io.__spec__ is not None\n    loader = _file_io.__spec__.loader\n    assert isinstance(loader, importlib.abc.SourceLoader)\n    source = io.StringIO(loader.get_source(\"trio._file_io\"))\n\n    # Find the class, then find the TYPE_CHECKING block.\n    for line in source:\n        if \"class AsyncIOWrapper\" in line:\n            break\n    else:  # pragma: no cover - should always find this\n        pytest.fail(\"No class definition line?\")\n\n    for line in source:\n        if \"if TYPE_CHECKING\" in line:\n            break\n    else:  # pragma: no cover - should always find this\n        pytest.fail(\"No TYPE CHECKING line?\")\n\n    # Now we should be at the type checking block.\n    found: list[tuple[str, str]] = []\n    for line in source:  # pragma: no branch - expected to break early\n        if line.strip() and not line.startswith(\" \" * 8):\n            break  # Dedented out of the if TYPE_CHECKING block.\n        match = re.match(r\"\\s*(async )?def ([a-zA-Z0-9_]+)\\(\", line)\n        if match is not None:\n            kind = \"async\" if match.group(1) is not None else \"sync\"\n            found.append((match.group(2), kind))\n\n    # Compare two lists so that we can easily see duplicates, and see what is different overall.\n    expected = [(fname, \"async\") for fname in _FILE_ASYNC_METHODS]\n    expected += [(fname, \"sync\") for fname in _FILE_SYNC_ATTRS]\n    # Ignore order, error if duplicates are present.\n    found.sort()\n    expected.sort()\n    assert found == expected\n\n\ndef test_sync_attrs_forwarded(\n    async_file: AsyncIOWrapper[mock.Mock],\n    wrapped: mock.Mock,\n) -> None:\n    for attr_name in _FILE_SYNC_ATTRS:\n        if attr_name not in dir(async_file):\n            continue\n\n        assert getattr(async_file, attr_name) is getattr(wrapped, attr_name)\n\n\ndef test_sync_attrs_match_wrapper(\n    async_file: AsyncIOWrapper[mock.Mock],\n    wrapped: mock.Mock,\n) -> None:\n    for attr_name in _FILE_SYNC_ATTRS:\n        if attr_name in dir(async_file):\n            continue\n\n        with pytest.raises(AttributeError):\n            getattr(async_file, attr_name)\n\n        with pytest.raises(AttributeError):\n            getattr(wrapped, attr_name)\n\n\ndef test_async_methods_generated_once(async_file: AsyncIOWrapper[mock.Mock]) -> None:\n    for meth_name in _FILE_ASYNC_METHODS:\n        if meth_name not in dir(async_file):\n            continue\n\n        assert getattr(async_file, meth_name) is getattr(async_file, meth_name)\n\n\n# I gave up on typing this one\ndef test_async_methods_signature(async_file: AsyncIOWrapper[mock.Mock]) -> None:\n    # use read as a representative of all async methods\n    assert async_file.read.__name__ == \"read\"\n    assert async_file.read.__qualname__ == \"AsyncIOWrapper.read\"\n\n    assert async_file.read.__doc__ is not None\n    assert \"io.StringIO.read\" in async_file.read.__doc__\n\n\nasync def test_async_methods_wrap(\n    async_file: AsyncIOWrapper[mock.Mock],\n    wrapped: mock.Mock,\n) -> None:\n    for meth_name in _FILE_ASYNC_METHODS:\n        if meth_name not in dir(async_file):\n            continue\n\n        meth = getattr(async_file, meth_name)\n        wrapped_meth = getattr(wrapped, meth_name)\n\n        value = await meth(sentinel.argument, keyword=sentinel.keyword)\n\n        wrapped_meth.assert_called_once_with(\n            sentinel.argument,\n            keyword=sentinel.keyword,\n        )\n        assert value == wrapped_meth()\n\n        wrapped.reset_mock()\n\n\ndef test_async_methods_match_wrapper(\n    async_file: AsyncIOWrapper[mock.Mock],\n    wrapped: mock.Mock,\n) -> None:\n    for meth_name in _FILE_ASYNC_METHODS:\n        if meth_name in dir(async_file):\n            continue\n\n        with pytest.raises(AttributeError):\n            getattr(async_file, meth_name)\n\n        with pytest.raises(AttributeError):\n            getattr(wrapped, meth_name)\n\n\nasync def test_open(path: pathlib.Path) -> None:\n    f = await trio.open_file(path, \"w\")\n\n    assert isinstance(f, AsyncIOWrapper)\n\n    await f.aclose()\n\n\nasync def test_open_context_manager(path: pathlib.Path) -> None:\n    async with await trio.open_file(path, \"w\") as f:\n        assert isinstance(f, AsyncIOWrapper)\n        assert not f.closed\n\n    assert f.closed\n\n\nasync def test_async_iter() -> None:\n    async_file = trio.wrap_file(io.StringIO(\"test\\nfoo\\nbar\"))\n    expected = list(async_file.wrapped)\n    async_file.wrapped.seek(0)\n\n    result = [line async for line in async_file]\n\n    assert result == expected\n\n\nasync def test_aclose_cancelled(path: pathlib.Path) -> None:\n    with _core.CancelScope() as cscope:\n        f = await trio.open_file(path, \"w\")\n        cscope.cancel()\n\n        with pytest.raises(_core.Cancelled):\n            await f.write(\"a\")\n\n        with pytest.raises(_core.Cancelled):\n            await f.aclose()\n\n    assert f.closed\n\n\nasync def test_detach_rewraps_asynciobase(tmp_path: pathlib.Path) -> None:\n    tmp_file = tmp_path / \"filename\"\n    tmp_file.touch()\n    # flake8-async does not like opening files in async mode\n    with open(tmp_file, mode=\"rb\", buffering=0) as raw:  # noqa: ASYNC230\n        buffered = io.BufferedReader(raw)\n\n        async_file = trio.wrap_file(buffered)\n\n        detached = await async_file.detach()\n\n        assert isinstance(detached, AsyncIOWrapper)\n        assert detached.wrapped is raw\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_generic.py",
    "content": "from __future__ import annotations\n\nfrom typing import NoReturn\n\nimport attrs\nimport pytest\n\nfrom .._highlevel_generic import StapledStream\nfrom ..abc import ReceiveStream, SendStream\n\n\n@attrs.define(slots=False)\nclass RecordSendStream(SendStream):\n    record: list[str | tuple[str, object]] = attrs.Factory(list)\n\n    async def send_all(self, data: object) -> None:\n        self.record.append((\"send_all\", data))\n\n    async def wait_send_all_might_not_block(self) -> None:\n        self.record.append(\"wait_send_all_might_not_block\")\n\n    async def aclose(self) -> None:\n        self.record.append(\"aclose\")\n\n\n@attrs.define(slots=False)\nclass RecordReceiveStream(ReceiveStream):\n    record: list[str | tuple[str, int | None]] = attrs.Factory(list)\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytes:\n        self.record.append((\"receive_some\", max_bytes))\n        return b\"\"\n\n    async def aclose(self) -> None:\n        self.record.append(\"aclose\")\n\n\nasync def test_StapledStream() -> None:\n    send_stream = RecordSendStream()\n    receive_stream = RecordReceiveStream()\n    stapled = StapledStream(send_stream, receive_stream)\n\n    assert stapled.send_stream is send_stream\n    assert stapled.receive_stream is receive_stream\n\n    await stapled.send_all(b\"foo\")\n    await stapled.wait_send_all_might_not_block()\n    assert send_stream.record == [\n        (\"send_all\", b\"foo\"),\n        \"wait_send_all_might_not_block\",\n    ]\n    send_stream.record.clear()\n\n    await stapled.send_eof()\n    assert send_stream.record == [\"aclose\"]\n    send_stream.record.clear()\n\n    async def fake_send_eof() -> None:\n        send_stream.record.append(\"send_eof\")\n\n    send_stream.send_eof = fake_send_eof  # type: ignore[attr-defined]\n    await stapled.send_eof()\n    assert send_stream.record == [\"send_eof\"]\n\n    send_stream.record.clear()\n    assert receive_stream.record == []\n\n    await stapled.receive_some(1234)\n    assert receive_stream.record == [(\"receive_some\", 1234)]\n    assert send_stream.record == []\n    receive_stream.record.clear()\n\n    await stapled.aclose()\n    assert receive_stream.record == [\"aclose\"]\n    assert send_stream.record == [\"aclose\"]\n\n\nasync def test_StapledStream_with_erroring_close() -> None:\n    # Make sure that if one of the aclose methods errors out, then the other\n    # one still gets called.\n    class BrokenSendStream(RecordSendStream):\n        async def aclose(self) -> NoReturn:\n            await super().aclose()\n            raise ValueError(\"send error\")\n\n    class BrokenReceiveStream(RecordReceiveStream):\n        async def aclose(self) -> NoReturn:\n            await super().aclose()\n            raise ValueError(\"recv error\")\n\n    stapled = StapledStream(BrokenSendStream(), BrokenReceiveStream())\n\n    with pytest.raises(ValueError, match=r\"^(send|recv) error$\") as excinfo:\n        await stapled.aclose()\n    assert isinstance(excinfo.value.__context__, ValueError)\n\n    assert stapled.send_stream.record == [\"aclose\"]\n    assert stapled.receive_stream.record == [\"aclose\"]\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_open_tcp_listeners.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport socket as stdlib_socket\nimport sys\nfrom socket import AddressFamily, SocketKind\nfrom typing import TYPE_CHECKING, cast, overload\n\nimport attrs\nimport pytest\n\nimport trio\nfrom trio import (\n    SocketListener,\n    open_tcp_listeners,\n    open_tcp_stream,\n    serve_tcp,\n)\nfrom trio.abc import HostnameResolver, SendStream, SocketFactory\nfrom trio.testing import open_stream_to_socket_listener\n\nfrom .. import socket as tsocket\nfrom .._core._tests.tutil import binds_ipv6, slow\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n    from typing_extensions import Buffer\n\n    from trio._socket import AddressFormat\n\n\nasync def test_open_tcp_listeners_basic() -> None:\n    listeners = await open_tcp_listeners(0)\n    assert isinstance(listeners, list)\n    for obj in listeners:\n        assert isinstance(obj, SocketListener)\n        # Binds to wildcard address by default\n        assert obj.socket.family in [tsocket.AF_INET, tsocket.AF_INET6]\n        assert obj.socket.getsockname()[0] in [\"0.0.0.0\", \"::\"]\n\n    listener = listeners[0]\n    # Make sure the backlog is at least 2\n    c1 = await open_stream_to_socket_listener(listener)\n    c2 = await open_stream_to_socket_listener(listener)\n\n    s1 = await listener.accept()\n    s2 = await listener.accept()\n\n    # Note that we don't know which client stream is connected to which server\n    # stream\n    await s1.send_all(b\"x\")\n    await s2.send_all(b\"x\")\n    assert await c1.receive_some(1) == b\"x\"\n    assert await c2.receive_some(1) == b\"x\"\n\n    for resource in [c1, c2, s1, s2, *listeners]:\n        await resource.aclose()\n\n\nasync def test_open_tcp_listeners_specific_port_specific_host() -> None:\n    # Pick a port\n    sock = tsocket.socket()\n    await sock.bind((\"127.0.0.1\", 0))\n    host, port = sock.getsockname()\n    sock.close()\n\n    (listener,) = await open_tcp_listeners(port, host=host)\n    async with listener:\n        assert listener.socket.getsockname() == (host, port)\n\n\n@binds_ipv6\n@slow\nasync def test_open_tcp_listeners_ipv6_v6only() -> None:\n    # Check IPV6_V6ONLY is working properly\n    (ipv6_listener,) = await open_tcp_listeners(0, host=\"::1\")\n    async with ipv6_listener:\n        _, port, *_ = ipv6_listener.socket.getsockname()\n\n        with pytest.raises(\n            OSError,\n            match=r\"(Error|all attempts to) connect(ing)* to (\\(')*127\\.0\\.0\\.1(', |:)\\d+(\\): Connection refused| failed)$\",\n        ):\n            # Windows retries failed connections so this takes seconds\n            # (and that's why this is marked @slow)\n            await open_tcp_stream(\"127.0.0.1\", port)\n\n\nasync def test_open_tcp_listeners_rebind() -> None:\n    (l1,) = await open_tcp_listeners(0, host=\"127.0.0.1\")\n    sockaddr1 = l1.socket.getsockname()\n\n    # Plain old rebinding while it's still there should fail, even if we have\n    # SO_REUSEADDR set\n    with stdlib_socket.socket() as probe:\n        probe.setsockopt(stdlib_socket.SOL_SOCKET, stdlib_socket.SO_REUSEADDR, 1)\n        with pytest.raises(\n            OSError,\n            match=r\"(Address (already )?in use|An attempt was made to access a socket in a way forbidden by its access permissions)$\",\n        ):\n            probe.bind(sockaddr1)\n\n    # Now use the first listener to set up some connections in various states,\n    # and make sure that they don't create any obstacle to rebinding a second\n    # listener after the first one is closed.\n    c_established = await open_stream_to_socket_listener(l1)\n    s_established = await l1.accept()\n\n    c_time_wait = await open_stream_to_socket_listener(l1)\n    s_time_wait = await l1.accept()\n    # Server-initiated close leaves socket in TIME_WAIT\n    await s_time_wait.aclose()\n\n    await l1.aclose()\n    (l2,) = await open_tcp_listeners(sockaddr1[1], host=\"127.0.0.1\")\n    sockaddr2 = l2.socket.getsockname()\n\n    assert sockaddr1 == sockaddr2\n    assert s_established.socket.getsockname() == sockaddr2\n    assert c_time_wait.socket.getpeername() == sockaddr2\n\n    for resource in [\n        l1,\n        l2,\n        c_established,\n        s_established,\n        c_time_wait,\n        s_time_wait,\n    ]:\n        await resource.aclose()\n\n\nclass FakeOSError(OSError):\n    pass\n\n\n@attrs.define(slots=False)\nclass FakeSocket(tsocket.SocketType):\n    _family: AddressFamily = attrs.field(converter=AddressFamily)\n    _type: SocketKind = attrs.field(converter=SocketKind)\n    _proto: int\n\n    closed: bool = False\n    poison_listen: bool = False\n    backlog: int | None = None\n\n    @property\n    def type(self) -> SocketKind:\n        return self._type\n\n    @property\n    def family(self) -> AddressFamily:\n        return self._family\n\n    @property\n    def proto(self) -> int:  # pragma: no cover\n        return self._proto\n\n    @overload\n    def getsockopt(self, /, level: int, optname: int) -> int: ...\n\n    @overload\n    def getsockopt(self, /, level: int, optname: int, buflen: int) -> bytes: ...\n\n    def getsockopt(\n        self,\n        /,\n        level: int,\n        optname: int,\n        buflen: int | None = None,\n    ) -> int | bytes:\n        if (level, optname) == (tsocket.SOL_SOCKET, tsocket.SO_ACCEPTCONN):\n            return True\n        raise AssertionError()  # pragma: no cover\n\n    @overload\n    def setsockopt(self, /, level: int, optname: int, value: int | Buffer) -> None: ...\n\n    @overload\n    def setsockopt(\n        self,\n        /,\n        level: int,\n        optname: int,\n        value: None,\n        optlen: int,\n    ) -> None: ...\n\n    def setsockopt(\n        self,\n        /,\n        level: int,\n        optname: int,\n        value: int | Buffer | None,\n        optlen: int | None = None,\n    ) -> None:\n        pass\n\n    async def bind(self, address: AddressFormat) -> None:\n        pass\n\n    def listen(self, /, backlog: int = min(stdlib_socket.SOMAXCONN, 128)) -> None:\n        assert self.backlog is None\n        assert backlog is not None\n        self.backlog = backlog\n        if self.poison_listen:\n            raise FakeOSError(\"whoops\")\n\n    def close(self) -> None:\n        self.closed = True\n\n\n@attrs.define(slots=False)\nclass FakeSocketFactory(SocketFactory):\n    poison_after: int\n    sockets: list[tsocket.SocketType] = attrs.Factory(list)\n    raise_on_family: dict[AddressFamily, int] = attrs.Factory(dict)  # family => errno\n\n    def socket(\n        self,\n        family: AddressFamily | int | None = None,\n        type_: SocketKind | int | None = None,\n        proto: int = 0,\n    ) -> tsocket.SocketType:\n        assert family is not None\n        assert type_ is not None\n        if isinstance(family, int) and not isinstance(family, AddressFamily):\n            family = AddressFamily(family)  # pragma: no cover\n        if family in self.raise_on_family:\n            raise OSError(self.raise_on_family[family], \"nope\")\n        sock = FakeSocket(family, type_, proto)\n        self.poison_after -= 1\n        if self.poison_after == 0:\n            sock.poison_listen = True\n        self.sockets.append(sock)\n        return sock\n\n\n@attrs.define(slots=False)\nclass FakeHostnameResolver(HostnameResolver):\n    family_addr_pairs: Sequence[tuple[AddressFamily, str]]\n\n    async def getaddrinfo(\n        self,\n        host: bytes | None,\n        port: bytes | str | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> list[\n        tuple[\n            AddressFamily,\n            SocketKind,\n            int,\n            str,\n            tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n        ]\n    ]:\n        assert isinstance(port, int)\n        return [\n            (family, tsocket.SOCK_STREAM, 0, \"\", (addr, port))\n            for family, addr in self.family_addr_pairs\n        ]\n\n    async def getnameinfo(\n        self,\n        sockaddr: tuple[str, int] | tuple[str, int, int, int],\n        flags: int,\n    ) -> tuple[str, str]:\n        raise NotImplementedError()\n\n\nasync def test_open_tcp_listeners_multiple_host_cleanup_on_error() -> None:\n    # If we were trying to bind to multiple hosts and one of them failed, they\n    # call get cleaned up before returning\n    fsf = FakeSocketFactory(3)\n    tsocket.set_custom_socket_factory(fsf)\n    tsocket.set_custom_hostname_resolver(\n        FakeHostnameResolver(\n            [\n                (tsocket.AF_INET, \"1.1.1.1\"),\n                (tsocket.AF_INET, \"2.2.2.2\"),\n                (tsocket.AF_INET, \"3.3.3.3\"),\n            ],\n        ),\n    )\n\n    with pytest.raises(FakeOSError):\n        await open_tcp_listeners(80, host=\"example.org\")\n\n    assert len(fsf.sockets) == 3\n    for sock in fsf.sockets:\n        # property only exists on FakeSocket\n        assert sock.closed  # type: ignore[attr-defined]\n\n\nasync def test_open_tcp_listeners_port_checking() -> None:\n    for host in [\"127.0.0.1\", None]:\n        with pytest.raises(TypeError):\n            await open_tcp_listeners(None, host=host)  # type: ignore[arg-type]\n        with pytest.raises(TypeError):\n            await open_tcp_listeners(b\"80\", host=host)  # type: ignore[arg-type]\n        with pytest.raises(TypeError):\n            await open_tcp_listeners(\"http\", host=host)  # type: ignore[arg-type]\n\n\nasync def test_serve_tcp() -> None:\n    async def handler(stream: SendStream) -> None:\n        await stream.send_all(b\"x\")\n\n    async with trio.open_nursery() as nursery:\n        # nursery.start is incorrectly typed, awaiting #2773\n        value = await nursery.start(serve_tcp, handler, 0)\n        assert isinstance(value, list)\n        listeners = cast(\"list[SocketListener]\", value)\n        stream = await open_stream_to_socket_listener(listeners[0])\n        async with stream:\n            assert await stream.receive_some(1) == b\"x\"\n            nursery.cancel_scope.cancel()\n\n\n@pytest.mark.parametrize(\n    \"try_families\",\n    [{tsocket.AF_INET}, {tsocket.AF_INET6}, {tsocket.AF_INET, tsocket.AF_INET6}],\n)\n@pytest.mark.parametrize(\n    \"fail_families\",\n    [{tsocket.AF_INET}, {tsocket.AF_INET6}, {tsocket.AF_INET, tsocket.AF_INET6}],\n)\nasync def test_open_tcp_listeners_some_address_families_unavailable(\n    try_families: set[AddressFamily],\n    fail_families: set[AddressFamily],\n) -> None:\n    fsf = FakeSocketFactory(\n        10,\n        raise_on_family=dict.fromkeys(fail_families, errno.EAFNOSUPPORT),\n    )\n    tsocket.set_custom_socket_factory(fsf)\n    tsocket.set_custom_hostname_resolver(\n        FakeHostnameResolver([(family, \"foo\") for family in try_families]),\n    )\n\n    should_succeed = try_families - fail_families\n\n    if not should_succeed:\n        with pytest.raises(OSError, match=\"This system doesn't support\") as exc_info:\n            await open_tcp_listeners(80, host=\"example.org\")\n\n        # open_listeners always creates an exceptiongroup with the\n        # unsupported address families, regardless of the value of\n        # strict_exception_groups or number of unsupported families.\n        assert isinstance(exc_info.value.__cause__, BaseExceptionGroup)\n        for subexc in exc_info.value.__cause__.exceptions:\n            assert \"nope\" in str(subexc)\n    else:\n        listeners = await open_tcp_listeners(80)\n        for listener in listeners:\n            should_succeed.remove(listener.socket.family)\n        assert not should_succeed\n\n\nasync def test_open_tcp_listeners_socket_fails_not_afnosupport() -> None:\n    fsf = FakeSocketFactory(\n        10,\n        raise_on_family={\n            tsocket.AF_INET: errno.EAFNOSUPPORT,\n            tsocket.AF_INET6: errno.EINVAL,\n        },\n    )\n    tsocket.set_custom_socket_factory(fsf)\n    tsocket.set_custom_hostname_resolver(\n        FakeHostnameResolver([(tsocket.AF_INET, \"foo\"), (tsocket.AF_INET6, \"bar\")]),\n    )\n\n    with pytest.raises(OSError, match=\"nope\") as exc_info:\n        await open_tcp_listeners(80, host=\"example.org\")\n    assert exc_info.value.errno == errno.EINVAL\n    assert exc_info.value.__cause__ is None\n    assert \"nope\" in str(exc_info.value)\n\n\n# We used to have an elaborate test that opened a real TCP listening socket\n# and then tried to measure its backlog by making connections to it. And most\n# of the time, it worked. But no matter what we tried, it was always fragile,\n# because it had to do things like use timeouts to guess when the listening\n# queue was full, sometimes the CI hosts go into SYN-cookie mode (where there\n# effectively is no backlog), sometimes the host might not be enough resources\n# to give us the full requested backlog... it was a mess. So now we just check\n# that the backlog argument is passed through correctly.\nasync def test_open_tcp_listeners_backlog() -> None:\n    fsf = FakeSocketFactory(99)\n    tsocket.set_custom_socket_factory(fsf)\n    for given, expected in [\n        (None, 0xFFFF),\n        (99999999, 0xFFFF),\n        (10, 10),\n        (1, 1),\n    ]:\n        listeners = await open_tcp_listeners(0, backlog=given)\n        assert listeners\n        for listener in listeners:\n            # `backlog` only exists on FakeSocket\n            assert listener.socket.backlog == expected  # type: ignore[attr-defined]\n\n\nasync def test_open_tcp_listeners_backlog_float_error() -> None:\n    fsf = FakeSocketFactory(99)\n    tsocket.set_custom_socket_factory(fsf)\n    for should_fail in (0.0, 2.18, 3.15, 9.75):\n        with pytest.raises(\n            TypeError,\n            match=f\"backlog must be an int or None, not {should_fail!r}\",\n        ):\n            await open_tcp_listeners(0, backlog=should_fail)  # type: ignore[arg-type]\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_open_tcp_stream.py",
    "content": "from __future__ import annotations\n\nimport socket\nimport sys\nfrom socket import AddressFamily, SocketKind\nfrom typing import TYPE_CHECKING\n\nimport attrs\nimport pytest\n\nimport trio\nfrom trio._highlevel_open_tcp_stream import (\n    close_all,\n    format_host_port,\n    open_tcp_stream,\n    reorder_for_rfc_6555_section_5_4,\n)\nfrom trio.socket import AF_INET, AF_INET6, IPPROTO_TCP, SOCK_STREAM, SocketType\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n    from trio.testing import MockClock\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup\n\n\ndef test_close_all() -> None:\n    class CloseMe(SocketType):\n        closed = False\n\n        def close(self) -> None:\n            self.closed = True\n\n    class CloseKiller(SocketType):\n        def close(self) -> None:\n            raise OSError(\"os error text\")\n\n    c: CloseMe = CloseMe()\n    with close_all() as to_close:\n        to_close.add(c)\n    assert c.closed\n\n    c = CloseMe()\n    with pytest.raises(RuntimeError):\n        with close_all() as to_close:\n            to_close.add(c)\n            raise RuntimeError\n    assert c.closed\n\n    c = CloseMe()\n    with pytest.raises(OSError, match=\"os error text\"):\n        with close_all() as to_close:\n            to_close.add(CloseKiller())\n            to_close.add(c)\n    assert c.closed\n\n\ndef test_reorder_for_rfc_6555_section_5_4() -> None:\n    def fake4(\n        i: int,\n    ) -> tuple[socket.AddressFamily, socket.SocketKind, int, str, tuple[str, int]]:\n        return (\n            AF_INET,\n            SOCK_STREAM,\n            IPPROTO_TCP,\n            \"\",\n            (f\"10.0.0.{i}\", 80),\n        )\n\n    def fake6(\n        i: int,\n    ) -> tuple[socket.AddressFamily, socket.SocketKind, int, str, tuple[str, int]]:\n        return (AF_INET6, SOCK_STREAM, IPPROTO_TCP, \"\", (f\"::{i}\", 80))\n\n    for fake in fake4, fake6:\n        # No effect on homogeneous lists\n        targets = [fake(0), fake(1), fake(2)]\n        reorder_for_rfc_6555_section_5_4(targets)\n        assert targets == [fake(0), fake(1), fake(2)]\n\n        # Single item lists also OK\n        targets = [fake(0)]\n        reorder_for_rfc_6555_section_5_4(targets)\n        assert targets == [fake(0)]\n\n    # If the list starts out with different families in positions 0 and 1,\n    # then it's left alone\n    orig = [fake4(0), fake6(0), fake4(1), fake6(1)]\n    targets = list(orig)\n    reorder_for_rfc_6555_section_5_4(targets)\n    assert targets == orig\n\n    # If not, it's reordered\n    targets = [fake4(0), fake4(1), fake4(2), fake6(0), fake6(1)]\n    reorder_for_rfc_6555_section_5_4(targets)\n    assert targets == [fake4(0), fake6(0), fake4(1), fake4(2), fake6(1)]\n\n\ndef test_format_host_port() -> None:\n    assert format_host_port(\"127.0.0.1\", 80) == \"127.0.0.1:80\"\n    assert format_host_port(b\"127.0.0.1\", 80) == \"127.0.0.1:80\"\n    assert format_host_port(\"example.com\", 443) == \"example.com:443\"\n    assert format_host_port(b\"example.com\", 443) == \"example.com:443\"\n    assert format_host_port(\"::1\", \"http\") == \"[::1]:http\"\n    assert format_host_port(b\"::1\", \"http\") == \"[::1]:http\"\n\n\n# Make sure we can connect to localhost using real kernel sockets\nasync def test_open_tcp_stream_real_socket_smoketest() -> None:\n    listen_sock = trio.socket.socket()\n    await listen_sock.bind((\"127.0.0.1\", 0))\n    _, listen_port = listen_sock.getsockname()\n    listen_sock.listen(1)\n    client_stream = await open_tcp_stream(\"127.0.0.1\", listen_port)\n    server_sock, _ = await listen_sock.accept()\n    await client_stream.send_all(b\"x\")\n    assert await server_sock.recv(1) == b\"x\"\n    await client_stream.aclose()\n    server_sock.close()\n\n    listen_sock.close()\n\n\nasync def test_open_tcp_stream_input_validation() -> None:\n    with pytest.raises(ValueError, match=r\"^host must be str or bytes, not None$\"):\n        await open_tcp_stream(None, 80)  # type: ignore[arg-type]\n    with pytest.raises(TypeError):\n        await open_tcp_stream(\"127.0.0.1\", b\"80\")  # type: ignore[arg-type]\n\n\ndef can_bind_127_0_0_2() -> bool:\n    with socket.socket() as s:\n        try:\n            s.bind((\"127.0.0.2\", 0))\n        except OSError:\n            return False\n        # s.getsockname() is typed as returning Any\n        return s.getsockname()[0] == \"127.0.0.2\"  # type: ignore[no-any-return]\n\n\nasync def test_local_address_real() -> None:\n    with trio.socket.socket() as listener:\n        await listener.bind((\"127.0.0.1\", 0))\n        listener.listen()\n\n        # It's hard to test local_address properly, because you need multiple\n        # local addresses that you can bind to. Fortunately, on most Linux\n        # systems, you can bind to any 127.*.*.* address, and they all go\n        # through the loopback interface. So we can use a non-standard\n        # loopback address. On other systems, the only address we know for\n        # certain we have is 127.0.0.1, so we can't really test local_address=\n        # properly -- passing local_address=127.0.0.1 is indistinguishable\n        # from not passing local_address= at all. But, we can still do a smoke\n        # test to make sure the local_address= code doesn't crash.\n        local_address = \"127.0.0.2\" if can_bind_127_0_0_2() else \"127.0.0.1\"\n\n        async with await open_tcp_stream(\n            *listener.getsockname(),\n            local_address=local_address,\n        ) as client_stream:\n            assert client_stream.socket.getsockname()[0] == local_address\n            if hasattr(trio.socket, \"IP_BIND_ADDRESS_NO_PORT\"):\n                assert client_stream.socket.getsockopt(\n                    trio.socket.IPPROTO_IP,\n                    trio.socket.IP_BIND_ADDRESS_NO_PORT,\n                )\n            server_sock, remote_addr = await listener.accept()\n            await client_stream.aclose()\n            server_sock.close()\n            # accept returns tuple[SocketType, object], due to typeshed returning `Any`\n            assert remote_addr[0] == local_address\n\n        # Trying to connect to an ipv4 address with the ipv6 wildcard\n        # local_address should fail\n        with pytest.raises(\n            OSError,\n            match=r\"^all attempts to connect* to *127\\.0\\.0\\.\\d:\\d+ failed$\",\n        ):\n            await open_tcp_stream(*listener.getsockname(), local_address=\"::\")\n\n        # But the ipv4 wildcard address should work\n        async with await open_tcp_stream(\n            *listener.getsockname(),\n            local_address=\"0.0.0.0\",\n        ) as client_stream:\n            server_sock, remote_addr = await listener.accept()\n            server_sock.close()\n            assert remote_addr == client_stream.socket.getsockname()\n\n\n# Now, thorough tests using fake sockets\n\n\n@attrs.define(eq=False, slots=False)\nclass FakeSocket(trio.socket.SocketType):\n    scenario: Scenario\n    _family: AddressFamily\n    _type: SocketKind\n    _proto: int\n\n    ip: str | int | None = None\n    port: str | int | None = None\n    succeeded: bool = False\n    closed: bool = False\n    failing: bool = False\n\n    @property\n    def type(self) -> SocketKind:\n        return self._type\n\n    @property\n    def family(self) -> AddressFamily:  # pragma: no cover\n        return self._family\n\n    @property\n    def proto(self) -> int:  # pragma: no cover\n        return self._proto\n\n    async def connect(self, sockaddr: tuple[str | int, str | int | None]) -> None:\n        self.ip = sockaddr[0]\n        self.port = sockaddr[1]\n        assert self.ip not in self.scenario.sockets\n        self.scenario.sockets[self.ip] = self\n        self.scenario.connect_times[self.ip] = trio.current_time()\n        delay, result = self.scenario.ip_dict[self.ip]\n        await trio.sleep(delay)\n        if result == \"error\":\n            raise OSError(\"sorry\")\n        if result == \"postconnect_fail\":\n            self.failing = True\n        self.succeeded = True\n\n    def close(self) -> None:\n        self.closed = True\n\n    # called when SocketStream is constructed\n    def setsockopt(self, *args: object, **kwargs: object) -> None:\n        if self.failing:\n            # raise something that isn't OSError as SocketStream\n            # ignores those\n            raise KeyboardInterrupt\n\n\nclass Scenario(trio.abc.SocketFactory, trio.abc.HostnameResolver):\n    def __init__(\n        self,\n        port: int,\n        ip_list: Sequence[tuple[str, float, str]],\n        supported_families: set[AddressFamily],\n    ) -> None:\n        # ip_list have to be unique\n        ip_order = [ip for (ip, _, _) in ip_list]\n        assert len(set(ip_order)) == len(ip_list)\n        ip_dict: dict[str | int, tuple[float, str]] = {}\n        for ip, delay, result in ip_list:\n            assert delay >= 0\n            assert result in [\"error\", \"success\", \"postconnect_fail\"]\n            ip_dict[ip] = (delay, result)\n\n        self.port = port\n        self.ip_order = ip_order\n        self.ip_dict = ip_dict\n        self.supported_families = supported_families\n        self.socket_count = 0\n        self.sockets: dict[str | int, FakeSocket] = {}\n        self.connect_times: dict[str | int, float] = {}\n\n    def socket(\n        self,\n        family: AddressFamily | int | None = None,\n        type_: SocketKind | int | None = None,\n        proto: int | None = None,\n    ) -> SocketType:\n        assert isinstance(family, AddressFamily)\n        assert isinstance(type_, SocketKind)\n        assert proto is not None\n        if family not in self.supported_families:\n            raise OSError(\"pretending not to support this family\")\n        self.socket_count += 1\n        return FakeSocket(self, family, type_, proto)\n\n    def _ip_to_gai_entry(self, ip: str) -> tuple[\n        AddressFamily,\n        SocketKind,\n        int,\n        str,\n        tuple[str, int, int, int] | tuple[str, int] | tuple[int, bytes],\n    ]:\n        sockaddr: tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]\n        if \":\" in ip:\n            family = trio.socket.AF_INET6\n            sockaddr = (ip, self.port, 0, 0)\n        else:\n            family = trio.socket.AF_INET\n            sockaddr = (ip, self.port)\n        return (family, SOCK_STREAM, IPPROTO_TCP, \"\", sockaddr)\n\n    async def getaddrinfo(\n        self,\n        host: bytes | None,\n        port: bytes | str | int | None,\n        family: int = -1,\n        type: int = -1,\n        proto: int = -1,\n        flags: int = -1,\n    ) -> list[\n        tuple[\n            AddressFamily,\n            SocketKind,\n            int,\n            str,\n            tuple[str, int, int, int] | tuple[str, int] | tuple[int, bytes],\n        ]\n    ]:\n        assert host == b\"test.example.com\"\n        assert port == self.port\n        assert family == trio.socket.AF_UNSPEC\n        assert type == trio.socket.SOCK_STREAM\n        assert proto == 0\n        assert flags == 0\n        return [self._ip_to_gai_entry(ip) for ip in self.ip_order]\n\n    async def getnameinfo(\n        self,\n        sockaddr: tuple[str, int] | tuple[str, int, int, int],\n        flags: int,\n    ) -> tuple[str, str]:\n        raise NotImplementedError\n\n    def check(self, succeeded: SocketType | None) -> None:\n        # sockets only go into self.sockets when connect is called; make sure\n        # all the sockets that were created did in fact go in there.\n        assert self.socket_count == len(self.sockets)\n\n        for ip, socket_ in self.sockets.items():\n            assert ip in self.ip_dict\n            if socket_ is not succeeded:\n                assert socket_.closed\n            assert socket_.port == self.port\n\n\nasync def run_scenario(\n    # The port to connect to\n    port: int,\n    # A list of\n    #  (ip, delay, result)\n    # tuples, where delay is in seconds and result is \"success\" or \"error\"\n    # The ip's will be returned from getaddrinfo in this order, and then\n    # connect() calls to them will have the given result.\n    ip_list: Sequence[tuple[str, float, str]],\n    *,\n    # If False, AF_INET4/6 sockets error out on creation, before connect is\n    # even called.\n    ipv4_supported: bool = True,\n    ipv6_supported: bool = True,\n    # Normally, we return (winning_sock, scenario object)\n    # If this is True, we require there to be an exception, and return\n    #   (exception, scenario object)\n    expect_error: tuple[type[BaseException], ...] | type[BaseException] = (),\n    happy_eyeballs_delay: float | None = 0.25,\n    local_address: str | None = None,\n) -> tuple[SocketType, Scenario] | tuple[BaseException, Scenario]:\n    supported_families = set()\n    if ipv4_supported:\n        supported_families.add(trio.socket.AF_INET)\n    if ipv6_supported:\n        supported_families.add(trio.socket.AF_INET6)\n    scenario = Scenario(port, ip_list, supported_families)\n    trio.socket.set_custom_hostname_resolver(scenario)\n    trio.socket.set_custom_socket_factory(scenario)\n\n    try:\n        stream = await open_tcp_stream(\n            \"test.example.com\",\n            port,\n            happy_eyeballs_delay=happy_eyeballs_delay,\n            local_address=local_address,\n        )\n        assert expect_error == ()\n        scenario.check(stream.socket)\n        return (stream.socket, scenario)\n    except AssertionError:  # pragma: no cover\n        raise\n    except expect_error as exc:\n        scenario.check(None)\n        return (exc, scenario)\n\n\nasync def test_one_host_quick_success(autojump_clock: MockClock) -> None:\n    sock, _scenario = await run_scenario(80, [(\"1.2.3.4\", 0.123, \"success\")])\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"1.2.3.4\"\n    assert trio.current_time() == 0.123\n\n\nasync def test_one_host_slow_success(autojump_clock: MockClock) -> None:\n    sock, _scenario = await run_scenario(81, [(\"1.2.3.4\", 100, \"success\")])\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"1.2.3.4\"\n    assert trio.current_time() == 100\n\n\nasync def test_one_host_quick_fail(autojump_clock: MockClock) -> None:\n    exc, _scenario = await run_scenario(\n        82,\n        [(\"1.2.3.4\", 0.123, \"error\")],\n        expect_error=OSError,\n    )\n    assert isinstance(exc, OSError)\n    assert trio.current_time() == 0.123\n\n\nasync def test_one_host_slow_fail(autojump_clock: MockClock) -> None:\n    exc, _scenario = await run_scenario(\n        83,\n        [(\"1.2.3.4\", 100, \"error\")],\n        expect_error=OSError,\n    )\n    assert isinstance(exc, OSError)\n    assert trio.current_time() == 100\n\n\nasync def test_one_host_failed_after_connect(autojump_clock: MockClock) -> None:\n    exc, _scenario = await run_scenario(\n        83,\n        [(\"1.2.3.4\", 1, \"postconnect_fail\")],\n        expect_error=KeyboardInterrupt,\n    )\n    assert isinstance(exc, KeyboardInterrupt)\n\n\n# With the default 0.250 second delay, the third attempt will win\nasync def test_basic_fallthrough(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 1, \"success\"),\n            (\"2.2.2.2\", 1, \"success\"),\n            (\"3.3.3.3\", 0.2, \"success\"),\n        ],\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"3.3.3.3\"\n    # current time is default time + default time + connection time\n    assert trio.current_time() == (0.250 + 0.250 + 0.2)\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.250,\n        \"3.3.3.3\": 0.500,\n    }\n\n\nasync def test_early_success(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 1, \"success\"),\n            (\"2.2.2.2\", 0.1, \"success\"),\n            (\"3.3.3.3\", 0.2, \"success\"),\n        ],\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"2.2.2.2\"\n    assert trio.current_time() == (0.250 + 0.1)\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.250,\n        # 3.3.3.3 was never even started\n    }\n\n\n# With a 0.450 second delay, the first attempt will win\nasync def test_custom_delay(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 1, \"success\"),\n            (\"2.2.2.2\", 1, \"success\"),\n            (\"3.3.3.3\", 0.2, \"success\"),\n        ],\n        happy_eyeballs_delay=0.450,\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"1.1.1.1\"\n    assert trio.current_time() == 1\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.450,\n        \"3.3.3.3\": 0.900,\n    }\n\n\nasync def test_none_default(autojump_clock: MockClock) -> None:\n    \"\"\"Copy of test_basic_fallthrough, but specifying the delay =None\"\"\"\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 1, \"success\"),\n            (\"2.2.2.2\", 1, \"success\"),\n            (\"3.3.3.3\", 0.2, \"success\"),\n        ],\n        happy_eyeballs_delay=None,\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"3.3.3.3\"\n    # current time is default time + default time + connection time\n    assert trio.current_time() == (0.250 + 0.250 + 0.2)\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.250,\n        \"3.3.3.3\": 0.500,\n    }\n\n\nasync def test_custom_errors_expedite(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 0.1, \"error\"),\n            (\"2.2.2.2\", 0.2, \"error\"),\n            (\"3.3.3.3\", 10, \"success\"),\n            # .25 is the default timeout\n            (\"4.4.4.4\", 0.25, \"success\"),\n        ],\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"4.4.4.4\"\n    assert trio.current_time() == (0.1 + 0.2 + 0.25 + 0.25)\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.1,\n        \"3.3.3.3\": 0.1 + 0.2,\n        \"4.4.4.4\": 0.1 + 0.2 + 0.25,\n    }\n\n\nasync def test_all_fail(autojump_clock: MockClock) -> None:\n    exc, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 0.1, \"error\"),\n            (\"2.2.2.2\", 0.2, \"error\"),\n            (\"3.3.3.3\", 10, \"error\"),\n            (\"4.4.4.4\", 0.250, \"error\"),\n        ],\n        expect_error=OSError,\n    )\n    assert isinstance(exc, OSError)\n\n    subexceptions = (pytest.RaisesExc(OSError, match=\"^sorry$\"),) * 4\n    assert pytest.RaisesGroup(\n        *subexceptions,\n        match=\"all attempts to connect to test.example.com:80 failed\",\n    ).matches(exc.__cause__)\n\n    assert trio.current_time() == (0.1 + 0.2 + 10)\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.1,\n        \"3.3.3.3\": 0.1 + 0.2,\n        \"4.4.4.4\": 0.1 + 0.2 + 0.25,\n    }\n\n\nasync def test_multi_success(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 0.5, \"error\"),\n            (\"2.2.2.2\", 10, \"success\"),\n            (\"3.3.3.3\", 10 - 1, \"success\"),\n            (\"4.4.4.4\", 10 - 2, \"success\"),\n            (\"5.5.5.5\", 0.5, \"error\"),\n        ],\n        happy_eyeballs_delay=1,\n    )\n    assert not scenario.sockets[\"1.1.1.1\"].succeeded\n    assert (\n        scenario.sockets[\"2.2.2.2\"].succeeded\n        or scenario.sockets[\"3.3.3.3\"].succeeded\n        or scenario.sockets[\"4.4.4.4\"].succeeded\n    )\n    assert not scenario.sockets[\"5.5.5.5\"].succeeded\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip in [\"2.2.2.2\", \"3.3.3.3\", \"4.4.4.4\"]\n    assert trio.current_time() == (0.5 + 10)\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"2.2.2.2\": 0.5,\n        \"3.3.3.3\": 1.5,\n        \"4.4.4.4\": 2.5,\n        \"5.5.5.5\": 3.5,\n    }\n\n\nasync def test_does_reorder(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        [\n            (\"1.1.1.1\", 10, \"error\"),\n            # This would win if we tried it first...\n            (\"2.2.2.2\", 1, \"success\"),\n            # But in fact we try this first, because of section 5.4\n            (\"::3\", 0.5, \"success\"),\n        ],\n        happy_eyeballs_delay=1,\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"::3\"\n    assert trio.current_time() == 1 + 0.5\n    assert scenario.connect_times == {\n        \"1.1.1.1\": 0,\n        \"::3\": 1,\n    }\n\n\nasync def test_handles_no_ipv4(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        # Here the ipv6 addresses fail at socket creation time, so the connect\n        # configuration doesn't matter\n        [\n            (\"::1\", 10, \"success\"),\n            (\"2.2.2.2\", 0, \"success\"),\n            (\"::3\", 0.1, \"success\"),\n            (\"4.4.4.4\", 0, \"success\"),\n        ],\n        happy_eyeballs_delay=1,\n        ipv4_supported=False,\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"::3\"\n    assert trio.current_time() == 1 + 0.1\n    assert scenario.connect_times == {\n        \"::1\": 0,\n        \"::3\": 1.0,\n    }\n\n\nasync def test_handles_no_ipv6(autojump_clock: MockClock) -> None:\n    sock, scenario = await run_scenario(\n        80,\n        # Here the ipv6 addresses fail at socket creation time, so the connect\n        # configuration doesn't matter\n        [\n            (\"::1\", 0, \"success\"),\n            (\"2.2.2.2\", 10, \"success\"),\n            (\"::3\", 0, \"success\"),\n            (\"4.4.4.4\", 0.1, \"success\"),\n        ],\n        happy_eyeballs_delay=1,\n        ipv6_supported=False,\n    )\n    assert isinstance(sock, FakeSocket)\n    assert sock.ip == \"4.4.4.4\"\n    assert trio.current_time() == 1 + 0.1\n    assert scenario.connect_times == {\n        \"2.2.2.2\": 0,\n        \"4.4.4.4\": 1.0,\n    }\n\n\nasync def test_no_hosts(autojump_clock: MockClock) -> None:\n    exc, _scenario = await run_scenario(80, [], expect_error=OSError)\n    assert \"no results found\" in str(exc)\n\n\nasync def test_cancel(autojump_clock: MockClock) -> None:\n    with trio.move_on_after(5) as cancel_scope:\n        exc, scenario = await run_scenario(\n            80,\n            [\n                (\"1.1.1.1\", 10, \"success\"),\n                (\"2.2.2.2\", 10, \"success\"),\n                (\"3.3.3.3\", 10, \"success\"),\n                (\"4.4.4.4\", 10, \"success\"),\n            ],\n            expect_error=BaseExceptionGroup,\n        )\n        assert isinstance(exc, BaseException)\n        # What comes out should be 1 or more Cancelled errors that all belong\n        # to this cancel_scope; this is the easiest way to check that\n        raise exc\n    assert cancel_scope.cancelled_caught\n\n    assert trio.current_time() == 5\n\n    # This should have been called already, but just to make sure, since the\n    # exception-handling logic in run_scenario is a bit complicated and the\n    # main thing we care about here is that all the sockets were cleaned up.\n    scenario.check(succeeded=None)\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_open_unix_stream.py",
    "content": "import os\nimport socket\nimport sys\nimport tempfile\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom trio import Path, open_unix_socket\nfrom trio._highlevel_open_unix_stream import close_on_error\n\nassert not TYPE_CHECKING or sys.platform != \"win32\"\n\nskip_if_not_unix = pytest.mark.skipif(\n    not hasattr(socket, \"AF_UNIX\"),\n    reason=\"Needs unix socket support\",\n)\n\n\n@skip_if_not_unix\ndef test_close_on_error() -> None:\n    class CloseMe:\n        closed = False\n\n        def close(self) -> None:\n            self.closed = True\n\n    with close_on_error(CloseMe()) as c:\n        pass\n    assert not c.closed\n\n    with pytest.raises(RuntimeError):\n        with close_on_error(CloseMe()) as c:\n            raise RuntimeError\n    assert c.closed\n\n\n@skip_if_not_unix\n@pytest.mark.parametrize(\"filename\", [4, 4.5])\nasync def test_open_with_bad_filename_type(filename: float) -> None:\n    with pytest.raises(TypeError):\n        await open_unix_socket(filename)  # type: ignore[arg-type]\n\n\n@skip_if_not_unix\nasync def test_open_bad_socket() -> None:\n    # mktemp is marked as insecure, but that's okay, we don't want the file to\n    # exist\n    name = tempfile.mktemp()\n    with pytest.raises(FileNotFoundError):\n        await open_unix_socket(name)\n\n\n@skip_if_not_unix\nasync def test_open_unix_socket() -> None:\n    for name_type in [Path, str]:\n        name = tempfile.mktemp()\n        serv_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n        with serv_sock:\n            serv_sock.bind(name)\n            try:\n                serv_sock.listen(1)\n\n                # The actual function we're testing\n                unix_socket = await open_unix_socket(name_type(name))\n\n                async with unix_socket:\n                    client, _ = serv_sock.accept()\n                    with client:\n                        await unix_socket.send_all(b\"test\")\n                        assert client.recv(2048) == b\"test\"\n\n                        client.sendall(b\"response\")\n                        received = await unix_socket.receive_some(2048)\n                        assert received == b\"response\"\n            finally:\n                os.unlink(name)\n\n\n@pytest.mark.skipif(hasattr(socket, \"AF_UNIX\"), reason=\"Test for non-unix platforms\")\nasync def test_error_on_no_unix() -> None:\n    with pytest.raises(\n        RuntimeError,\n        match=r\"^Unix sockets are not supported on this platform$\",\n    ):\n        await open_unix_socket(\"\")\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_serve_listeners.py",
    "content": "from __future__ import annotations\n\nimport errno\nfrom functools import partial\nfrom typing import TYPE_CHECKING, NoReturn, cast\n\nimport attrs\nimport pytest\n\nimport trio\nfrom trio import Nursery, StapledStream, TaskStatus\nfrom trio.testing import (\n    MemoryReceiveStream,\n    MemorySendStream,\n    MockClock,\n    memory_stream_pair,\n    wait_all_tasks_blocked,\n)\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n    from trio._channel import MemoryReceiveChannel, MemorySendChannel\n    from trio.abc import Stream\n\n# types are somewhat tentative - I just bruteforced them until I got something that didn't\n# give errors\nStapledMemoryStream = StapledStream[MemorySendStream, MemoryReceiveStream]\n\n\n@attrs.define(eq=False, slots=False)\nclass MemoryListener(trio.abc.Listener[StapledMemoryStream]):\n    closed: bool = False\n    accepted_streams: list[trio.abc.Stream] = attrs.Factory(list)\n    queued_streams: tuple[\n        MemorySendChannel[StapledMemoryStream],\n        MemoryReceiveChannel[StapledMemoryStream],\n    ] = attrs.Factory(lambda: trio.open_memory_channel[StapledMemoryStream](1))\n    accept_hook: Callable[[], Awaitable[object]] | None = None\n\n    async def connect(self) -> StapledMemoryStream:\n        assert not self.closed\n        client, server = memory_stream_pair()\n        await self.queued_streams[0].send(server)\n        return client\n\n    async def accept(self) -> StapledMemoryStream:\n        await trio.lowlevel.checkpoint()\n        assert not self.closed\n        if self.accept_hook is not None:\n            await self.accept_hook()\n        stream = await self.queued_streams[1].receive()\n        self.accepted_streams.append(stream)\n        return stream\n\n    async def aclose(self) -> None:\n        self.closed = True\n        await trio.lowlevel.checkpoint()\n\n\nasync def test_serve_listeners_basic() -> None:\n    listeners = [MemoryListener(), MemoryListener()]\n\n    record = []\n\n    def close_hook() -> None:\n        # Make sure this is a forceful close\n        assert trio.current_effective_deadline() == float(\"-inf\")\n        record.append(\"closed\")\n\n    async def handler(stream: StapledMemoryStream) -> None:\n        await stream.send_all(b\"123\")\n        assert await stream.receive_some(10) == b\"456\"\n        stream.send_stream.close_hook = close_hook\n        stream.receive_stream.close_hook = close_hook\n\n    async def client(listener: MemoryListener) -> None:\n        s = await listener.connect()\n        assert await s.receive_some(10) == b\"123\"\n        await s.send_all(b\"456\")\n\n    async def do_tests(parent_nursery: Nursery) -> None:\n        async with trio.open_nursery() as nursery:\n            for listener in listeners:\n                for _ in range(3):\n                    nursery.start_soon(client, listener)\n\n        await wait_all_tasks_blocked()\n\n        # verifies that all 6 streams x 2 directions each were closed ok\n        assert len(record) == 12\n\n        parent_nursery.cancel_scope.cancel()\n\n    async with trio.open_nursery() as nursery:\n        value = await nursery.start(\n            trio.serve_listeners,\n            handler,\n            listeners,\n        )\n        assert isinstance(value, list)\n        l2 = cast(\"list[MemoryListener]\", value)\n        assert l2 == listeners\n        # This is just split into another function because gh-136 isn't\n        # implemented yet\n        nursery.start_soon(do_tests, nursery)\n\n    for listener in listeners:\n        assert listener.closed\n\n\nasync def test_serve_listeners_accept_unrecognized_error() -> None:\n    for error in [KeyError(), OSError(errno.ECONNABORTED, \"ECONNABORTED\")]:\n        listener = MemoryListener()\n\n        async def raise_error() -> NoReturn:\n            raise error  # noqa: B023  # Set from loop\n\n        def check_error(e: BaseException) -> bool:\n            return e is error  # noqa: B023\n\n        listener.accept_hook = raise_error\n\n        with pytest.RaisesGroup(pytest.RaisesExc(check=check_error)):\n            await trio.serve_listeners(None, [listener])  # type: ignore[arg-type]\n\n\nasync def test_serve_listeners_accept_capacity_error(\n    autojump_clock: MockClock,\n    caplog: pytest.LogCaptureFixture,\n) -> None:\n    listener = MemoryListener()\n\n    async def raise_EMFILE() -> NoReturn:\n        raise OSError(errno.EMFILE, \"out of file descriptors\")\n\n    listener.accept_hook = raise_EMFILE\n\n    # It retries every 100 ms, so in 950 ms it will retry at 0, 100, ..., 900\n    # = 10 times total\n    with trio.move_on_after(0.950):\n        await trio.serve_listeners(None, [listener])  # type: ignore[arg-type]\n\n    assert len(caplog.records) == 10\n    for record in caplog.records:\n        assert \"retrying\" in record.msg\n        assert record.exc_info is not None\n        assert isinstance(record.exc_info[1], OSError)\n        assert record.exc_info[1].errno == errno.EMFILE\n\n\nasync def test_serve_listeners_connection_nursery(autojump_clock: MockClock) -> None:\n    listener = MemoryListener()\n\n    async def handler(stream: Stream) -> None:\n        await trio.sleep(1)\n\n    class Done(Exception):\n        pass\n\n    async def connection_watcher(\n        *,\n        task_status: TaskStatus[Nursery] = trio.TASK_STATUS_IGNORED,\n    ) -> NoReturn:\n        async with trio.open_nursery() as nursery:\n            task_status.started(nursery)\n            await wait_all_tasks_blocked()\n            assert len(nursery.child_tasks) == 10\n            raise Done\n\n    # the exception is wrapped twice because we open two nested nurseries\n    with pytest.RaisesGroup(pytest.RaisesGroup(Done)):\n        async with trio.open_nursery() as nursery:\n            value = await nursery.start(connection_watcher)\n            assert isinstance(value, trio.Nursery)\n            handler_nursery: trio.Nursery = value\n            await nursery.start(\n                partial(\n                    trio.serve_listeners,\n                    handler,\n                    [listener],\n                    handler_nursery=handler_nursery,\n                ),\n            )\n            for _ in range(10):\n                nursery.start_soon(listener.connect)\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_socket.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport socket as stdlib_socket\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom .. import _core, socket as tsocket\nfrom .._highlevel_socket import *\nfrom ..testing import (\n    assert_checkpoints,\n    check_half_closeable_stream,\n    wait_all_tasks_blocked,\n)\nfrom .test_socket import setsockopt_tests\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n\nasync def test_SocketStream_basics() -> None:\n    # stdlib socket bad (even if connected)\n    stdlib_a, stdlib_b = stdlib_socket.socketpair()\n    with stdlib_a, stdlib_b:\n        with pytest.raises(TypeError):\n            SocketStream(stdlib_a)  # type: ignore[arg-type]\n\n    # DGRAM socket bad\n    with tsocket.socket(type=tsocket.SOCK_DGRAM) as sock:\n        with pytest.raises(\n            ValueError,\n            match=r\"^SocketStream requires a SOCK_STREAM socket$\",\n        ):\n            # TODO: does not raise an error?\n            SocketStream(sock)\n\n    a, b = tsocket.socketpair()\n    with a, b:\n        s = SocketStream(a)\n        assert s.socket is a\n\n    # Use a real, connected socket to test socket options, because\n    # socketpair() might give us a unix socket that doesn't support any of\n    # these options\n    with tsocket.socket() as listen_sock:\n        await listen_sock.bind((\"127.0.0.1\", 0))\n        listen_sock.listen(1)\n        with tsocket.socket() as client_sock:\n            await client_sock.connect(listen_sock.getsockname())\n\n            s = SocketStream(client_sock)\n\n            # TCP_NODELAY enabled by default\n            assert s.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)\n            # We can disable it though\n            s.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False)\n            assert not s.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)\n\n            res = s.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, 1)\n            assert isinstance(res, bytes)\n\n            setsockopt_tests(s)\n\n\nasync def test_SocketStream_send_all() -> None:\n    BIG = 10000000\n\n    a_sock, b_sock = tsocket.socketpair()\n    with a_sock, b_sock:\n        a = SocketStream(a_sock)\n        b = SocketStream(b_sock)\n\n        # Check a send_all that has to be split into multiple parts (on most\n        # platforms... on Windows every send() either succeeds or fails as a\n        # whole)\n        async def sender() -> None:\n            data = bytearray(BIG)\n            await a.send_all(data)\n            # send_all uses memoryviews internally, which temporarily \"lock\"\n            # the object they view. If it doesn't clean them up properly, then\n            # some bytearray operations might raise an error afterwards, which\n            # would be a pretty weird and annoying side-effect to spring on\n            # users. So test that this doesn't happen, by forcing the\n            # bytearray's underlying buffer to be realloc'ed:\n            data += bytes(BIG)\n            # (Note: the above line of code doesn't do a very good job at\n            # testing anything, because:\n            # - on CPython, the refcount GC generally cleans up memoryviews\n            #   for us even if we're sloppy.\n            # - on PyPy3, at least as of 5.7.0, the memoryview code and the\n            #   bytearray code conspire so that resizing never fails – if\n            #   resizing forces the bytearray's internal buffer to move, then\n            #   all memoryview references are automagically updated (!!).\n            #   See:\n            #   https://gist.github.com/njsmith/0ffd38ec05ad8e34004f34a7dc492227\n            # But I'm leaving the test here in hopes that if this ever changes\n            # and we break our implementation of send_all, then we'll get some\n            # early warning...)\n\n        async def receiver() -> None:\n            # Make sure the sender fills up the kernel buffers and blocks\n            await wait_all_tasks_blocked()\n            nbytes = 0\n            while nbytes < BIG:\n                nbytes += len(await b.receive_some(BIG))\n            assert nbytes == BIG\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sender)\n            nursery.start_soon(receiver)\n\n        # We know that we received BIG bytes of NULs so far. Make sure that\n        # was all the data in there.\n        await a.send_all(b\"e\")\n        assert await b.receive_some(10) == b\"e\"\n        await a.send_eof()\n        assert await b.receive_some(10) == b\"\"\n\n\nasync def fill_stream(s: SocketStream) -> None:\n    async def sender() -> None:\n        while True:\n            await s.send_all(b\"x\" * 10000)\n\n    async def waiter(nursery: _core.Nursery) -> None:\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(sender)\n        nursery.start_soon(waiter, nursery)\n\n\nasync def test_SocketStream_generic() -> None:\n    async def stream_maker() -> tuple[\n        SocketStream,\n        SocketStream,\n    ]:\n        left, right = tsocket.socketpair()\n        return SocketStream(left), SocketStream(right)\n\n    async def clogged_stream_maker() -> tuple[SocketStream, SocketStream]:\n        left, right = await stream_maker()\n        await fill_stream(left)\n        await fill_stream(right)\n        return left, right\n\n    await check_half_closeable_stream(stream_maker, clogged_stream_maker)\n\n\nasync def test_SocketListener() -> None:\n    # Not a Trio socket\n    with stdlib_socket.socket() as s:\n        s.bind((\"127.0.0.1\", 0))\n        s.listen(10)\n        with pytest.raises(TypeError):\n            SocketListener(s)  # type: ignore[arg-type]\n\n    # Not a SOCK_STREAM\n    with tsocket.socket(type=tsocket.SOCK_DGRAM) as s:\n        await s.bind((\"127.0.0.1\", 0))\n        with pytest.raises(\n            ValueError,\n            match=r\"^SocketListener requires a SOCK_STREAM socket$\",\n        ) as excinfo:\n            SocketListener(s)\n        excinfo.match(r\".*SOCK_STREAM\")\n\n    # Didn't call .listen()\n    # macOS has no way to check for this, so skip testing it there.\n    if sys.platform != \"darwin\":\n        with tsocket.socket() as s:\n            await s.bind((\"127.0.0.1\", 0))\n            with pytest.raises(\n                ValueError,\n                match=r\"^SocketListener requires a listening socket$\",\n            ) as excinfo:\n                SocketListener(s)\n            excinfo.match(r\".*listen\")\n\n    listen_sock = tsocket.socket()\n    await listen_sock.bind((\"127.0.0.1\", 0))\n    listen_sock.listen(10)\n    listener = SocketListener(listen_sock)\n\n    assert listener.socket is listen_sock\n\n    client_sock = tsocket.socket()\n    await client_sock.connect(listen_sock.getsockname())\n    with assert_checkpoints():\n        server_stream = await listener.accept()\n    assert isinstance(server_stream, SocketStream)\n    assert server_stream.socket.getsockname() == listen_sock.getsockname()\n    assert server_stream.socket.getpeername() == client_sock.getsockname()\n\n    with assert_checkpoints():\n        await listener.aclose()\n\n    with assert_checkpoints():\n        await listener.aclose()\n\n    with assert_checkpoints():\n        with pytest.raises(_core.ClosedResourceError):\n            await listener.accept()\n\n    client_sock.close()\n    await server_stream.aclose()\n\n\nasync def test_SocketListener_socket_closed_underfoot() -> None:\n    listen_sock = tsocket.socket()\n    await listen_sock.bind((\"127.0.0.1\", 0))\n    listen_sock.listen(10)\n    listener = SocketListener(listen_sock)\n\n    # Close the socket, not the listener\n    listen_sock.close()\n\n    # SocketListener gives correct error\n    with assert_checkpoints():\n        with pytest.raises(_core.ClosedResourceError):\n            await listener.accept()\n\n\nasync def test_SocketListener_accept_errors() -> None:\n    class FakeSocket(tsocket.SocketType):\n        def __init__(self, events: Sequence[SocketType | BaseException]) -> None:\n            self._events = iter(events)\n\n        type = tsocket.SOCK_STREAM\n\n        # Fool the check for SO_ACCEPTCONN in SocketListener.__init__\n        @overload\n        def getsockopt(self, /, level: int, optname: int) -> int: ...\n\n        @overload\n        def getsockopt(  # noqa: F811\n            self,\n            /,\n            level: int,\n            optname: int,\n            buflen: int,\n        ) -> bytes: ...\n\n        def getsockopt(  # noqa: F811\n            self,\n            /,\n            level: int,\n            optname: int,\n            buflen: int | None = None,\n        ) -> int | bytes:\n            return True\n\n        @overload\n        def setsockopt(\n            self,\n            /,\n            level: int,\n            optname: int,\n            value: int | Buffer,\n        ) -> None: ...\n\n        @overload\n        def setsockopt(  # noqa: F811\n            self,\n            /,\n            level: int,\n            optname: int,\n            value: None,\n            optlen: int,\n        ) -> None: ...\n\n        def setsockopt(  # noqa: F811\n            self,\n            /,\n            level: int,\n            optname: int,\n            value: int | Buffer | None,\n            optlen: int | None = None,\n        ) -> None:\n            pass\n\n        async def accept(self) -> tuple[SocketType, object]:\n            await _core.checkpoint()\n            event = next(self._events)\n            if isinstance(event, BaseException):\n                raise event\n            else:\n                return event, None\n\n    fake_server_sock = FakeSocket([])\n\n    fake_listen_sock = FakeSocket(\n        [\n            OSError(errno.ECONNABORTED, \"Connection aborted\"),\n            OSError(errno.EPERM, \"Permission denied\"),\n            OSError(errno.EPROTO, \"Bad protocol\"),\n            fake_server_sock,\n            OSError(errno.EMFILE, \"Out of file descriptors\"),\n            OSError(errno.EFAULT, \"attempt to write to read-only memory\"),\n            OSError(errno.ENOBUFS, \"out of buffers\"),\n            fake_server_sock,\n        ],\n    )\n\n    listener = SocketListener(fake_listen_sock)\n\n    with assert_checkpoints():\n        stream = await listener.accept()\n        assert stream.socket is fake_server_sock\n\n    for code, match in {\n        errno.EMFILE: r\"\\[\\w+ \\d+\\] Out of file descriptors$\",\n        errno.EFAULT: r\"\\[\\w+ \\d+\\] attempt to write to read-only memory$\",\n        errno.ENOBUFS: r\"\\[\\w+ \\d+\\] out of buffers$\",\n    }.items():\n        with assert_checkpoints():\n            with pytest.raises(OSError, match=match) as excinfo:\n                await listener.accept()\n            assert excinfo.value.errno == code\n\n    with assert_checkpoints():\n        stream = await listener.accept()\n        assert stream.socket is fake_server_sock\n\n\nasync def test_socket_stream_works_when_peer_has_already_closed() -> None:\n    sock_a, sock_b = tsocket.socketpair()\n    with sock_a, sock_b:\n        await sock_b.send(b\"x\")\n        sock_b.close()\n        stream = SocketStream(sock_a)\n        assert await stream.receive_some(1) == b\"x\"\n        assert await stream.receive_some(1) == b\"\"\n"
  },
  {
    "path": "src/trio/_tests/test_highlevel_ssl_helpers.py",
    "content": "from __future__ import annotations\n\nfrom functools import partial\nfrom typing import TYPE_CHECKING, NoReturn, cast\n\nimport attrs\nimport pytest\n\nimport trio\nfrom trio.socket import AF_INET, IPPROTO_TCP, SOCK_STREAM\n\nfrom .._highlevel_ssl_helpers import (\n    open_ssl_over_tcp_listeners,\n    open_ssl_over_tcp_stream,\n    serve_ssl_over_tcp,\n)\n\n# using noqa because linters don't understand how pytest fixtures work.\nfrom .test_ssl import SERVER_CTX, client_ctx  # noqa: F401\n\nif TYPE_CHECKING:\n    from socket import AddressFamily, SocketKind\n    from ssl import SSLContext\n\n    from trio.abc import Stream\n\n    from .._highlevel_socket import SocketListener\n    from .._ssl import SSLListener\n\n\nasync def echo_handler(stream: Stream) -> None:\n    async with stream:\n        try:\n            while True:\n                data = await stream.receive_some(10000)\n                if not data:\n                    break\n                await stream.send_all(data)\n        except trio.BrokenResourceError:\n            pass\n\n\n# Resolver that always returns the given sockaddr, no matter what host/port\n# you ask for.\n@attrs.define(slots=False)\nclass FakeHostnameResolver(trio.abc.HostnameResolver):\n    sockaddr: tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]\n\n    async def getaddrinfo(\n        self,\n        host: bytes | None,\n        port: bytes | str | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> list[\n        tuple[\n            AddressFamily,\n            SocketKind,\n            int,\n            str,\n            tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n        ]\n    ]:\n        return [(AF_INET, SOCK_STREAM, IPPROTO_TCP, \"\", self.sockaddr)]\n\n    async def getnameinfo(\n        self,\n        sockaddr: tuple[str, int] | tuple[str, int, int, int],\n        flags: int,\n    ) -> NoReturn:  # pragma: no cover\n        raise NotImplementedError\n\n\n# This uses serve_ssl_over_tcp, which uses open_ssl_over_tcp_listeners...\n# using noqa because linters don't understand how pytest fixtures work.\nasync def test_open_ssl_over_tcp_stream_and_everything_else(\n    client_ctx: SSLContext,  # noqa: F811 # linters doesn't understand fixture\n) -> None:\n    async with trio.open_nursery() as nursery:\n        # TODO: this function wraps an SSLListener around a SocketListener, this is illegal\n        # according to current type hints, and probably for good reason. But there should\n        # maybe be a different wrapper class/function that could be used instead?\n        value = await nursery.start(\n            partial(\n                serve_ssl_over_tcp,\n                echo_handler,\n                0,\n                SERVER_CTX,\n                host=\"127.0.0.1\",\n            ),\n        )\n        assert isinstance(value, list)\n        res = cast(\"list[SSLListener[SocketListener]]\", value)  # type: ignore[type-var]\n        (listener,) = res\n        async with listener:\n            # listener.transport_listener is of type Listener[Stream]\n            tp_listener: SocketListener = listener.transport_listener  # type: ignore[assignment]\n\n            sockaddr = tp_listener.socket.getsockname()\n            hostname_resolver = FakeHostnameResolver(sockaddr)\n            trio.socket.set_custom_hostname_resolver(hostname_resolver)\n\n            # We don't have the right trust set up\n            # (checks that ssl_context=None is doing some validation)\n            stream = await open_ssl_over_tcp_stream(\"trio-test-1.example.org\", 80)\n            async with stream:\n                with pytest.raises(trio.BrokenResourceError):\n                    await stream.do_handshake()\n\n            # We have the trust but not the hostname\n            # (checks custom ssl_context + hostname checking)\n            stream = await open_ssl_over_tcp_stream(\n                \"xyzzy.example.org\",\n                80,\n                ssl_context=client_ctx,\n            )\n            async with stream:\n                with pytest.raises(trio.BrokenResourceError):\n                    await stream.do_handshake()\n\n            # This one should work!\n            stream = await open_ssl_over_tcp_stream(\n                \"trio-test-1.example.org\",\n                80,\n                ssl_context=client_ctx,\n            )\n            async with stream:\n                assert isinstance(stream, trio.SSLStream)\n                assert stream.server_hostname == \"trio-test-1.example.org\"\n                await stream.send_all(b\"x\")\n                assert await stream.receive_some(1) == b\"x\"\n\n            # Check https_compatible settings are being passed through\n            assert not stream._https_compatible\n            stream = await open_ssl_over_tcp_stream(\n                \"trio-test-1.example.org\",\n                80,\n                ssl_context=client_ctx,\n                https_compatible=True,\n                # also, smoke test happy_eyeballs_delay\n                happy_eyeballs_delay=1,\n            )\n            async with stream:\n                assert stream._https_compatible\n\n            # Stop the echo server\n            nursery.cancel_scope.cancel()\n\n\nasync def test_open_ssl_over_tcp_listeners() -> None:\n    (listener,) = await open_ssl_over_tcp_listeners(0, SERVER_CTX, host=\"127.0.0.1\")\n    async with listener:\n        assert isinstance(listener, trio.SSLListener)\n        tl = listener.transport_listener\n        assert isinstance(tl, trio.SocketListener)\n        assert tl.socket.getsockname()[0] == \"127.0.0.1\"\n\n        assert not listener._https_compatible\n\n    (listener,) = await open_ssl_over_tcp_listeners(\n        0,\n        SERVER_CTX,\n        host=\"127.0.0.1\",\n        https_compatible=True,\n    )\n    async with listener:\n        assert listener._https_compatible\n"
  },
  {
    "path": "src/trio/_tests/test_path.py",
    "content": "from __future__ import annotations\n\nimport os\nimport pathlib\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nimport trio\nfrom trio._file_io import AsyncIOWrapper\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n\n@pytest.fixture\ndef path(tmp_path: pathlib.Path) -> trio.Path:\n    return trio.Path(tmp_path / \"test\")\n\n\ndef method_pair(\n    path: str,\n    method_name: str,\n) -> tuple[Callable[[], object], Callable[[], Awaitable[object]]]:\n    sync_path = pathlib.Path(path)\n    async_path = trio.Path(path)\n    return getattr(sync_path, method_name), getattr(async_path, method_name)\n\n\n@pytest.mark.skipif(os.name == \"nt\", reason=\"OS is not posix\")\ndef test_instantiate_posix() -> None:\n    assert isinstance(trio.Path(), trio.PosixPath)\n\n\n@pytest.mark.skipif(os.name != \"nt\", reason=\"OS is not Windows\")\ndef test_instantiate_windows() -> None:\n    assert isinstance(trio.Path(), trio.WindowsPath)\n\n\nasync def test_open_is_async_context_manager(path: trio.Path) -> None:\n    async with await path.open(\"w\") as f:\n        assert isinstance(f, AsyncIOWrapper)\n\n    assert f.closed\n\n\ndef test_magic() -> None:\n    path = trio.Path(\"test\")\n\n    assert str(path) == \"test\"\n    assert bytes(path) == b\"test\"\n\n\nEitherPathType = type[trio.Path] | type[pathlib.Path]\nPathOrStrType = EitherPathType | type[str]\ncls_pairs: list[tuple[EitherPathType, EitherPathType]] = [\n    (trio.Path, pathlib.Path),\n    (pathlib.Path, trio.Path),\n    (trio.Path, trio.Path),\n]\n\n\n@pytest.mark.parametrize((\"cls_a\", \"cls_b\"), cls_pairs)\ndef test_cmp_magic(cls_a: EitherPathType, cls_b: EitherPathType) -> None:\n    a, b = cls_a(\"\"), cls_b(\"\")\n    assert a == b\n    assert not a != b  # noqa: SIM202  # negate-not-equal-op\n\n    a, b = cls_a(\"a\"), cls_b(\"b\")\n    assert a < b\n    assert b > a\n\n    # this is intentionally testing equivalence with none, due to the\n    # other=sentinel logic in _forward_magic\n    assert not a == None  # noqa\n    assert not b == None  # noqa\n\n\n# upstream python3.8 bug: we should also test (pathlib.Path, trio.Path), but\n# __*div__ does not properly raise NotImplementedError like the other comparison\n# magic, so trio.Path's implementation does not get dispatched\ncls_pairs_str: list[tuple[PathOrStrType, PathOrStrType]] = [\n    (trio.Path, pathlib.Path),\n    (trio.Path, trio.Path),\n    (trio.Path, str),\n    (str, trio.Path),\n]\n\n\n@pytest.mark.parametrize((\"cls_a\", \"cls_b\"), cls_pairs_str)\ndef test_div_magic(cls_a: PathOrStrType, cls_b: PathOrStrType) -> None:\n    a, b = cls_a(\"a\"), cls_b(\"b\")\n\n    result = a / b  # type: ignore[operator]\n    # Type checkers think str / str could happen. Check each combo manually in type_tests/.\n    assert isinstance(result, trio.Path)\n    assert str(result) == os.path.join(\"a\", \"b\")\n\n\n@pytest.mark.parametrize(\n    (\"cls_a\", \"cls_b\"),\n    [(trio.Path, pathlib.Path), (trio.Path, trio.Path)],\n)\n@pytest.mark.parametrize(\"path\", [\"foo\", \"foo/bar/baz\", \"./foo\"])\ndef test_hash_magic(\n    cls_a: EitherPathType,\n    cls_b: EitherPathType,\n    path: str,\n) -> None:\n    a, b = cls_a(path), cls_b(path)\n    assert hash(a) == hash(b)\n\n\ndef test_forwarded_properties(path: trio.Path) -> None:\n    # use `name` as a representative of forwarded properties\n\n    assert \"name\" in dir(path)\n    assert path.name == \"test\"\n\n\ndef test_async_method_signature(path: trio.Path) -> None:\n    # use `resolve` as a representative of wrapped methods\n\n    assert path.resolve.__name__ == \"resolve\"\n    assert path.resolve.__qualname__ == \"Path.resolve\"\n\n    assert path.resolve.__doc__ is not None\n    assert path.resolve.__qualname__ in path.resolve.__doc__\n\n\n@pytest.mark.parametrize(\"method_name\", [\"is_dir\", \"is_file\"])\nasync def test_compare_async_stat_methods(method_name: str) -> None:\n    method, async_method = method_pair(\".\", method_name)\n\n    result = method()\n    async_result = await async_method()\n\n    assert result == async_result\n\n\ndef test_invalid_name_not_wrapped(path: trio.Path) -> None:\n    with pytest.raises(AttributeError):\n        getattr(path, \"invalid_fake_attr\")  # noqa: B009  # \"get-attr-with-constant\"\n\n\n@pytest.mark.parametrize(\"method_name\", [\"absolute\", \"resolve\"])\nasync def test_async_methods_rewrap(method_name: str) -> None:\n    method, async_method = method_pair(\".\", method_name)\n\n    result = method()\n    async_result = await async_method()\n\n    assert isinstance(async_result, trio.Path)\n    assert str(result) == str(async_result)\n\n\ndef test_forward_methods_rewrap(path: trio.Path, tmp_path: pathlib.Path) -> None:\n    with_name = path.with_name(\"foo\")\n    with_suffix = path.with_suffix(\".py\")\n\n    assert isinstance(with_name, trio.Path)\n    assert with_name == tmp_path / \"foo\"\n    assert isinstance(with_suffix, trio.Path)\n    assert with_suffix == tmp_path / \"test.py\"\n\n\ndef test_forward_properties_rewrap(path: trio.Path) -> None:\n    assert isinstance(path.parent, trio.Path)\n\n\ndef test_forward_methods_without_rewrap(path: trio.Path) -> None:\n    assert \"totally-unique-path\" in str(path.joinpath(\"totally-unique-path\"))\n\n\ndef test_repr() -> None:\n    path = trio.Path(\".\")\n\n    assert repr(path) == \"trio.Path('.')\"\n\n\n@pytest.mark.parametrize(\"meth\", [trio.Path.__init__, trio.Path.joinpath])\nasync def test_path_wraps_path(\n    path: trio.Path,\n    meth: Callable[[trio.Path, trio.Path], object],\n) -> None:\n    wrapped = await path.absolute()\n    result = meth(path, wrapped)\n    if result is None:\n        result = path\n\n    assert wrapped == result\n\n\ndef test_path_nonpath() -> None:\n    with pytest.raises(TypeError):\n        trio.Path(1)  # type: ignore\n\n\nasync def test_open_file_can_open_path(path: trio.Path) -> None:\n    async with await trio.open_file(path, \"w\") as f:\n        assert f.name == os.fspath(path)\n\n\nasync def test_globmethods(path: trio.Path) -> None:\n    # Populate a directory tree\n    await path.mkdir()\n    await (path / \"foo\").mkdir()\n    await (path / \"foo\" / \"_bar.txt\").write_bytes(b\"\")\n    await (path / \"bar.txt\").write_bytes(b\"\")\n    await (path / \"bar.dat\").write_bytes(b\"\")\n\n    # Path.glob\n    for pattern, results in {\n        \"*.txt\": {\"bar.txt\"},\n        \"**/*.txt\": {\"_bar.txt\", \"bar.txt\"},\n    }.items():\n        entries = set()\n        for entry in await path.glob(pattern):\n            assert isinstance(entry, trio.Path)\n            entries.add(entry.name)\n\n        assert entries == results\n\n    # Path.rglob\n    entries = set()\n    for entry in await path.rglob(\"*.txt\"):\n        assert isinstance(entry, trio.Path)\n        entries.add(entry.name)\n\n    assert entries == {\"_bar.txt\", \"bar.txt\"}\n\n\nasync def test_as_uri(path: trio.Path) -> None:\n    path = await path.parent.resolve()\n\n    assert path.as_uri().startswith(\"file:///\")\n\n\nasync def test_iterdir(path: trio.Path) -> None:\n    # Populate a directory\n    await path.mkdir()\n    await (path / \"foo\").mkdir()\n    await (path / \"bar.txt\").write_bytes(b\"\")\n\n    entries = set()\n    for entry in await path.iterdir():\n        assert isinstance(entry, trio.Path)\n        entries.add(entry.name)\n\n    assert entries == {\"bar.txt\", \"foo\"}\n\n\nasync def test_classmethods() -> None:\n    assert isinstance(await trio.Path.home(), trio.Path)\n\n    # pathlib.Path has only two classmethods\n    assert str(await trio.Path.home()) == os.path.expanduser(\"~\")  # noqa: ASYNC240\n    assert str(await trio.Path.cwd()) == os.getcwd()\n\n    # Wrapped method has docstring\n    assert trio.Path.home.__doc__\n\n\n@pytest.mark.parametrize(\n    \"wrapper\",\n    [\n        trio._path._wraps_async,\n        trio._path._wrap_method,\n        trio._path._wrap_method_path,\n        trio._path._wrap_method_path_iterable,\n    ],\n)\ndef test_wrapping_without_docstrings(\n    wrapper: Callable[[Callable[[], None]], Callable[[], None]],\n) -> None:\n    @wrapper\n    def func_without_docstring() -> None: ...  # pragma: no cover\n\n    assert func_without_docstring.__doc__ is None\n"
  },
  {
    "path": "src/trio/_tests/test_repl.py",
    "content": "from __future__ import annotations\n\nimport os\nimport pathlib\nimport signal\nimport subprocess\nimport sys\nfrom functools import partial\nfrom typing import Protocol\n\nimport pytest\n\nimport trio._repl\n\n\nclass RawInput(Protocol):\n    def __call__(self, prompt: str = \"\") -> str: ...\n\n\ndef build_raw_input(cmds: list[str]) -> RawInput:\n    \"\"\"\n    Pass in a list of strings.\n    Returns a callable that returns each string, each time its called\n    When there are not more strings to return, raise EOFError\n    \"\"\"\n    cmds_iter = iter(cmds)\n    prompts = []\n\n    def _raw_helper(prompt: str = \"\") -> str:\n        prompts.append(prompt)\n        try:\n            return next(cmds_iter)\n        except StopIteration:\n            raise EOFError from None\n\n    return _raw_helper\n\n\ndef test_build_raw_input() -> None:\n    \"\"\"Quick test of our helper function.\"\"\"\n    raw_input = build_raw_input([\"cmd1\"])\n    assert raw_input() == \"cmd1\"\n    with pytest.raises(EOFError):\n        raw_input()\n\n\nasync def test_basic_interaction(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    \"\"\"\n    Run some basic commands through the interpreter while capturing stdout.\n    Ensure that the interpreted prints the expected results.\n    \"\"\"\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            # evaluate simple expression and recall the value\n            \"x = 1\",\n            \"print(f'{x=}')\",\n            # Literal gets printed\n            \"'hello'\",\n            # define and call sync function\n            \"def func():\",\n            \"  print(x + 1)\",\n            \"\",\n            \"func()\",\n            # define and call async function\n            \"async def afunc():\",\n            \"  return 4\",\n            \"\",\n            \"await afunc()\",\n            # import works\n            \"import sys\",\n            \"sys.stdout.write('hello stdout\\\\n')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, _err = capsys.readouterr()\n    assert out.splitlines() == [\"x=1\", \"'hello'\", \"2\", \"4\", \"hello stdout\", \"13\"]\n\n\nasync def test_system_exits_quit_interpreter(monkeypatch: pytest.MonkeyPatch) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            \"raise SystemExit\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    with pytest.raises(SystemExit):\n        await trio._repl.run_repl(console)\n\n\nasync def test_KI_interrupts(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            \"import signal, trio, trio.lowlevel\",\n            \"async def f():\",\n            \"  trio.lowlevel.spawn_system_task(\"\n            \"    trio.to_thread.run_sync,\"\n            \"    signal.raise_signal, signal.SIGINT,\"\n            \"  )\",  # just awaiting this kills the test runner?!\n            \"  await trio.sleep_forever()\",\n            \"  print('should not see this')\",\n            \"\",\n            \"await f()\",\n            \"print('AFTER KeyboardInterrupt')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, err = capsys.readouterr()\n    assert \"KeyboardInterrupt\" in err\n    assert \"should\" not in out\n    assert \"AFTER KeyboardInterrupt\" in out\n\n\nasync def test_system_exits_in_exc_group(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            \"import sys\",\n            \"if sys.version_info < (3, 11):\",\n            \"  from exceptiongroup import BaseExceptionGroup\",\n            \"\",\n            \"raise BaseExceptionGroup('', [RuntimeError(), SystemExit()])\",\n            \"print('AFTER BaseExceptionGroup')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, _err = capsys.readouterr()\n    # assert that raise SystemExit in an exception group\n    # doesn't quit\n    assert \"AFTER BaseExceptionGroup\" in out\n\n\nasync def test_system_exits_in_nested_exc_group(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            \"import sys\",\n            \"if sys.version_info < (3, 11):\",\n            \"  from exceptiongroup import BaseExceptionGroup\",\n            \"\",\n            \"raise BaseExceptionGroup(\",\n            \"  '', [BaseExceptionGroup('', [RuntimeError(), SystemExit()])])\",\n            \"print('AFTER BaseExceptionGroup')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, _err = capsys.readouterr()\n    # assert that raise SystemExit in an exception group\n    # doesn't quit\n    assert \"AFTER BaseExceptionGroup\" in out\n\n\nasync def test_base_exception_captured(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            # The statement after raise should still get executed\n            \"raise BaseException\",\n            \"print('AFTER BaseException')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, err = capsys.readouterr()\n    assert \"_threads.py\" not in err\n    assert \"_repl.py\" not in err\n    assert \"AFTER BaseException\" in out\n\n\nasync def test_exc_group_captured(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            # The statement after raise should still get executed\n            \"raise ExceptionGroup('', [KeyError()])\",\n            \"print('AFTER ExceptionGroup')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, _err = capsys.readouterr()\n    assert \"AFTER ExceptionGroup\" in out\n\n\nasync def test_base_exception_capture_from_coroutine(\n    capsys: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    console = trio._repl.TrioInteractiveConsole()\n    raw_input = build_raw_input(\n        [\n            \"async def async_func_raises_base_exception():\",\n            \"  raise BaseException\",\n            \"\",\n            # This will raise, but the statement after should still\n            # be executed\n            \"await async_func_raises_base_exception()\",\n            \"print('AFTER BaseException')\",\n        ],\n    )\n    monkeypatch.setattr(console, \"raw_input\", raw_input)\n    await trio._repl.run_repl(console)\n    out, err = capsys.readouterr()\n    assert \"_threads.py\" not in err\n    assert \"_repl.py\" not in err\n    assert \"AFTER BaseException\" in out\n\n\ndef test_main_entrypoint() -> None:\n    \"\"\"\n    Basic smoke test when running via the package __main__ entrypoint.\n    \"\"\"\n    repl = subprocess.run([sys.executable, \"-m\", \"trio\"], input=b\"exit()\")\n    assert repl.returncode == 0\n\n\ndef should_try_newline_injection() -> bool:\n    if sys.platform != \"linux\":\n        return False\n\n    sysctl = pathlib.Path(\"/proc/sys/dev/tty/legacy_tiocsti\")\n    if not sysctl.exists():  # pragma: no cover\n        return True\n\n    else:\n        return sysctl.read_text() == \"1\"\n\n\n@pytest.mark.skipif(\n    not should_try_newline_injection(),\n    reason=\"the ioctl we use is disabled in CI\",\n)\ndef test_ki_newline_injection() -> None:  # TODO: test this line\n    # TODO: we want to remove this functionality, eg by using vendored\n    #       pyrepls.\n    assert sys.platform != \"win32\"\n\n    import pty\n\n    # NOTE: this cannot be subprocess.Popen because pty.fork\n    #       does some magic to set the controlling terminal.\n    # (which I don't know how to replicate... so I copied this\n    # structure from pty.spawn...)\n    pid, pty_fd = pty.fork()  # type: ignore[attr-defined,unused-ignore]\n    if pid == 0:\n        os.execlp(sys.executable, *[sys.executable, \"-u\", \"-m\", \"trio\"])\n\n    # setup:\n    buffer = b\"\"\n    while not buffer.endswith(b\"import trio\\r\\n>>> \"):\n        buffer += os.read(pty_fd, 4096)\n\n    # sanity check:\n    print(buffer.decode())\n    buffer = b\"\"\n    os.write(pty_fd, b'print(\"hello!\")\\n')\n    while not buffer.endswith(b\">>> \"):\n        buffer += os.read(pty_fd, 4096)\n\n    assert buffer.count(b\"hello!\") == 2\n\n    # press ctrl+c\n    print(buffer.decode())\n    buffer = b\"\"\n    os.kill(pid, signal.SIGINT)\n    while not buffer.endswith(b\">>> \"):\n        buffer += os.read(pty_fd, 4096)\n\n    assert b\"KeyboardInterrupt\" in buffer\n\n    # press ctrl+c later\n    print(buffer.decode())\n    buffer = b\"\"\n    os.write(pty_fd, b'print(\"hello!\")')\n    os.kill(pid, signal.SIGINT)\n    while not buffer.endswith(b\">>> \"):\n        buffer += os.read(pty_fd, 4096)\n\n    assert b\"KeyboardInterrupt\" in buffer\n    print(buffer.decode())\n    os.close(pty_fd)\n    os.waitpid(pid, 0)[1]\n\n\nasync def test_ki_in_repl() -> None:\n    async with trio.open_nursery() as nursery:\n        proc = await nursery.start(\n            partial(\n                trio.run_process,\n                [sys.executable, \"-u\", \"-m\", \"trio\"],\n                stdout=subprocess.PIPE,\n                stderr=subprocess.STDOUT,\n                stdin=subprocess.PIPE,\n                creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == \"win32\" else 0,  # type: ignore[attr-defined,unused-ignore]\n            )\n        )\n\n        async with proc.stdout:\n            # setup\n            buffer = b\"\"\n            async for part in proc.stdout:  # pragma: no branch\n                buffer += part\n                # TODO: consider making run_process stdout have some universal newlines thing\n                if buffer.replace(b\"\\r\\n\", b\"\\n\").endswith(b\"import trio\\n>>> \"):\n                    break\n\n            # ensure things work\n            print(buffer.decode())\n            buffer = b\"\"\n            await proc.stdin.send_all(b'print(\"hello!\")\\n')\n            async for part in proc.stdout:  # pragma: no branch\n                buffer += part\n                if buffer.endswith(b\">>> \"):\n                    break\n\n            assert b\"hello!\" in buffer\n            print(buffer.decode())\n\n            # this seems to be necessary on Windows for reasons\n            # (the parents of process groups ignore ctrl+c by default...)\n            if sys.platform == \"win32\":\n                buffer = b\"\"\n                await proc.stdin.send_all(\n                    b\"import ctypes; ctypes.windll.kernel32.SetConsoleCtrlHandler(None, False)\\n\"\n                )\n                async for part in proc.stdout:  # pragma: no branch\n                    buffer += part\n                    if buffer.endswith(b\">>> \"):\n                        break\n\n                print(buffer.decode())\n\n            # try to decrease flakiness...\n            buffer = b\"\"\n            await proc.stdin.send_all(\n                b\"import coverage; trio.lowlevel.enable_ki_protection(coverage.pytracer.PyTracer._trace)\\n\"\n            )\n            async for part in proc.stdout:  # pragma: no branch\n                buffer += part\n                if buffer.endswith(b\">>> \"):\n                    break\n\n            print(buffer.decode())\n\n            # ensure that ctrl+c on a prompt works\n            # NOTE: for some reason, signal.SIGINT doesn't work for this test.\n            # Using CTRL_C_EVENT is also why we need subprocess.CREATE_NEW_PROCESS_GROUP\n            signal_sent = signal.CTRL_C_EVENT if sys.platform == \"win32\" else signal.SIGINT  # type: ignore[attr-defined,unused-ignore]\n            os.kill(proc.pid, signal_sent)\n            if sys.platform == \"win32\":\n                # we rely on EOFError which... doesn't happen with pipes.\n                # I'm not sure how to fix it...\n                await proc.stdin.send_all(b\"\\n\")\n            else:\n                # we test injection separately\n                await proc.stdin.send_all(b\"\\n\")\n\n            buffer = b\"\"\n            async for part in proc.stdout:  # pragma: no branch\n                buffer += part\n                if buffer.endswith(b\">>> \"):\n                    break\n\n            assert b\"KeyboardInterrupt\" in buffer\n\n            # ensure ctrl+c while a command runs works\n            print(buffer.decode())\n            await proc.stdin.send_all(b'print(\"READY\"); await trio.sleep_forever()\\n')\n            killed = False\n            buffer = b\"\"\n            async for part in proc.stdout:  # pragma: no branch\n                buffer += part\n                if buffer.replace(b\"\\r\\n\", b\"\\n\").endswith(b\"READY\\n\") and not killed:\n                    os.kill(proc.pid, signal_sent)\n                    killed = True\n                if buffer.endswith(b\">>> \"):\n                    break\n\n            assert b\"trio\" in buffer\n            assert b\"KeyboardInterrupt\" in buffer\n\n            # make sure it works for sync commands too\n            # (though this would be hard to break)\n            print(buffer.decode())\n            await proc.stdin.send_all(\n                b'import time; print(\"READY\"); time.sleep(99999)\\n'\n            )\n            killed = False\n            buffer = b\"\"\n            async for part in proc.stdout:  # pragma: no branch\n                buffer += part\n                if buffer.replace(b\"\\r\\n\", b\"\\n\").endswith(b\"READY\\n\") and not killed:\n                    os.kill(proc.pid, signal_sent)\n                    killed = True\n                if buffer.endswith(b\">>> \"):\n                    break\n\n            assert b\"Traceback\" in buffer\n            assert b\"KeyboardInterrupt\" in buffer\n\n            print(buffer.decode())\n\n        # kill the process\n        nursery.cancel_scope.cancel()\n"
  },
  {
    "path": "src/trio/_tests/test_scheduler_determinism.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport trio\n\nif TYPE_CHECKING:\n    import pytest\n\n\nasync def scheduler_trace() -> tuple[tuple[str, int], ...]:\n    \"\"\"Returns a scheduler-dependent value we can use to check determinism.\"\"\"\n    trace = []\n\n    async def tracer(name: str) -> None:\n        for i in range(50):\n            trace.append((name, i))\n            await trio.lowlevel.checkpoint()\n\n    async with trio.open_nursery() as nursery:\n        for i in range(5):\n            nursery.start_soon(tracer, str(i))\n\n    return tuple(trace)\n\n\ndef test_the_trio_scheduler_is_not_deterministic() -> None:\n    # At least, not yet.  See https://github.com/python-trio/trio/issues/32\n    traces = [trio.run(scheduler_trace) for _ in range(10)]\n    assert len(set(traces)) == len(traces)\n\n\ndef test_the_trio_scheduler_is_deterministic_if_seeded(\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    monkeypatch.setattr(trio._core._run, \"_ALLOW_DETERMINISTIC_SCHEDULING\", True)\n    traces = []\n    for _ in range(10):\n        state = trio._core._run._r.getstate()\n        try:\n            trio._core._run._r.seed(0)\n            traces.append(trio.run(scheduler_trace))\n        finally:\n            trio._core._run._r.setstate(state)\n\n    assert len(traces) == 10\n    assert len(set(traces)) == 1\n"
  },
  {
    "path": "src/trio/_tests/test_signals.py",
    "content": "from __future__ import annotations\n\nimport signal\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport pytest\n\nimport trio\n\nfrom .. import _core\nfrom .._signals import _signal_handler, get_pending_signal_count, open_signal_receiver\n\nif TYPE_CHECKING:\n    from types import FrameType\n\n\nasync def test_open_signal_receiver() -> None:\n    orig = signal.getsignal(signal.SIGILL)\n    with open_signal_receiver(signal.SIGILL) as receiver:\n        # Raise it a few times, to exercise signal coalescing, both at the\n        # call_soon level and at the SignalQueue level\n        signal.raise_signal(signal.SIGILL)\n        signal.raise_signal(signal.SIGILL)\n        await _core.wait_all_tasks_blocked()\n        signal.raise_signal(signal.SIGILL)\n        await _core.wait_all_tasks_blocked()\n        async for signum in receiver:  # pragma: no branch\n            assert signum == signal.SIGILL\n            break\n        assert get_pending_signal_count(receiver) == 0\n        signal.raise_signal(signal.SIGILL)\n        async for signum in receiver:  # pragma: no branch\n            assert signum == signal.SIGILL\n            break\n        assert get_pending_signal_count(receiver) == 0\n    with pytest.raises(RuntimeError):\n        await receiver.__anext__()\n    assert signal.getsignal(signal.SIGILL) is orig\n\n\nasync def test_open_signal_receiver_restore_handler_after_one_bad_signal() -> None:\n    orig = signal.getsignal(signal.SIGILL)\n    with pytest.raises(\n        ValueError,\n        match=r\"(signal number out of range|invalid signal value)$\",\n    ):\n        with open_signal_receiver(signal.SIGILL, 1234567):\n            pass  # pragma: no cover\n    # Still restored even if we errored out\n    assert signal.getsignal(signal.SIGILL) is orig\n\n\ndef test_open_signal_receiver_empty_fail() -> None:\n    with pytest.raises(TypeError, match=\"No signals were provided\"):\n        with open_signal_receiver():\n            pass\n\n\nasync def test_open_signal_receiver_restore_handler_after_duplicate_signal() -> None:\n    orig = signal.getsignal(signal.SIGILL)\n    with open_signal_receiver(signal.SIGILL, signal.SIGILL):\n        pass\n    # Still restored correctly\n    assert signal.getsignal(signal.SIGILL) is orig\n\n\nasync def test_catch_signals_wrong_thread() -> None:\n    async def naughty() -> None:\n        with open_signal_receiver(signal.SIGINT):\n            pass  # pragma: no cover\n\n    with pytest.raises(RuntimeError):\n        await trio.to_thread.run_sync(trio.run, naughty)\n\n\nasync def test_open_signal_receiver_conflict() -> None:\n    with pytest.RaisesGroup(trio.BusyResourceError):\n        with open_signal_receiver(signal.SIGILL) as receiver:\n            async with trio.open_nursery() as nursery:\n                nursery.start_soon(receiver.__anext__)\n                nursery.start_soon(receiver.__anext__)\n\n\n# Blocks until all previous calls to run_sync_soon(idempotent=True) have been\n# processed.\nasync def wait_run_sync_soon_idempotent_queue_barrier() -> None:\n    ev = trio.Event()\n    token = _core.current_trio_token()\n    token.run_sync_soon(ev.set, idempotent=True)\n    await ev.wait()\n\n\nasync def test_open_signal_receiver_no_starvation() -> None:\n    # Set up a situation where there are always 2 pending signals available to\n    # report, and make sure that instead of getting the same signal reported\n    # over and over, it alternates between reporting both of them.\n    with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:\n        try:\n            print(signal.getsignal(signal.SIGILL))\n            previous = None\n            for _ in range(10):\n                signal.raise_signal(signal.SIGILL)\n                signal.raise_signal(signal.SIGFPE)\n                await wait_run_sync_soon_idempotent_queue_barrier()\n                if previous is None:\n                    previous = await receiver.__anext__()\n                else:\n                    got = await receiver.__anext__()\n                    assert got in [signal.SIGILL, signal.SIGFPE]\n                    assert got != previous\n                    previous = got\n            # Clear out the last signal so that it doesn't get redelivered\n            while get_pending_signal_count(receiver) != 0:\n                await receiver.__anext__()\n        except BaseException:  # pragma: no cover\n            # If there's an unhandled exception above, then exiting the\n            # open_signal_receiver block might cause the signal to be\n            # redelivered and give us a core dump instead of a traceback...\n            import traceback\n\n            traceback.print_exc()\n\n\nasync def test_catch_signals_race_condition_on_exit() -> None:\n    delivered_directly: set[int] = set()\n\n    def direct_handler(signo: int, frame: FrameType | None) -> None:\n        delivered_directly.add(signo)\n\n    print(1)\n    # Test the version where the call_soon *doesn't* have a chance to run\n    # before we exit the with block:\n    with _signal_handler({signal.SIGILL, signal.SIGFPE}, direct_handler):\n        with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:\n            signal.raise_signal(signal.SIGILL)\n            signal.raise_signal(signal.SIGFPE)\n        await wait_run_sync_soon_idempotent_queue_barrier()\n    assert delivered_directly == {signal.SIGILL, signal.SIGFPE}\n    delivered_directly.clear()\n\n    print(2)\n    # Test the version where the call_soon *does* have a chance to run before\n    # we exit the with block:\n    with _signal_handler({signal.SIGILL, signal.SIGFPE}, direct_handler):\n        with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:\n            signal.raise_signal(signal.SIGILL)\n            signal.raise_signal(signal.SIGFPE)\n            await wait_run_sync_soon_idempotent_queue_barrier()\n            assert get_pending_signal_count(receiver) == 2\n    assert delivered_directly == {signal.SIGILL, signal.SIGFPE}\n    delivered_directly.clear()\n\n    # Again, but with a SIG_IGN signal:\n\n    print(3)\n    with _signal_handler({signal.SIGILL}, signal.SIG_IGN):\n        with open_signal_receiver(signal.SIGILL) as receiver:\n            signal.raise_signal(signal.SIGILL)\n        await wait_run_sync_soon_idempotent_queue_barrier()\n    # test passes if the process reaches this point without dying\n\n    print(4)\n    with _signal_handler({signal.SIGILL}, signal.SIG_IGN):\n        with open_signal_receiver(signal.SIGILL) as receiver:\n            signal.raise_signal(signal.SIGILL)\n            await wait_run_sync_soon_idempotent_queue_barrier()\n            assert get_pending_signal_count(receiver) == 1\n    # test passes if the process reaches this point without dying\n\n    # Check exception chaining if there are multiple exception-raising\n    # handlers\n    def raise_handler(signum: int, frame: FrameType | None) -> NoReturn:\n        raise RuntimeError(signum)\n\n    with _signal_handler({signal.SIGILL, signal.SIGFPE}, raise_handler):\n        with pytest.raises(RuntimeError) as excinfo:\n            with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:\n                signal.raise_signal(signal.SIGILL)\n                signal.raise_signal(signal.SIGFPE)\n                await wait_run_sync_soon_idempotent_queue_barrier()\n                assert get_pending_signal_count(receiver) == 2\n        exc = excinfo.value\n        signums = {exc.args[0]}\n        assert isinstance(exc.__context__, RuntimeError)\n        signums.add(exc.__context__.args[0])\n        assert signums == {signal.SIGILL, signal.SIGFPE}\n"
  },
  {
    "path": "src/trio/_tests/test_socket.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport inspect\nimport os\nimport socket as stdlib_socket\nimport sys\nimport tempfile\nfrom pathlib import Path\nfrom socket import AddressFamily, SocketKind\nfrom typing import TYPE_CHECKING, TypeAlias, cast\n\nimport attrs\nimport pytest\n\nfrom .. import _core, socket as tsocket\nfrom .._core._tests.tutil import binds_ipv6, can_create_ipv6, creates_ipv6, slow\nfrom .._socket import _NUMERIC_ONLY, AddressFormat, SocketType, _SocketType, _try_sync\nfrom ..testing import assert_checkpoints, wait_all_tasks_blocked\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from .._highlevel_socket import SocketStream\n\n    GaiTuple: TypeAlias = tuple[\n        AddressFamily,\n        SocketKind,\n        int,\n        str,\n        tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n    ]\n    GetAddrInfoResponse: TypeAlias = list[GaiTuple]\n    GetAddrInfoArgs: TypeAlias = tuple[\n        str | bytes | None,\n        str | bytes | int | None,\n        int,\n        int,\n        int,\n        int,\n    ]\nelse:\n    GaiTuple: object\n    GetAddrInfoResponse = object\n    GetAddrInfoArgs = object\n\n################################################################\n# utils\n################################################################\n\n\nclass MonkeypatchedGAI:\n    __slots__ = (\"_orig_getaddrinfo\", \"_responses\", \"record\")\n\n    def __init__(\n        self,\n        orig_getaddrinfo: Callable[\n            [str | bytes | None, str | bytes | int | None, int, int, int, int],\n            GetAddrInfoResponse,\n        ],\n    ) -> None:\n        self._orig_getaddrinfo = orig_getaddrinfo\n        self._responses: dict[\n            GetAddrInfoArgs,\n            GetAddrInfoResponse | str,\n        ] = {}\n        self.record: list[GetAddrInfoArgs] = []\n\n    # get a normalized getaddrinfo argument tuple\n    def _frozenbind(\n        self,\n        host: str | bytes | None,\n        port: str | bytes | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> GetAddrInfoArgs:\n        sig = inspect.signature(self._orig_getaddrinfo)\n        bound = sig.bind(host, port, family=family, type=type, proto=proto, flags=flags)\n        bound.apply_defaults()\n        frozenbound = bound.args\n        assert not bound.kwargs\n        return frozenbound\n\n    def set(\n        self,\n        response: GetAddrInfoResponse | str,\n        host: str | bytes | None,\n        port: str | bytes | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> None:\n        self._responses[\n            self._frozenbind(\n                host,\n                port,\n                family=family,\n                type=type,\n                proto=proto,\n                flags=flags,\n            )\n        ] = response\n\n    def getaddrinfo(\n        self,\n        host: str | bytes | None,\n        port: str | bytes | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> GetAddrInfoResponse | str:\n        bound = self._frozenbind(host, port, family, type, proto, flags)\n        self.record.append(bound)\n        if bound in self._responses:\n            return self._responses[bound]\n        elif flags & stdlib_socket.AI_NUMERICHOST:\n            return self._orig_getaddrinfo(host, port, family, type, proto, flags)\n        else:\n            raise RuntimeError(f\"gai called with unexpected arguments {bound}\")\n\n\n@pytest.fixture\ndef monkeygai(monkeypatch: pytest.MonkeyPatch) -> MonkeypatchedGAI:\n    controller = MonkeypatchedGAI(stdlib_socket.getaddrinfo)\n    monkeypatch.setattr(stdlib_socket, \"getaddrinfo\", controller.getaddrinfo)\n    return controller\n\n\nasync def test__try_sync() -> None:\n    with assert_checkpoints():\n        async with _try_sync():\n            pass\n\n    with assert_checkpoints():\n        with pytest.raises(KeyError):\n            async with _try_sync():\n                raise KeyError\n\n    async with _try_sync():\n        raise BlockingIOError\n\n    def _is_ValueError(exc: BaseException) -> bool:\n        return isinstance(exc, ValueError)\n\n    async with _try_sync(_is_ValueError):\n        raise ValueError\n\n    with assert_checkpoints():\n        with pytest.raises(BlockingIOError):\n            async with _try_sync(_is_ValueError):\n                raise BlockingIOError\n\n\n################################################################\n# basic re-exports\n################################################################\n\n\ndef test_socket_has_some_reexports() -> None:\n    assert tsocket.SOL_SOCKET == stdlib_socket.SOL_SOCKET\n    assert tsocket.TCP_NODELAY == stdlib_socket.TCP_NODELAY\n    assert tsocket.gaierror == stdlib_socket.gaierror\n    assert tsocket.ntohs == stdlib_socket.ntohs\n\n\n################################################################\n# name resolution\n################################################################\n\n\nasync def test_getaddrinfo(monkeygai: MonkeypatchedGAI) -> None:\n    def check(got: GetAddrInfoResponse, expected: GetAddrInfoResponse) -> None:\n        # win32 returns 0 for the proto field\n        # musl and glibc have inconsistent handling of the canonical name\n        # field (https://github.com/python-trio/trio/issues/1499)\n        # Neither field gets used much and there isn't much opportunity for us\n        # to mess them up, so we don't bother checking them here\n        def interesting_fields(\n            gai_tup: GaiTuple,\n        ) -> tuple[\n            AddressFamily,\n            SocketKind,\n            tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n        ]:\n            # (family, type, proto, canonname, sockaddr)\n            family, type_, _proto, _canonname, sockaddr = gai_tup\n            return (family, type_, sockaddr)\n\n        def filtered(\n            gai_list: GetAddrInfoResponse,\n        ) -> list[\n            tuple[\n                AddressFamily,\n                SocketKind,\n                tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n            ]\n        ]:\n            return [interesting_fields(gai_tup) for gai_tup in gai_list]\n\n        assert filtered(got) == filtered(expected)\n\n    # Simple non-blocking non-error cases, ipv4 and ipv6:\n    with assert_checkpoints():\n        res = await tsocket.getaddrinfo(\"127.0.0.1\", \"12345\", type=tsocket.SOCK_STREAM)\n\n    check(\n        res,\n        [\n            (\n                tsocket.AF_INET,  # 127.0.0.1 is ipv4\n                tsocket.SOCK_STREAM,\n                tsocket.IPPROTO_TCP,\n                \"\",\n                (\"127.0.0.1\", 12345),\n            ),\n        ],\n    )\n\n    with assert_checkpoints():\n        res = await tsocket.getaddrinfo(\"::1\", \"12345\", type=tsocket.SOCK_DGRAM)\n    check(\n        res,\n        [\n            (\n                tsocket.AF_INET6,\n                tsocket.SOCK_DGRAM,\n                tsocket.IPPROTO_UDP,\n                \"\",\n                (\"::1\", 12345, 0, 0),\n            ),\n        ],\n    )\n\n    monkeygai.set(\"x\", b\"host\", \"port\", family=0, type=0, proto=0, flags=0)\n    with assert_checkpoints():\n        res = await tsocket.getaddrinfo(\"host\", \"port\")\n    assert res == \"x\"\n    assert monkeygai.record[-1] == (b\"host\", \"port\", 0, 0, 0, 0)\n\n    # check raising an error from a non-blocking getaddrinfo\n    with assert_checkpoints():\n        with pytest.raises(tsocket.gaierror) as excinfo:\n            await tsocket.getaddrinfo(\"::1\", \"12345\", type=-1)\n    # Linux + glibc, Windows\n    expected_errnos = {tsocket.EAI_SOCKTYPE}\n    # Linux + musl\n    expected_errnos.add(tsocket.EAI_SERVICE)\n    # macOS\n    if hasattr(tsocket, \"EAI_BADHINTS\"):\n        expected_errnos.add(tsocket.EAI_BADHINTS)\n    assert excinfo.value.errno in expected_errnos\n\n    # check raising an error from a blocking getaddrinfo (exploits the fact\n    # that monkeygai raises if it gets a non-numeric request it hasn't been\n    # given an answer for)\n    with assert_checkpoints():\n        with pytest.raises(RuntimeError):\n            await tsocket.getaddrinfo(\"asdf\", \"12345\")\n\n\nasync def test_getnameinfo() -> None:\n    # Trivial test:\n    ni_numeric = stdlib_socket.NI_NUMERICHOST | stdlib_socket.NI_NUMERICSERV\n    with assert_checkpoints():\n        got = await tsocket.getnameinfo((\"127.0.0.1\", 1234), ni_numeric)\n    assert got == (\"127.0.0.1\", \"1234\")\n\n    # getnameinfo requires a numeric address as input:\n    with assert_checkpoints():\n        with pytest.raises(tsocket.gaierror):\n            await tsocket.getnameinfo((\"google.com\", 80), 0)\n\n    with assert_checkpoints():\n        with pytest.raises(tsocket.gaierror):\n            await tsocket.getnameinfo((\"localhost\", 80), 0)\n\n    # Blocking call to get expected values:\n    host, service = stdlib_socket.getnameinfo((\"127.0.0.1\", 80), 0)\n\n    # Some working calls:\n    got = await tsocket.getnameinfo((\"127.0.0.1\", 80), 0)\n    assert got == (host, service)\n\n    got = await tsocket.getnameinfo((\"127.0.0.1\", 80), tsocket.NI_NUMERICHOST)\n    assert got == (\"127.0.0.1\", service)\n\n    got = await tsocket.getnameinfo((\"127.0.0.1\", 80), tsocket.NI_NUMERICSERV)\n    assert got == (host, \"80\")\n\n\n################################################################\n# constructors\n################################################################\n\n\nasync def test_from_stdlib_socket() -> None:\n    sa, sb = stdlib_socket.socketpair()\n    assert not isinstance(sa, tsocket.SocketType)\n    with sa, sb:\n        ta = tsocket.from_stdlib_socket(sa)\n        assert isinstance(ta, tsocket.SocketType)\n        assert sa.fileno() == ta.fileno()\n        await ta.send(b\"x\")\n        assert sb.recv(1) == b\"x\"\n\n    # rejects other types\n    with pytest.raises(TypeError):\n        tsocket.from_stdlib_socket(1)  # type: ignore[arg-type]\n\n    class MySocket(stdlib_socket.socket):\n        pass\n\n    with MySocket() as mysock:\n        with pytest.raises(TypeError):\n            tsocket.from_stdlib_socket(mysock)\n\n\nasync def test_from_fd() -> None:\n    sa, sb = stdlib_socket.socketpair()\n    ta = tsocket.fromfd(sa.fileno(), sa.family, sa.type, sa.proto)\n    with sa, sb, ta:\n        assert ta.fileno() != sa.fileno()\n        await ta.send(b\"x\")\n        assert sb.recv(3) == b\"x\"\n\n\nasync def test_socketpair_simple() -> None:\n    async def child(sock: SocketType) -> None:\n        print(\"sending hello\")\n        await sock.send(b\"h\")\n        assert await sock.recv(1) == b\"h\"\n\n    a, b = tsocket.socketpair()\n    with a, b:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(child, a)\n            nursery.start_soon(child, b)\n\n\n@pytest.mark.skipif(not hasattr(tsocket, \"fromshare\"), reason=\"windows only\")\nasync def test_fromshare() -> None:\n    if TYPE_CHECKING and sys.platform != \"win32\":  # pragma: no cover\n        return\n    a, b = tsocket.socketpair()\n    with a, b:\n        # share with ourselves\n        shared = a.share(os.getpid())\n        a2 = tsocket.fromshare(shared)\n        with a2:\n            assert a.fileno() != a2.fileno()\n            await a2.send(b\"x\")\n            assert await b.recv(1) == b\"x\"\n\n\nasync def test_socket() -> None:\n    with tsocket.socket() as s:\n        assert isinstance(s, tsocket.SocketType)\n        assert s.family == tsocket.AF_INET\n\n\n@creates_ipv6\nasync def test_socket_v6() -> None:\n    with tsocket.socket(tsocket.AF_INET6, tsocket.SOCK_DGRAM) as s:\n        assert isinstance(s, tsocket.SocketType)\n        assert s.family == tsocket.AF_INET6\n\n\n@pytest.mark.skipif(sys.platform != \"linux\", reason=\"linux only\")\nasync def test_sniff_sockopts() -> None:\n    from socket import AF_INET, AF_INET6, SOCK_DGRAM, SOCK_STREAM\n\n    # generate the combinations of families/types we're testing:\n    families = (AF_INET, AF_INET6) if can_create_ipv6 else (AF_INET,)\n    sockets = [\n        stdlib_socket.socket(family, type_)\n        for family in families\n        for type_ in [SOCK_DGRAM, SOCK_STREAM]\n    ]\n    for socket in sockets:\n        # regular Trio socket constructor\n        tsocket_socket = tsocket.socket(fileno=socket.fileno())\n        # check family / type for correctness:\n        assert tsocket_socket.family == socket.family\n        assert tsocket_socket.type == socket.type\n        tsocket_socket.detach()\n\n        # fromfd constructor\n        tsocket_from_fd = tsocket.fromfd(socket.fileno(), AF_INET, SOCK_STREAM)\n        # check family / type for correctness:\n        assert tsocket_from_fd.family == socket.family\n        assert tsocket_from_fd.type == socket.type\n        tsocket_from_fd.close()\n\n        socket.close()\n\n\n################################################################\n# _SocketType\n################################################################\n\n\nasync def test_SocketType_basics() -> None:\n    sock = tsocket.socket()\n    with sock as cm_enter_value:\n        assert cm_enter_value is sock\n        assert isinstance(sock.fileno(), int)\n        assert not sock.get_inheritable()\n        sock.set_inheritable(True)\n        assert sock.get_inheritable()\n\n        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False)\n        assert not sock.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)\n        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, True)\n        assert sock.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)\n    # closed sockets have fileno() == -1\n    assert sock.fileno() == -1\n\n    # smoke test\n    repr(sock)\n\n    # detach\n    with tsocket.socket() as sock:\n        fd = sock.fileno()\n        assert sock.detach() == fd\n        assert sock.fileno() == -1\n\n    # close\n    sock = tsocket.socket()\n    assert sock.fileno() >= 0\n    sock.close()\n    assert sock.fileno() == -1\n\n    # share was tested above together with fromshare\n\n    # check __dir__\n    assert \"family\" in dir(sock)\n    assert \"recv\" in dir(sock)\n    assert \"setsockopt\" in dir(sock)\n\n    # our __getattr__ handles unknown names\n    with pytest.raises(AttributeError):\n        sock.asdf  # type: ignore[attr-defined]  # noqa: B018\n\n    # type family proto\n    stdlib_sock = stdlib_socket.socket()\n    sock = tsocket.from_stdlib_socket(stdlib_sock)\n    assert sock.type == stdlib_sock.type\n    assert sock.family == stdlib_sock.family\n    assert sock.proto == stdlib_sock.proto\n    sock.close()\n\n\nasync def test_SocketType_setsockopt() -> None:\n    sock = tsocket.socket()\n    with sock as _:\n        setsockopt_tests(sock)\n\n\ndef setsockopt_tests(sock: SocketType | SocketStream) -> None:\n    \"\"\"Extract these out, to be reused for SocketStream also.\"\"\"\n    # specifying optlen. Not supported on pypy, and I couldn't find\n    # valid calls on darwin or win32.\n    if hasattr(tsocket, \"SO_BINDTODEVICE\"):\n        try:\n            sock.setsockopt(tsocket.SOL_SOCKET, tsocket.SO_BINDTODEVICE, None, 0)\n        except (\n            OSError\n        ) as e:  # pragma: no cover  # all CI runners support SO_BINDTODEVICE\n            assert e.errno in [  # noqa: PT017\n                # some versions of Python have the attribute yet can run on\n                # platforms that do not support it. For instance, MacOS 15\n                # gained support for SO_BINDTODEVICE and CPython 3.13.1 was\n                # built on it (presumably), but our CI runners ran MacOS 14 and\n                # so failed.\n                42,\n                # Older Linux kernels (prior to patch\n                # https://lore.kernel.org/netdev/m37drhs1jn.fsf@bernat.ch/t/)\n                # do not support SO_BINDTODEVICE as an unprivileged user.\n                errno.EPERM,\n            ]\n\n    # specifying value\n    sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False)\n\n    # specifying both\n    with pytest.raises(TypeError, match=\"invalid value for argument 'value'\"):\n        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False, 5)  # type: ignore[call-overload]\n\n    # specifying neither\n    with pytest.raises(TypeError, match=\"invalid value for argument 'value'\"):\n        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, None)  # type: ignore[call-overload]\n\n\nasync def test_SocketType_dup() -> None:\n    a, b = tsocket.socketpair()\n    with a, b:\n        a2 = a.dup()\n        with a2:\n            assert isinstance(a2, tsocket.SocketType)\n            assert a2.fileno() != a.fileno()\n            a.close()\n            await a2.send(b\"x\")\n            assert await b.recv(1) == b\"x\"\n\n\nasync def test_SocketType_shutdown() -> None:\n    a, b = tsocket.socketpair()\n    with a, b:\n        await a.send(b\"x\")\n        assert await b.recv(1) == b\"x\"\n        assert not a.did_shutdown_SHUT_WR\n        assert not b.did_shutdown_SHUT_WR\n        a.shutdown(tsocket.SHUT_WR)\n        assert a.did_shutdown_SHUT_WR\n        assert not b.did_shutdown_SHUT_WR\n        assert await b.recv(1) == b\"\"\n        await b.send(b\"y\")\n        assert await a.recv(1) == b\"y\"\n\n    a, b = tsocket.socketpair()\n    with a, b:\n        assert not a.did_shutdown_SHUT_WR\n        a.shutdown(tsocket.SHUT_RD)\n        assert not a.did_shutdown_SHUT_WR\n\n    a, b = tsocket.socketpair()\n    with a, b:\n        assert not a.did_shutdown_SHUT_WR\n        a.shutdown(tsocket.SHUT_RDWR)\n        assert a.did_shutdown_SHUT_WR\n\n\n@pytest.mark.parametrize(\n    (\"address\", \"socket_type\"),\n    [\n        (\"127.0.0.1\", tsocket.AF_INET),\n        pytest.param(\"::1\", tsocket.AF_INET6, marks=binds_ipv6),\n    ],\n)\nasync def test_SocketType_simple_server(\n    address: str,\n    socket_type: AddressFamily,\n) -> None:\n    # listen, bind, accept, connect, getpeername, getsockname\n    listener = tsocket.socket(socket_type)\n    client = tsocket.socket(socket_type)\n    with listener, client:\n        await listener.bind((address, 0))\n        listener.listen(20)\n        addr = listener.getsockname()[:2]\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(client.connect, addr)\n            server, client_addr = await listener.accept()\n        with server:\n            assert client_addr == server.getpeername() == client.getsockname()\n            await server.send(b\"x\")\n            assert await client.recv(1) == b\"x\"\n\n\nasync def test_SocketType_is_readable() -> None:\n    a, b = tsocket.socketpair()\n    with a, b:\n        assert not a.is_readable()\n        await b.send(b\"x\")\n        await _core.wait_readable(a)\n        assert a.is_readable()\n        assert await a.recv(1) == b\"x\"\n        assert not a.is_readable()\n\n\n# On some macOS systems, getaddrinfo likes to return V4-mapped addresses even\n# when we *don't* pass AI_V4MAPPED.\n# https://github.com/python-trio/trio/issues/580\ndef gai_without_v4mapped_is_buggy() -> bool:  # pragma: no cover\n    try:\n        stdlib_socket.getaddrinfo(\"1.2.3.4\", 0, family=stdlib_socket.AF_INET6)\n    except stdlib_socket.gaierror:\n        return False\n    else:\n        return True\n\n\n@attrs.define(slots=False)\nclass Addresses:\n    bind_all: str\n    localhost: str\n    arbitrary: str\n    broadcast: str\n\n\n# Direct thorough tests of the implicit resolver helpers\n@pytest.mark.parametrize(\n    (\"socket_type\", \"addrs\"),\n    [\n        (\n            tsocket.AF_INET,\n            Addresses(\n                bind_all=\"0.0.0.0\",\n                localhost=\"127.0.0.1\",\n                arbitrary=\"1.2.3.4\",\n                broadcast=\"255.255.255.255\",\n            ),\n        ),\n        pytest.param(\n            tsocket.AF_INET6,\n            Addresses(\n                bind_all=\"::\",\n                localhost=\"::1\",\n                arbitrary=\"1::2\",\n                broadcast=\"::ffff:255.255.255.255\",\n            ),\n            marks=creates_ipv6,\n        ),\n    ],\n)\nasync def test_SocketType_resolve(socket_type: AddressFamily, addrs: Addresses) -> None:\n    v6 = socket_type == tsocket.AF_INET6\n\n    def pad(addr: tuple[str | int, ...]) -> tuple[str | int, ...]:\n        if v6:\n            while len(addr) < 4:\n                addr += (0,)\n        return addr\n\n    def assert_eq(\n        actual: tuple[str | int, ...],\n        expected: tuple[str | int, ...],\n    ) -> None:\n        assert pad(expected) == pad(actual)\n\n    with tsocket.socket(family=socket_type) as sock:\n        # testing internal functionality, so we check it against the internal type\n        assert isinstance(sock, _SocketType)\n\n        # For some reason the stdlib special-cases \"\" to pass NULL to\n        # getaddrinfo. They also error out on None, but whatever, None is much\n        # more consistent, so we accept it too.\n        # TODO: this implies that we can send host=None, but what does that imply for the return value, and other stuff?\n        for null in [None, \"\"]:\n            got = await sock._resolve_address_nocp((null, 80), local=True)\n            assert not isinstance(got, (str, bytes))\n            assert_eq(got, (addrs.bind_all, 80))\n            got = await sock._resolve_address_nocp((null, 80), local=False)\n            assert not isinstance(got, (str, bytes))\n            assert_eq(got, (addrs.localhost, 80))\n\n        # AI_PASSIVE only affects the wildcard address, so for everything else\n        # local=True/local=False should work the same:\n        for local in [False, True]:\n\n            async def res(\n                args: (\n                    tuple[str, int]\n                    | tuple[str, int, int]\n                    | tuple[str, int, int, int]\n                    | tuple[str, str]\n                    | tuple[str, str, int]\n                    | tuple[str, str, int, int]\n                ),\n            ) -> tuple[str | int, ...]:\n                value = await sock._resolve_address_nocp(\n                    args,\n                    local=local,  # noqa: B023  # local is not bound in function definition\n                )\n                assert isinstance(value, tuple)\n                return cast(\"tuple[str | int, ...]\", value)\n\n            assert_eq(await res((addrs.arbitrary, \"http\")), (addrs.arbitrary, 80))\n            if v6:\n                # Check handling of different length ipv6 address tuples\n                assert_eq(await res((\"1::2\", 80)), (\"1::2\", 80, 0, 0))\n                assert_eq(await res((\"1::2\", 80, 0)), (\"1::2\", 80, 0, 0))\n                assert_eq(await res((\"1::2\", 80, 0, 0)), (\"1::2\", 80, 0, 0))\n                # Non-zero flowinfo/scopeid get passed through\n                assert_eq(await res((\"1::2\", 80, 1)), (\"1::2\", 80, 1, 0))\n                assert_eq(await res((\"1::2\", 80, 1, 2)), (\"1::2\", 80, 1, 2))\n\n                # And again with a string port, as a trick to avoid the\n                # already-resolved address fastpath and make sure we call\n                # getaddrinfo\n                assert_eq(await res((\"1::2\", \"80\")), (\"1::2\", 80, 0, 0))\n                assert_eq(await res((\"1::2\", \"80\", 0)), (\"1::2\", 80, 0, 0))\n                assert_eq(await res((\"1::2\", \"80\", 0, 0)), (\"1::2\", 80, 0, 0))\n                assert_eq(await res((\"1::2\", \"80\", 1)), (\"1::2\", 80, 1, 0))\n                assert_eq(await res((\"1::2\", \"80\", 1, 2)), (\"1::2\", 80, 1, 2))\n\n                # V4 mapped addresses resolved if V6ONLY is False\n                sock.setsockopt(tsocket.IPPROTO_IPV6, tsocket.IPV6_V6ONLY, False)\n                assert_eq(await res((\"1.2.3.4\", \"http\")), (\"::ffff:1.2.3.4\", 80))\n\n            # Check the <broadcast> special case, because why not\n            assert_eq(await res((\"<broadcast>\", 123)), (addrs.broadcast, 123))\n\n            # But not if it's true (at least on systems where getaddrinfo works\n            # correctly)\n            if v6 and not gai_without_v4mapped_is_buggy():\n                sock.setsockopt(tsocket.IPPROTO_IPV6, tsocket.IPV6_V6ONLY, True)\n                with pytest.raises(tsocket.gaierror) as excinfo:\n                    await res((\"1.2.3.4\", 80))\n                # Windows, macOS, musl/Linux\n                expected_errnos = {tsocket.EAI_NONAME, tsocket.EAI_NODATA}\n                # Linux\n                if hasattr(tsocket, \"EAI_ADDRFAMILY\"):\n                    expected_errnos.add(tsocket.EAI_ADDRFAMILY)\n                assert excinfo.value.errno in expected_errnos\n\n            # A family where we know nothing about the addresses, so should just\n            # pass them through. This should work on Linux, which is enough to\n            # smoke test the basic functionality...\n            try:\n                netlink_sock = tsocket.socket(\n                    family=tsocket.AF_NETLINK,\n                    type=tsocket.SOCK_DGRAM,\n                )\n            except (AttributeError, OSError):\n                pass\n            else:\n                assert isinstance(netlink_sock, _SocketType)\n                assert (\n                    await netlink_sock._resolve_address_nocp(\"asdf\", local=local)\n                    == \"asdf\"\n                )\n                netlink_sock.close()\n\n            address = r\"^address should be a \\(host, port(, \\[flowinfo, \\[scopeid\\]\\])*\\) tuple$\"\n            with pytest.raises(ValueError, match=address):\n                await res(\"1.2.3.4\")  # type: ignore[arg-type]\n            with pytest.raises(ValueError, match=address):\n                await res((\"1.2.3.4\",))  # type: ignore[arg-type]\n            with pytest.raises(\n                ValueError,\n                match=address,\n            ):\n                if v6:\n                    await res((\"1.2.3.4\", 80, 0, 0, 0))  # type: ignore[arg-type]\n                else:\n                    # I guess in theory there could be enough overloads that this could error?\n                    await res((\"1.2.3.4\", 80, 0, 0))\n\n\nasync def test_SocketType_unresolved_names() -> None:\n    with tsocket.socket() as sock:\n        await sock.bind((\"localhost\", 0))\n        assert sock.getsockname()[0] == \"127.0.0.1\"\n        sock.listen(10)\n\n        with tsocket.socket() as sock2:\n            await sock2.connect((\"localhost\", sock.getsockname()[1]))\n            assert sock2.getpeername() == sock.getsockname()\n\n    # check gaierror propagates out\n    with tsocket.socket() as sock:\n        with pytest.raises(tsocket.gaierror):\n            # definitely not a valid request\n            await sock.bind((\"1.2:3\", -1))\n\n\n# This tests all the complicated paths through _nonblocking_helper, using recv\n# as a stand-in for all the methods that use _nonblocking_helper.\nasync def test_SocketType_non_blocking_paths() -> None:\n    a, b = stdlib_socket.socketpair()\n    with a, b:\n        ta = tsocket.from_stdlib_socket(a)\n        b.setblocking(False)\n\n        # cancel before even calling\n        b.send(b\"1\")\n        with _core.CancelScope() as cscope:\n            cscope.cancel()\n            with assert_checkpoints():\n                with pytest.raises(_core.Cancelled):\n                    await ta.recv(10)\n        # immediate success (also checks that the previous attempt didn't\n        # actually read anything)\n        with assert_checkpoints():\n            assert await ta.recv(10) == b\"1\"\n        # immediate failure\n        with assert_checkpoints():\n            with pytest.raises(TypeError):\n                await ta.recv(\"haha\")  # type: ignore[arg-type]\n        # block then succeed\n\n        async def do_successful_blocking_recv() -> None:\n            with assert_checkpoints():\n                assert await ta.recv(10) == b\"2\"\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_successful_blocking_recv)\n            await wait_all_tasks_blocked()\n            b.send(b\"2\")\n        # block then cancelled\n\n        async def do_cancelled_blocking_recv() -> None:\n            with assert_checkpoints():\n                with pytest.raises(_core.Cancelled):\n                    await ta.recv(10)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_cancelled_blocking_recv)\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n        # Okay, here's the trickiest one: we want to exercise the path where\n        # the task is signaled to wake, goes to recv, but then the recv fails,\n        # so it has to go back to sleep and try again. Strategy: have two\n        # tasks waiting on two sockets (to work around the rule against having\n        # two tasks waiting on the same socket), wake them both up at the same\n        # time, and whichever one runs first \"steals\" the data from the\n        # other:\n        tb = tsocket.from_stdlib_socket(b)\n\n        async def t1() -> None:\n            with assert_checkpoints():\n                assert await ta.recv(1) == b\"a\"\n            with assert_checkpoints():\n                assert await tb.recv(1) == b\"b\"\n\n        async def t2() -> None:\n            with assert_checkpoints():\n                assert await tb.recv(1) == b\"b\"\n            with assert_checkpoints():\n                assert await ta.recv(1) == b\"a\"\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(t1)\n            nursery.start_soon(t2)\n            await wait_all_tasks_blocked()\n            a.send(b\"b\")\n            b.send(b\"a\")\n            await wait_all_tasks_blocked()\n            a.send(b\"b\")\n            b.send(b\"a\")\n\n\n# This tests the complicated paths through connect\n@slow\nasync def test_SocketType_connect_paths() -> None:\n    with tsocket.socket() as sock:\n        with pytest.raises(\n            ValueError,\n            match=r\"^address should be a \\(host, port(, \\[flowinfo, \\[scopeid\\]\\])*\\) tuple$\",\n        ):\n            # Should be a tuple\n            await sock.connect(\"localhost\")\n\n    # cancelled before we start\n    with tsocket.socket() as sock:\n        with _core.CancelScope() as cancel_scope:\n            cancel_scope.cancel()\n            with pytest.raises(_core.Cancelled):\n                await sock.connect((\"127.0.0.1\", 80))\n\n    # Cancelled in between the connect() call and the connect completing\n    with _core.CancelScope() as cancel_scope:\n        with tsocket.socket() as sock, tsocket.socket() as listener:\n            await listener.bind((\"127.0.0.1\", 0))\n            listener.listen()\n\n            # Swap in our weird subclass under the trio.socket._SocketType's\n            # nose -- and then swap it back out again before we hit\n            # wait_socket_writable, which insists on a real socket.\n            class CancelSocket(stdlib_socket.socket):\n                def connect(\n                    self,\n                    address: AddressFormat,\n                ) -> None:\n                    # accessing private method only available in _SocketType\n                    assert isinstance(sock, _SocketType)\n\n                    cancel_scope.cancel()\n                    sock._sock = stdlib_socket.fromfd(\n                        self.detach(),\n                        self.family,\n                        self.type,\n                    )\n                    sock._sock.connect(address)\n                    # If connect *doesn't* raise, then pretend it did\n                    raise BlockingIOError  # pragma: no cover\n\n            # accessing private method only available in _SocketType\n            assert isinstance(sock, _SocketType)\n            sock._sock.close()\n            sock._sock = CancelSocket()\n\n            with assert_checkpoints():\n                with pytest.raises(_core.Cancelled):\n                    await sock.connect(listener.getsockname())\n            assert sock.fileno() == -1\n\n    # Failed connect (hopefully after raising BlockingIOError)\n    with tsocket.socket() as sock:\n        with pytest.raises(\n            OSError,\n            match=r\"^\\[\\w+ \\d+\\] Error connecting to \\('127\\.0\\.0\\.\\d', \\d+\\): (Connection refused|Unknown error)$\",\n        ):\n            # TCP port 2 is not assigned. Pretty sure nothing will be\n            # listening there. (We used to bind a port and then *not* call\n            # listen() to ensure nothing was listening there, but it turns\n            # out on macOS if you do this it takes 30 seconds for the\n            # connect to fail. Really. Also if you use a non-routable\n            # address. This way fails instantly though. As long as nothing\n            # is listening on port 2.)\n\n            # Windows retries failed connections so this takes seconds\n            # (and that's why this is marked @slow)\n            await sock.connect((\"127.0.0.1\", 2))\n\n\n# Fix issue #1810\n@slow\nasync def test_address_in_socket_error() -> None:\n    address = \"127.0.0.1\"\n    with tsocket.socket() as sock:\n        with pytest.raises(\n            OSError,\n            match=rf\"^\\[\\w+ \\d+\\] Error connecting to \\({address!r}, 2\\): (Connection refused|Unknown error)$\",\n        ):\n            # Windows retries failed connections so this takes seconds\n            # (and that's why this is marked @slow)\n            await sock.connect((address, 2))\n\n\nasync def test_resolve_address_exception_in_connect_closes_socket() -> None:\n    # Here we are testing issue 247, any cancellation will leave the socket closed\n    with _core.CancelScope() as cancel_scope:\n        with tsocket.socket() as sock:\n\n            async def _resolve_address_nocp(\n                address: AddressFormat,\n                *,\n                local: bool,\n            ) -> None:\n                assert address == \"\"\n                assert not local\n                cancel_scope.cancel()\n                await _core.checkpoint()\n\n            assert isinstance(sock, _SocketType)\n            sock._resolve_address_nocp = _resolve_address_nocp  # type: ignore[method-assign]\n            with assert_checkpoints():\n                with pytest.raises(_core.Cancelled):\n                    await sock.connect(\"\")\n            assert sock.fileno() == -1\n\n\nasync def test_send_recv_variants() -> None:\n    a, b = tsocket.socketpair()\n    with a, b:\n        # recv, including with flags\n        assert await a.send(b\"x\") == 1\n        assert await b.recv(10, tsocket.MSG_PEEK) == b\"x\"\n        assert await b.recv(10) == b\"x\"\n\n        # recv_into\n        await a.send(b\"x\")\n        buf = bytearray(10)\n        await b.recv_into(buf)\n        assert buf == b\"x\" + b\"\\x00\" * 9\n\n        if hasattr(a, \"sendmsg\"):\n            assert await a.sendmsg([b\"xxx\"], []) == 3\n            assert await b.recv(10) == b\"xxx\"\n\n    a = tsocket.socket(type=tsocket.SOCK_DGRAM)\n    b = tsocket.socket(type=tsocket.SOCK_DGRAM)\n    with a, b:\n        await a.bind((\"127.0.0.1\", 0))\n        await b.bind((\"127.0.0.1\", 0))\n\n        targets = [b.getsockname(), (\"localhost\", b.getsockname()[1])]\n\n        # recvfrom + sendto, with and without names\n        for target in targets:\n            assert await a.sendto(b\"xxx\", target) == 3\n            data, addr = await b.recvfrom(10)\n            assert data == b\"xxx\"\n            assert addr == a.getsockname()\n\n        # sendto + flags\n        #\n        # I can't find any flags that send() accepts... on Linux at least\n        # passing MSG_MORE to send_some on a connected UDP socket seems to\n        # just be ignored.\n        #\n        # But there's no MSG_MORE on Windows or macOS. I guess send_some flags\n        # are really not very useful, but at least this tests them a bit.\n        if hasattr(tsocket, \"MSG_MORE\"):\n            await a.sendto(b\"xxx\", tsocket.MSG_MORE, b.getsockname())\n            await a.sendto(b\"yyy\", tsocket.MSG_MORE, b.getsockname())\n            await a.sendto(b\"zzz\", b.getsockname())\n            data, addr = await b.recvfrom(10)\n            assert data == b\"xxxyyyzzz\"\n            assert addr == a.getsockname()\n\n        # recvfrom_into\n        assert await a.sendto(b\"xxx\", b.getsockname()) == 3\n        buf = bytearray(10)\n        nbytes, addr = await b.recvfrom_into(buf)\n        assert nbytes == 3\n        assert buf == b\"xxx\" + b\"\\x00\" * 7\n        assert addr == a.getsockname()\n\n        if hasattr(b, \"recvmsg\"):\n            assert await a.sendto(b\"xxx\", b.getsockname()) == 3\n            data, ancdata, msg_flags, addr = await b.recvmsg(10)\n            assert data == b\"xxx\"\n            assert ancdata == []\n            assert msg_flags == 0\n            assert addr == a.getsockname()\n\n        if hasattr(b, \"recvmsg_into\"):\n            assert await a.sendto(b\"xyzw\", b.getsockname()) == 4\n            buf1 = bytearray(2)\n            buf2 = bytearray(3)\n            ret = await b.recvmsg_into([buf1, buf2])\n            nbytes, ancdata, msg_flags, addr = ret\n            assert nbytes == 4\n            assert buf1 == b\"xy\"\n            assert buf2 == b\"zw\" + b\"\\x00\"\n            assert ancdata == []\n            assert msg_flags == 0\n            assert addr == a.getsockname()\n\n        if hasattr(a, \"sendmsg\"):\n            for target in targets:\n                assert await a.sendmsg([b\"x\", b\"yz\"], [], 0, target) == 3\n                assert await b.recvfrom(10) == (b\"xyz\", a.getsockname())\n\n    a = tsocket.socket(type=tsocket.SOCK_DGRAM)\n    b = tsocket.socket(type=tsocket.SOCK_DGRAM)\n    with a, b:\n        await b.bind((\"127.0.0.1\", 0))\n        await a.connect(b.getsockname())\n        # send on a connected udp socket; each call creates a separate\n        # datagram\n        await a.send(b\"xxx\")\n        await a.send(b\"yyy\")\n        assert await b.recv(10) == b\"xxx\"\n        assert await b.recv(10) == b\"yyy\"\n\n\nasync def test_idna(monkeygai: MonkeypatchedGAI) -> None:\n    # This is the encoding for \"faß.de\", which uses one of the characters that\n    # IDNA 2003 handles incorrectly:\n    monkeygai.set(\"ok faß.de\", b\"xn--fa-hia.de\", 80)\n    monkeygai.set(\"ok ::1\", \"::1\", 80, flags=_NUMERIC_ONLY)\n    monkeygai.set(\"ok ::1\", b\"::1\", 80, flags=_NUMERIC_ONLY)\n    # Some things that should not reach the underlying socket.getaddrinfo:\n    monkeygai.set(\"bad\", \"fass.de\", 80)\n    # We always call socket.getaddrinfo with bytes objects:\n    monkeygai.set(\"bad\", \"xn--fa-hia.de\", 80)\n\n    assert await tsocket.getaddrinfo(\"::1\", 80) == \"ok ::1\"\n    assert await tsocket.getaddrinfo(b\"::1\", 80) == \"ok ::1\"\n    assert await tsocket.getaddrinfo(\"faß.de\", 80) == \"ok faß.de\"\n    assert await tsocket.getaddrinfo(\"xn--fa-hia.de\", 80) == \"ok faß.de\"\n    assert await tsocket.getaddrinfo(b\"xn--fa-hia.de\", 80) == \"ok faß.de\"\n\n\nasync def test_getprotobyname() -> None:\n    # These are the constants used in IP header fields, so the numeric values\n    # had *better* be stable across systems...\n    assert await tsocket.getprotobyname(\"udp\") == 17\n    assert await tsocket.getprotobyname(\"tcp\") == 6\n\n\nasync def test_custom_hostname_resolver(monkeygai: MonkeypatchedGAI) -> None:\n    # This intentionally breaks the signatures used in HostnameResolver\n    class CustomResolver:\n        async def getaddrinfo(\n            self,\n            host: str,\n            port: str,\n            family: int,\n            type: int,\n            proto: int,\n            flags: int,\n        ) -> tuple[str, str, str, int, int, int, int]:\n            return (\"custom_gai\", host, port, family, type, proto, flags)\n\n        async def getnameinfo(\n            self,\n            sockaddr: tuple[str, int] | tuple[str, int, int, int],\n            flags: int,\n        ) -> tuple[str, tuple[str, int] | tuple[str, int, int, int], int]:\n            return (\"custom_gni\", sockaddr, flags)\n\n    cr = CustomResolver()\n\n    assert tsocket.set_custom_hostname_resolver(cr) is None  # type: ignore[arg-type]\n\n    # Check that the arguments are all getting passed through.\n    # We have to use valid calls to avoid making the underlying system\n    # getaddrinfo cranky when it's used for NUMERIC checks.\n    for vals in [\n        (tsocket.AF_INET, 0, 0, 0),\n        (0, tsocket.SOCK_STREAM, 0, 0),\n        (0, 0, tsocket.IPPROTO_TCP, 0),\n        (0, 0, 0, tsocket.AI_CANONNAME),\n    ]:\n        assert await tsocket.getaddrinfo(\"localhost\", \"foo\", *vals) == (\n            \"custom_gai\",\n            b\"localhost\",\n            \"foo\",\n            *vals,\n        )\n\n    # IDNA encoding is handled before calling the special object\n    got = await tsocket.getaddrinfo(\"föö\", \"foo\")\n    expected = (\"custom_gai\", b\"xn--f-1gaa\", \"foo\", 0, 0, 0, 0)\n    assert got == expected\n\n    assert await tsocket.getnameinfo(\"a\", 0) == (  # type: ignore[arg-type]\n        \"custom_gni\",\n        \"a\",\n        0,\n    )\n\n    # We can set it back to None\n    assert tsocket.set_custom_hostname_resolver(None) is cr\n\n    # And now Trio switches back to calling socket.getaddrinfo (specifically\n    # our monkeypatched version of socket.getaddrinfo)\n    monkeygai.set(\"x\", b\"host\", \"port\", family=0, type=0, proto=0, flags=0)\n    assert await tsocket.getaddrinfo(\"host\", \"port\") == \"x\"\n\n\nasync def test_custom_socket_factory() -> None:\n    class CustomSocketFactory:\n        def socket(\n            self,\n            family: AddressFamily,\n            type: SocketKind,\n            proto: int,\n        ) -> tuple[str, AddressFamily, SocketKind, int]:\n            return (\"hi\", family, type, proto)\n\n    csf = CustomSocketFactory()\n\n    assert tsocket.set_custom_socket_factory(csf) is None  # type: ignore[arg-type]\n\n    assert tsocket.socket() == (\"hi\", tsocket.AF_INET, tsocket.SOCK_STREAM, 0)\n    assert tsocket.socket(1, 2, 3) == (\"hi\", 1, 2, 3)\n\n    # socket with fileno= doesn't call our custom method\n    fd = stdlib_socket.socket().detach()\n    wrapped = tsocket.socket(fileno=fd)\n    assert hasattr(wrapped, \"bind\")\n    wrapped.close()\n\n    # Likewise for socketpair\n    a, b = tsocket.socketpair()\n    with a, b:\n        assert hasattr(a, \"bind\")\n        assert hasattr(b, \"bind\")\n\n    assert tsocket.set_custom_socket_factory(None) is csf\n\n\ndef test_SocketType_is_abstract() -> None:\n    with pytest.raises(TypeError):\n        tsocket.SocketType()\n\n\n@pytest.mark.skipif(not hasattr(tsocket, \"AF_UNIX\"), reason=\"no unix domain sockets\")\nasync def test_unix_domain_socket() -> None:\n    # Bind has a special branch to use a thread, since it has to do filesystem\n    # traversal. Maybe connect should too? Not sure.\n\n    async def check_AF_UNIX(path: str | bytes | os.PathLike[str]) -> None:\n        with tsocket.socket(family=tsocket.AF_UNIX) as lsock:\n            await lsock.bind(path)\n            lsock.listen(10)\n            with tsocket.socket(family=tsocket.AF_UNIX) as csock:\n                await csock.connect(path)\n                ssock, _ = await lsock.accept()\n                with ssock:\n                    await csock.send(b\"x\")\n                    assert await ssock.recv(1) == b\"x\"\n\n    # Can't use tmpdir fixture, because we can exceed the maximum AF_UNIX path\n    # length on macOS.\n    with tempfile.TemporaryDirectory() as tmpdir:\n        # Test passing various supported types as path\n        # Must use different filenames to prevent \"address already in use\"\n        await check_AF_UNIX(f\"{tmpdir}/sock\")\n        await check_AF_UNIX(Path(f\"{tmpdir}/sock1\"))\n        await check_AF_UNIX(os.fsencode(f\"{tmpdir}/sock2\"))\n\n    try:\n        cookie = os.urandom(20).hex().encode(\"ascii\")\n        await check_AF_UNIX(b\"\\x00trio-test-\" + cookie)\n    except FileNotFoundError:\n        # macOS doesn't support abstract filenames with the leading NUL byte\n        pass\n\n\nasync def test_interrupted_by_close() -> None:\n    a_stdlib, b_stdlib = stdlib_socket.socketpair()\n    with a_stdlib, b_stdlib:\n        a_stdlib.setblocking(False)\n\n        data = b\"x\" * 99999\n\n        try:\n            while True:\n                a_stdlib.send(data)\n        except BlockingIOError:\n            pass\n\n        a = tsocket.from_stdlib_socket(a_stdlib)\n\n        async def sender() -> None:\n            with pytest.raises(_core.ClosedResourceError):\n                await a.send(data)\n\n        async def receiver() -> None:\n            with pytest.raises(_core.ClosedResourceError):\n                await a.recv(1)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sender)\n            nursery.start_soon(receiver)\n            await wait_all_tasks_blocked()\n            a.close()\n\n\nasync def test_many_sockets() -> None:\n    total = 1000  # Must be more than MAX_AFD_GROUP_SIZE\n    sockets = []\n    # Open at most <total> socket pairs\n    for opened in range(0, total, 2):\n        try:\n            a, b = stdlib_socket.socketpair()\n        except OSError as exc:  # pragma: no cover\n            # Semi-expecting following errors (sockets are files):\n            # EMFILE: \"Too many open files\" (reached kernel cap)\n            # ENFILE: \"File table overflow\" (beyond kernel cap)\n            assert exc.errno in (errno.EMFILE, errno.ENFILE)  # noqa: PT017\n            print(f\"Unable to open more than {opened} sockets.\")\n            # Stop opening any more sockets if too many are open\n            break\n        sockets += [a, b]\n    async with _core.open_nursery() as nursery:\n        for socket in sockets:\n            nursery.start_soon(_core.wait_readable, socket)\n        await _core.wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n    for socket in sockets:\n        socket.close()\n"
  },
  {
    "path": "src/trio/_tests/test_ssl.py",
    "content": "from __future__ import annotations\n\nimport os\nimport socket as stdlib_socket\nimport ssl\nimport sys\nimport threading\nfrom contextlib import asynccontextmanager, contextmanager, suppress\nfrom functools import partial\nfrom ssl import SSLContext\nfrom typing import TYPE_CHECKING, Any, NoReturn, TypeAlias\n\nimport pytest\n\nfrom trio import StapledStream\nfrom trio._tests.pytest_plugin import skip_if_optional_else_raise\nfrom trio.abc import ReceiveStream, SendStream\nfrom trio.testing import MemoryReceiveStream, MemorySendStream\n\ntry:\n    import trustme\n    from OpenSSL import SSL\nexcept ImportError as error:\n    skip_if_optional_else_raise(error)\n\nimport trio\n\nfrom .. import _core, socket as tsocket\nfrom .._abc import Stream\nfrom .._core import BrokenResourceError, ClosedResourceError\nfrom .._core._tests.tutil import slow\nfrom .._highlevel_generic import aclose_forcefully\nfrom .._highlevel_open_tcp_stream import open_tcp_stream\nfrom .._highlevel_socket import SocketListener, SocketStream\nfrom .._ssl import NeedHandshakeError, SSLListener, SSLStream, _is_eof\nfrom .._util import ConflictDetector\nfrom ..testing import (\n    Sequencer,\n    assert_checkpoints,\n    check_two_way_stream,\n    lockstep_stream_pair,\n    memory_stream_pair,\n)\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncIterator, Awaitable, Callable, Iterator\n\n    from trio._core import MockClock\n    from trio._ssl import T_Stream\n\n    from .._core._run import CancelScope\n\n# We have two different kinds of echo server fixtures we use for testing. The\n# first is a real server written using the stdlib ssl module and blocking\n# sockets. It runs in a thread and we talk to it over a real socketpair(), to\n# validate interoperability in a semi-realistic setting.\n#\n# The second is a very weird virtual echo server that lives inside a custom\n# Stream class. It lives entirely inside the Python object space; there are no\n# operating system calls in it at all. No threads, no I/O, nothing. It's\n# 'send_all' call takes encrypted data from a client and feeds it directly into\n# the server-side TLS state engine to decrypt, then takes that data, feeds it\n# back through to get the encrypted response, and returns it from 'receive_some'. This\n# gives us full control and reproducibility. This server is written using\n# PyOpenSSL, so that we can trigger renegotiations on demand. It also allows\n# us to insert random (virtual) delays, to really exercise all the weird paths\n# in SSLStream's state engine.\n#\n# Both present a certificate for \"trio-test-1.example.org\".\n\nTRIO_TEST_CA = trustme.CA()\nTRIO_TEST_1_CERT = TRIO_TEST_CA.issue_server_cert(\"trio-test-1.example.org\")\n\nSERVER_CTX = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)\nif hasattr(ssl, \"OP_IGNORE_UNEXPECTED_EOF\"):\n    SERVER_CTX.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF\n\nTRIO_TEST_1_CERT.configure_cert(SERVER_CTX)\n\n\n# TLS 1.3 has a lot of changes from previous versions. So we want to run tests\n# with both TLS 1.3, and TLS 1.2.\n# \"tls13\" means that we're willing to negotiate TLS 1.3. Usually that's\n# what will happen, but the renegotiation tests explicitly force a\n# downgrade on the server side. \"tls12\" means we refuse to negotiate TLS\n# 1.3, so we'll almost certainly use TLS 1.2.\n@pytest.fixture(scope=\"module\", params=[\"tls13\", \"tls12\"])\ndef client_ctx(request: pytest.FixtureRequest) -> ssl.SSLContext:\n    ctx = ssl.create_default_context()\n\n    if hasattr(ssl, \"OP_IGNORE_UNEXPECTED_EOF\"):\n        ctx.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF\n\n    TRIO_TEST_CA.configure_trust(ctx)\n    if request.param in [\"default\", \"tls13\"]:\n        return ctx\n    elif request.param == \"tls12\":\n        ctx.maximum_version = ssl.TLSVersion.TLSv1_2\n        return ctx\n    else:  # pragma: no cover\n        raise AssertionError()\n\n\n# The blocking socket server.\ndef ssl_echo_serve_sync(\n    sock: stdlib_socket.socket,\n    *,\n    expect_fail: bool = False,\n) -> None:\n    try:\n        wrapped = SERVER_CTX.wrap_socket(\n            sock,\n            server_side=True,\n            suppress_ragged_eofs=False,\n        )\n        with wrapped:\n            wrapped.do_handshake()\n            while True:\n                data = wrapped.recv(4096)\n                if not data:\n                    # other side has initiated a graceful shutdown; we try to\n                    # respond in kind but it's legal for them to have already\n                    # gone away.\n                    with suppress(BrokenPipeError, ssl.SSLZeroReturnError):\n                        wrapped.unwrap()\n                    return\n                wrapped.sendall(data)\n    # This is an obscure workaround for an openssl bug. In server mode, in\n    # some versions, openssl sends some extra data at the end of do_handshake\n    # that it shouldn't send. Normally this is harmless, but, if the other\n    # side shuts down the connection before it reads that data, it might cause\n    # the OS to report a ECONNREST or even ECONNABORTED (which is just wrong,\n    # since ECONNABORTED is supposed to mean that connect() failed, but what\n    # can you do). In this case the other side did nothing wrong, but there's\n    # no way to recover, so we let it pass, and just cross our fingers its not\n    # hiding any (other) real bugs. For more details see:\n    #\n    #   https://github.com/python-trio/trio/issues/1293\n    #\n    # Also, this happens frequently but non-deterministically, so we have to\n    # 'no cover' it to avoid coverage flapping.\n    except (ConnectionResetError, ConnectionAbortedError):  # pragma: no cover\n        return\n    except Exception as exc:\n        if expect_fail:\n            print(\"ssl_echo_serve_sync got error as expected:\", exc)\n        else:  # pragma: no cover\n            print(\"ssl_echo_serve_sync got unexpected error:\", exc)\n            raise\n    else:\n        if expect_fail:  # pragma: no cover\n            raise RuntimeError(\"failed to fail?\")\n    finally:\n        sock.close()\n\n\n# Fixture that gives a raw socket connected to a trio-test-1 echo server\n# (running in a thread). Useful for testing making connections with different\n# SSLContexts.\n@asynccontextmanager\nasync def ssl_echo_server_raw(expect_fail: bool = False) -> AsyncIterator[SocketStream]:\n    a, b = stdlib_socket.socketpair()\n    async with trio.open_nursery() as nursery:\n        # Exiting the 'with a, b' context manager closes the sockets, which\n        # causes the thread to exit (possibly with an error), which allows the\n        # nursery context manager to exit too.\n        with a, b:\n            nursery.start_soon(\n                trio.to_thread.run_sync,\n                partial(ssl_echo_serve_sync, b, expect_fail=expect_fail),\n            )\n\n            yield SocketStream(tsocket.from_stdlib_socket(a))\n\n\n# Fixture that gives a properly set up SSLStream connected to a trio-test-1\n# echo server (running in a thread)\n@asynccontextmanager\nasync def ssl_echo_server(\n    client_ctx: SSLContext,\n    expect_fail: bool = False,\n) -> AsyncIterator[SSLStream[Stream]]:\n    async with ssl_echo_server_raw(expect_fail=expect_fail) as sock:\n        yield SSLStream(sock, client_ctx, server_hostname=\"trio-test-1.example.org\")\n\n\n# The weird in-memory server ... thing.\n# Doesn't inherit from Stream because I left out the methods that we don't\n# actually need.\n# jakkdl: it seems to implement all the abstract methods (now), so I made it inherit\n#         from Stream for the sake of typechecking.\nclass PyOpenSSLEchoStream(Stream):\n    def __init__(\n        self,\n        sleeper: Callable[[str], Awaitable[None]] | None = None,\n    ) -> None:\n        ctx = SSL.Context(SSL.SSLv23_METHOD)\n        # TLS 1.3 removes renegotiation support. Which is great for them, but\n        # we still have to support versions before that, and that means we\n        # need to test renegotiation support, which means we need to force this\n        # to use a lower version where this test server can trigger\n        # renegotiations.\n        from cryptography.hazmat.bindings.openssl.binding import Binding\n\n        b = Binding()\n        ctx.set_options(b.lib.SSL_OP_NO_TLSv1_3)\n\n        # Unfortunately there's currently no way to say \"use 1.3 or worse\", we\n        # can only disable specific versions. And if the two sides start\n        # negotiating 1.4 at some point in the future, it *might* mean that\n        # our tests silently stop working properly. So the next line is a\n        # tripwire to remind us we need to revisit this stuff in 5 years or\n        # whatever when the next TLS version is released:\n        assert not hasattr(SSL, \"OP_NO_TLSv1_4\")\n        TRIO_TEST_1_CERT.configure_cert(ctx)\n        self._conn = SSL.Connection(ctx, None)\n        self._conn.set_accept_state()\n        self._lot = _core.ParkingLot()\n        self._pending_cleartext = bytearray()\n\n        self._send_all_conflict_detector = ConflictDetector(\n            \"simultaneous calls to PyOpenSSLEchoStream.send_all\",\n        )\n        self._receive_some_conflict_detector = ConflictDetector(\n            \"simultaneous calls to PyOpenSSLEchoStream.receive_some\",\n        )\n\n        self.sleeper: Callable[[str], Awaitable[None]]\n        if sleeper is None:\n\n            async def no_op_sleeper(_: object) -> None:\n                return\n\n            self.sleeper = no_op_sleeper\n        else:\n            self.sleeper = sleeper\n\n    async def aclose(self) -> None:\n        self._conn.bio_shutdown()\n\n    def renegotiate_pending(self) -> bool:\n        return self._conn.renegotiate_pending()\n\n    def renegotiate(self) -> None:\n        # Returns false if a renegotiation is already in progress, meaning\n        # nothing happens.\n        assert self._conn.renegotiate()\n\n    async def wait_send_all_might_not_block(self) -> None:\n        with self._send_all_conflict_detector:\n            await _core.checkpoint()\n            await _core.checkpoint()\n            await self.sleeper(\"wait_send_all_might_not_block\")\n\n    async def send_all(self, data: bytes) -> None:\n        print(\"  --> transport_stream.send_all\")\n        with self._send_all_conflict_detector:\n            await _core.checkpoint()\n            await _core.checkpoint()\n            await self.sleeper(\"send_all\")\n            self._conn.bio_write(data)\n            while True:\n                await self.sleeper(\"send_all\")\n                try:\n                    data = self._conn.recv(1)\n                except SSL.ZeroReturnError:\n                    self._conn.shutdown()\n                    print(\"renegotiations:\", self._conn.total_renegotiations())\n                    break\n                except SSL.WantReadError:\n                    break\n                else:\n                    self._pending_cleartext += data\n            self._lot.unpark_all()\n            await self.sleeper(\"send_all\")\n            print(\"  <-- transport_stream.send_all finished\")\n\n    async def receive_some(self, nbytes: int | None = None) -> bytes:\n        print(\"  --> transport_stream.receive_some\")\n        if nbytes is None:\n            nbytes = 65536  # arbitrary\n        with self._receive_some_conflict_detector:\n            try:\n                await _core.checkpoint()\n                await _core.checkpoint()\n                while True:\n                    await self.sleeper(\"receive_some\")\n                    try:\n                        return self._conn.bio_read(nbytes)\n                    except SSL.WantReadError:\n                        # No data in our ciphertext buffer; try to generate\n                        # some.\n                        if self._pending_cleartext:\n                            # We have some cleartext; maybe we can encrypt it\n                            # and then return it.\n                            print(\"    trying\", self._pending_cleartext)\n                            try:\n                                # PyOpenSSL bug: doesn't accept bytearray\n                                # https://github.com/pyca/pyopenssl/issues/621\n                                next_byte = self._pending_cleartext[0:1]\n                                self._conn.send(bytes(next_byte))\n                            # Apparently this next bit never gets hit in the\n                            # test suite, but it's not an interesting omission\n                            # so let's pragma it.\n                            except SSL.WantReadError:  # pragma: no cover\n                                # We didn't manage to send the cleartext (and\n                                # in particular we better leave it there to\n                                # try again, due to openssl's retry\n                                # semantics), but it's possible we pushed a\n                                # renegotiation forward and *now* we have data\n                                # to send.\n                                try:\n                                    return self._conn.bio_read(nbytes)\n                                except SSL.WantReadError:\n                                    # Nope. We're just going to have to wait\n                                    # for someone to call send_all() to give\n                                    # use more data.\n                                    print(\"parking (a)\")\n                                    await self._lot.park()\n                            else:\n                                # We successfully sent that byte, so we don't\n                                # have to again.\n                                del self._pending_cleartext[0:1]\n                        else:\n                            # no pending cleartext; nothing to do but wait for\n                            # someone to call send_all\n                            print(\"parking (b)\")\n                            await self._lot.park()\n            finally:\n                await self.sleeper(\"receive_some\")\n                print(\"  <-- transport_stream.receive_some finished\")\n\n\nasync def test_PyOpenSSLEchoStream_gives_resource_busy_errors() -> None:\n    # Make sure that PyOpenSSLEchoStream complains if two tasks call send_all\n    # at the same time, or ditto for receive_some. The tricky cases where SSLStream\n    # might accidentally do this are during renegotiation, which we test using\n    # PyOpenSSLEchoStream, so this makes sure that if we do have a bug then\n    # PyOpenSSLEchoStream will notice and complain.\n\n    async def do_test(\n        func1: str,\n        args1: tuple[object, ...],\n        func2: str,\n        args2: tuple[object, ...],\n    ) -> None:\n        s = PyOpenSSLEchoStream()\n        with pytest.RaisesGroup(\n            pytest.RaisesExc(_core.BusyResourceError, match=\"simultaneous\")\n        ):\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(getattr(s, func1), *args1)\n                nursery.start_soon(getattr(s, func2), *args2)\n\n    await do_test(\"send_all\", (b\"x\",), \"send_all\", (b\"x\",))\n    await do_test(\"send_all\", (b\"x\",), \"wait_send_all_might_not_block\", ())\n    await do_test(\n        \"wait_send_all_might_not_block\",\n        (),\n        \"wait_send_all_might_not_block\",\n        (),\n    )\n    await do_test(\"receive_some\", (1,), \"receive_some\", (1,))\n\n\n@contextmanager\ndef virtual_ssl_echo_server(\n    client_ctx: SSLContext,\n    sleeper: Callable[[str], Awaitable[None]] | None = None,\n) -> Iterator[SSLStream[PyOpenSSLEchoStream]]:\n    fakesock = PyOpenSSLEchoStream(sleeper=sleeper)\n    yield SSLStream(fakesock, client_ctx, server_hostname=\"trio-test-1.example.org\")\n\n\ndef ssl_wrap_pair(  # type: ignore[explicit-any]\n    client_ctx: SSLContext,\n    client_transport: T_Stream,\n    server_transport: T_Stream,\n    *,\n    client_kwargs: dict[str, Any] | None = None,\n    server_kwargs: dict[str, Any] | None = None,\n) -> tuple[SSLStream[T_Stream], SSLStream[T_Stream]]:\n    if server_kwargs is None:\n        server_kwargs = {}\n    if client_kwargs is None:\n        client_kwargs = {}\n    client_ssl = SSLStream(\n        client_transport,\n        client_ctx,\n        server_hostname=\"trio-test-1.example.org\",\n        **client_kwargs,\n    )\n    server_ssl = SSLStream(\n        server_transport,\n        SERVER_CTX,\n        server_side=True,\n        **server_kwargs,\n    )\n    return client_ssl, server_ssl\n\n\nMemoryStapledStream: TypeAlias = StapledStream[MemorySendStream, MemoryReceiveStream]\n\n\ndef ssl_memory_stream_pair(\n    client_ctx: SSLContext,\n    client_kwargs: dict[str, str | bytes | bool | None] | None = None,\n    server_kwargs: dict[str, str | bytes | bool | None] | None = None,\n) -> tuple[\n    SSLStream[MemoryStapledStream],\n    SSLStream[MemoryStapledStream],\n]:\n    client_transport, server_transport = memory_stream_pair()\n    return ssl_wrap_pair(\n        client_ctx,\n        client_transport,\n        server_transport,\n        client_kwargs=client_kwargs,\n        server_kwargs=server_kwargs,\n    )\n\n\nMyStapledStream: TypeAlias = StapledStream[SendStream, ReceiveStream]\n\n\ndef ssl_lockstep_stream_pair(\n    client_ctx: SSLContext,\n    client_kwargs: dict[str, str | bytes | bool | None] | None = None,\n    server_kwargs: dict[str, str | bytes | bool | None] | None = None,\n) -> tuple[\n    SSLStream[MyStapledStream],\n    SSLStream[MyStapledStream],\n]:\n    client_transport, server_transport = lockstep_stream_pair()\n    return ssl_wrap_pair(\n        client_ctx,\n        client_transport,\n        server_transport,\n        client_kwargs=client_kwargs,\n        server_kwargs=server_kwargs,\n    )\n\n\n# Simple smoke test for handshake/send/receive/shutdown talking to a\n# synchronous server, plus make sure that we do the bare minimum of\n# certificate checking (even though this is really Python's responsibility)\nasync def test_ssl_client_basics(client_ctx: SSLContext) -> None:\n    # Everything OK\n    async with ssl_echo_server(client_ctx) as s:\n        assert not s.server_side\n        await s.send_all(b\"x\")\n        assert await s.receive_some(1) == b\"x\"\n        await s.aclose()\n\n    # Didn't configure the CA file, should fail\n    async with ssl_echo_server_raw(expect_fail=True) as sock:\n        bad_client_ctx = ssl.create_default_context()\n        s = SSLStream(sock, bad_client_ctx, server_hostname=\"trio-test-1.example.org\")\n        assert not s.server_side\n        with pytest.raises(BrokenResourceError) as excinfo:\n            await s.send_all(b\"x\")\n        assert isinstance(excinfo.value.__cause__, ssl.SSLError)\n\n    # Trusted CA, but wrong host name\n    async with ssl_echo_server_raw(expect_fail=True) as sock:\n        s = SSLStream(sock, client_ctx, server_hostname=\"trio-test-2.example.org\")\n        assert not s.server_side\n        with pytest.raises(BrokenResourceError) as excinfo:\n            await s.send_all(b\"x\")\n        assert isinstance(excinfo.value.__cause__, ssl.CertificateError)\n\n\nasync def test_ssl_server_basics(client_ctx: SSLContext) -> None:\n    a, b = stdlib_socket.socketpair()\n    with a, b:\n        server_sock = tsocket.from_stdlib_socket(b)\n        server_transport = SSLStream(\n            SocketStream(server_sock),\n            SERVER_CTX,\n            server_side=True,\n        )\n        assert server_transport.server_side\n\n        def client() -> None:\n            with client_ctx.wrap_socket(\n                a,\n                server_hostname=\"trio-test-1.example.org\",\n            ) as client_sock:\n                client_sock.sendall(b\"x\")\n                assert client_sock.recv(1) == b\"y\"\n                client_sock.sendall(b\"z\")\n                client_sock.unwrap()\n\n        t = threading.Thread(target=client)\n        t.start()\n\n        assert await server_transport.receive_some(1) == b\"x\"\n        await server_transport.send_all(b\"y\")\n        assert await server_transport.receive_some(1) == b\"z\"\n        assert await server_transport.receive_some(1) == b\"\"\n        await server_transport.aclose()\n\n        t.join()\n\n\nasync def test_attributes(client_ctx: SSLContext) -> None:\n    async with ssl_echo_server_raw(expect_fail=True) as sock:\n        good_ctx = client_ctx\n        bad_ctx = ssl.create_default_context()\n        s = SSLStream(sock, good_ctx, server_hostname=\"trio-test-1.example.org\")\n\n        assert s.transport_stream is sock\n\n        # Forwarded attribute getting\n        assert s.context is good_ctx\n        assert s.server_side == False  # noqa\n        assert s.server_hostname == \"trio-test-1.example.org\"\n        with pytest.raises(AttributeError):\n            s.asfdasdfsa  # noqa: B018  # \"useless expression\"\n\n        # __dir__\n        assert \"transport_stream\" in dir(s)\n        assert \"context\" in dir(s)\n\n        # Setting the attribute goes through to the underlying object\n\n        # most attributes on SSLObject are read-only\n        with pytest.raises(AttributeError):\n            s.server_side = True\n        with pytest.raises(AttributeError):\n            s.server_hostname = \"asdf\"\n\n        # but .context is *not*. Check that we forward attribute setting by\n        # making sure that after we set the bad context our handshake indeed\n        # fails:\n        s.context = bad_ctx\n        assert s.context is bad_ctx\n        with pytest.raises(BrokenResourceError) as excinfo:\n            await s.do_handshake()\n        assert isinstance(excinfo.value.__cause__, ssl.SSLError)\n\n\n# Note: this test fails horribly if we force TLS 1.2 and trigger a\n# renegotiation at the beginning (e.g. by switching to the pyopenssl\n# server). Usually the client crashes in SSLObject.write with \"UNEXPECTED\n# RECORD\"; sometimes we get something more exotic like a SyscallError. This is\n# odd because openssl isn't doing any syscalls, but so it goes. After lots of\n# websearching I'm pretty sure this is due to a bug in OpenSSL, where it just\n# can't reliably handle full-duplex communication combined with\n# renegotiation. Nice, eh?\n#\n#   https://rt.openssl.org/Ticket/Display.html?id=3712\n#   https://rt.openssl.org/Ticket/Display.html?id=2481\n#   http://openssl.6102.n7.nabble.com/TLS-renegotiation-failure-on-receiving-application-data-during-handshake-td48127.html\n#   https://stackoverflow.com/questions/18728355/ssl-renegotiation-with-full-duplex-socket-communication\n#\n# In some variants of this test (maybe only against the java server?) I've\n# also seen cases where our send_all blocks waiting to write, and then our receive_some\n# also blocks waiting to write, and they never wake up again. It looks like\n# some kind of deadlock. I suspect there may be an issue where we've filled up\n# the send buffers, and the remote side is trying to handle the renegotiation\n# from inside a write() call, so it has a problem: there's all this application\n# data clogging up the pipe, but it can't process and return it to the\n# application because it's in write(), and it doesn't want to buffer infinite\n# amounts of data, and... actually I guess those are the only two choices.\n#\n# NSS even documents that you shouldn't try to do a renegotiation except when\n# the connection is idle:\n#\n#   https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/SSL_functions/sslfnc.html#1061582\n#\n# I begin to see why HTTP/2 forbids renegotiation and TLS 1.3 removes it...\n\n\nasync def test_full_duplex_basics(client_ctx: SSLContext) -> None:\n    CHUNKS = 30\n    CHUNK_SIZE = 32768\n    EXPECTED = CHUNKS * CHUNK_SIZE\n\n    sent = bytearray()\n    received = bytearray()\n\n    async def sender(s: Stream) -> None:\n        nonlocal sent\n        for i in range(CHUNKS):\n            print(i)\n            chunk = bytes([i] * CHUNK_SIZE)\n            sent += chunk\n            await s.send_all(chunk)\n\n    async def receiver(s: Stream) -> None:\n        nonlocal received\n        while len(received) < EXPECTED:\n            chunk = await s.receive_some(CHUNK_SIZE // 2)\n            received += chunk\n\n    async with ssl_echo_server(client_ctx) as s:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sender, s)\n            nursery.start_soon(receiver, s)\n            # And let's have some doing handshakes too, everyone\n            # simultaneously\n            nursery.start_soon(s.do_handshake)\n            nursery.start_soon(s.do_handshake)\n\n        await s.aclose()\n\n    assert len(sent) == len(received) == EXPECTED\n    assert sent == received\n\n\nasync def test_renegotiation_simple(client_ctx: SSLContext) -> None:\n    with virtual_ssl_echo_server(client_ctx) as s:\n        await s.do_handshake()\n        s.transport_stream.renegotiate()\n        await s.send_all(b\"a\")\n        assert await s.receive_some(1) == b\"a\"\n\n        # Have to send some more data back and forth to make sure the\n        # renegotiation is finished before shutting down the\n        # connection... otherwise openssl raises an error. I think this is a\n        # bug in openssl but what can ya do.\n        await s.send_all(b\"b\")\n        assert await s.receive_some(1) == b\"b\"\n\n        await s.aclose()\n\n\n@slow\nasync def test_renegotiation_randomized(\n    mock_clock: MockClock,\n    client_ctx: SSLContext,\n) -> None:\n    # The only blocking things in this function are our random sleeps, so 0 is\n    # a good threshold.\n    mock_clock.autojump_threshold = 0\n\n    import random\n\n    r = random.Random(0)\n\n    async def sleeper(_: object) -> None:\n        await trio.sleep(r.uniform(0, 10))\n\n    async def clear() -> None:\n        while s.transport_stream.renegotiate_pending():\n            with assert_checkpoints():\n                await send(b\"-\")\n            with assert_checkpoints():\n                await expect(b\"-\")\n        print(\"-- clear --\")\n\n    async def send(byte: bytes) -> None:\n        await s.transport_stream.sleeper(\"outer send\")\n        print(\"calling SSLStream.send_all\", byte)\n        with assert_checkpoints():\n            await s.send_all(byte)\n\n    async def expect(expected: bytes) -> None:\n        await s.transport_stream.sleeper(\"expect\")\n        print(\"calling SSLStream.receive_some, expecting\", expected)\n        assert len(expected) == 1\n        with assert_checkpoints():\n            assert await s.receive_some(1) == expected\n\n    with virtual_ssl_echo_server(client_ctx, sleeper=sleeper) as s:\n        await s.do_handshake()\n\n        await send(b\"a\")\n        s.transport_stream.renegotiate()\n        await expect(b\"a\")\n\n        await clear()\n\n        for i in range(100):\n            b1 = bytes([i % 0xFF])\n            b2 = bytes([(2 * i) % 0xFF])\n            s.transport_stream.renegotiate()\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(send, b1)\n                nursery.start_soon(expect, b1)\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(expect, b2)\n                nursery.start_soon(send, b2)\n            await clear()\n\n        for i in range(100):\n            b1 = bytes([i % 0xFF])\n            b2 = bytes([(2 * i) % 0xFF])\n            await send(b1)\n            s.transport_stream.renegotiate()\n            await expect(b1)\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(expect, b2)\n                nursery.start_soon(send, b2)\n            await clear()\n\n    # Checking that wait_send_all_might_not_block and receive_some don't\n    # conflict:\n\n    # 1) Set up a situation where expect (receive_some) is blocked sending,\n    # and wait_send_all_might_not_block comes in.\n\n    # Our receive_some() call will get stuck when it hits send_all\n    async def sleeper_with_slow_send_all(method: str) -> None:\n        if method == \"send_all\":\n            # ignore ASYNC116, not sleep_forever, trying to test a large but finite sleep\n            await trio.sleep(100000)  # noqa: ASYNC116\n\n    # And our wait_send_all_might_not_block call will give it time to get\n    # stuck, and then start\n    async def sleep_then_wait_writable() -> None:\n        await trio.sleep(1000)\n        await s.wait_send_all_might_not_block()\n\n    with virtual_ssl_echo_server(client_ctx, sleeper=sleeper_with_slow_send_all) as s:\n        await send(b\"x\")\n        s.transport_stream.renegotiate()\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect, b\"x\")\n            nursery.start_soon(sleep_then_wait_writable)\n\n        await clear()\n\n        await s.aclose()\n\n    # 2) Same, but now wait_send_all_might_not_block is stuck when\n    # receive_some tries to send.\n\n    async def sleeper_with_slow_wait_writable_and_expect(method: str) -> None:\n        if method == \"wait_send_all_might_not_block\":\n            # ignore ASYNC116, not sleep_forever, trying to test a large but finite sleep\n            await trio.sleep(100000)  # noqa: ASYNC116\n        elif method == \"expect\":\n            await trio.sleep(1000)\n\n    with virtual_ssl_echo_server(\n        client_ctx,\n        sleeper=sleeper_with_slow_wait_writable_and_expect,\n    ) as s:\n        await send(b\"x\")\n        s.transport_stream.renegotiate()\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect, b\"x\")\n            nursery.start_soon(s.wait_send_all_might_not_block)\n\n        await clear()\n\n        await s.aclose()\n\n\nasync def test_resource_busy_errors(client_ctx: SSLContext) -> None:\n    S: TypeAlias = trio.SSLStream[\n        trio.StapledStream[trio.abc.SendStream, trio.abc.ReceiveStream]\n    ]\n\n    async def do_send_all(s: S) -> None:\n        with assert_checkpoints():\n            await s.send_all(b\"x\")\n\n    async def do_receive_some(s: S) -> None:\n        with assert_checkpoints():\n            await s.receive_some(1)\n\n    async def do_wait_send_all_might_not_block(s: S) -> None:\n        with assert_checkpoints():\n            await s.wait_send_all_might_not_block()\n\n    async def do_test(\n        func1: Callable[[S], Awaitable[None]],\n        func2: Callable[[S], Awaitable[None]],\n    ) -> None:\n        s, _ = ssl_lockstep_stream_pair(client_ctx)\n        with pytest.RaisesGroup(\n            pytest.RaisesExc(_core.BusyResourceError, match=\"another task\")\n        ):\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(func1, s)\n                nursery.start_soon(func2, s)\n\n    await do_test(do_send_all, do_send_all)\n    await do_test(do_receive_some, do_receive_some)\n    await do_test(do_send_all, do_wait_send_all_might_not_block)\n    await do_test(do_wait_send_all_might_not_block, do_wait_send_all_might_not_block)\n\n\nasync def test_wait_writable_calls_underlying_wait_writable() -> None:\n    record = []\n\n    class NotAStream(Stream):\n        async def wait_send_all_might_not_block(self) -> None:\n            record.append(\"ok\")\n\n        # define methods that are abstract in Stream\n        async def aclose(self) -> None:\n            raise AssertionError(\"Should not get called\")  # pragma: no cover\n\n        async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:\n            raise AssertionError(\"Should not get called\")  # pragma: no cover\n\n        async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n            raise AssertionError(\"Should not get called\")  # pragma: no cover\n\n    ctx = ssl.create_default_context()\n    s = SSLStream(NotAStream(), ctx, server_hostname=\"x\")\n    await s.wait_send_all_might_not_block()\n    assert record == [\"ok\"]\n\n\n@pytest.mark.skipif(\n    os.name == \"nt\" and sys.version_info >= (3, 10),\n    reason=\"frequently fails on Windows + Python 3.10\",\n)\nasync def test_checkpoints(client_ctx: SSLContext) -> None:\n    async with ssl_echo_server(client_ctx) as s:\n        with assert_checkpoints():\n            await s.do_handshake()\n        with assert_checkpoints():\n            await s.do_handshake()\n        with assert_checkpoints():\n            await s.wait_send_all_might_not_block()\n        with assert_checkpoints():\n            await s.send_all(b\"xxx\")\n        with assert_checkpoints():\n            await s.receive_some(1)\n        # These receive_some's in theory could return immediately, because the\n        # \"xxx\" was sent in a single record and after the first\n        # receive_some(1) the rest are sitting inside the SSLObject's internal\n        # buffers.\n        with assert_checkpoints():\n            await s.receive_some(1)\n        with assert_checkpoints():\n            await s.receive_some(1)\n        with assert_checkpoints():\n            await s.unwrap()\n\n    async with ssl_echo_server(client_ctx) as s:\n        await s.do_handshake()\n        with assert_checkpoints():\n            await s.aclose()\n\n\nasync def test_send_all_empty_string(client_ctx: SSLContext) -> None:\n    async with ssl_echo_server(client_ctx) as s:\n        await s.do_handshake()\n\n        # underlying SSLObject interprets writing b\"\" as indicating an EOF,\n        # for some reason. Make sure we don't inherit this.\n        with assert_checkpoints():\n            await s.send_all(b\"\")\n        with assert_checkpoints():\n            await s.send_all(b\"\")\n        await s.send_all(b\"x\")\n        assert await s.receive_some(1) == b\"x\"\n\n        await s.aclose()\n\n\n@pytest.mark.parametrize(\"https_compatible\", [False, True])\nasync def test_SSLStream_generic(\n    client_ctx: SSLContext,\n    https_compatible: bool,\n) -> None:\n    async def stream_maker() -> tuple[\n        SSLStream[MemoryStapledStream],\n        SSLStream[MemoryStapledStream],\n    ]:\n        return ssl_memory_stream_pair(\n            client_ctx,\n            client_kwargs={\"https_compatible\": https_compatible},\n            server_kwargs={\"https_compatible\": https_compatible},\n        )\n\n    async def clogged_stream_maker() -> tuple[\n        SSLStream[MyStapledStream],\n        SSLStream[MyStapledStream],\n    ]:\n        client, server = ssl_lockstep_stream_pair(client_ctx)\n        # If we don't do handshakes up front, then we run into a problem in\n        # the following situation:\n        # - server does wait_send_all_might_not_block\n        # - client does receive_some to unclog it\n        # Then the client's receive_some will actually send some data to start\n        # the handshake, and itself get stuck.\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(client.do_handshake)\n            nursery.start_soon(server.do_handshake)\n        return client, server\n\n    await check_two_way_stream(stream_maker, clogged_stream_maker)\n\n\nasync def test_unwrap(client_ctx: SSLContext) -> None:\n    client_ssl, server_ssl = ssl_memory_stream_pair(client_ctx)\n    client_transport = client_ssl.transport_stream\n    server_transport = server_ssl.transport_stream\n\n    seq = Sequencer()\n\n    async def client() -> None:\n        await client_ssl.do_handshake()\n        await client_ssl.send_all(b\"x\")\n        assert await client_ssl.receive_some(1) == b\"y\"\n        await client_ssl.send_all(b\"z\")\n\n        # After sending that, disable outgoing data from our end, to make\n        # sure the server doesn't see our EOF until after we've sent some\n        # trailing data\n        async with seq(0):\n            send_all_hook = client_transport.send_stream.send_all_hook\n            client_transport.send_stream.send_all_hook = None\n\n        assert await client_ssl.receive_some(1) == b\"\"\n        assert client_ssl.transport_stream is client_transport\n        # We just received EOF. Unwrap the connection and send some more.\n        raw, trailing = await client_ssl.unwrap()\n        assert raw is client_transport\n        assert trailing == b\"\"\n        assert client_ssl.transport_stream is None\n        await raw.send_all(b\"trailing\")\n\n        # Reconnect the streams. Now the server will receive both our shutdown\n        # acknowledgement + the trailing data in a single lump.\n        client_transport.send_stream.send_all_hook = send_all_hook\n        await client_transport.send_stream.send_all_hook()\n\n    async def server() -> None:\n        await server_ssl.do_handshake()\n        assert await server_ssl.receive_some(1) == b\"x\"\n        await server_ssl.send_all(b\"y\")\n        assert await server_ssl.receive_some(1) == b\"z\"\n        # Now client is blocked waiting for us to send something, but\n        # instead we close the TLS connection (with sequencer to make sure\n        # that the client won't see and automatically respond before we've had\n        # a chance to disable the client->server transport)\n        async with seq(1):\n            raw, trailing = await server_ssl.unwrap()\n        assert raw is server_transport\n        assert trailing == b\"trailing\"\n        assert server_ssl.transport_stream is None\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client)\n        nursery.start_soon(server)\n\n\nasync def test_closing_nice_case(client_ctx: SSLContext) -> None:\n    # the nice case: graceful closes all around\n\n    client_ssl, server_ssl = ssl_memory_stream_pair(client_ctx)\n    client_transport = client_ssl.transport_stream\n\n    # Both the handshake and the close require back-and-forth discussion, so\n    # we need to run them concurrently\n    async def client_closer() -> None:\n        with assert_checkpoints():\n            await client_ssl.aclose()\n\n    async def server_closer() -> None:\n        assert await server_ssl.receive_some(10) == b\"\"\n        assert await server_ssl.receive_some(10) == b\"\"\n        with assert_checkpoints():\n            await server_ssl.aclose()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client_closer)\n        nursery.start_soon(server_closer)\n\n    # closing the SSLStream also closes its transport\n    with pytest.raises(ClosedResourceError):\n        await client_transport.send_all(b\"123\")\n\n    # once closed, it's OK to close again\n    with assert_checkpoints():\n        await client_ssl.aclose()\n    with assert_checkpoints():\n        await client_ssl.aclose()\n\n    # Trying to send more data does not work\n    with pytest.raises(ClosedResourceError):\n        await server_ssl.send_all(b\"123\")\n\n    # And once the connection is has been closed *locally*, then instead of\n    # getting empty bytestrings we get a proper error\n    with pytest.raises(ClosedResourceError):\n        assert await client_ssl.receive_some(10) == b\"\"\n\n    with pytest.raises(ClosedResourceError):\n        await client_ssl.unwrap()\n\n    with pytest.raises(ClosedResourceError):\n        await client_ssl.do_handshake()\n\n    # Check that a graceful close *before* handshaking gives a clean EOF on\n    # the other side\n    client_ssl, server_ssl = ssl_memory_stream_pair(client_ctx)\n\n    async def expect_eof_server() -> None:\n        with assert_checkpoints():\n            assert await server_ssl.receive_some(10) == b\"\"\n        with assert_checkpoints():\n            await server_ssl.aclose()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client_ssl.aclose)\n        nursery.start_soon(expect_eof_server)\n\n\nasync def test_send_all_fails_in_the_middle(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    async def bad_hook() -> NoReturn:\n        raise KeyError\n\n    client.transport_stream.send_stream.send_all_hook = bad_hook\n\n    with pytest.raises(KeyError):\n        await client.send_all(b\"x\")\n\n    with pytest.raises(BrokenResourceError):\n        await client.wait_send_all_might_not_block()\n\n    closed = 0\n\n    def close_hook() -> None:\n        nonlocal closed\n        closed += 1\n\n    client.transport_stream.send_stream.close_hook = close_hook\n    client.transport_stream.receive_stream.close_hook = close_hook\n    await client.aclose()\n\n    assert closed == 2\n\n\nasync def test_ssl_over_ssl(client_ctx: SSLContext) -> None:\n    client_0, server_0 = memory_stream_pair()\n\n    client_1 = SSLStream(\n        client_0,\n        client_ctx,\n        server_hostname=\"trio-test-1.example.org\",\n    )\n    server_1 = SSLStream(server_0, SERVER_CTX, server_side=True)\n\n    client_2 = SSLStream(\n        client_1,\n        client_ctx,\n        server_hostname=\"trio-test-1.example.org\",\n    )\n    server_2 = SSLStream(server_1, SERVER_CTX, server_side=True)\n\n    async def client() -> None:\n        await client_2.send_all(b\"hi\")\n        assert await client_2.receive_some(10) == b\"bye\"\n\n    async def server() -> None:\n        assert await server_2.receive_some(10) == b\"hi\"\n        await server_2.send_all(b\"bye\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client)\n        nursery.start_soon(server)\n\n\nasync def test_ssl_bad_shutdown(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    await trio.aclose_forcefully(client)\n    # now the server sees a broken stream\n    with pytest.raises(BrokenResourceError):\n        await server.receive_some(10)\n    with pytest.raises(BrokenResourceError):\n        await server.send_all(b\"x\" * 10)\n\n    await server.aclose()\n\n\nasync def test_ssl_bad_shutdown_but_its_ok(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(\n        client_ctx,\n        server_kwargs={\"https_compatible\": True},\n        client_kwargs={\"https_compatible\": True},\n    )\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    await trio.aclose_forcefully(client)\n    # the server sees that as a clean shutdown\n    assert await server.receive_some(10) == b\"\"\n    with pytest.raises(BrokenResourceError):\n        await server.send_all(b\"x\" * 10)\n\n    await server.aclose()\n\n\nasync def test_ssl_handshake_failure_during_aclose() -> None:\n    # Weird scenario: aclose() triggers an automatic handshake, and this\n    # fails. This also exercises a bit of code in aclose() that was otherwise\n    # uncovered, for re-raising exceptions after calling aclose_forcefully on\n    # the underlying transport.\n    async with ssl_echo_server_raw(expect_fail=True) as sock:\n        # Don't configure trust correctly\n        client_ctx = ssl.create_default_context()\n        s = SSLStream(sock, client_ctx, server_hostname=\"trio-test-1.example.org\")\n        # It's a little unclear here whether aclose should swallow the error\n        # or let it escape. We *do* swallow the error if it arrives when we're\n        # sending close_notify, because both sides closing the connection\n        # simultaneously is allowed. But I guess when https_compatible=False\n        # then it's bad if we can get through a whole connection with a peer\n        # that has no valid certificate, and never raise an error.\n        with pytest.raises(BrokenResourceError):\n            await s.aclose()\n\n\nasync def test_ssl_only_closes_stream_once(client_ctx: SSLContext) -> None:\n    # We used to have a bug where if transport_stream.aclose() raised an\n    # error, we would call it again. This checks that that's fixed.\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    client_orig_close_hook = client.transport_stream.send_stream.close_hook\n    transport_close_count = 0\n\n    def close_hook() -> NoReturn:\n        nonlocal transport_close_count\n        assert client_orig_close_hook is not None\n        client_orig_close_hook()\n        transport_close_count += 1\n        raise KeyError\n\n    client.transport_stream.send_stream.close_hook = close_hook\n\n    with pytest.raises(KeyError):\n        await client.aclose()\n    assert transport_close_count == 1\n\n\nasync def test_ssl_https_compatibility_disagreement(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(\n        client_ctx,\n        server_kwargs={\"https_compatible\": False},\n        client_kwargs={\"https_compatible\": True},\n    )\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    # client is in HTTPS-mode, server is not\n    # so client doing graceful_shutdown causes an error on server\n    async def receive_and_expect_error() -> None:\n        with pytest.raises(BrokenResourceError) as excinfo:\n            await server.receive_some(10)\n\n        assert _is_eof(excinfo.value.__cause__)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.aclose)\n        nursery.start_soon(receive_and_expect_error)\n\n\nasync def test_https_mode_eof_before_handshake(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(\n        client_ctx,\n        server_kwargs={\"https_compatible\": True},\n        client_kwargs={\"https_compatible\": True},\n    )\n\n    async def server_expect_clean_eof() -> None:\n        assert await server.receive_some(10) == b\"\"\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.aclose)\n        nursery.start_soon(server_expect_clean_eof)\n\n\nasync def test_send_error_during_handshake(client_ctx: SSLContext) -> None:\n    client, _server = ssl_memory_stream_pair(client_ctx)\n\n    async def bad_hook() -> NoReturn:\n        raise KeyError\n\n    client.transport_stream.send_stream.send_all_hook = bad_hook\n\n    with pytest.raises(KeyError):\n        with assert_checkpoints():\n            await client.do_handshake()\n\n    with pytest.raises(BrokenResourceError):\n        with assert_checkpoints():\n            await client.do_handshake()\n\n\nasync def test_receive_error_during_handshake(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async def bad_hook() -> NoReturn:\n        raise KeyError\n\n    client.transport_stream.receive_stream.receive_some_hook = bad_hook\n\n    async def client_side(cancel_scope: CancelScope) -> None:\n        with pytest.raises(KeyError):\n            with assert_checkpoints():\n                await client.do_handshake()\n        cancel_scope.cancel()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client_side, nursery.cancel_scope)\n        nursery.start_soon(server.do_handshake)\n\n    with pytest.raises(BrokenResourceError):\n        with assert_checkpoints():\n            await client.do_handshake()\n\n\ndef test_selected_alpn_protocol_before_handshake(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    with pytest.raises(NeedHandshakeError):\n        client.selected_alpn_protocol()\n\n    with pytest.raises(NeedHandshakeError):\n        server.selected_alpn_protocol()\n\n\nasync def test_selected_alpn_protocol_when_not_set(client_ctx: SSLContext) -> None:\n    # ALPN protocol still returns None when it's not set,\n    # instead of raising an exception\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    assert client.selected_alpn_protocol() is None\n    assert server.selected_alpn_protocol() is None\n\n    assert client.selected_alpn_protocol() == server.selected_alpn_protocol()\n\n\ndef test_selected_npn_protocol_before_handshake(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    with pytest.raises(NeedHandshakeError):\n        client.selected_npn_protocol()\n\n    with pytest.raises(NeedHandshakeError):\n        server.selected_npn_protocol()\n\n\n@pytest.mark.filterwarnings(\n    r\"ignore: ssl module. NPN is deprecated, use ALPN instead:UserWarning\",\n    r\"ignore:ssl NPN is deprecated, use ALPN instead:DeprecationWarning\",\n)\nasync def test_selected_npn_protocol_when_not_set(client_ctx: SSLContext) -> None:\n    # NPN protocol still returns None when it's not set,\n    # instead of raising an exception\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    assert client.selected_npn_protocol() is None\n    assert server.selected_npn_protocol() is None\n\n    assert client.selected_npn_protocol() == server.selected_npn_protocol()\n\n\ndef test_get_channel_binding_before_handshake(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    with pytest.raises(NeedHandshakeError):\n        client.get_channel_binding()\n\n    with pytest.raises(NeedHandshakeError):\n        server.get_channel_binding()\n\n\nasync def test_get_channel_binding_after_handshake(client_ctx: SSLContext) -> None:\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    assert client.get_channel_binding() is not None\n    assert server.get_channel_binding() is not None\n\n    assert client.get_channel_binding() == server.get_channel_binding()\n\n\nasync def test_getpeercert(client_ctx: SSLContext) -> None:\n    # Make sure we're not affected by https://bugs.python.org/issue29334\n    client, server = ssl_memory_stream_pair(client_ctx)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(client.do_handshake)\n        nursery.start_soon(server.do_handshake)\n\n    assert server.getpeercert() is None\n    print(client.getpeercert())\n    assert (\"DNS\", \"trio-test-1.example.org\") in client.getpeercert()[\"subjectAltName\"]\n\n\nasync def test_SSLListener(client_ctx: SSLContext) -> None:\n    async def setup(\n        https_compatible: bool = False,\n    ) -> tuple[tsocket.SocketType, SSLListener[SocketStream], SSLStream[SocketStream]]:\n        listen_sock = tsocket.socket()\n        await listen_sock.bind((\"127.0.0.1\", 0))\n        listen_sock.listen(1)\n        socket_listener = SocketListener(listen_sock)\n        ssl_listener = SSLListener(\n            socket_listener,\n            SERVER_CTX,\n            https_compatible=https_compatible,\n        )\n\n        transport_client = await open_tcp_stream(*listen_sock.getsockname())\n        ssl_client = SSLStream(\n            transport_client,\n            client_ctx,\n            server_hostname=\"trio-test-1.example.org\",\n        )\n        return listen_sock, ssl_listener, ssl_client\n\n    listen_sock, ssl_listener, ssl_client = await setup()\n\n    async with ssl_client:\n        ssl_server = await ssl_listener.accept()\n\n        async with ssl_server:\n            assert not ssl_server._https_compatible\n\n            # Make sure the connection works\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(ssl_client.do_handshake)\n                nursery.start_soon(ssl_server.do_handshake)\n\n        # Test SSLListener.aclose\n        await ssl_listener.aclose()\n        assert listen_sock.fileno() == -1\n\n    ################\n\n    # Test https_compatible\n    _, ssl_listener, ssl_client = await setup(https_compatible=True)\n\n    ssl_server = await ssl_listener.accept()\n\n    assert ssl_server._https_compatible\n\n    await aclose_forcefully(ssl_listener)\n    await aclose_forcefully(ssl_client)\n    await aclose_forcefully(ssl_server)\n"
  },
  {
    "path": "src/trio/_tests/test_subprocess.py",
    "content": "from __future__ import annotations\n\nimport gc\nimport os\nimport random\nimport signal\nimport subprocess\nimport sys\nfrom collections.abc import AsyncIterator, Callable\nfrom contextlib import AbstractAsyncContextManager, asynccontextmanager\nfrom functools import partial\nfrom pathlib import Path as SyncPath\nfrom signal import Signals\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    NoReturn,\n    TypeAlias,\n)\nfrom unittest import mock\n\nimport pytest\n\nimport trio\n\nfrom .. import (\n    Event,\n    Process,\n    _core,\n    fail_after,\n    move_on_after,\n    run_process,\n    sleep,\n    sleep_forever,\n)\nfrom .._core._tests.tutil import skip_if_fbsd_pipes_broken, slow\nfrom ..lowlevel import open_process\nfrom ..testing import MockClock, assert_no_checkpoints, wait_all_tasks_blocked\n\nif TYPE_CHECKING:\n    from types import FrameType\n\n    from .._abc import ReceiveStream\n\nif sys.platform == \"win32\":\n    SignalType: TypeAlias = None\nelse:\n    SignalType: TypeAlias = Signals\n\nSIGKILL: SignalType\nSIGTERM: SignalType\nSIGUSR1: SignalType\n\nposix = os.name == \"posix\"\nif (not TYPE_CHECKING and posix) or sys.platform != \"win32\":\n    from signal import SIGKILL, SIGTERM, SIGUSR1\nelse:\n    SIGKILL, SIGTERM, SIGUSR1 = None, None, None\n\n\n# Since Windows has very few command-line utilities generally available,\n# all of our subprocesses are Python processes running short bits of\n# (mostly) cross-platform code.\ndef python(code: str) -> list[str]:\n    return [sys.executable, \"-u\", \"-c\", \"import sys; \" + code]\n\n\nEXIT_TRUE = python(\"sys.exit(0)\")\nEXIT_FALSE = python(\"sys.exit(1)\")\nCAT = python(\"sys.stdout.buffer.write(sys.stdin.buffer.read())\")\n\nif posix:\n\n    def SLEEP(seconds: int) -> list[str]:\n        return [\"sleep\", str(seconds)]\n\nelse:\n\n    def SLEEP(seconds: int) -> list[str]:\n        return python(f\"import time; time.sleep({seconds})\")\n\n\n@asynccontextmanager\nasync def open_process_then_kill(  # type: ignore[misc, explicit-any]\n    *args: Any,\n    **kwargs: Any,\n) -> AsyncIterator[Process]:\n    proc = await open_process(*args, **kwargs)\n    try:\n        yield proc\n    finally:\n        proc.kill()\n        await proc.wait()\n\n\n@asynccontextmanager\nasync def run_process_in_nursery(  # type: ignore[misc, explicit-any]\n    *args: Any,\n    **kwargs: Any,\n) -> AsyncIterator[Process]:\n    async with _core.open_nursery() as nursery:\n        kwargs.setdefault(\"check\", False)\n        value = await nursery.start(partial(run_process, *args, **kwargs))\n        assert isinstance(value, Process)\n        proc: Process = value\n        yield proc\n        nursery.cancel_scope.cancel()\n\n\nbackground_process_param = pytest.mark.parametrize(\n    \"background_process\",\n    [open_process_then_kill, run_process_in_nursery],\n    ids=[\"open_process\", \"run_process in nursery\"],\n)\n\nBackgroundProcessType: TypeAlias = Callable[  # type: ignore[explicit-any]\n    ...,\n    AbstractAsyncContextManager[Process],\n]\n\n\n@background_process_param\nasync def test_basic(background_process: BackgroundProcessType) -> None:\n    async with background_process(EXIT_TRUE) as proc:\n        await proc.wait()\n    assert isinstance(proc, Process)\n    assert proc._pidfd is None\n    assert proc.returncode == 0\n    assert repr(proc) == f\"<trio.Process {EXIT_TRUE}: exited with status 0>\"\n\n    async with background_process(EXIT_FALSE) as proc:\n        await proc.wait()\n    assert proc.returncode == 1\n    assert repr(proc) == \"<trio.Process {!r}: {}>\".format(\n        EXIT_FALSE,\n        \"exited with status 1\",\n    )\n\n\n@background_process_param\nasync def test_basic_no_pidfd(background_process: BackgroundProcessType) -> None:\n    with mock.patch(\"trio._subprocess.can_try_pidfd_open\", new=False):\n        async with background_process(EXIT_TRUE) as proc:\n            assert proc._pidfd is None\n            await proc.wait()\n        assert isinstance(proc, Process)\n        assert proc._pidfd is None\n        assert proc.returncode == 0\n        assert repr(proc) == f\"<trio.Process {EXIT_TRUE}: exited with status 0>\"\n\n        async with background_process(EXIT_FALSE) as proc:\n            await proc.wait()\n        assert proc.returncode == 1\n        assert repr(proc) == \"<trio.Process {!r}: {}>\".format(\n            EXIT_FALSE,\n            \"exited with status 1\",\n        )\n\n\n@background_process_param\nasync def test_auto_update_returncode(\n    background_process: BackgroundProcessType,\n) -> None:\n    async with background_process(SLEEP(9999)) as p:\n        assert p.returncode is None\n        assert \"running\" in repr(p)\n        p.kill()\n        p._proc.wait()\n        assert p.returncode is not None\n        assert \"exited\" in repr(p)\n        assert p._pidfd is None\n        assert p.returncode is not None\n\n\n@background_process_param\nasync def test_multi_wait(background_process: BackgroundProcessType) -> None:\n    async with background_process(SLEEP(10)) as proc:\n        # Check that wait (including multi-wait) tolerates being cancelled\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(proc.wait)\n            nursery.start_soon(proc.wait)\n            nursery.start_soon(proc.wait)\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n        # Now try waiting for real\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(proc.wait)\n            nursery.start_soon(proc.wait)\n            nursery.start_soon(proc.wait)\n            await wait_all_tasks_blocked()\n            proc.kill()\n\n\n@background_process_param\nasync def test_multi_wait_no_pidfd(background_process: BackgroundProcessType) -> None:\n    with mock.patch(\"trio._subprocess.can_try_pidfd_open\", new=False):\n        async with background_process(SLEEP(10)) as proc:\n            # Check that wait (including multi-wait) tolerates being cancelled\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(proc.wait)\n                nursery.start_soon(proc.wait)\n                nursery.start_soon(proc.wait)\n                await wait_all_tasks_blocked()\n                nursery.cancel_scope.cancel()\n\n            # Now try waiting for real\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(proc.wait)\n                nursery.start_soon(proc.wait)\n                nursery.start_soon(proc.wait)\n                await wait_all_tasks_blocked()\n                proc.kill()\n\n\nCOPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR = python(\n    \"data = sys.stdin.buffer.read(); \"\n    \"sys.stdout.buffer.write(data); \"\n    \"sys.stderr.buffer.write(data[::-1])\",\n)\n\n\n@background_process_param\nasync def test_pipes(background_process: BackgroundProcessType) -> None:\n    async with background_process(\n        COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,\n        stdin=subprocess.PIPE,\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n    ) as proc:\n        msg = b\"the quick brown fox jumps over the lazy dog\"\n\n        async def feed_input() -> None:\n            assert proc.stdin is not None\n            await proc.stdin.send_all(msg)\n            await proc.stdin.aclose()\n\n        async def check_output(stream: ReceiveStream, expected: bytes) -> None:\n            seen = bytearray()\n            async for chunk in stream:\n                seen += chunk\n            assert seen == expected\n\n        assert proc.stdout is not None\n        assert proc.stderr is not None\n\n        async with _core.open_nursery() as nursery:\n            # fail eventually if something is broken\n            nursery.cancel_scope.deadline = _core.current_time() + 30.0\n            nursery.start_soon(feed_input)\n            nursery.start_soon(check_output, proc.stdout, msg)\n            nursery.start_soon(check_output, proc.stderr, msg[::-1])\n\n        assert not nursery.cancel_scope.cancelled_caught\n        assert await proc.wait() == 0\n\n\n@background_process_param\nasync def test_interactive(background_process: BackgroundProcessType) -> None:\n    # Test some back-and-forth with a subprocess. This one works like so:\n    # in: 32\\n\n    # out: 0000...0000\\n (32 zeroes)\n    # err: 1111...1111\\n (64 ones)\n    # in: 10\\n\n    # out: 2222222222\\n (10 twos)\n    # err: 3333....3333\\n (20 threes)\n    # in: EOF\n    # out: EOF\n    # err: EOF\n\n    async with background_process(\n        python(\n            \"idx = 0\\n\"\n            \"while True:\\n\"\n            \"    line = sys.stdin.readline()\\n\"\n            \"    if line == '': break\\n\"\n            \"    request = int(line.strip())\\n\"\n            \"    print(str(idx * 2) * request)\\n\"\n            \"    print(str(idx * 2 + 1) * request * 2, file=sys.stderr)\\n\"\n            \"    idx += 1\\n\",\n        ),\n        stdin=subprocess.PIPE,\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n    ) as proc:\n        newline = b\"\\n\" if posix else b\"\\r\\n\"\n\n        async def expect(idx: int, request: int) -> None:\n            async with _core.open_nursery() as nursery:\n\n                async def drain_one(\n                    stream: ReceiveStream,\n                    count: int,\n                    digit: int,\n                ) -> None:\n                    while count > 0:\n                        result = await stream.receive_some(count)\n                        assert result == (f\"{digit}\".encode() * len(result))\n                        count -= len(result)\n                    assert count == 0\n                    assert await stream.receive_some(len(newline)) == newline\n\n                assert proc.stdout is not None\n                assert proc.stderr is not None\n                nursery.start_soon(drain_one, proc.stdout, request, idx * 2)\n                nursery.start_soon(drain_one, proc.stderr, request * 2, idx * 2 + 1)\n\n        assert proc.stdin is not None\n        assert proc.stdout is not None\n        assert proc.stderr is not None\n        with fail_after(5):\n            await proc.stdin.send_all(b\"12\")\n            await sleep(0.1)\n            await proc.stdin.send_all(b\"345\" + newline)\n            await expect(0, 12345)\n            await proc.stdin.send_all(b\"100\" + newline + b\"200\" + newline)\n            await expect(1, 100)\n            await expect(2, 200)\n            await proc.stdin.send_all(b\"0\" + newline)\n            await expect(3, 0)\n            await proc.stdin.send_all(b\"999999\")\n            with move_on_after(0.1) as scope:\n                await expect(4, 0)\n            assert scope.cancelled_caught\n            await proc.stdin.send_all(newline)\n            await expect(4, 999999)\n            await proc.stdin.aclose()\n            assert await proc.stdout.receive_some(1) == b\"\"\n            assert await proc.stderr.receive_some(1) == b\"\"\n            await proc.wait()\n\n    assert proc.returncode == 0\n\n\nasync def test_run() -> None:\n    data = bytes(random.randint(0, 255) for _ in range(2**18))\n\n    result = await run_process(\n        CAT,\n        stdin=data,\n        capture_stdout=True,\n        capture_stderr=True,\n    )\n    assert result.args == CAT\n    assert result.returncode == 0\n    assert result.stdout == data\n    assert result.stderr == b\"\"\n\n    result = await run_process(CAT, capture_stdout=True)\n    assert result.args == CAT\n    assert result.returncode == 0\n    assert result.stdout == b\"\"\n    assert result.stderr is None\n\n    result = await run_process(\n        COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,\n        stdin=data,\n        capture_stdout=True,\n        capture_stderr=True,\n    )\n    assert result.args == COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR\n    assert result.returncode == 0\n    assert result.stdout == data\n    assert result.stderr == data[::-1]\n\n    # invalid combinations\n    with pytest.raises(UnicodeError):\n        await run_process(CAT, stdin=\"oh no, it's text\")\n\n    pipe_stdout_error = r\"^stdout=subprocess\\.PIPE is only valid with nursery\\.start, since that's the only way to access the pipe(; use nursery\\.start or pass the data you want to write directly)*$\"\n    with pytest.raises(ValueError, match=pipe_stdout_error):\n        await run_process(CAT, stdin=subprocess.PIPE)\n    with pytest.raises(ValueError, match=pipe_stdout_error):\n        await run_process(CAT, stdout=subprocess.PIPE)\n    with pytest.raises(\n        ValueError,\n        match=pipe_stdout_error.replace(\"stdout\", \"stderr\", 1),\n    ):\n        await run_process(CAT, stderr=subprocess.PIPE)\n    with pytest.raises(\n        ValueError,\n        match=r\"^can't specify both stdout and capture_stdout$\",\n    ):\n        await run_process(CAT, capture_stdout=True, stdout=subprocess.DEVNULL)\n    with pytest.raises(\n        ValueError,\n        match=r\"^can't specify both stderr and capture_stderr$\",\n    ):\n        await run_process(CAT, capture_stderr=True, stderr=None)\n\n\nasync def test_run_check() -> None:\n    cmd = python(\"sys.stderr.buffer.write(b'test\\\\n'); sys.exit(1)\")\n    with pytest.raises(subprocess.CalledProcessError) as excinfo:\n        await run_process(cmd, stdin=subprocess.DEVNULL, capture_stderr=True)\n    assert excinfo.value.cmd == cmd\n    assert excinfo.value.returncode == 1\n    assert excinfo.value.stderr == b\"test\\n\"\n    assert excinfo.value.stdout is None\n\n    result = await run_process(\n        cmd,\n        capture_stdout=True,\n        capture_stderr=True,\n        check=False,\n    )\n    assert result.args == cmd\n    assert result.stdout == b\"\"\n    assert result.stderr == b\"test\\n\"\n    assert result.returncode == 1\n\n\n@skip_if_fbsd_pipes_broken\nasync def test_run_with_broken_pipe() -> None:\n    result = await run_process(\n        [sys.executable, \"-c\", \"import sys; sys.stdin.close()\"],\n        stdin=b\"x\" * 131072,\n    )\n    assert result.returncode == 0\n    assert result.stdout is result.stderr is None\n\n\n@background_process_param\nasync def test_stderr_stdout(background_process: BackgroundProcessType) -> None:\n    async with background_process(\n        COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,\n        stdin=subprocess.PIPE,\n        stdout=subprocess.PIPE,\n        stderr=subprocess.STDOUT,\n    ) as proc:\n        assert proc.stdio is not None\n        assert proc.stdout is not None\n        assert proc.stderr is None\n        await proc.stdio.send_all(b\"1234\")\n        await proc.stdio.send_eof()\n\n        output = []\n        while True:\n            chunk = await proc.stdio.receive_some(16)\n            if chunk == b\"\":\n                break\n            output.append(chunk)\n        assert b\"\".join(output) == b\"12344321\"\n    assert proc.returncode == 0\n\n    # equivalent test with run_process()\n    result = await run_process(\n        COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,\n        stdin=b\"1234\",\n        capture_stdout=True,\n        stderr=subprocess.STDOUT,\n    )\n    assert result.returncode == 0\n    assert result.stdout == b\"12344321\"\n    assert result.stderr is None\n\n    # this one hits the branch where stderr=STDOUT but stdout\n    # is not redirected\n    async with background_process(\n        CAT,\n        stdin=subprocess.PIPE,\n        stderr=subprocess.STDOUT,\n    ) as proc:\n        assert proc.stdout is None\n        assert proc.stderr is None\n        await proc.stdin.aclose()\n        await proc.wait()\n    assert proc.returncode == 0\n\n    if posix:\n        try:\n            r, w = os.pipe()\n\n            async with background_process(\n                COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,\n                stdin=subprocess.PIPE,\n                stdout=w,\n                stderr=subprocess.STDOUT,\n            ) as proc:\n                os.close(w)\n                assert proc.stdio is None\n                assert proc.stdout is None\n                assert proc.stderr is None\n                await proc.stdin.send_all(b\"1234\")\n                await proc.stdin.aclose()\n                assert await proc.wait() == 0\n                assert os.read(r, 4096) == b\"12344321\"\n                assert os.read(r, 4096) == b\"\"\n        finally:\n            os.close(r)\n\n\nasync def test_errors() -> None:\n    with pytest.raises(TypeError) as excinfo:\n        # call-overload on unix, call-arg on windows\n        await open_process([\"ls\"], encoding=\"utf-8\")  # type: ignore\n    assert \"unbuffered byte streams\" in str(excinfo.value)\n    assert \"the 'encoding' option is not supported\" in str(excinfo.value)\n\n    if posix:\n        with pytest.raises(TypeError) as excinfo:\n            await open_process([\"ls\"], shell=True)\n        with pytest.raises(TypeError) as excinfo:\n            await open_process(\"ls\", shell=False)\n\n\n@background_process_param\nasync def test_signals(background_process: BackgroundProcessType) -> None:\n    async def test_one_signal(\n        send_it: Callable[[Process], None],\n        signum: signal.Signals | None,\n    ) -> None:\n        with move_on_after(1.0) as scope:\n            async with background_process(SLEEP(3600)) as proc:\n                send_it(proc)\n                await proc.wait()\n        assert not scope.cancelled_caught\n        if posix:\n            assert signum is not None\n            assert proc.returncode == -signum\n        else:\n            assert proc.returncode != 0\n\n    await test_one_signal(Process.kill, SIGKILL)\n    await test_one_signal(Process.terminate, SIGTERM)\n    # Test that we can send arbitrary signals.\n    #\n    # We used to use SIGINT here, but it turns out that the Python interpreter\n    # has race conditions that can cause it to explode in weird ways if it\n    # tries to handle SIGINT during startup. SIGUSR1's default disposition is\n    # to terminate the target process, and Python doesn't try to do anything\n    # clever to handle it.\n    if (not TYPE_CHECKING and posix) or sys.platform != \"win32\":\n        await test_one_signal(lambda proc: proc.send_signal(SIGUSR1), SIGUSR1)\n\n\n@pytest.mark.skipif(not posix, reason=\"POSIX specific\")\n@background_process_param\nasync def test_wait_reapable_fails(background_process: BackgroundProcessType) -> None:\n    if TYPE_CHECKING and sys.platform == \"win32\":\n        return\n    old_sigchld = signal.signal(signal.SIGCHLD, signal.SIG_IGN)\n    try:\n        # With SIGCHLD disabled, the wait() syscall will wait for the\n        # process to exit but then fail with ECHILD. Make sure we\n        # support this case as the stdlib subprocess module does.\n        async with background_process(SLEEP(3600)) as proc:\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(proc.wait)\n                await wait_all_tasks_blocked()\n                proc.kill()\n                nursery.cancel_scope.deadline = _core.current_time() + 1.0\n            assert not nursery.cancel_scope.cancelled_caught\n            assert proc.returncode == 0  # exit status unknowable, so...\n    finally:\n        signal.signal(signal.SIGCHLD, old_sigchld)\n\n\n@pytest.mark.skipif(not posix, reason=\"POSIX specific\")\n@background_process_param\nasync def test_wait_reapable_fails_no_pidfd(\n    background_process: BackgroundProcessType,\n) -> None:\n    if TYPE_CHECKING and sys.platform == \"win32\":\n        return\n    with mock.patch(\"trio._subprocess.can_try_pidfd_open\", new=False):\n        old_sigchld = signal.signal(signal.SIGCHLD, signal.SIG_IGN)\n        try:\n            # With SIGCHLD disabled, the wait() syscall will wait for the\n            # process to exit but then fail with ECHILD. Make sure we\n            # support this case as the stdlib subprocess module does.\n            async with background_process(SLEEP(3600)) as proc:\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(proc.wait)\n                    await wait_all_tasks_blocked()\n                    proc.kill()\n                    nursery.cancel_scope.deadline = _core.current_time() + 1.0\n                assert not nursery.cancel_scope.cancelled_caught\n                assert proc.returncode == 0  # exit status unknowable, so...\n        finally:\n            signal.signal(signal.SIGCHLD, old_sigchld)\n\n\n@slow\ndef test_waitid_eintr() -> None:\n    # This only matters on PyPy (where we're coding EINTR handling\n    # ourselves) but the test works on all waitid platforms.\n    from .._subprocess_platform import wait_child_exiting\n\n    if TYPE_CHECKING and (sys.platform == \"win32\" or sys.platform == \"darwin\"):\n        return\n\n    if not wait_child_exiting.__module__.endswith(\"waitid\"):\n        pytest.skip(\"waitid only\")\n\n    # despite the TYPE_CHECKING early return silencing warnings about signal.SIGALRM etc\n    # this import is still checked on win32&darwin and raises [attr-defined].\n    # Linux doesn't raise [attr-defined] though, so we need [unused-ignore]\n    from .._subprocess_platform.waitid import (  # type: ignore[attr-defined, unused-ignore]\n        sync_wait_reapable,\n    )\n\n    got_alarm = False\n    sleeper = subprocess.Popen([\"sleep\", \"3600\"])\n\n    def on_alarm(sig: int, frame: FrameType | None) -> None:\n        nonlocal got_alarm\n        got_alarm = True\n        sleeper.kill()\n\n    old_sigalrm = signal.signal(signal.SIGALRM, on_alarm)\n    try:\n        signal.alarm(1)\n        sync_wait_reapable(sleeper.pid)\n        assert sleeper.wait(timeout=1) == -9\n    finally:\n        if sleeper.returncode is None:  # pragma: no cover\n            # We only get here if something fails in the above;\n            # if the test passes, wait() will reap the process\n            sleeper.kill()\n            sleeper.wait()\n        signal.signal(signal.SIGALRM, old_sigalrm)\n\n\nasync def test_custom_deliver_cancel() -> None:\n    custom_deliver_cancel_called = False\n\n    async def custom_deliver_cancel(proc: Process) -> None:\n        nonlocal custom_deliver_cancel_called\n        custom_deliver_cancel_called = True\n        proc.terminate()\n        # Make sure this does get cancelled when the process exits, and that\n        # the process really exited.\n        try:\n            await sleep_forever()\n        finally:\n            assert proc.returncode is not None\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(\n            partial(run_process, SLEEP(9999), deliver_cancel=custom_deliver_cancel),\n        )\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n\n    assert custom_deliver_cancel_called\n\n\ndef test_bad_deliver_cancel() -> None:\n    async def custom_deliver_cancel(proc: Process) -> None:\n        proc.terminate()\n        raise ValueError(\"foo\")\n\n    async def do_stuff() -> None:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(\n                partial(run_process, SLEEP(9999), deliver_cancel=custom_deliver_cancel),\n            )\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n    # double wrap from our nursery + the internal nursery\n    with pytest.RaisesGroup(\n        pytest.RaisesGroup(pytest.RaisesExc(ValueError, match=\"^foo$\"))\n    ):\n        _core.run(do_stuff, strict_exception_groups=True)\n\n\nasync def test_warn_on_failed_cancel_terminate(monkeypatch: pytest.MonkeyPatch) -> None:\n    original_terminate = Process.terminate\n\n    def broken_terminate(self: Process) -> NoReturn:\n        original_terminate(self)\n        raise OSError(\"whoops\")\n\n    monkeypatch.setattr(Process, \"terminate\", broken_terminate)\n\n    with pytest.warns(RuntimeWarning, match=\".*whoops.*\"):  # noqa: PT031\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(run_process, SLEEP(9999))\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n\n@pytest.mark.skipif(not posix, reason=\"posix only\")\nasync def test_warn_on_cancel_SIGKILL_escalation(\n    autojump_clock: MockClock,\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    monkeypatch.setattr(Process, \"terminate\", lambda *args: None)\n\n    with pytest.warns(RuntimeWarning, match=\".*ignored SIGTERM.*\"):  # noqa: PT031\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(run_process, SLEEP(9999))\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n\n# the background_process_param exercises a lot of run_process cases, but it uses\n# check=False, so lets have a test that uses check=True as well\nasync def test_run_process_background_fail() -> None:\n    with pytest.RaisesGroup(subprocess.CalledProcessError):\n        async with _core.open_nursery() as nursery:\n            value = await nursery.start(run_process, EXIT_FALSE)\n            assert isinstance(value, Process)\n            proc: Process = value\n    assert proc.returncode == 1\n\n\n@pytest.mark.skipif(\n    not SyncPath(\"/dev/fd\").exists(),\n    reason=\"requires a way to iterate through open files\",\n)\nasync def test_for_leaking_fds() -> None:\n    gc.collect()  # address possible flakiness on PyPy\n\n    starting_fds = set(SyncPath(\"/dev/fd\").iterdir())  # noqa: ASYNC240\n    await run_process(EXIT_TRUE)\n    assert set(SyncPath(\"/dev/fd\").iterdir()) == starting_fds  # noqa: ASYNC240\n\n    with pytest.raises(subprocess.CalledProcessError):\n        await run_process(EXIT_FALSE)\n    assert set(SyncPath(\"/dev/fd\").iterdir()) == starting_fds  # noqa: ASYNC240\n\n    with pytest.raises(PermissionError):\n        await run_process([\"/dev/fd/0\"])\n    assert set(SyncPath(\"/dev/fd\").iterdir()) == starting_fds  # noqa: ASYNC240\n\n\nasync def test_run_process_internal_error(monkeypatch: pytest.MonkeyPatch) -> None:\n    # There's probably less extreme ways of triggering errors inside the nursery\n    # in run_process.\n    async def very_broken_open(*args: object, **kwargs: object) -> str:\n        return \"oops\"\n\n    monkeypatch.setattr(trio._subprocess, \"_open_process\", very_broken_open)\n    with pytest.RaisesGroup(AttributeError, AttributeError):\n        await run_process(EXIT_TRUE, capture_stdout=True)\n\n\n# regression test for #2209\nasync def test_subprocess_pidfd_unnotified() -> None:\n    noticed_exit = None\n\n    async def wait_and_tell(proc: Process) -> None:\n        nonlocal noticed_exit\n        noticed_exit = Event()\n        await proc.wait()\n        noticed_exit.set()\n\n    proc = await open_process(SLEEP(9999))\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(wait_and_tell, proc)\n        await wait_all_tasks_blocked()\n        assert isinstance(noticed_exit, Event)\n        proc.terminate()\n        # without giving trio a chance to do so,\n        with assert_no_checkpoints():\n            # wait until the process has actually exited;\n            proc._proc.wait()\n            # force a call to poll (that closes the pidfd on linux)\n            proc.poll()\n        with move_on_after(5):\n            # Some platforms use threads to wait for exit, so it might take a bit\n            # for everything to notice\n            await noticed_exit.wait()\n        assert noticed_exit.is_set(), \"child task wasn't woken after poll, DEADLOCK\"\n"
  },
  {
    "path": "src/trio/_tests/test_sync.py",
    "content": "from __future__ import annotations\n\nimport re\nimport weakref\nfrom collections.abc import Callable\nfrom typing import TypeAlias\n\nimport pytest\n\nfrom .. import _core\nfrom .._core._parking_lot import GLOBAL_PARKING_LOT_BREAKER\nfrom .._sync import *\nfrom .._timeouts import sleep_forever\nfrom ..testing import assert_checkpoints, wait_all_tasks_blocked\n\n\nasync def test_Event() -> None:\n    e = Event()\n    assert not e.is_set()\n    assert e.statistics().tasks_waiting == 0\n\n    with pytest.warns(\n        DeprecationWarning,\n        match=r\"trio\\.Event\\.__bool__ is deprecated since Trio 0\\.31\\.0; use trio\\.Event\\.is_set instead \\(https://github.com/python-trio/trio/issues/3238\\)\",\n    ):\n        e.__bool__()\n\n    e.set()\n    assert e.is_set()\n    with assert_checkpoints():\n        await e.wait()\n\n    e = Event()\n\n    record = []\n\n    async def child() -> None:\n        record.append(\"sleeping\")\n        await e.wait()\n        record.append(\"woken\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child)\n        nursery.start_soon(child)\n        await wait_all_tasks_blocked()\n        assert record == [\"sleeping\", \"sleeping\"]\n        assert e.statistics().tasks_waiting == 2\n        e.set()\n        await wait_all_tasks_blocked()\n        assert record == [\"sleeping\", \"sleeping\", \"woken\", \"woken\"]\n\n\nasync def test_CapacityLimiter() -> None:\n    assert CapacityLimiter(0).total_tokens == 0\n    with pytest.raises(TypeError):\n        CapacityLimiter(1.0)\n    with pytest.raises(ValueError, match=r\"^total_tokens must be >= 0$\"):\n        CapacityLimiter(-1)\n    c = CapacityLimiter(2)\n    repr(c)  # smoke test\n    assert c.total_tokens == 2\n    assert c.borrowed_tokens == 0\n    assert c.available_tokens == 2\n    with pytest.raises(RuntimeError):\n        c.release()\n    assert c.borrowed_tokens == 0\n    c.acquire_nowait()\n    assert c.borrowed_tokens == 1\n    assert c.available_tokens == 1\n\n    stats = c.statistics()\n    assert stats.borrowed_tokens == 1\n    assert stats.total_tokens == 2\n    assert stats.borrowers == [_core.current_task()]\n    assert stats.tasks_waiting == 0\n\n    # Can't re-acquire when we already have it\n    with pytest.raises(RuntimeError):\n        c.acquire_nowait()\n    assert c.borrowed_tokens == 1\n    with pytest.raises(RuntimeError):\n        await c.acquire()\n    assert c.borrowed_tokens == 1\n\n    # We can acquire on behalf of someone else though\n    with assert_checkpoints():\n        await c.acquire_on_behalf_of(\"someone\")\n\n    # But then we've run out of capacity\n    assert c.borrowed_tokens == 2\n    with pytest.raises(_core.WouldBlock):\n        c.acquire_on_behalf_of_nowait(\"third party\")\n\n    assert set(c.statistics().borrowers) == {_core.current_task(), \"someone\"}\n\n    # Until we release one\n    c.release_on_behalf_of(_core.current_task())\n    assert c.statistics().borrowers == [\"someone\"]\n\n    c.release_on_behalf_of(\"someone\")\n    assert c.borrowed_tokens == 0\n    with assert_checkpoints():\n        async with c:\n            assert c.borrowed_tokens == 1\n\n    async with _core.open_nursery() as nursery:\n        await c.acquire_on_behalf_of(\"value 1\")\n        await c.acquire_on_behalf_of(\"value 2\")\n        nursery.start_soon(c.acquire_on_behalf_of, \"value 3\")\n        await wait_all_tasks_blocked()\n        assert c.borrowed_tokens == 2\n        assert c.statistics().tasks_waiting == 1\n        c.release_on_behalf_of(\"value 2\")\n        # Fairness:\n        assert c.borrowed_tokens == 2\n        with pytest.raises(_core.WouldBlock):\n            c.acquire_nowait()\n\n    c.release_on_behalf_of(\"value 3\")\n    c.release_on_behalf_of(\"value 1\")\n\n\nasync def test_CapacityLimiter_inf() -> None:\n    from math import inf\n\n    c = CapacityLimiter(inf)\n    repr(c)  # smoke test\n    assert c.total_tokens == inf\n    assert c.borrowed_tokens == 0\n    assert c.available_tokens == inf\n    with pytest.raises(RuntimeError):\n        c.release()\n    assert c.borrowed_tokens == 0\n    c.acquire_nowait()\n    assert c.borrowed_tokens == 1\n    assert c.available_tokens == inf\n\n\nasync def test_CapacityLimiter_change_total_tokens() -> None:\n    c = CapacityLimiter(2)\n\n    with pytest.raises(TypeError):\n        c.total_tokens = 1.0\n\n    with pytest.raises(ValueError, match=r\"^total_tokens must be >= 0$\"):\n        c.total_tokens = -1\n\n    with pytest.raises(ValueError, match=r\"^total_tokens must be >= 0$\"):\n        c.total_tokens = -10\n\n    assert c.total_tokens == 2\n\n    async with _core.open_nursery() as nursery:\n        for i in range(5):\n            nursery.start_soon(c.acquire_on_behalf_of, i)\n            await wait_all_tasks_blocked()\n        assert set(c.statistics().borrowers) == {0, 1}\n        assert c.statistics().tasks_waiting == 3\n        c.total_tokens += 2\n        assert set(c.statistics().borrowers) == {0, 1, 2, 3}\n        assert c.statistics().tasks_waiting == 1\n        c.total_tokens -= 3\n        assert c.borrowed_tokens == 4\n        assert c.total_tokens == 1\n        c.release_on_behalf_of(0)\n        c.release_on_behalf_of(1)\n        c.release_on_behalf_of(2)\n        assert set(c.statistics().borrowers) == {3}\n        assert c.statistics().tasks_waiting == 1\n        c.release_on_behalf_of(3)\n        assert set(c.statistics().borrowers) == {4}\n        assert c.statistics().tasks_waiting == 0\n\n\n# regression test for issue #548\nasync def test_CapacityLimiter_memleak_548() -> None:\n    limiter = CapacityLimiter(total_tokens=1)\n    await limiter.acquire()\n\n    async with _core.open_nursery() as n:\n        n.start_soon(limiter.acquire)\n        await wait_all_tasks_blocked()  # give it a chance to run the task\n        n.cancel_scope.cancel()\n\n    # if this is 1, the acquire call (despite being killed) is still there in the task, and will\n    # leak memory all the while the limiter is active\n    assert len(limiter._pending_borrowers) == 0\n\n\nasync def test_CapacityLimiter_zero_limit_tokens() -> None:\n    c = CapacityLimiter(5)\n\n    assert c.total_tokens == 5\n\n    async with _core.open_nursery() as nursery:\n        c.total_tokens = 0\n\n        for i in range(5):\n            nursery.start_soon(c.acquire_on_behalf_of, i)\n            await wait_all_tasks_blocked()\n\n        assert set(c.statistics().borrowers) == set()\n        assert c.statistics().tasks_waiting == 5\n\n        c.total_tokens = 5\n\n        assert set(c.statistics().borrowers) == {0, 1, 2, 3, 4}\n\n        nursery.start_soon(c.acquire_on_behalf_of, 5)\n        await wait_all_tasks_blocked()\n\n        assert c.statistics().tasks_waiting == 1\n\n        for i in range(5):\n            c.release_on_behalf_of(i)\n\n        assert c.statistics().tasks_waiting == 0\n        c.release_on_behalf_of(5)\n\n        # making sure that zero limit capacity limiter doesn't let any tasks through\n\n        c.total_tokens = 0\n\n        with pytest.raises(_core.WouldBlock):\n            c.acquire_nowait()\n\n        nursery.start_soon(c.acquire_on_behalf_of, 6)\n        await wait_all_tasks_blocked()\n\n        assert c.statistics().tasks_waiting == 1\n        assert c.statistics().borrowers == []\n\n        c.total_tokens = 1\n        assert c.statistics().tasks_waiting == 0\n        assert c.statistics().borrowers == [6]\n        c.release_on_behalf_of(6)\n\n        await c.acquire_on_behalf_of(0)  # total_tokens is 1\n\n        nursery.start_soon(c.acquire_on_behalf_of, 1)\n        await wait_all_tasks_blocked()\n        c.total_tokens = 0\n\n        assert c.statistics().borrowers == [0]\n\n        c.release_on_behalf_of(0)\n        await wait_all_tasks_blocked()\n        assert c.statistics().borrowers == []\n        assert c.statistics().tasks_waiting == 1\n\n        c.total_tokens = 1\n        await wait_all_tasks_blocked()\n        assert c.statistics().borrowers == [1]\n        assert c.statistics().tasks_waiting == 0\n\n        c.release_on_behalf_of(1)\n\n        c.total_tokens = 0\n\n        nursery.cancel_scope.cancel()\n\n    assert c.total_tokens == 0\n    assert c.statistics().borrowers == []\n    assert c._pending_borrowers == {}\n\n\nasync def test_Semaphore() -> None:\n    with pytest.raises(TypeError):\n        Semaphore(1.0)  # type: ignore[arg-type]\n    with pytest.raises(ValueError, match=r\"^initial value must be >= 0$\"):\n        Semaphore(-1)\n    s = Semaphore(1)\n    repr(s)  # smoke test\n    assert s.value == 1\n    assert s.max_value is None\n    s.release()\n    assert s.value == 2\n    assert s.statistics().tasks_waiting == 0\n    s.acquire_nowait()\n    assert s.value == 1\n    with assert_checkpoints():\n        await s.acquire()\n    assert s.value == 0\n    with pytest.raises(_core.WouldBlock):\n        s.acquire_nowait()\n\n    s.release()\n    assert s.value == 1\n    with assert_checkpoints():\n        async with s:\n            assert s.value == 0\n    assert s.value == 1\n    s.acquire_nowait()\n\n    record = []\n\n    async def do_acquire(s: Semaphore) -> None:\n        record.append(\"started\")\n        await s.acquire()\n        record.append(\"finished\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(do_acquire, s)\n        await wait_all_tasks_blocked()\n        assert record == [\"started\"]\n        assert s.value == 0\n        s.release()\n        # Fairness:\n        assert s.value == 0\n        with pytest.raises(_core.WouldBlock):\n            s.acquire_nowait()\n    assert record == [\"started\", \"finished\"]\n\n\ndef test_Semaphore_bounded() -> None:\n    with pytest.raises(TypeError):\n        Semaphore(1, max_value=1.0)  # type: ignore[arg-type]\n    with pytest.raises(ValueError, match=r\"^max_values must be >= initial_value$\"):\n        Semaphore(2, max_value=1)\n    bs = Semaphore(1, max_value=1)\n    assert bs.max_value == 1\n    repr(bs)  # smoke test\n    with pytest.raises(ValueError, match=r\"^semaphore released too many times$\"):\n        bs.release()\n    assert bs.value == 1\n    bs.acquire_nowait()\n    assert bs.value == 0\n    bs.release()\n    assert bs.value == 1\n\n\n@pytest.mark.parametrize(\"lockcls\", [Lock, StrictFIFOLock], ids=lambda fn: fn.__name__)\nasync def test_Lock_and_StrictFIFOLock(\n    lockcls: type[Lock | StrictFIFOLock],\n) -> None:\n    l = lockcls()  # noqa\n    assert not l.locked()\n\n    # make sure locks can be weakref'ed (gh-331)\n    r = weakref.ref(l)\n    assert r() is l\n\n    repr(l)  # smoke test\n    # make sure repr uses the right name for subclasses\n    assert lockcls.__name__ in repr(l)\n    with assert_checkpoints():\n        async with l:\n            assert l.locked()\n            repr(l)  # smoke test (repr branches on locked/unlocked)\n    assert not l.locked()\n    l.acquire_nowait()\n    assert l.locked()\n    l.release()\n    assert not l.locked()\n    with assert_checkpoints():\n        await l.acquire()\n    assert l.locked()\n    l.release()\n    assert not l.locked()\n\n    l.acquire_nowait()\n    with pytest.raises(RuntimeError):\n        # Error out if we already own the lock\n        l.acquire_nowait()\n    l.release()\n    with pytest.raises(RuntimeError):\n        # Error out if we don't own the lock\n        l.release()\n\n    holder_task = None\n\n    async def holder() -> None:\n        nonlocal holder_task\n        holder_task = _core.current_task()\n        async with l:\n            await sleep_forever()\n\n    async with _core.open_nursery() as nursery:\n        assert not l.locked()\n        nursery.start_soon(holder)\n        await wait_all_tasks_blocked()\n        assert l.locked()\n        # WouldBlock if someone else holds the lock\n        with pytest.raises(_core.WouldBlock):\n            l.acquire_nowait()\n        # Can't release a lock someone else holds\n        with pytest.raises(RuntimeError):\n            l.release()\n\n        statistics = l.statistics()\n        print(statistics)\n        assert statistics.locked\n        assert statistics.owner is holder_task\n        assert statistics.tasks_waiting == 0\n\n        nursery.start_soon(holder)\n        await wait_all_tasks_blocked()\n        statistics = l.statistics()\n        print(statistics)\n        assert statistics.tasks_waiting == 1\n\n        nursery.cancel_scope.cancel()\n\n    statistics = l.statistics()\n    assert not statistics.locked\n    assert statistics.owner is None\n    assert statistics.tasks_waiting == 0\n\n\nasync def test_Condition() -> None:\n    with pytest.raises(TypeError):\n        Condition(Semaphore(1))  # type: ignore[arg-type]\n    with pytest.raises(TypeError):\n        Condition(StrictFIFOLock)  # type: ignore[arg-type]\n    l = Lock()  # noqa\n    c = Condition(l)\n    assert not l.locked()\n    assert not c.locked()\n    with assert_checkpoints():\n        await c.acquire()\n    assert l.locked()\n    assert c.locked()\n\n    c = Condition()\n    assert not c.locked()\n    c.acquire_nowait()\n    assert c.locked()\n    with pytest.raises(RuntimeError):\n        c.acquire_nowait()\n    c.release()\n\n    with pytest.raises(RuntimeError):\n        # Can't wait without holding the lock\n        await c.wait()\n    with pytest.raises(RuntimeError):\n        # Can't notify without holding the lock\n        c.notify()\n    with pytest.raises(RuntimeError):\n        # Can't notify without holding the lock\n        c.notify_all()\n\n    finished_waiters = set()\n\n    async def waiter(i: int) -> None:\n        async with c:\n            await c.wait()\n        finished_waiters.add(i)\n\n    async with _core.open_nursery() as nursery:\n        for i in range(3):\n            nursery.start_soon(waiter, i)\n            await wait_all_tasks_blocked()\n        async with c:\n            c.notify()\n        assert c.locked()\n        await wait_all_tasks_blocked()\n        assert finished_waiters == {0}\n        async with c:\n            c.notify_all()\n        await wait_all_tasks_blocked()\n        assert finished_waiters == {0, 1, 2}\n\n    finished_waiters = set()\n    async with _core.open_nursery() as nursery:\n        for i in range(3):\n            nursery.start_soon(waiter, i)\n            await wait_all_tasks_blocked()\n        async with c:\n            c.notify(2)\n            statistics = c.statistics()\n            print(statistics)\n            assert statistics.tasks_waiting == 1\n            assert statistics.lock_statistics.tasks_waiting == 2\n        # exiting the context manager hands off the lock to the first task\n        assert c.statistics().lock_statistics.tasks_waiting == 1\n\n        await wait_all_tasks_blocked()\n        assert finished_waiters == {0, 1}\n\n        async with c:\n            c.notify_all()\n\n    # After being cancelled still hold the lock (!)\n    # (Note that c.__aexit__ checks that we hold the lock as well)\n    with _core.CancelScope() as scope:\n        async with c:\n            scope.cancel()\n            try:\n                await c.wait()\n            finally:\n                assert c.locked()\n\n\nfrom .._channel import open_memory_channel\nfrom .._sync import AsyncContextManagerMixin\n\n# Three ways of implementing a Lock in terms of a channel. Used to let us put\n# the channel through the generic lock tests.\n\n\nclass ChannelLock1(AsyncContextManagerMixin):\n    def __init__(self, capacity: int) -> None:\n        self.s, self.r = open_memory_channel[None](capacity)\n        for _ in range(capacity - 1):\n            self.s.send_nowait(None)\n\n    def acquire_nowait(self) -> None:\n        self.s.send_nowait(None)\n\n    async def acquire(self) -> None:\n        await self.s.send(None)\n\n    def release(self) -> None:\n        self.r.receive_nowait()\n\n\nclass ChannelLock2(AsyncContextManagerMixin):\n    def __init__(self) -> None:\n        self.s, self.r = open_memory_channel[None](10)\n        self.s.send_nowait(None)\n\n    def acquire_nowait(self) -> None:\n        self.r.receive_nowait()\n\n    async def acquire(self) -> None:\n        await self.r.receive()\n\n    def release(self) -> None:\n        self.s.send_nowait(None)\n\n\nclass ChannelLock3(AsyncContextManagerMixin):\n    def __init__(self) -> None:\n        self.s, self.r = open_memory_channel[None](0)\n        # self.acquired is true when one task acquires the lock and\n        # only becomes false when it's released and no tasks are\n        # waiting to acquire.\n        self.acquired = False\n\n    def acquire_nowait(self) -> None:\n        assert not self.acquired\n        self.acquired = True\n\n    async def acquire(self) -> None:\n        if self.acquired:\n            await self.s.send(None)\n        else:\n            self.acquired = True\n            await _core.checkpoint()\n\n    def release(self) -> None:\n        try:\n            self.r.receive_nowait()\n        except _core.WouldBlock:\n            assert self.acquired\n            self.acquired = False\n\n\nlock_factories = [\n    lambda: CapacityLimiter(1),\n    lambda: Semaphore(1),\n    Lock,\n    StrictFIFOLock,\n    lambda: ChannelLock1(10),\n    lambda: ChannelLock1(1),\n    ChannelLock2,\n    ChannelLock3,\n]\nlock_factory_names = [\n    \"CapacityLimiter(1)\",\n    \"Semaphore(1)\",\n    \"Lock\",\n    \"StrictFIFOLock\",\n    \"ChannelLock1(10)\",\n    \"ChannelLock1(1)\",\n    \"ChannelLock2\",\n    \"ChannelLock3\",\n]\n\ngeneric_lock_test = pytest.mark.parametrize(\n    \"lock_factory\",\n    lock_factories,\n    ids=lock_factory_names,\n)\n\nLockLike: TypeAlias = (\n    CapacityLimiter\n    | Semaphore\n    | Lock\n    | StrictFIFOLock\n    | ChannelLock1\n    | ChannelLock2\n    | ChannelLock3\n)\nLockFactory: TypeAlias = Callable[[], LockLike]\n\n\n# Spawn a bunch of workers that take a lock and then yield; make sure that\n# only one worker is ever in the critical section at a time.\n@generic_lock_test\nasync def test_generic_lock_exclusion(lock_factory: LockFactory) -> None:\n    LOOPS = 10\n    WORKERS = 5\n    in_critical_section = False\n    acquires = 0\n\n    async def worker(lock_like: LockLike) -> None:\n        nonlocal in_critical_section, acquires\n        for _ in range(LOOPS):\n            async with lock_like:\n                acquires += 1\n                assert not in_critical_section\n                in_critical_section = True\n                await _core.checkpoint()\n                await _core.checkpoint()\n                assert in_critical_section\n                in_critical_section = False\n\n    async with _core.open_nursery() as nursery:\n        lock_like = lock_factory()\n        for _ in range(WORKERS):\n            nursery.start_soon(worker, lock_like)\n    assert not in_critical_section\n    assert acquires == LOOPS * WORKERS\n\n\n# Several workers queue on the same lock; make sure they each get it, in\n# order.\n@generic_lock_test\nasync def test_generic_lock_fifo_fairness(lock_factory: LockFactory) -> None:\n    initial_order = []\n    record = []\n    LOOPS = 5\n\n    async def loopy(name: int, lock_like: LockLike) -> None:\n        # Record the order each task was initially scheduled in\n        initial_order.append(name)\n        for _ in range(LOOPS):\n            async with lock_like:\n                record.append(name)\n\n    lock_like = lock_factory()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(loopy, 1, lock_like)\n        nursery.start_soon(loopy, 2, lock_like)\n        nursery.start_soon(loopy, 3, lock_like)\n    # The first three could be in any order due to scheduling randomness,\n    # but after that they should repeat in the same order\n    for i in range(LOOPS):\n        assert record[3 * i : 3 * (i + 1)] == initial_order\n\n\n@generic_lock_test\nasync def test_generic_lock_acquire_nowait_blocks_acquire(\n    lock_factory: LockFactory,\n) -> None:\n    lock_like = lock_factory()\n\n    record = []\n\n    async def lock_taker() -> None:\n        record.append(\"started\")\n        async with lock_like:\n            pass\n        record.append(\"finished\")\n\n    async with _core.open_nursery() as nursery:\n        lock_like.acquire_nowait()\n        nursery.start_soon(lock_taker)\n        await wait_all_tasks_blocked()\n        assert record == [\"started\"]\n        lock_like.release()\n\n\nasync def test_lock_acquire_unowned_lock() -> None:\n    \"\"\"Test that trying to acquire a lock whose owner has exited raises an error.\n    see https://github.com/python-trio/trio/issues/3035\n    \"\"\"\n    assert not GLOBAL_PARKING_LOT_BREAKER\n    lock = trio.Lock()\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(lock.acquire)\n    owner_str = re.escape(str(lock._lot.broken_by[0]))\n    with pytest.raises(\n        trio.BrokenResourceError,\n        match=f\"^Owner of this lock exited without releasing: {owner_str}$\",\n    ):\n        await lock.acquire()\n    assert not GLOBAL_PARKING_LOT_BREAKER\n\n\nasync def test_lock_multiple_acquire() -> None:\n    \"\"\"Test for error if awaiting on a lock whose owner exits without releasing.\n    see https://github.com/python-trio/trio/issues/3035\"\"\"\n    assert not GLOBAL_PARKING_LOT_BREAKER\n    lock = trio.Lock()\n    with pytest.RaisesGroup(\n        pytest.RaisesExc(\n            trio.BrokenResourceError,\n            match=\"^Owner of this lock exited without releasing: \",\n        ),\n    ):\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(lock.acquire)\n            nursery.start_soon(lock.acquire)\n    assert not GLOBAL_PARKING_LOT_BREAKER\n\n\nasync def test_lock_handover() -> None:\n    assert not GLOBAL_PARKING_LOT_BREAKER\n    child_task: Task | None = None\n    lock = trio.Lock()\n\n    # this task acquires the lock\n    lock.acquire_nowait()\n    assert {\n        _core.current_task(): [\n            lock._lot,\n        ],\n    } == GLOBAL_PARKING_LOT_BREAKER\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(lock.acquire)\n        await wait_all_tasks_blocked()\n\n        # hand over the lock to the child task\n        lock.release()\n\n        # check values, and get the identifier out of the dict for later check\n        assert len(GLOBAL_PARKING_LOT_BREAKER) == 1\n        child_task = next(iter(GLOBAL_PARKING_LOT_BREAKER))\n        assert GLOBAL_PARKING_LOT_BREAKER[child_task] == [lock._lot]\n\n    assert lock._lot.broken_by == [child_task]\n    assert not GLOBAL_PARKING_LOT_BREAKER\n"
  },
  {
    "path": "src/trio/_tests/test_testing.py",
    "content": "from __future__ import annotations\n\n# XX this should get broken up, like testing.py did\nimport tempfile\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom .. import _core, sleep, socket as tsocket\nfrom .._core._tests.tutil import can_bind_ipv6\nfrom .._highlevel_generic import StapledStream, aclose_forcefully\nfrom .._highlevel_socket import SocketListener\nfrom ..testing import *\nfrom ..testing._check_streams import _assert_raises\nfrom ..testing._memory_streams import _UnboundedByteQueue\n\nif TYPE_CHECKING:\n    from trio import Nursery\n    from trio.abc import ReceiveStream, SendStream\n\n\nasync def test_wait_all_tasks_blocked() -> None:\n    record = []\n\n    async def busy_bee() -> None:\n        for _ in range(10):\n            await _core.checkpoint()\n        record.append(\"busy bee exhausted\")\n\n    async def waiting_for_bee_to_leave() -> None:\n        await wait_all_tasks_blocked()\n        record.append(\"quiet at last!\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(busy_bee)\n        nursery.start_soon(waiting_for_bee_to_leave)\n        nursery.start_soon(waiting_for_bee_to_leave)\n\n    # check cancellation\n    record = []\n\n    async def cancelled_while_waiting() -> None:\n        try:\n            await wait_all_tasks_blocked()\n        except _core.Cancelled:\n            record.append(\"ok\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(cancelled_while_waiting)\n        nursery.cancel_scope.cancel()\n    assert record == [\"ok\"]\n\n\nasync def test_wait_all_tasks_blocked_with_timeouts(mock_clock: MockClock) -> None:\n    record = []\n\n    async def timeout_task() -> None:\n        record.append(\"tt start\")\n        await sleep(5)\n        record.append(\"tt finished\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(timeout_task)\n        await wait_all_tasks_blocked()\n        assert record == [\"tt start\"]\n        mock_clock.jump(10)\n        await wait_all_tasks_blocked()\n        assert record == [\"tt start\", \"tt finished\"]\n\n\nasync def test_wait_all_tasks_blocked_with_cushion() -> None:\n    record = []\n\n    async def blink() -> None:\n        record.append(\"blink start\")\n        await sleep(0.01)\n        await sleep(0.01)\n        await sleep(0.01)\n        record.append(\"blink end\")\n\n    async def wait_no_cushion() -> None:\n        await wait_all_tasks_blocked()\n        record.append(\"wait_no_cushion end\")\n\n    async def wait_small_cushion() -> None:\n        await wait_all_tasks_blocked(0.02)\n        record.append(\"wait_small_cushion end\")\n\n    async def wait_big_cushion() -> None:\n        await wait_all_tasks_blocked(0.03)\n        record.append(\"wait_big_cushion end\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(blink)\n        nursery.start_soon(wait_no_cushion)\n        nursery.start_soon(wait_small_cushion)\n        nursery.start_soon(wait_small_cushion)\n        nursery.start_soon(wait_big_cushion)\n\n    assert record == [\n        \"blink start\",\n        \"wait_no_cushion end\",\n        \"blink end\",\n        \"wait_small_cushion end\",\n        \"wait_small_cushion end\",\n        \"wait_big_cushion end\",\n    ]\n\n\n################################################################\n\n\nasync def test_assert_checkpoints(recwarn: pytest.WarningsRecorder) -> None:\n    with assert_checkpoints():\n        await _core.checkpoint()\n\n    with pytest.raises(AssertionError):\n        with assert_checkpoints():\n            1 + 1  # noqa: B018  # \"useless expression\"\n\n    # partial yield cases\n    # if you have a schedule point but not a cancel point, or vice-versa, then\n    # that's not a checkpoint.\n    for partial_yield in [\n        _core.checkpoint_if_cancelled,\n        _core.cancel_shielded_checkpoint,\n    ]:\n        print(partial_yield)\n        with pytest.raises(AssertionError):\n            with assert_checkpoints():\n                await partial_yield()\n\n    # But both together count as a checkpoint\n    with assert_checkpoints():\n        await _core.checkpoint_if_cancelled()\n        await _core.cancel_shielded_checkpoint()\n\n\nasync def test_assert_no_checkpoints(recwarn: pytest.WarningsRecorder) -> None:\n    with assert_no_checkpoints():\n        1 + 1  # noqa: B018  # \"useless expression\"\n\n    with pytest.raises(AssertionError):\n        with assert_no_checkpoints():\n            await _core.checkpoint()\n\n    # partial yield cases\n    # if you have a schedule point but not a cancel point, or vice-versa, then\n    # that doesn't make *either* version of assert_{no_,}yields happy.\n    for partial_yield in [\n        _core.checkpoint_if_cancelled,\n        _core.cancel_shielded_checkpoint,\n    ]:\n        print(partial_yield)\n        with pytest.raises(AssertionError):\n            with assert_no_checkpoints():\n                await partial_yield()\n\n    # And both together also count as a checkpoint\n    with pytest.raises(AssertionError):\n        with assert_no_checkpoints():\n            await _core.checkpoint_if_cancelled()\n            await _core.cancel_shielded_checkpoint()\n\n\n################################################################\n\n\nasync def test_Sequencer() -> None:\n    record = []\n\n    def t(val: object) -> None:\n        print(val)\n        record.append(val)\n\n    async def f1(seq: Sequencer) -> None:\n        async with seq(1):\n            t((\"f1\", 1))\n        async with seq(3):\n            t((\"f1\", 3))\n        async with seq(4):\n            t((\"f1\", 4))\n\n    async def f2(seq: Sequencer) -> None:\n        async with seq(0):\n            t((\"f2\", 0))\n        async with seq(2):\n            t((\"f2\", 2))\n\n    seq = Sequencer()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(f1, seq)\n        nursery.start_soon(f2, seq)\n        async with seq(5):\n            await wait_all_tasks_blocked()\n        assert record == [(\"f2\", 0), (\"f1\", 1), (\"f2\", 2), (\"f1\", 3), (\"f1\", 4)]\n\n    seq = Sequencer()\n    # Catches us if we try to reuse a sequence point:\n    async with seq(0):\n        pass\n    with pytest.raises(RuntimeError):\n        async with seq(0):\n            pass  # pragma: no cover\n\n\nasync def test_Sequencer_cancel() -> None:\n    # Killing a blocked task makes everything blow up\n    record = []\n    seq = Sequencer()\n\n    async def child(i: int) -> None:\n        with _core.CancelScope() as scope:\n            if i == 1:\n                scope.cancel()\n            try:\n                async with seq(i):\n                    pass  # pragma: no cover\n            except RuntimeError:\n                record.append(f\"seq({i}) RuntimeError\")\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child, 1)\n        nursery.start_soon(child, 2)\n        async with seq(0):\n            pass  # pragma: no cover\n\n    assert record == [\"seq(1) RuntimeError\", \"seq(2) RuntimeError\"]\n\n    # Late arrivals also get errors\n    with pytest.raises(RuntimeError):\n        async with seq(3):\n            pass  # pragma: no cover\n\n\n################################################################\ndef test__assert_raises() -> None:\n    with pytest.raises(AssertionError):\n        with _assert_raises(RuntimeError):\n            1 + 1  # noqa: B018  # \"useless expression\"\n\n    with pytest.raises(TypeError):\n        with _assert_raises(RuntimeError):\n            \"foo\" + 1  # type: ignore[operator] # noqa: B018  # \"useless expression\"\n\n    with _assert_raises(RuntimeError):\n        raise RuntimeError\n\n\n# This is a private implementation detail, but it's complex enough to be worth\n# testing directly\nasync def test__UnboundeByteQueue() -> None:\n    ubq = _UnboundedByteQueue()\n\n    ubq.put(b\"123\")\n    ubq.put(b\"456\")\n    assert ubq.get_nowait(1) == b\"1\"\n    assert ubq.get_nowait(10) == b\"23456\"\n    ubq.put(b\"789\")\n    assert ubq.get_nowait() == b\"789\"\n\n    with pytest.raises(_core.WouldBlock):\n        ubq.get_nowait(10)\n    with pytest.raises(_core.WouldBlock):\n        ubq.get_nowait()\n\n    with pytest.raises(TypeError):\n        ubq.put(\"string\")  # type: ignore[arg-type]\n\n    ubq.put(b\"abc\")\n    with assert_checkpoints():\n        assert await ubq.get(10) == b\"abc\"\n    ubq.put(b\"def\")\n    ubq.put(b\"ghi\")\n    with assert_checkpoints():\n        assert await ubq.get(1) == b\"d\"\n    with assert_checkpoints():\n        assert await ubq.get() == b\"efghi\"\n\n    async def putter(data: bytes) -> None:\n        await wait_all_tasks_blocked()\n        ubq.put(data)\n\n    async def getter(expect: bytes) -> None:\n        with assert_checkpoints():\n            assert await ubq.get() == expect\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(getter, b\"xyz\")\n        nursery.start_soon(putter, b\"xyz\")\n\n    # Two gets at the same time -> BusyResourceError\n    with pytest.RaisesGroup(_core.BusyResourceError):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(getter, b\"asdf\")\n            nursery.start_soon(getter, b\"asdf\")\n\n    # Closing\n\n    ubq.close()\n    with pytest.raises(_core.ClosedResourceError):\n        ubq.put(b\"---\")\n\n    assert ubq.get_nowait(10) == b\"\"\n    assert ubq.get_nowait() == b\"\"\n    assert await ubq.get(10) == b\"\"\n    assert await ubq.get() == b\"\"\n\n    # close is idempotent\n    ubq.close()\n\n    # close wakes up blocked getters\n    ubq2 = _UnboundedByteQueue()\n\n    async def closer() -> None:\n        await wait_all_tasks_blocked()\n        ubq2.close()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(getter, b\"\")\n        nursery.start_soon(closer)\n\n\nasync def test_MemorySendStream() -> None:\n    mss = MemorySendStream()\n\n    async def do_send_all(data: bytes) -> None:\n        with assert_checkpoints():\n            await mss.send_all(data)\n\n    await do_send_all(b\"123\")\n    assert mss.get_data_nowait(1) == b\"1\"\n    assert mss.get_data_nowait() == b\"23\"\n\n    with assert_checkpoints():\n        await mss.wait_send_all_might_not_block()\n\n    with pytest.raises(_core.WouldBlock):\n        mss.get_data_nowait()\n    with pytest.raises(_core.WouldBlock):\n        mss.get_data_nowait(10)\n\n    await do_send_all(b\"456\")\n    with assert_checkpoints():\n        assert await mss.get_data() == b\"456\"\n\n    # Call send_all twice at once; one should get BusyResourceError and one\n    # should succeed. But we can't let the error propagate, because it might\n    # cause the other to be cancelled before it can finish doing its thing,\n    # and we don't know which one will get the error.\n    resource_busy_count = 0\n\n    async def do_send_all_count_resourcebusy() -> None:\n        nonlocal resource_busy_count\n        try:\n            await do_send_all(b\"xxx\")\n        except _core.BusyResourceError:\n            resource_busy_count += 1\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(do_send_all_count_resourcebusy)\n        nursery.start_soon(do_send_all_count_resourcebusy)\n\n    assert resource_busy_count == 1\n\n    with assert_checkpoints():\n        await mss.aclose()\n\n    assert await mss.get_data() == b\"xxx\"\n    assert await mss.get_data() == b\"\"\n    with pytest.raises(_core.ClosedResourceError):\n        await do_send_all(b\"---\")\n\n    # hooks\n\n    assert mss.send_all_hook is None\n    assert mss.wait_send_all_might_not_block_hook is None\n    assert mss.close_hook is None\n\n    record = []\n\n    async def send_all_hook() -> None:\n        # hook runs after send_all does its work (can pull data out)\n        assert mss2.get_data_nowait() == b\"abc\"\n        record.append(\"send_all_hook\")\n\n    async def wait_send_all_might_not_block_hook() -> None:\n        record.append(\"wait_send_all_might_not_block_hook\")\n\n    def close_hook() -> None:\n        record.append(\"close_hook\")\n\n    mss2 = MemorySendStream(\n        send_all_hook,\n        wait_send_all_might_not_block_hook,\n        close_hook,\n    )\n\n    assert mss2.send_all_hook is send_all_hook\n    assert mss2.wait_send_all_might_not_block_hook is wait_send_all_might_not_block_hook\n    assert mss2.close_hook is close_hook\n\n    await mss2.send_all(b\"abc\")\n    await mss2.wait_send_all_might_not_block()\n    await aclose_forcefully(mss2)\n    mss2.close()\n\n    assert record == [\n        \"send_all_hook\",\n        \"wait_send_all_might_not_block_hook\",\n        \"close_hook\",\n        \"close_hook\",\n    ]\n\n\nasync def test_MemoryReceiveStream() -> None:\n    mrs = MemoryReceiveStream()\n\n    async def do_receive_some(max_bytes: int | None) -> bytes:\n        with assert_checkpoints():\n            return await mrs.receive_some(max_bytes)\n\n    mrs.put_data(b\"abc\")\n    assert await do_receive_some(1) == b\"a\"\n    assert await do_receive_some(10) == b\"bc\"\n    mrs.put_data(b\"abc\")\n    assert await do_receive_some(None) == b\"abc\"\n\n    with pytest.RaisesGroup(_core.BusyResourceError):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_receive_some, 10)\n            nursery.start_soon(do_receive_some, 10)\n\n    assert mrs.receive_some_hook is None\n\n    mrs.put_data(b\"def\")\n    mrs.put_eof()\n    mrs.put_eof()\n\n    assert await do_receive_some(10) == b\"def\"\n    assert await do_receive_some(10) == b\"\"\n    assert await do_receive_some(10) == b\"\"\n\n    with pytest.raises(_core.ClosedResourceError):\n        mrs.put_data(b\"---\")\n\n    async def receive_some_hook() -> None:\n        mrs2.put_data(b\"xxx\")\n\n    record = []\n\n    def close_hook() -> None:\n        record.append(\"closed\")\n\n    mrs2 = MemoryReceiveStream(receive_some_hook, close_hook)\n    assert mrs2.receive_some_hook is receive_some_hook\n    assert mrs2.close_hook is close_hook\n\n    mrs2.put_data(b\"yyy\")\n    assert await mrs2.receive_some(10) == b\"yyyxxx\"\n    assert await mrs2.receive_some(10) == b\"xxx\"\n    assert await mrs2.receive_some(10) == b\"xxx\"\n\n    mrs2.put_data(b\"zzz\")\n    mrs2.receive_some_hook = None\n    assert await mrs2.receive_some(10) == b\"zzz\"\n\n    mrs2.put_data(b\"lost on close\")\n    with assert_checkpoints():\n        await mrs2.aclose()\n    assert record == [\"closed\"]\n\n    with pytest.raises(_core.ClosedResourceError):\n        await mrs2.receive_some(10)\n\n\nasync def test_MemoryRecvStream_closing() -> None:\n    mrs = MemoryReceiveStream()\n    # close with no pending data\n    mrs.close()\n    with pytest.raises(_core.ClosedResourceError):\n        assert await mrs.receive_some(10) == b\"\"\n    # repeated closes ok\n    mrs.close()\n    # put_data now fails\n    with pytest.raises(_core.ClosedResourceError):\n        mrs.put_data(b\"123\")\n\n    mrs2 = MemoryReceiveStream()\n    # close with pending data\n    mrs2.put_data(b\"xyz\")\n    mrs2.close()\n    with pytest.raises(_core.ClosedResourceError):\n        await mrs2.receive_some(10)\n\n\nasync def test_memory_stream_pump() -> None:\n    mss = MemorySendStream()\n    mrs = MemoryReceiveStream()\n\n    # no-op if no data present\n    memory_stream_pump(mss, mrs)\n\n    await mss.send_all(b\"123\")\n    memory_stream_pump(mss, mrs)\n    assert await mrs.receive_some(10) == b\"123\"\n\n    await mss.send_all(b\"456\")\n    assert memory_stream_pump(mss, mrs, max_bytes=1)\n    assert await mrs.receive_some(10) == b\"4\"\n    assert memory_stream_pump(mss, mrs, max_bytes=1)\n    assert memory_stream_pump(mss, mrs, max_bytes=1)\n    assert not memory_stream_pump(mss, mrs, max_bytes=1)\n    assert await mrs.receive_some(10) == b\"56\"\n\n    mss.close()\n    memory_stream_pump(mss, mrs)\n    assert await mrs.receive_some(10) == b\"\"\n\n\nasync def test_memory_stream_one_way_pair() -> None:\n    s, r = memory_stream_one_way_pair()\n    assert s.send_all_hook is not None\n    assert s.wait_send_all_might_not_block_hook is None\n    assert s.close_hook is not None\n    assert r.receive_some_hook is None\n    await s.send_all(b\"123\")\n    assert await r.receive_some(10) == b\"123\"\n\n    async def receiver(expected: bytes) -> None:\n        assert await r.receive_some(10) == expected\n\n    # This fails if we pump on r.receive_some_hook; we need to pump on s.send_all_hook\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(receiver, b\"abc\")\n        await wait_all_tasks_blocked()\n        await s.send_all(b\"abc\")\n\n    # And this fails if we don't pump from close_hook\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(receiver, b\"\")\n        await wait_all_tasks_blocked()\n        await s.aclose()\n\n    s, r = memory_stream_one_way_pair()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(receiver, b\"\")\n        await wait_all_tasks_blocked()\n        s.close()\n\n    s, r = memory_stream_one_way_pair()\n\n    old = s.send_all_hook\n    s.send_all_hook = None\n    await s.send_all(b\"456\")\n\n    async def cancel_after_idle(nursery: Nursery) -> None:\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n\n    async def check_for_cancel() -> None:\n        with pytest.raises(_core.Cancelled):\n            # This should block forever... or until cancelled. Even though we\n            # sent some data on the send stream.\n            await r.receive_some(10)\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(cancel_after_idle, nursery)\n        nursery.start_soon(check_for_cancel)\n\n    s.send_all_hook = old\n    await s.send_all(b\"789\")\n    assert await r.receive_some(10) == b\"456789\"\n\n\nasync def test_memory_stream_pair() -> None:\n    a, b = memory_stream_pair()\n    await a.send_all(b\"123\")\n    await b.send_all(b\"abc\")\n    assert await b.receive_some(10) == b\"123\"\n    assert await a.receive_some(10) == b\"abc\"\n\n    await a.send_eof()\n    assert await b.receive_some(10) == b\"\"\n\n    async def sender() -> None:\n        await wait_all_tasks_blocked()\n        await b.send_all(b\"xyz\")\n\n    async def receiver() -> None:\n        assert await a.receive_some(10) == b\"xyz\"\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(receiver)\n        nursery.start_soon(sender)\n\n\nasync def test_memory_streams_with_generic_tests() -> None:\n    async def one_way_stream_maker() -> tuple[MemorySendStream, MemoryReceiveStream]:\n        return memory_stream_one_way_pair()\n\n    await check_one_way_stream(one_way_stream_maker, None)\n\n    async def half_closeable_stream_maker() -> tuple[\n        StapledStream[MemorySendStream, MemoryReceiveStream],\n        StapledStream[MemorySendStream, MemoryReceiveStream],\n    ]:\n        return memory_stream_pair()\n\n    await check_half_closeable_stream(half_closeable_stream_maker, None)\n\n\nasync def test_lockstep_streams_with_generic_tests() -> None:\n    async def one_way_stream_maker() -> tuple[SendStream, ReceiveStream]:\n        return lockstep_stream_one_way_pair()\n\n    await check_one_way_stream(one_way_stream_maker, one_way_stream_maker)\n\n    async def two_way_stream_maker() -> tuple[\n        StapledStream[SendStream, ReceiveStream],\n        StapledStream[SendStream, ReceiveStream],\n    ]:\n        return lockstep_stream_pair()\n\n    await check_two_way_stream(two_way_stream_maker, two_way_stream_maker)\n\n\nasync def test_open_stream_to_socket_listener() -> None:\n    async def check(listener: SocketListener) -> None:\n        async with listener:\n            client_stream = await open_stream_to_socket_listener(listener)\n            async with client_stream:\n                server_stream = await listener.accept()\n                async with server_stream:\n                    await client_stream.send_all(b\"x\")\n                    assert await server_stream.receive_some(1) == b\"x\"\n\n    # Listener bound to localhost\n    sock = tsocket.socket()\n    await sock.bind((\"127.0.0.1\", 0))\n    sock.listen(10)\n    await check(SocketListener(sock))\n\n    # Listener bound to IPv4 wildcard (needs special handling)\n    sock = tsocket.socket()\n    await sock.bind((\"0.0.0.0\", 0))\n    sock.listen(10)\n    await check(SocketListener(sock))\n\n    # true on all CI systems\n    if can_bind_ipv6:  # pragma: no branch\n        # Listener bound to IPv6 wildcard (needs special handling)\n        sock = tsocket.socket(family=tsocket.AF_INET6)\n        await sock.bind((\"::\", 0))\n        sock.listen(10)\n        await check(SocketListener(sock))\n\n    if hasattr(tsocket, \"AF_UNIX\"):\n        # Listener bound to Unix-domain socket\n        sock = tsocket.socket(family=tsocket.AF_UNIX)\n        # can't use pytest's tmpdir; if we try then macOS says \"OSError:\n        # AF_UNIX path too long\"\n        with tempfile.TemporaryDirectory() as tmpdir:\n            path = f\"{tmpdir}/sock\"\n            await sock.bind(path)\n            sock.listen(10)\n            await check(SocketListener(sock))\n\n\ndef test_trio_test() -> None:\n    async def busy_kitchen(\n        *,\n        mock_clock: object,\n        autojump_clock: object,\n    ) -> None: ...  # pragma: no cover\n\n    with pytest.raises(ValueError, match=r\"^too many clocks spoil the broth!$\"):\n        trio_test(busy_kitchen)(\n            mock_clock=MockClock(),\n            autojump_clock=MockClock(autojump_threshold=0),\n        )\n"
  },
  {
    "path": "src/trio/_tests/test_testing_raisesgroup.py",
    "content": "from __future__ import annotations\n\nimport re\nimport sys\nfrom types import TracebackType\n\nimport pytest\n\nimport trio\nfrom trio.testing import _Matcher as Matcher, _RaisesGroup as RaisesGroup\nfrom trio.testing._raises_group import repr_callable\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup, ExceptionGroup\n\n\ndef wrap_escape(s: str) -> str:\n    return \"^\" + re.escape(s) + \"$\"\n\n\ndef fails_raises_group(\n    msg: str, add_prefix: bool = True\n) -> pytest.RaisesExc[AssertionError]:\n    assert (\n        msg[-1] != \"\\n\"\n    ), \"developer error, expected string should not end with newline\"\n    prefix = \"Raised exception group did not match: \" if add_prefix else \"\"\n    return pytest.raises(AssertionError, match=wrap_escape(prefix + msg))\n\n\ndef test_raises_group() -> None:\n    with pytest.raises(\n        ValueError,\n        match=wrap_escape(\n            f'Invalid argument \"{TypeError()!r}\" must be exception type, Matcher, or RaisesGroup.',\n        ),\n    ):\n        RaisesGroup(TypeError())  # type: ignore[call-overload]\n    with RaisesGroup(ValueError):\n        raise ExceptionGroup(\"foo\", (ValueError(),))\n\n    with (\n        fails_raises_group(\"'SyntaxError' is not of type 'ValueError'\"),\n        RaisesGroup(ValueError),\n    ):\n        raise ExceptionGroup(\"foo\", (SyntaxError(),))\n\n    # multiple exceptions\n    with RaisesGroup(ValueError, SyntaxError):\n        raise ExceptionGroup(\"foo\", (ValueError(), SyntaxError()))\n\n    # order doesn't matter\n    with RaisesGroup(SyntaxError, ValueError):\n        raise ExceptionGroup(\"foo\", (ValueError(), SyntaxError()))\n\n    # nested exceptions\n    with RaisesGroup(RaisesGroup(ValueError)):\n        raise ExceptionGroup(\"foo\", (ExceptionGroup(\"bar\", (ValueError(),)),))\n\n    with RaisesGroup(\n        SyntaxError,\n        RaisesGroup(ValueError),\n        RaisesGroup(RuntimeError),\n    ):\n        raise ExceptionGroup(\n            \"foo\",\n            (\n                SyntaxError(),\n                ExceptionGroup(\"bar\", (ValueError(),)),\n                ExceptionGroup(\"\", (RuntimeError(),)),\n            ),\n        )\n\n\ndef test_incorrect_number_exceptions() -> None:\n    # We previously gave an error saying the number of exceptions was wrong,\n    # but we now instead indicate excess/missing exceptions\n    with (\n        fails_raises_group(\n            \"1 matched exception. Unexpected exception(s): [RuntimeError()]\"\n        ),\n        RaisesGroup(ValueError),\n    ):\n        raise ExceptionGroup(\"\", (RuntimeError(), ValueError()))\n\n    # will error if there's missing exceptions\n    with (\n        fails_raises_group(\n            \"1 matched exception. Too few exceptions raised, found no match for: ['SyntaxError']\"\n        ),\n        RaisesGroup(ValueError, SyntaxError),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(),))\n\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"1 matched exception. \\n\"\n            \"Too few exceptions raised!\\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'ValueError'\\n\"\n            \"    It matches ValueError() which was paired with 'ValueError'\"\n        ),\n        RaisesGroup(ValueError, ValueError),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(),))\n\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"1 matched exception. \\n\"\n            \"Unexpected exception(s)!\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ValueError():\\n\"\n            \"    It matches 'ValueError' which was paired with ValueError()\"\n        ),\n        RaisesGroup(ValueError),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(), ValueError()))\n\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"1 matched exception. \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'ValueError'\\n\"\n            \"    It matches ValueError() which was paired with 'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  SyntaxError():\\n\"\n            \"    'SyntaxError' is not of type 'ValueError'\"\n        ),\n        RaisesGroup(ValueError, ValueError),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(), SyntaxError()])\n\n\ndef test_flatten_subgroups() -> None:\n    # loose semantics, as with expect*\n    with RaisesGroup(ValueError, flatten_subgroups=True):\n        raise ExceptionGroup(\"\", (ExceptionGroup(\"\", (ValueError(),)),))\n\n    with RaisesGroup(ValueError, TypeError, flatten_subgroups=True):\n        raise ExceptionGroup(\"\", (ExceptionGroup(\"\", (ValueError(), TypeError())),))\n    with RaisesGroup(ValueError, TypeError, flatten_subgroups=True):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError()]), TypeError()])\n\n    # mixed loose is possible if you want it to be at least N deep\n    with RaisesGroup(RaisesGroup(ValueError, flatten_subgroups=True)):\n        raise ExceptionGroup(\"\", (ExceptionGroup(\"\", (ValueError(),)),))\n    with RaisesGroup(RaisesGroup(ValueError, flatten_subgroups=True)):\n        raise ExceptionGroup(\n            \"\",\n            (ExceptionGroup(\"\", (ExceptionGroup(\"\", (ValueError(),)),)),),\n        )\n\n    # but not the other way around\n    with pytest.raises(\n        ValueError,\n        match=r\"^You cannot specify a nested structure inside a RaisesGroup with\",\n    ):\n        RaisesGroup(RaisesGroup(ValueError), flatten_subgroups=True)  # type: ignore[call-overload]\n\n    # flatten_subgroups is not sufficient to catch fully unwrapped\n    with (\n        fails_raises_group(\n            \"'ValueError' is not an exception group, but would match with `allow_unwrapped=True`\"\n        ),\n        RaisesGroup(ValueError, flatten_subgroups=True),\n    ):\n        raise ValueError\n    with (\n        fails_raises_group(\n            \"RaisesGroup(ValueError, flatten_subgroups=True): 'ValueError' is not an exception group, but would match with `allow_unwrapped=True`\"\n        ),\n        RaisesGroup(RaisesGroup(ValueError, flatten_subgroups=True)),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(),))\n\n    # helpful suggestion if flatten_subgroups would make it pass\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'ValueError'\\n\"\n            \"  'TypeError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [ValueError(), TypeError()]):\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'TypeError'\\n\"\n            \"Did you mean to use `flatten_subgroups=True`?\",\n            add_prefix=False,\n        ),\n        RaisesGroup(ValueError, TypeError),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError(), TypeError()])])\n    # but doesn't consider check (otherwise we'd break typing guarantees)\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'ValueError'\\n\"\n            \"  'TypeError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [ValueError(), TypeError()]):\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'TypeError'\\n\"\n            \"Did you mean to use `flatten_subgroups=True`?\",\n            add_prefix=False,\n        ),\n        RaisesGroup(\n            ValueError,\n            TypeError,\n            check=lambda eg: len(eg.exceptions) == 1,\n        ),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError(), TypeError()])])\n    # correct number of exceptions, and flatten_subgroups would make it pass\n    # This now doesn't print a repr of the caught exception at all, but that can be found in the traceback\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"  Did you mean to use `flatten_subgroups=True`?\",\n            add_prefix=False,\n        ),\n        RaisesGroup(ValueError),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError()])])\n    # correct number of exceptions, but flatten_subgroups wouldn't help, so we don't suggest it\n    with (\n        fails_raises_group(\"Unexpected nested 'ExceptionGroup', expected 'ValueError'\"),\n        RaisesGroup(ValueError),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [TypeError()])])\n\n    # flatten_subgroups can be suggested if nested. This will implicitly ask the user to\n    # do `RaisesGroup(RaisesGroup(ValueError, flatten_subgroups=True))` which is unlikely\n    # to be what they actually want - but I don't think it's worth trying to special-case\n    with (\n        fails_raises_group(\n            \"RaisesGroup(ValueError): Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"  Did you mean to use `flatten_subgroups=True`?\",\n        ),\n        RaisesGroup(RaisesGroup(ValueError)),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError()])])],\n        )\n\n    # Don't mention \"unexpected nested\" if expecting an ExceptionGroup.\n    # Although it should perhaps be an error to specify `RaisesGroup(ExceptionGroup)` in\n    # favor of doing `RaisesGroup(RaisesGroup(...))`.\n    with (\n        fails_raises_group(\"'BaseExceptionGroup' is not of type 'ExceptionGroup'\"),\n        RaisesGroup(ExceptionGroup),\n    ):\n        raise BaseExceptionGroup(\"\", [BaseExceptionGroup(\"\", [KeyboardInterrupt()])])\n\n\ndef test_catch_unwrapped_exceptions() -> None:\n    # Catches lone exceptions with strict=False\n    # just as except* would\n    with RaisesGroup(ValueError, allow_unwrapped=True):\n        raise ValueError\n\n    # expecting multiple unwrapped exceptions is not possible\n    with pytest.raises(\n        ValueError,\n        match=r\"^You cannot specify multiple exceptions with\",\n    ):\n        RaisesGroup(SyntaxError, ValueError, allow_unwrapped=True)  # type: ignore[call-overload]\n    # if users want one of several exception types they need to use a Matcher\n    # (which the error message suggests)\n    with RaisesGroup(\n        Matcher(check=lambda e: isinstance(e, (SyntaxError, ValueError))),\n        allow_unwrapped=True,\n    ):\n        raise ValueError\n\n    # Unwrapped nested `RaisesGroup` is likely a user error, so we raise an error.\n    with pytest.raises(ValueError, match=\"has no effect when expecting\"):\n        RaisesGroup(RaisesGroup(ValueError), allow_unwrapped=True)  # type: ignore[call-overload]\n\n    # But it *can* be used to check for nesting level +- 1 if they move it to\n    # the nested RaisesGroup. Users should probably use `Matcher`s instead though.\n    with RaisesGroup(RaisesGroup(ValueError, allow_unwrapped=True)):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError()])])\n    with RaisesGroup(RaisesGroup(ValueError, allow_unwrapped=True)):\n        raise ExceptionGroup(\"\", [ValueError()])\n\n    # with allow_unwrapped=False (default) it will not be caught\n    with (\n        fails_raises_group(\n            \"'ValueError' is not an exception group, but would match with `allow_unwrapped=True`\"\n        ),\n        RaisesGroup(ValueError),\n    ):\n        raise ValueError(\"value error text\")\n\n    # allow_unwrapped on its own won't match against nested groups\n    with (\n        fails_raises_group(\n            \"Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"  Did you mean to use `flatten_subgroups=True`?\",\n        ),\n        RaisesGroup(ValueError, allow_unwrapped=True),\n    ):\n        raise ExceptionGroup(\"foo\", [ExceptionGroup(\"bar\", [ValueError()])])\n\n    # you need both allow_unwrapped and flatten_subgroups to fully emulate except*\n    with RaisesGroup(ValueError, allow_unwrapped=True, flatten_subgroups=True):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError()])])\n\n    # code coverage\n    with (\n        fails_raises_group(\n            \"Raised exception (group) did not match: 'TypeError' is not of type 'ValueError'\",\n            add_prefix=False,\n        ),\n        RaisesGroup(ValueError, allow_unwrapped=True),\n    ):\n        raise TypeError(\"this text doesn't show up in the error message\")\n    with (\n        fails_raises_group(\n            \"Raised exception (group) did not match: Matcher(ValueError): 'TypeError' is not of type 'ValueError'\",\n            add_prefix=False,\n        ),\n        RaisesGroup(Matcher(ValueError), allow_unwrapped=True),\n    ):\n        raise TypeError\n\n    # check we don't suggest unwrapping with nested RaisesGroup\n    with (\n        fails_raises_group(\"'ValueError' is not an exception group\"),\n        RaisesGroup(RaisesGroup(ValueError)),\n    ):\n        raise ValueError\n\n\ndef test_match() -> None:\n    # supports match string\n    with RaisesGroup(ValueError, match=\"bar\"):\n        raise ExceptionGroup(\"bar\", (ValueError(),))\n\n    # now also works with ^$\n    with RaisesGroup(ValueError, match=\"^bar$\"):\n        raise ExceptionGroup(\"bar\", (ValueError(),))\n\n    # it also includes notes\n    with RaisesGroup(ValueError, match=\"my note\"):\n        e = ExceptionGroup(\"bar\", (ValueError(),))\n        e.add_note(\"my note\")\n        raise e\n\n    # and technically you can match it all with ^$\n    # but you're probably better off using a Matcher at that point\n    with RaisesGroup(ValueError, match=\"^bar\\nmy note$\"):\n        e = ExceptionGroup(\"bar\", (ValueError(),))\n        e.add_note(\"my note\")\n        raise e\n\n    with (\n        fails_raises_group(\n            \"Regex pattern 'foo' did not match 'bar' of 'ExceptionGroup'\"\n        ),\n        RaisesGroup(ValueError, match=\"foo\"),\n    ):\n        raise ExceptionGroup(\"bar\", (ValueError(),))\n\n    # Suggest a fix for easy pitfall of adding match to the RaisesGroup instead of\n    # using a Matcher.\n    # This requires a single expected & raised exception, the expected is a type,\n    # and `isinstance(raised, expected_type)`.\n    with (\n        fails_raises_group(\n            \"Regex pattern 'foo' did not match 'bar' of 'ExceptionGroup', but matched the expected 'ValueError'. You might want RaisesGroup(Matcher(ValueError, match='foo'))\"\n        ),\n        RaisesGroup(ValueError, match=\"foo\"),\n    ):\n        raise ExceptionGroup(\"bar\", [ValueError(\"foo\")])\n\n\ndef test_check() -> None:\n    exc = ExceptionGroup(\"\", (ValueError(),))\n\n    def is_exc(e: ExceptionGroup[ValueError]) -> bool:\n        return e is exc\n\n    is_exc_repr = repr_callable(is_exc)\n    with RaisesGroup(ValueError, check=is_exc):\n        raise exc\n\n    with (\n        fails_raises_group(f\"check {is_exc_repr} did not return True\"),\n        RaisesGroup(ValueError, check=is_exc),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(),))\n\n\ndef test_unwrapped_match_check() -> None:\n    def my_check(e: object) -> bool:  # pragma: no cover\n        return True\n\n    msg = (\n        \"`allow_unwrapped=True` bypasses the `match` and `check` parameters\"\n        \" if the exception is unwrapped. If you intended to match/check the\"\n        \" exception you should use a `Matcher` object. If you want to match/check\"\n        \" the exceptiongroup when the exception *is* wrapped you need to\"\n        \" do e.g. `if isinstance(exc.value, ExceptionGroup):\"\n        \" assert RaisesGroup(...).matches(exc.value)` afterwards.\"\n    )\n    with pytest.raises(ValueError, match=re.escape(msg)):\n        RaisesGroup(ValueError, allow_unwrapped=True, match=\"foo\")  # type: ignore[call-overload]\n    with pytest.raises(ValueError, match=re.escape(msg)):\n        RaisesGroup(ValueError, allow_unwrapped=True, check=my_check)  # type: ignore[call-overload]\n\n    # Users should instead use a Matcher\n    rg = RaisesGroup(Matcher(ValueError, match=\"^foo$\"), allow_unwrapped=True)\n    with rg:\n        raise ValueError(\"foo\")\n    with rg:\n        raise ExceptionGroup(\"\", [ValueError(\"foo\")])\n\n    # or if they wanted to match/check the group, do a conditional `.matches()`\n    with RaisesGroup(ValueError, allow_unwrapped=True) as exc:\n        raise ExceptionGroup(\"bar\", [ValueError(\"foo\")])\n    if isinstance(exc.value, ExceptionGroup):  # pragma: no branch\n        assert RaisesGroup(ValueError, match=\"bar\").matches(exc.value)\n\n\ndef test_RaisesGroup_matches() -> None:\n    rg = RaisesGroup(ValueError)\n    assert not rg.matches(None)\n    assert not rg.matches(ValueError())\n    assert rg.matches(ExceptionGroup(\"\", (ValueError(),)))\n\n\ndef test_message() -> None:\n    def check_message(\n        message: str,\n        body: RaisesGroup[BaseException],\n    ) -> None:\n        with (\n            pytest.raises(\n                AssertionError,\n                match=f\"^DID NOT RAISE any exception, expected {re.escape(message)}$\",\n            ),\n            body,\n        ):\n            ...\n\n    # basic\n    check_message(\"ExceptionGroup(ValueError)\", RaisesGroup(ValueError))\n    # multiple exceptions\n    check_message(\n        \"ExceptionGroup(ValueError, ValueError)\",\n        RaisesGroup(ValueError, ValueError),\n    )\n    # nested\n    check_message(\n        \"ExceptionGroup(ExceptionGroup(ValueError))\",\n        RaisesGroup(RaisesGroup(ValueError)),\n    )\n\n    # Matcher\n    check_message(\n        \"ExceptionGroup(Matcher(ValueError, match='my_str'))\",\n        RaisesGroup(Matcher(ValueError, \"my_str\")),\n    )\n    check_message(\n        \"ExceptionGroup(Matcher(match='my_str'))\",\n        RaisesGroup(Matcher(match=\"my_str\")),\n    )\n\n    # BaseExceptionGroup\n    check_message(\n        \"BaseExceptionGroup(KeyboardInterrupt)\",\n        RaisesGroup(KeyboardInterrupt),\n    )\n    # BaseExceptionGroup with type inside Matcher\n    check_message(\n        \"BaseExceptionGroup(Matcher(KeyboardInterrupt))\",\n        RaisesGroup(Matcher(KeyboardInterrupt)),\n    )\n    # Base-ness transfers to parent containers\n    check_message(\n        \"BaseExceptionGroup(BaseExceptionGroup(KeyboardInterrupt))\",\n        RaisesGroup(RaisesGroup(KeyboardInterrupt)),\n    )\n    # but not to child containers\n    check_message(\n        \"BaseExceptionGroup(BaseExceptionGroup(KeyboardInterrupt), ExceptionGroup(ValueError))\",\n        RaisesGroup(RaisesGroup(KeyboardInterrupt), RaisesGroup(ValueError)),\n    )\n\n\ndef test_assert_message() -> None:\n    # the message does not need to list all parameters to RaisesGroup, nor all exceptions\n    # in the exception group, as those are both visible in the traceback.\n    # first fails to match\n    with (\n        fails_raises_group(\"'TypeError' is not of type 'ValueError'\"),\n        RaisesGroup(ValueError),\n    ):\n        raise ExceptionGroup(\"a\", [TypeError()])\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(ValueError)\\n\"\n            \"  RaisesGroup(ValueError, match='a')\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [RuntimeError()]):\\n\"\n            \"    RaisesGroup(ValueError): 'RuntimeError' is not of type 'ValueError'\\n\"\n            \"    RaisesGroup(ValueError, match='a'): Regex pattern 'a' did not match '' of 'ExceptionGroup'\\n\"\n            \"  RuntimeError():\\n\"\n            \"    RaisesGroup(ValueError): 'RuntimeError' is not an exception group\\n\"\n            \"    RaisesGroup(ValueError, match='a'): 'RuntimeError' is not an exception group\",\n            add_prefix=False,  # to see the full structure\n        ),\n        RaisesGroup(RaisesGroup(ValueError), RaisesGroup(ValueError, match=\"a\")),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [ExceptionGroup(\"\", [RuntimeError()]), RuntimeError()],\n        )\n\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"2 matched exceptions. \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(RuntimeError)\\n\"\n            \"  RaisesGroup(ValueError)\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  RuntimeError():\\n\"\n            # \"    'RuntimeError' is not of type 'ValueError'\\n\"\n            # \"    Matcher(TypeError): 'RuntimeError' is not of type 'TypeError'\\n\"\n            \"    RaisesGroup(RuntimeError): 'RuntimeError' is not an exception group, but would match with `allow_unwrapped=True`\\n\"\n            \"    RaisesGroup(ValueError): 'RuntimeError' is not an exception group\\n\"\n            \"  ValueError('bar'):\\n\"\n            \"    It matches 'ValueError' which was paired with ValueError('foo')\\n\"\n            \"    RaisesGroup(RuntimeError): 'ValueError' is not an exception group\\n\"\n            \"    RaisesGroup(ValueError): 'ValueError' is not an exception group, but would match with `allow_unwrapped=True`\",\n            add_prefix=False,  # to see the full structure\n        ),\n        RaisesGroup(\n            ValueError,\n            Matcher(TypeError),\n            RaisesGroup(RuntimeError),\n            RaisesGroup(ValueError),\n        ),\n    ):\n        raise ExceptionGroup(\n            \"a\",\n            [RuntimeError(), TypeError(), ValueError(\"foo\"), ValueError(\"bar\")],\n        )\n\n    with (\n        fails_raises_group(\n            \"1 matched exception. 'AssertionError' is not of type 'TypeError'\"\n        ),\n        RaisesGroup(ValueError, TypeError),\n    ):\n        raise ExceptionGroup(\"a\", [ValueError(), AssertionError()])\n\n    with (\n        fails_raises_group(\n            \"Matcher(ValueError): 'TypeError' is not of type 'ValueError'\"\n        ),\n        RaisesGroup(Matcher(ValueError)),\n    ):\n        raise ExceptionGroup(\"a\", [TypeError()])\n\n    # suggest escaping\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: Regex pattern 'h(ell)o' did not match 'h(ell)o' of 'ExceptionGroup'\\n\"\n            \"  Did you mean to `re.escape()` the regex?\",\n            add_prefix=False,  # to see the full structure\n        ),\n        RaisesGroup(ValueError, match=\"h(ell)o\"),\n    ):\n        raise ExceptionGroup(\"h(ell)o\", [ValueError()])\n    with (\n        fails_raises_group(\n            \"Matcher(match='h(ell)o'): Regex pattern 'h(ell)o' did not match 'h(ell)o'\\n\"\n            \"  Did you mean to `re.escape()` the regex?\",\n        ),\n        RaisesGroup(Matcher(match=\"h(ell)o\")),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(\"h(ell)o\")])\n\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'ValueError'\\n\"\n            \"  'ValueError'\\n\"\n            \"  'ValueError'\\n\"\n            \"  'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [ValueError(), TypeError()]):\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\",\n            add_prefix=False,  # to see the full structure\n        ),\n        RaisesGroup(ValueError, ValueError, ValueError, ValueError),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError(), TypeError()])])\n\n\ndef test_message_indent() -> None:\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(ValueError, ValueError)\\n\"\n            \"  'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [TypeError(), RuntimeError()]):\\n\"\n            \"    RaisesGroup(ValueError, ValueError): \\n\"\n            \"      The following expected exceptions did not find a match:\\n\"\n            \"        'ValueError'\\n\"\n            \"        'ValueError'\\n\"\n            \"      The following raised exceptions did not find a match\\n\"\n            \"        TypeError():\\n\"\n            \"          'TypeError' is not of type 'ValueError'\\n\"\n            \"          'TypeError' is not of type 'ValueError'\\n\"\n            \"        RuntimeError():\\n\"\n            \"          'RuntimeError' is not of type 'ValueError'\\n\"\n            \"          'RuntimeError' is not of type 'ValueError'\\n\"\n            # TODO: this line is not great, should maybe follow the same format as the other and say\n            # 'ValueError': Unexpected nested 'ExceptionGroup' (?)\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"  TypeError():\\n\"\n            \"    RaisesGroup(ValueError, ValueError): 'TypeError' is not an exception group\\n\"\n            \"    'TypeError' is not of type 'ValueError'\",\n            add_prefix=False,\n        ),\n        RaisesGroup(\n            RaisesGroup(ValueError, ValueError),\n            ValueError,\n        ),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [\n                ExceptionGroup(\"\", [TypeError(), RuntimeError()]),\n                TypeError(),\n            ],\n        )\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"RaisesGroup(ValueError, ValueError): \\n\"\n            \"  The following expected exceptions did not find a match:\\n\"\n            \"    'ValueError'\\n\"\n            \"    'ValueError'\\n\"\n            \"  The following raised exceptions did not find a match\\n\"\n            \"    TypeError():\\n\"\n            \"      'TypeError' is not of type 'ValueError'\\n\"\n            \"      'TypeError' is not of type 'ValueError'\\n\"\n            \"    RuntimeError():\\n\"\n            \"      'RuntimeError' is not of type 'ValueError'\\n\"\n            \"      'RuntimeError' is not of type 'ValueError'\",\n            add_prefix=False,\n        ),\n        RaisesGroup(\n            RaisesGroup(ValueError, ValueError),\n        ),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [\n                ExceptionGroup(\"\", [TypeError(), RuntimeError()]),\n            ],\n        )\n\n\ndef test_suggestion_on_nested_and_brief_error() -> None:\n    # Make sure \"Did you mean\" suggestion gets indented iff it follows a single-line error\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(ValueError)\\n\"\n            \"  'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [ExceptionGroup('', [ValueError()])]):\\n\"\n            \"    RaisesGroup(ValueError): Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"      Did you mean to use `flatten_subgroups=True`?\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\",\n        ),\n        RaisesGroup(RaisesGroup(ValueError), ValueError),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [ExceptionGroup(\"\", [ExceptionGroup(\"\", [ValueError()])])],\n        )\n    # if indented here it would look like another raised exception\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(ValueError, ValueError)\\n\"\n            \"  'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('', [ValueError(), ExceptionGroup('', [ValueError()])]):\\n\"\n            \"    RaisesGroup(ValueError, ValueError): \\n\"\n            \"      1 matched exception. \\n\"\n            \"      The following expected exceptions did not find a match:\\n\"\n            \"        'ValueError'\\n\"\n            \"          It matches ValueError() which was paired with 'ValueError'\\n\"\n            \"      The following raised exceptions did not find a match\\n\"\n            \"        ExceptionGroup('', [ValueError()]):\\n\"\n            \"          Unexpected nested 'ExceptionGroup', expected 'ValueError'\\n\"\n            \"      Did you mean to use `flatten_subgroups=True`?\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\"\n        ),\n        RaisesGroup(RaisesGroup(ValueError, ValueError), ValueError),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [ExceptionGroup(\"\", [ValueError(), ExceptionGroup(\"\", [ValueError()])])],\n        )\n\n    # re.escape always comes after single-line errors\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(Exception, match='^hello')\\n\"\n            \"  'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ExceptionGroup('^hello', [Exception()]):\\n\"\n            \"    RaisesGroup(Exception, match='^hello'): Regex pattern '^hello' did not match '^hello' of 'ExceptionGroup'\\n\"\n            \"      Did you mean to `re.escape()` the regex?\\n\"\n            \"    Unexpected nested 'ExceptionGroup', expected 'ValueError'\"\n        ),\n        RaisesGroup(RaisesGroup(Exception, match=\"^hello\"), ValueError),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"^hello\", [Exception()])])\n\n\ndef test_assert_message_nested() -> None:\n    # we only get one instance of aaaaaaaaaa... and bbbbbb..., but we do get multiple instances of ccccc... and dddddd..\n    # but I think this now only prints the full repr when that is necessary to disambiguate exceptions\n    with (\n        fails_raises_group(\n            \"Raised exception group did not match: \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  RaisesGroup(ValueError)\\n\"\n            \"  RaisesGroup(RaisesGroup(ValueError))\\n\"\n            \"  RaisesGroup(Matcher(TypeError, match='foo'))\\n\"\n            \"  RaisesGroup(TypeError, ValueError)\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  TypeError('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'):\\n\"\n            \"    RaisesGroup(ValueError): 'TypeError' is not an exception group\\n\"\n            \"    RaisesGroup(RaisesGroup(ValueError)): 'TypeError' is not an exception group\\n\"\n            \"    RaisesGroup(Matcher(TypeError, match='foo')): 'TypeError' is not an exception group\\n\"\n            \"    RaisesGroup(TypeError, ValueError): 'TypeError' is not an exception group\\n\"\n            \"  ExceptionGroup('Exceptions from Trio nursery', [TypeError('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')]):\\n\"\n            \"    RaisesGroup(ValueError): 'TypeError' is not of type 'ValueError'\\n\"\n            \"    RaisesGroup(RaisesGroup(ValueError)): RaisesGroup(ValueError): 'TypeError' is not an exception group\\n\"\n            \"    RaisesGroup(Matcher(TypeError, match='foo')): Matcher(TypeError, match='foo'): Regex pattern 'foo' did not match 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'\\n\"\n            \"    RaisesGroup(TypeError, ValueError): 1 matched exception. Too few exceptions raised, found no match for: ['ValueError']\\n\"\n            \"  ExceptionGroup('Exceptions from Trio nursery', [TypeError('cccccccccccccccccccccccccccccccccccccccc'), TypeError('dddddddddddddddddddddddddddddddddddddddd')]):\\n\"\n            \"    RaisesGroup(ValueError): \\n\"\n            \"      The following expected exceptions did not find a match:\\n\"\n            \"        'ValueError'\\n\"\n            \"      The following raised exceptions did not find a match\\n\"\n            \"        TypeError('cccccccccccccccccccccccccccccccccccccccc'):\\n\"\n            \"          'TypeError' is not of type 'ValueError'\\n\"\n            \"        TypeError('dddddddddddddddddddddddddddddddddddddddd'):\\n\"\n            \"          'TypeError' is not of type 'ValueError'\\n\"\n            \"    RaisesGroup(RaisesGroup(ValueError)): \\n\"\n            \"      The following expected exceptions did not find a match:\\n\"\n            \"        RaisesGroup(ValueError)\\n\"\n            \"      The following raised exceptions did not find a match\\n\"\n            \"        TypeError('cccccccccccccccccccccccccccccccccccccccc'):\\n\"\n            \"          RaisesGroup(ValueError): 'TypeError' is not an exception group\\n\"\n            \"        TypeError('dddddddddddddddddddddddddddddddddddddddd'):\\n\"\n            \"          RaisesGroup(ValueError): 'TypeError' is not an exception group\\n\"\n            \"    RaisesGroup(Matcher(TypeError, match='foo')): \\n\"\n            \"      The following expected exceptions did not find a match:\\n\"\n            \"        Matcher(TypeError, match='foo')\\n\"\n            \"      The following raised exceptions did not find a match\\n\"\n            \"        TypeError('cccccccccccccccccccccccccccccccccccccccc'):\\n\"\n            \"          Matcher(TypeError, match='foo'): Regex pattern 'foo' did not match 'cccccccccccccccccccccccccccccccccccccccc'\\n\"\n            \"        TypeError('dddddddddddddddddddddddddddddddddddddddd'):\\n\"\n            \"          Matcher(TypeError, match='foo'): Regex pattern 'foo' did not match 'dddddddddddddddddddddddddddddddddddddddd'\\n\"\n            \"    RaisesGroup(TypeError, ValueError): \\n\"\n            \"      1 matched exception. \\n\"\n            \"      The following expected exceptions did not find a match:\\n\"\n            \"        'ValueError'\\n\"\n            \"      The following raised exceptions did not find a match\\n\"\n            \"        TypeError('dddddddddddddddddddddddddddddddddddddddd'):\\n\"\n            \"          It matches 'TypeError' which was paired with TypeError('cccccccccccccccccccccccccccccccccccccccc')\\n\"\n            \"          'TypeError' is not of type 'ValueError'\",\n            add_prefix=False,  # to see the full structure\n        ),\n        RaisesGroup(\n            RaisesGroup(ValueError),\n            RaisesGroup(RaisesGroup(ValueError)),\n            RaisesGroup(Matcher(TypeError, match=\"foo\")),\n            RaisesGroup(TypeError, ValueError),\n        ),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [\n                TypeError(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"),\n                ExceptionGroup(\n                    \"Exceptions from Trio nursery\",\n                    [TypeError(\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\")],\n                ),\n                ExceptionGroup(\n                    \"Exceptions from Trio nursery\",\n                    [\n                        TypeError(\"cccccccccccccccccccccccccccccccccccccccc\"),\n                        TypeError(\"dddddddddddddddddddddddddddddddddddddddd\"),\n                    ],\n                ),\n            ],\n        )\n\n\n@pytest.mark.skipif(\n    \"hypothesis\" in sys.modules,\n    reason=\"hypothesis may have monkeypatched _check_repr\",\n)\ndef test_check_no_patched_repr() -> None:\n    # We make `_check_repr` monkeypatchable to avoid this very ugly and verbose\n    # repr. The other tests that use `check` make use of `_check_repr` so they'll\n    # continue passing in case it is patched - but we have this one test that\n    # demonstrates just how nasty it gets otherwise.\n    with (\n        pytest.raises(\n            AssertionError,\n            match=(\n                r\"^Raised exception group did not match: \\n\"\n                r\"The following expected exceptions did not find a match:\\n\"\n                r\"  Matcher\\(check=<function test_check_no_patched_repr.<locals>.<lambda> at .*>\\)\\n\"\n                r\"  'TypeError'\\n\"\n                r\"The following raised exceptions did not find a match\\n\"\n                r\"  ValueError\\('foo'\\):\\n\"\n                r\"    Matcher\\(check=<function test_check_no_patched_repr.<locals>.<lambda> at .*>\\): check did not return True\\n\"\n                r\"    'ValueError' is not of type 'TypeError'\\n\"\n                r\"  ValueError\\('bar'\\):\\n\"\n                r\"    Matcher\\(check=<function test_check_no_patched_repr.<locals>.<lambda> at .*>\\): check did not return True\\n\"\n                r\"    'ValueError' is not of type 'TypeError'$\"\n            ),\n        ),\n        RaisesGroup(Matcher(check=lambda x: False), TypeError),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(\"foo\"), ValueError(\"bar\")])\n\n\ndef test_misordering_example() -> None:\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"3 matched exceptions. \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  Matcher(ValueError, match='foo')\\n\"\n            \"    It matches ValueError('foo') which was paired with 'ValueError'\\n\"\n            \"    It matches ValueError('foo') which was paired with 'ValueError'\\n\"\n            \"    It matches ValueError('foo') which was paired with 'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ValueError('bar'):\\n\"\n            \"    It matches 'ValueError' which was paired with ValueError('foo')\\n\"\n            \"    It matches 'ValueError' which was paired with ValueError('foo')\\n\"\n            \"    It matches 'ValueError' which was paired with ValueError('foo')\\n\"\n            \"    Matcher(ValueError, match='foo'): Regex pattern 'foo' did not match 'bar'\\n\"\n            \"There exist a possible match when attempting an exhaustive check, but RaisesGroup uses a greedy algorithm. Please make your expected exceptions more stringent with `Matcher` etc so the greedy algorithm can function.\"\n        ),\n        RaisesGroup(\n            ValueError, ValueError, ValueError, Matcher(ValueError, match=\"foo\")\n        ),\n    ):\n        raise ExceptionGroup(\n            \"\",\n            [\n                ValueError(\"foo\"),\n                ValueError(\"foo\"),\n                ValueError(\"foo\"),\n                ValueError(\"bar\"),\n            ],\n        )\n\n\ndef test_brief_error_on_one_fail() -> None:\n    \"\"\"if only one raised and one expected fail to match up, we print a full table iff\n    the raised exception would match one of the expected that previously got matched\"\"\"\n    # no also-matched\n    with (\n        fails_raises_group(\n            \"1 matched exception. 'TypeError' is not of type 'RuntimeError'\"\n        ),\n        RaisesGroup(ValueError, RuntimeError),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(), TypeError()])\n\n    # raised would match an expected\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"1 matched exception. \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'RuntimeError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  TypeError():\\n\"\n            \"    It matches 'Exception' which was paired with ValueError()\\n\"\n            \"    'TypeError' is not of type 'RuntimeError'\"\n        ),\n        RaisesGroup(Exception, RuntimeError),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(), TypeError()])\n\n    # expected would match a raised\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"1 matched exception. \\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  'ValueError'\\n\"\n            \"    It matches ValueError() which was paired with 'ValueError'\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  TypeError():\\n\"\n            \"    'TypeError' is not of type 'ValueError'\"\n        ),\n        RaisesGroup(ValueError, ValueError),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(), TypeError()])\n\n\ndef test_identity_oopsies() -> None:\n    # it's both possible to have several instances of the same exception in the same group\n    # and to expect multiple of the same type\n    # this previously messed up the logic\n\n    with (\n        fails_raises_group(\n            \"3 matched exceptions. 'RuntimeError' is not of type 'TypeError'\"\n        ),\n        RaisesGroup(ValueError, ValueError, ValueError, TypeError),\n    ):\n        raise ExceptionGroup(\n            \"\", [ValueError(), ValueError(), ValueError(), RuntimeError()]\n        )\n\n    e = ValueError(\"foo\")\n    m = Matcher(match=\"bar\")\n    with (\n        fails_raises_group(\n            \"\\n\"\n            \"The following expected exceptions did not find a match:\\n\"\n            \"  Matcher(match='bar')\\n\"\n            \"  Matcher(match='bar')\\n\"\n            \"  Matcher(match='bar')\\n\"\n            \"The following raised exceptions did not find a match\\n\"\n            \"  ValueError('foo'):\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"  ValueError('foo'):\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"  ValueError('foo'):\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\\n\"\n            \"    Matcher(match='bar'): Regex pattern 'bar' did not match 'foo'\"\n        ),\n        RaisesGroup(m, m, m),\n    ):\n        raise ExceptionGroup(\"\", [e, e, e])\n\n\ndef test_matcher() -> None:\n    with pytest.raises(\n        ValueError,\n        match=r\"^You must specify at least one parameter to match on.$\",\n    ):\n        Matcher()  # type: ignore[call-overload]\n    with pytest.raises(\n        ValueError,\n        match=f\"^exception_type {re.escape(repr(object))} must be a subclass of BaseException$\",\n    ):\n        Matcher(object)  # type: ignore[type-var]\n\n    with RaisesGroup(Matcher(ValueError)):\n        raise ExceptionGroup(\"\", (ValueError(),))\n    with (\n        fails_raises_group(\n            \"Matcher(TypeError): 'ValueError' is not of type 'TypeError'\"\n        ),\n        RaisesGroup(Matcher(TypeError)),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(),))\n\n\ndef test_matcher_match() -> None:\n    with RaisesGroup(Matcher(ValueError, \"foo\")):\n        raise ExceptionGroup(\"\", (ValueError(\"foo\"),))\n    with (\n        fails_raises_group(\n            \"Matcher(ValueError, match='foo'): Regex pattern 'foo' did not match 'bar'\"\n        ),\n        RaisesGroup(Matcher(ValueError, \"foo\")),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(\"bar\"),))\n\n    # Can be used without specifying the type\n    with RaisesGroup(Matcher(match=\"foo\")):\n        raise ExceptionGroup(\"\", (ValueError(\"foo\"),))\n    with (\n        fails_raises_group(\n            \"Matcher(match='foo'): Regex pattern 'foo' did not match 'bar'\"\n        ),\n        RaisesGroup(Matcher(match=\"foo\")),\n    ):\n        raise ExceptionGroup(\"\", (ValueError(\"bar\"),))\n\n    # check ^$\n    with RaisesGroup(Matcher(ValueError, match=\"^bar$\")):\n        raise ExceptionGroup(\"\", [ValueError(\"bar\")])\n    with (\n        fails_raises_group(\n            \"Matcher(ValueError, match='^bar$'): Regex pattern '^bar$' did not match 'barr'\"\n        ),\n        RaisesGroup(Matcher(ValueError, match=\"^bar$\")),\n    ):\n        raise ExceptionGroup(\"\", [ValueError(\"barr\")])\n\n\ndef test_Matcher_check() -> None:\n    def check_oserror_and_errno_is_5(e: BaseException) -> bool:\n        return isinstance(e, OSError) and e.errno == 5\n\n    with RaisesGroup(Matcher(check=check_oserror_and_errno_is_5)):\n        raise ExceptionGroup(\"\", (OSError(5, \"\"),))\n\n    # specifying exception_type narrows the parameter type to the callable\n    def check_errno_is_5(e: OSError) -> bool:\n        return e.errno == 5\n\n    with RaisesGroup(Matcher(OSError, check=check_errno_is_5)):\n        raise ExceptionGroup(\"\", (OSError(5, \"\"),))\n\n    # avoid printing overly verbose repr multiple times\n    with (\n        fails_raises_group(\n            f\"Matcher(OSError, check={check_errno_is_5!r}): check did not return True\"\n        ),\n        RaisesGroup(Matcher(OSError, check=check_errno_is_5)),\n    ):\n        raise ExceptionGroup(\"\", (OSError(6, \"\"),))\n\n    # in nested cases you still get it multiple times though\n    # to address this you'd need logic in Matcher.__repr__ and RaisesGroup.__repr__\n    with (\n        fails_raises_group(\n            f\"RaisesGroup(Matcher(OSError, check={check_errno_is_5!r})): Matcher(OSError, check={check_errno_is_5!r}): check did not return True\"\n        ),\n        RaisesGroup(RaisesGroup(Matcher(OSError, check=check_errno_is_5))),\n    ):\n        raise ExceptionGroup(\"\", [ExceptionGroup(\"\", [OSError(6, \"\")])])\n\n\ndef test_matcher_tostring() -> None:\n    assert str(Matcher(ValueError)) == \"Matcher(ValueError)\"\n    assert str(Matcher(match=\"[a-z]\")) == \"Matcher(match='[a-z]')\"\n    pattern_no_flags = re.compile(r\"noflag\", 0)\n    assert str(Matcher(match=pattern_no_flags)) == \"Matcher(match='noflag')\"\n    pattern_flags = re.compile(r\"noflag\", re.IGNORECASE)\n    assert str(Matcher(match=pattern_flags)) == f\"Matcher(match={pattern_flags!r})\"\n    assert (\n        str(Matcher(ValueError, match=\"re\", check=bool))\n        == f\"Matcher(ValueError, match='re', check={bool!r})\"\n    )\n\n\ndef test_raisesgroup_tostring() -> None:\n    def check_str_and_repr(s: str) -> None:\n        evaled = eval(s)\n        assert s == str(evaled) == repr(evaled)\n\n    check_str_and_repr(\"RaisesGroup(ValueError)\")\n    check_str_and_repr(\"RaisesGroup(RaisesGroup(ValueError))\")\n    check_str_and_repr(\"RaisesGroup(Matcher(ValueError))\")\n    check_str_and_repr(\"RaisesGroup(ValueError, allow_unwrapped=True)\")\n    check_str_and_repr(\"RaisesGroup(ValueError, match='aoeu')\")\n\n    assert (\n        str(RaisesGroup(ValueError, match=\"[a-z]\", check=bool))\n        == f\"RaisesGroup(ValueError, match='[a-z]', check={bool!r})\"\n    )\n\n\ndef test__ExceptionInfo(monkeypatch: pytest.MonkeyPatch) -> None:\n    monkeypatch.setattr(\n        trio.testing._raises_group,\n        \"ExceptionInfo\",\n        trio.testing._raises_group._ExceptionInfo,\n    )\n    with RaisesGroup(ValueError) as excinfo:\n        raise ExceptionGroup(\"\", (ValueError(\"hello\"),))\n    assert excinfo.type is ExceptionGroup\n    assert excinfo.value.exceptions[0].args == (\"hello\",)\n    assert isinstance(excinfo.tb, TracebackType)\n\n\ndef test_raisesgroup_matcher_deprecation() -> None:\n    with pytest.deprecated_call():\n        trio.testing.Matcher  # type: ignore # noqa: B018\n\n    with pytest.deprecated_call():\n        trio.testing.RaisesGroup  # type: ignore # noqa: B018\n\n    with pytest.deprecated_call():\n        from trio.testing import Matcher  # type: ignore # noqa: F401\n\n    with pytest.deprecated_call():\n        from trio.testing import RaisesGroup  # type: ignore # noqa: F401\n"
  },
  {
    "path": "src/trio/_tests/test_threads.py",
    "content": "from __future__ import annotations\n\nimport contextvars\nimport queue as stdlib_queue\nimport re\nimport sys\nimport threading\nimport time\nimport weakref\nfrom functools import partial\nfrom typing import (\n    TYPE_CHECKING,\n    NoReturn,\n    TypeVar,\n)\n\nimport pytest\nimport sniffio\n\nfrom .. import (\n    CancelScope,\n    CapacityLimiter,\n    Event,\n    _core,\n    fail_after,\n    move_on_after,\n    sleep,\n    sleep_forever,\n)\nfrom .._core._tests.test_ki import ki_self\nfrom .._core._tests.tutil import slow\nfrom .._threads import (\n    active_thread_count,\n    current_default_thread_limiter,\n    from_thread_check_cancelled,\n    from_thread_run,\n    from_thread_run_sync,\n    to_thread_run_sync,\n    wait_all_threads_completed,\n)\nfrom ..testing import wait_all_tasks_blocked\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator, Awaitable, Callable\n\n    from outcome import Outcome\n\n    from ..lowlevel import Task\n\nRecordType = list[tuple[str, threading.Thread | type[BaseException]]]\nT = TypeVar(\"T\")\n\n\nasync def test_do_in_trio_thread() -> None:\n    trio_thread = threading.current_thread()\n\n    async def check_case(  # type: ignore[explicit-any]\n        do_in_trio_thread: Callable[..., threading.Thread],\n        fn: Callable[..., T | Awaitable[T]],\n        expected: tuple[str, T],\n        trio_token: _core.TrioToken | None = None,\n    ) -> None:\n        record: RecordType = []\n\n        def threadfn() -> None:\n            try:\n                record.append((\"start\", threading.current_thread()))\n                x = do_in_trio_thread(fn, record, trio_token=trio_token)\n                record.append((\"got\", x))\n            except BaseException as exc:\n                print(exc)\n                record.append((\"error\", type(exc)))\n\n        child_thread = threading.Thread(target=threadfn, daemon=True)\n        child_thread.start()\n        while child_thread.is_alive():\n            print(\"yawn\")\n            await sleep(0.01)\n        assert record == [(\"start\", child_thread), (\"f\", trio_thread), expected]\n\n    token = _core.current_trio_token()\n\n    def f1(record: RecordType) -> int:\n        assert not _core.currently_ki_protected()\n        record.append((\"f\", threading.current_thread()))\n        return 2\n\n    await check_case(from_thread_run_sync, f1, (\"got\", 2), trio_token=token)\n\n    def f2(record: RecordType) -> NoReturn:\n        assert not _core.currently_ki_protected()\n        record.append((\"f\", threading.current_thread()))\n        raise ValueError\n\n    await check_case(from_thread_run_sync, f2, (\"error\", ValueError), trio_token=token)\n\n    async def f3(record: RecordType) -> int:\n        assert not _core.currently_ki_protected()\n        await _core.checkpoint()\n        record.append((\"f\", threading.current_thread()))\n        return 3\n\n    await check_case(from_thread_run, f3, (\"got\", 3), trio_token=token)\n\n    async def f4(record: RecordType) -> NoReturn:\n        assert not _core.currently_ki_protected()\n        await _core.checkpoint()\n        record.append((\"f\", threading.current_thread()))\n        raise KeyError\n\n    await check_case(from_thread_run, f4, (\"error\", KeyError), trio_token=token)\n\n\nasync def test_do_in_trio_thread_from_trio_thread() -> None:\n    with pytest.raises(RuntimeError):\n        from_thread_run_sync(lambda: None)  # pragma: no branch\n\n    async def foo() -> None:  # pragma: no cover\n        pass\n\n    with pytest.raises(RuntimeError):\n        from_thread_run(foo)\n\n\ndef test_run_in_trio_thread_ki() -> None:\n    # if we get a control-C during a run_in_trio_thread, then it propagates\n    # back to the caller (slick!)\n    record = set()\n\n    async def check_run_in_trio_thread() -> None:\n        token = _core.current_trio_token()\n\n        def trio_thread_fn() -> None:\n            print(\"in Trio thread\")\n            assert not _core.currently_ki_protected()\n            print(\"ki_self\")\n            try:\n                ki_self()\n            finally:\n                import sys\n\n                print(\"finally\", sys.exc_info())\n\n        async def trio_thread_afn() -> None:\n            trio_thread_fn()\n\n        def external_thread_fn() -> None:\n            try:\n                print(\"running\")\n                from_thread_run_sync(trio_thread_fn, trio_token=token)\n            except KeyboardInterrupt:\n                print(\"ok1\")\n                record.add(\"ok1\")\n            try:\n                from_thread_run(trio_thread_afn, trio_token=token)\n            except KeyboardInterrupt:\n                print(\"ok2\")\n                record.add(\"ok2\")\n\n        thread = threading.Thread(target=external_thread_fn)\n        thread.start()\n        print(\"waiting\")\n        while thread.is_alive():  # noqa: ASYNC110\n            await sleep(0.01)  # Fine to poll in tests.\n        print(\"waited, joining\")\n        thread.join()\n        print(\"done\")\n\n    _core.run(check_run_in_trio_thread)\n    assert record == {\"ok1\", \"ok2\"}\n\n\ndef test_await_in_trio_thread_while_main_exits() -> None:\n    record = []\n    ev = Event()\n\n    async def trio_fn() -> None:\n        record.append(\"sleeping\")\n        ev.set()\n        await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)\n\n    def thread_fn(token: _core.TrioToken) -> None:\n        try:\n            from_thread_run(trio_fn, trio_token=token)\n        except _core.Cancelled:\n            record.append(\"cancelled\")\n\n    async def main() -> threading.Thread:\n        token = _core.current_trio_token()\n        thread = threading.Thread(target=thread_fn, args=(token,))\n        thread.start()\n        await ev.wait()\n        assert record == [\"sleeping\"]\n        return thread\n\n    thread = _core.run(main)\n    thread.join()\n    assert record == [\"sleeping\", \"cancelled\"]\n\n\nasync def test_named_thread() -> None:\n    ending = \" from trio._tests.test_threads.test_named_thread\"\n\n    def inner(name: str = \"inner\" + ending) -> threading.Thread:\n        assert threading.current_thread().name == name\n        return threading.current_thread()\n\n    def f(name: str) -> Callable[[], threading.Thread]:\n        return partial(inner, name)\n\n    # test defaults\n    await to_thread_run_sync(inner)\n    await to_thread_run_sync(inner, thread_name=None)\n\n    # functools.partial doesn't have __name__, so defaults to None\n    await to_thread_run_sync(f(\"None\" + ending))\n\n    # test that you can set a custom name, and that it's reset afterwards\n    async def test_thread_name(name: str) -> None:\n        thread = await to_thread_run_sync(f(name), thread_name=name)\n        assert re.match(r\"Trio thread [0-9]*\", thread.name)\n\n    await test_thread_name(\"\")\n    await test_thread_name(\"fobiedoo\")\n    await test_thread_name(\"name_longer_than_15_characters\")\n\n    await test_thread_name(\"💙\")\n\n\ndef _get_thread_name(ident: int | None = None) -> str | None:\n    import ctypes\n    import ctypes.util\n\n    libpthread_path = ctypes.util.find_library(\"pthread\")\n    if not libpthread_path:\n        # musl includes pthread functions directly in libc.so\n        # (but note that find_library(\"c\") does not work on musl,\n        #  see: https://github.com/python/cpython/issues/65821)\n        # so try that library instead\n        # if it doesn't exist, CDLL() will fail below\n        libpthread_path = \"libc.so\"\n    try:\n        libpthread = ctypes.CDLL(libpthread_path)\n    except Exception:\n        print(f\"no pthread on {sys.platform}\")\n        return None\n\n    pthread_getname_np = getattr(libpthread, \"pthread_getname_np\", None)\n\n    # this should never fail on any platforms afaik\n    assert pthread_getname_np\n\n    # thankfully getname signature doesn't differ between platforms\n    pthread_getname_np.argtypes = [\n        ctypes.c_void_p,\n        ctypes.c_char_p,\n        ctypes.c_size_t,\n    ]\n    pthread_getname_np.restype = ctypes.c_int\n\n    name_buffer = ctypes.create_string_buffer(b\"\", size=16)\n    if ident is None:\n        ident = threading.get_ident()\n    assert pthread_getname_np(ident, name_buffer, 16) == 0\n    try:\n        return name_buffer.value.decode()\n    except UnicodeDecodeError as e:  # pragma: no cover\n        # used for debugging when testing via CI\n        pytest.fail(f\"value: {name_buffer.value!r}, exception: {e}\")\n\n\n# test os thread naming\n# this depends on pthread being available, which is the case on 99.9% of linux machines\n# and most mac machines. So unless the platform is linux it will just skip\n# in case it fails to fetch the os thread name.\nasync def test_named_thread_os() -> None:\n    def inner(name: str) -> threading.Thread:\n        os_thread_name = _get_thread_name()\n        if os_thread_name is None and sys.platform != \"linux\":\n            pytest.skip(f\"no pthread OS support on {sys.platform}\")\n        else:\n            assert os_thread_name == name[:15]\n\n        return threading.current_thread()\n\n    def f(name: str) -> Callable[[], threading.Thread]:\n        return partial(inner, name)\n\n    # test defaults\n    default = \"None from trio._tests.test_threads.test_named_thread\"\n    await to_thread_run_sync(f(default))\n    await to_thread_run_sync(f(default), thread_name=None)\n\n    # test that you can set a custom name, and that it's reset afterwards\n    async def test_thread_name(name: str, expected: str | None = None) -> None:\n        if expected is None:\n            expected = name\n        thread = await to_thread_run_sync(f(expected), thread_name=name)\n\n        os_thread_name = _get_thread_name(thread.ident)\n        assert os_thread_name is not None, \"should skip earlier if this is the case\"\n        assert re.match(r\"Trio thread [0-9]*\", os_thread_name)\n\n    await test_thread_name(\"\")\n    await test_thread_name(\"fobiedoo\")\n    await test_thread_name(\"name_longer_than_15_characters\")\n\n    await test_thread_name(\"💙\", expected=\"?\")\n\n\ndef test_has_pthread_setname_np() -> None:\n    from trio._core._thread_cache import get_os_thread_name_func\n\n    k = get_os_thread_name_func()\n    if k is None:\n        assert sys.platform != \"linux\"\n        pytest.skip(f\"no pthread_setname_np on {sys.platform}\")\n\n\nasync def test_run_in_worker_thread() -> None:\n    trio_thread = threading.current_thread()\n\n    def f(x: T) -> tuple[T, threading.Thread]:\n        return (x, threading.current_thread())\n\n    x, child_thread = await to_thread_run_sync(f, 1)\n    assert x == 1\n    assert child_thread != trio_thread\n\n    def g() -> NoReturn:\n        raise ValueError(threading.current_thread())\n\n    with pytest.raises(\n        ValueError,\n        match=r\"^<Thread\\(Trio thread \\d+, started daemon \\d+\\)>$\",\n    ) as excinfo:\n        await to_thread_run_sync(g)\n    print(excinfo.value.args)\n    assert excinfo.value.args[0] != trio_thread\n\n\nasync def test_run_in_worker_thread_cancellation() -> None:\n    register: list[str | None] = [None]\n\n    def f(q: stdlib_queue.Queue[None]) -> None:\n        # Make the thread block for a controlled amount of time\n        register[0] = \"blocking\"\n        q.get()\n        register[0] = \"finished\"\n\n    async def child(q: stdlib_queue.Queue[None], abandon_on_cancel: bool) -> None:\n        record.append(\"start\")\n        try:\n            return await to_thread_run_sync(f, q, abandon_on_cancel=abandon_on_cancel)\n        finally:\n            record.append(\"exit\")\n\n    record: list[str] = []\n    q: stdlib_queue.Queue[None] = stdlib_queue.Queue()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child, q, True)\n        # Give it a chance to get started. (This is important because\n        # to_thread_run_sync does a checkpoint_if_cancelled before\n        # blocking on the thread, and we don't want to trigger this.)\n        await wait_all_tasks_blocked()\n        assert record == [\"start\"]\n        # Then cancel it.\n        nursery.cancel_scope.cancel()\n    # The task exited, but the thread didn't:\n    assert register[0] != \"finished\"\n    # Put the thread out of its misery:\n    q.put(None)\n    while register[0] != \"finished\":\n        time.sleep(0.01)  # noqa: ASYNC251  # Need to wait for OS thread\n\n    # This one can't be cancelled\n    record = []\n    register[0] = None\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child, q, False)\n        await wait_all_tasks_blocked()\n        nursery.cancel_scope.cancel()\n        with _core.CancelScope(shield=True):\n            for _ in range(10):\n                await _core.checkpoint()\n        # It's still running\n        assert record == [\"start\"]\n        q.put(None)\n        # Now it exits\n\n    # But if we cancel *before* it enters, the entry is itself a cancellation\n    # point\n    with _core.CancelScope() as scope:\n        scope.cancel()\n        await child(q, False)\n    assert scope.cancelled_caught\n\n\n# Make sure that if trio.run exits, and then the thread finishes, then that's\n# handled gracefully. (Requires that the thread result machinery be prepared\n# for call_soon to raise RunFinishedError.)\ndef test_run_in_worker_thread_abandoned(\n    capfd: pytest.CaptureFixture[str],\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    monkeypatch.setattr(_core._thread_cache, \"IDLE_TIMEOUT\", 0.01)\n\n    q1: stdlib_queue.Queue[None] = stdlib_queue.Queue()\n    q2: stdlib_queue.Queue[threading.Thread] = stdlib_queue.Queue()\n\n    def thread_fn() -> None:\n        q1.get()\n        q2.put(threading.current_thread())\n\n    async def main() -> None:\n        async def child() -> None:\n            await to_thread_run_sync(thread_fn, abandon_on_cancel=True)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(child)\n            await wait_all_tasks_blocked()\n            nursery.cancel_scope.cancel()\n\n    _core.run(main)\n\n    q1.put(None)\n    # This makes sure:\n    # - the thread actually ran\n    # - that thread has finished before we check for its output\n    thread = q2.get()\n    while thread.is_alive():\n        time.sleep(0.01)  # pragma: no cover\n\n    # Make sure we don't have a \"Exception in thread ...\" dump to the console:\n    out, err = capfd.readouterr()\n    assert \"Exception in thread\" not in out\n    assert \"Exception in thread\" not in err\n\n\n@pytest.mark.parametrize(\"MAX\", [3, 5, 10])\n@pytest.mark.parametrize(\"cancel\", [False, True])\n@pytest.mark.parametrize(\"use_default_limiter\", [False, True])\nasync def test_run_in_worker_thread_limiter(\n    MAX: int,\n    cancel: bool,\n    use_default_limiter: bool,\n) -> None:\n    # This test is a bit tricky. The goal is to make sure that if we set\n    # limiter=CapacityLimiter(MAX), then in fact only MAX threads are ever\n    # running at a time, even if there are more concurrent calls to\n    # to_thread_run_sync, and even if some of those are cancelled. And\n    # also to make sure that the default limiter actually limits.\n    COUNT = 2 * MAX\n    gate = threading.Event()\n    lock = threading.Lock()\n    if use_default_limiter:\n        c = current_default_thread_limiter()\n        orig_total_tokens = c.total_tokens\n        c.total_tokens = MAX\n        limiter_arg = None\n    else:\n        c = CapacityLimiter(MAX)\n        orig_total_tokens = MAX\n        limiter_arg = c\n    try:\n        # We used to use regular variables and 'nonlocal' here, but it turns\n        # out that it's not safe to assign to closed-over variables that are\n        # visible in multiple threads, at least as of CPython 3.10 and PyPy\n        # 7.3:\n        #\n        #   https://bugs.python.org/issue30744\n        #   https://bitbucket.org/pypy/pypy/issues/2591/\n        #\n        # Mutating them in-place is OK though (as long as you use proper\n        # locking etc.).\n        class state:\n            ran: int\n            high_water: int\n            running: int\n            parked: int\n\n        state.ran = 0\n        state.high_water = 0\n        state.running = 0\n        state.parked = 0\n\n        token = _core.current_trio_token()\n\n        def thread_fn(cancel_scope: CancelScope) -> None:\n            print(\"thread_fn start\")\n            from_thread_run_sync(cancel_scope.cancel, trio_token=token)\n            with lock:\n                state.ran += 1\n                state.running += 1\n                state.high_water = max(state.high_water, state.running)\n                # The Trio thread below watches this value and uses it as a\n                # signal that all the stats calculations have finished.\n                state.parked += 1\n            gate.wait()\n            with lock:\n                state.parked -= 1\n                state.running -= 1\n            print(\"thread_fn exiting\")\n\n        async def run_thread(event: Event) -> None:\n            with _core.CancelScope() as cancel_scope:\n                await to_thread_run_sync(\n                    thread_fn,\n                    cancel_scope,\n                    abandon_on_cancel=cancel,\n                    limiter=limiter_arg,\n                )\n            print(\"run_thread finished, cancelled:\", cancel_scope.cancelled_caught)\n            event.set()\n\n        async with _core.open_nursery() as nursery:\n            print(\"spawning\")\n            events = []\n            for _ in range(COUNT):\n                events.append(Event())\n                nursery.start_soon(run_thread, events[-1])\n                await wait_all_tasks_blocked()\n            # In the cancel case, we in particular want to make sure that the\n            # cancelled tasks don't release the semaphore. So let's wait until\n            # at least one of them has exited, and that everything has had a\n            # chance to settle down from this, before we check that everyone\n            # who's supposed to be waiting is waiting:\n            if cancel:\n                print(\"waiting for first cancellation to clear\")\n                await events[0].wait()\n                await wait_all_tasks_blocked()\n            # Then wait until the first MAX threads are parked in gate.wait(),\n            # and the next MAX threads are parked on the semaphore, to make\n            # sure no-one is sneaking past, and to make sure the high_water\n            # check below won't fail due to scheduling issues. (It could still\n            # fail if too many threads are let through here.)\n            while (  # noqa: ASYNC110\n                state.parked != MAX or c.statistics().tasks_waiting != MAX\n            ):\n                await sleep(0.01)  # pragma: no cover\n            # Then release the threads\n            gate.set()\n\n        assert state.high_water == MAX\n\n        if cancel:\n            # Some threads might still be running; need to wait to them to\n            # finish before checking that all threads ran. We can do this\n            # using the CapacityLimiter.\n            while c.borrowed_tokens > 0:  # noqa: ASYNC110\n                await sleep(0.01)  # pragma: no cover\n\n        assert state.ran == COUNT\n        assert state.running == 0\n    finally:\n        c.total_tokens = orig_total_tokens\n\n\nasync def test_run_in_worker_thread_custom_limiter() -> None:\n    # Basically just checking that we only call acquire_on_behalf_of and\n    # release_on_behalf_of, since that's part of our documented API.\n    record = []\n\n    class CustomLimiter:\n        async def acquire_on_behalf_of(self, borrower: Task) -> None:\n            record.append(\"acquire\")\n            self._borrower = borrower\n\n        def release_on_behalf_of(self, borrower: Task) -> None:\n            record.append(\"release\")\n            assert borrower == self._borrower\n\n    # TODO: should CapacityLimiter have an abc or protocol so users can modify it?\n    # because currently it's `final` so writing code like this is not allowed.\n    await to_thread_run_sync(lambda: None, limiter=CustomLimiter())  # type: ignore[arg-type]\n    assert record == [\"acquire\", \"release\"]\n\n\nasync def test_run_in_worker_thread_limiter_error() -> None:\n    record = []\n\n    class BadCapacityLimiter:\n        async def acquire_on_behalf_of(self, borrower: Task) -> None:\n            record.append(\"acquire\")\n\n        def release_on_behalf_of(self, borrower: Task) -> NoReturn:\n            record.append(\"release\")\n            raise ValueError(\"release on behalf\")\n\n    bs = BadCapacityLimiter()\n\n    with pytest.raises(ValueError, match=r\"^release on behalf$\") as excinfo:\n        await to_thread_run_sync(lambda: None, limiter=bs)  # type: ignore[arg-type]\n    assert excinfo.value.__context__ is None\n    assert record == [\"acquire\", \"release\"]\n    record = []\n\n    # If the original function raised an error, then the semaphore error\n    # chains with it\n    d: dict[str, object] = {}\n    with pytest.raises(ValueError, match=r\"^release on behalf$\") as excinfo:\n        await to_thread_run_sync(lambda: d[\"x\"], limiter=bs)  # type: ignore[arg-type]\n    assert isinstance(excinfo.value.__context__, KeyError)\n    assert record == [\"acquire\", \"release\"]\n\n\nasync def test_run_in_worker_thread_fail_to_spawn(\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    # Test the unlikely but possible case where trying to spawn a thread fails\n    def bad_start(self: object, *args: object) -> NoReturn:\n        raise RuntimeError(\"the engines canna take it captain\")\n\n    monkeypatch.setattr(_core._thread_cache.ThreadCache, \"start_thread_soon\", bad_start)\n\n    limiter = current_default_thread_limiter()\n    assert limiter.borrowed_tokens == 0\n\n    # We get an appropriate error, and the limiter is cleanly released\n    with pytest.raises(RuntimeError) as excinfo:\n        await to_thread_run_sync(lambda: None)  # pragma: no cover\n    assert \"engines\" in str(excinfo.value)\n\n    assert limiter.borrowed_tokens == 0\n\n\nasync def test_trio_to_thread_run_sync_token() -> None:\n    # Test that to_thread_run_sync automatically injects the current trio token\n    # into a spawned thread\n    def thread_fn() -> _core.TrioToken:\n        callee_token = from_thread_run_sync(_core.current_trio_token)\n        return callee_token\n\n    caller_token = _core.current_trio_token()\n    callee_token = await to_thread_run_sync(thread_fn)\n    assert callee_token == caller_token\n\n\nasync def test_trio_to_thread_run_sync_expected_error() -> None:\n    # Test correct error when passed async function\n    async def async_fn() -> None:  # pragma: no cover\n        pass\n\n    with pytest.raises(TypeError, match=\"expected a sync function\"):\n        await to_thread_run_sync(async_fn)  # type: ignore[unused-coroutine]\n\n\ntrio_test_contextvar: contextvars.ContextVar[str] = contextvars.ContextVar(\n    \"trio_test_contextvar\",\n)\n\n\nasync def test_trio_to_thread_run_sync_contextvars() -> None:\n    trio_thread = threading.current_thread()\n    trio_test_contextvar.set(\"main\")\n\n    def f() -> tuple[str, threading.Thread]:\n        value = trio_test_contextvar.get()\n        with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n            sniffio.current_async_library()\n        return (value, threading.current_thread())\n\n    value, child_thread = await to_thread_run_sync(f)\n    assert value == \"main\"\n    assert child_thread != trio_thread\n\n    def g() -> tuple[str, str, threading.Thread]:\n        parent_value = trio_test_contextvar.get()\n        trio_test_contextvar.set(\"worker\")\n        inner_value = trio_test_contextvar.get()\n        with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n            sniffio.current_async_library()\n        return (\n            parent_value,\n            inner_value,\n            threading.current_thread(),\n        )\n\n    parent_value, inner_value, child_thread = await to_thread_run_sync(g)\n    current_value = trio_test_contextvar.get()\n    assert parent_value == \"main\"\n    assert inner_value == \"worker\"\n    assert current_value == \"main\", (\n        \"The contextvar value set on the worker would not propagate back to the main\"\n        \" thread\"\n    )\n    assert sniffio.current_async_library() == \"trio\"\n\n\nasync def test_trio_from_thread_run_sync() -> None:\n    # Test that to_thread_run_sync correctly \"hands off\" the trio token to\n    # trio.from_thread.run_sync()\n    def thread_fn_1() -> float:\n        trio_time = from_thread_run_sync(_core.current_time)\n        return trio_time\n\n    trio_time = await to_thread_run_sync(thread_fn_1)\n    assert isinstance(trio_time, float)\n\n    # Test correct error when passed async function\n    async def async_fn() -> None:  # pragma: no cover\n        pass\n\n    def thread_fn_2() -> None:\n        from_thread_run_sync(async_fn)  # type: ignore[unused-coroutine]\n\n    with pytest.raises(TypeError, match=\"expected a synchronous function\"):\n        await to_thread_run_sync(thread_fn_2)\n\n\nasync def test_trio_from_thread_run() -> None:\n    # Test that to_thread_run_sync correctly \"hands off\" the trio token to\n    # trio.from_thread.run()\n    record = []\n\n    async def back_in_trio_fn() -> None:\n        _core.current_time()  # implicitly checks that we're in trio\n        record.append(\"back in trio\")\n\n    def thread_fn() -> None:\n        record.append(\"in thread\")\n        from_thread_run(back_in_trio_fn)\n\n    await to_thread_run_sync(thread_fn)\n    assert record == [\"in thread\", \"back in trio\"]\n\n    # Test correct error when passed sync function\n    def sync_fn() -> None:  # pragma: no cover\n        pass\n\n    with pytest.raises(TypeError, match=\"appears to be synchronous\"):\n        await to_thread_run_sync(from_thread_run, sync_fn)  # type: ignore[arg-type]\n\n\nasync def test_trio_from_thread_token() -> None:\n    # Test that to_thread_run_sync and spawned trio.from_thread.run_sync()\n    # share the same Trio token\n    def thread_fn() -> _core.TrioToken:\n        callee_token = from_thread_run_sync(_core.current_trio_token)\n        return callee_token\n\n    caller_token = _core.current_trio_token()\n    callee_token = await to_thread_run_sync(thread_fn)\n    assert callee_token == caller_token\n\n\nasync def test_trio_from_thread_token_kwarg() -> None:\n    # Test that to_thread_run_sync and spawned trio.from_thread.run_sync() can\n    # use an explicitly defined token\n    def thread_fn(token: _core.TrioToken) -> _core.TrioToken:\n        callee_token = from_thread_run_sync(_core.current_trio_token, trio_token=token)\n        return callee_token\n\n    caller_token = _core.current_trio_token()\n    callee_token = await to_thread_run_sync(thread_fn, caller_token)\n    assert callee_token == caller_token\n\n\nasync def test_from_thread_no_token() -> None:\n    # Test that a \"raw call\" to trio.from_thread.run() fails because no token\n    # has been provided\n\n    with pytest.raises(RuntimeError):\n        from_thread_run_sync(_core.current_time)\n\n\nasync def test_trio_from_thread_run_sync_contextvars() -> None:\n    trio_test_contextvar.set(\"main\")\n\n    def thread_fn() -> tuple[str, str, str, str, str]:\n        thread_parent_value = trio_test_contextvar.get()\n        trio_test_contextvar.set(\"worker\")\n        thread_current_value = trio_test_contextvar.get()\n        with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n            sniffio.current_async_library()\n\n        def back_in_main() -> tuple[str, str]:\n            back_parent_value = trio_test_contextvar.get()\n            trio_test_contextvar.set(\"back_in_main\")\n            back_current_value = trio_test_contextvar.get()\n            assert sniffio.current_async_library() == \"trio\"\n            return back_parent_value, back_current_value\n\n        back_parent_value, back_current_value = from_thread_run_sync(back_in_main)\n        thread_after_value = trio_test_contextvar.get()\n        with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n            sniffio.current_async_library()\n        return (\n            thread_parent_value,\n            thread_current_value,\n            thread_after_value,\n            back_parent_value,\n            back_current_value,\n        )\n\n    (\n        thread_parent_value,\n        thread_current_value,\n        thread_after_value,\n        back_parent_value,\n        back_current_value,\n    ) = await to_thread_run_sync(thread_fn)\n    current_value = trio_test_contextvar.get()\n    assert current_value == thread_parent_value == \"main\"\n    assert thread_current_value == back_parent_value == thread_after_value == \"worker\"\n    assert sniffio.current_async_library() == \"trio\"\n    assert back_current_value == \"back_in_main\"\n\n\nasync def test_trio_from_thread_run_contextvars() -> None:\n    trio_test_contextvar.set(\"main\")\n\n    def thread_fn() -> tuple[str, str, str, str, str]:\n        thread_parent_value = trio_test_contextvar.get()\n        trio_test_contextvar.set(\"worker\")\n        thread_current_value = trio_test_contextvar.get()\n        with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n            sniffio.current_async_library()\n\n        async def async_back_in_main() -> tuple[str, str]:\n            back_parent_value = trio_test_contextvar.get()\n            trio_test_contextvar.set(\"back_in_main\")\n            back_current_value = trio_test_contextvar.get()\n            assert sniffio.current_async_library() == \"trio\"\n            return back_parent_value, back_current_value\n\n        back_parent_value, back_current_value = from_thread_run(async_back_in_main)\n        thread_after_value = trio_test_contextvar.get()\n        with pytest.raises(sniffio.AsyncLibraryNotFoundError):\n            sniffio.current_async_library()\n        return (\n            thread_parent_value,\n            thread_current_value,\n            thread_after_value,\n            back_parent_value,\n            back_current_value,\n        )\n\n    (\n        thread_parent_value,\n        thread_current_value,\n        thread_after_value,\n        back_parent_value,\n        back_current_value,\n    ) = await to_thread_run_sync(thread_fn)\n    current_value = trio_test_contextvar.get()\n    assert current_value == thread_parent_value == \"main\"\n    assert thread_current_value == back_parent_value == thread_after_value == \"worker\"\n    assert back_current_value == \"back_in_main\"\n    assert sniffio.current_async_library() == \"trio\"\n\n\ndef test_run_fn_as_system_task_caught_badly_typed_token() -> None:\n    with pytest.raises(RuntimeError):\n        from_thread_run_sync(\n            _core.current_time,\n            trio_token=\"Not TrioTokentype\",  # type: ignore[arg-type]\n        )\n\n\nasync def test_from_thread_inside_trio_thread() -> None:\n    def not_called() -> None:  # pragma: no cover\n        raise AssertionError()\n\n    trio_token = _core.current_trio_token()\n    with pytest.raises(RuntimeError):\n        from_thread_run_sync(not_called, trio_token=trio_token)\n\n\ndef test_from_thread_run_during_shutdown() -> None:\n    save = []\n    record = []\n\n    async def agen(token: _core.TrioToken | None) -> AsyncGenerator[None, None]:\n        try:\n            yield\n        finally:\n            with _core.CancelScope(shield=True):\n                try:\n                    await to_thread_run_sync(\n                        partial(from_thread_run, sleep, 0, trio_token=token),\n                    )\n                except _core.RunFinishedError:\n                    record.append(\"finished\")\n                else:\n                    record.append(\"clean\")\n\n    async def main(use_system_task: bool) -> None:\n        save.append(agen(_core.current_trio_token() if use_system_task else None))\n        await save[-1].asend(None)\n\n    _core.run(main, True)  # System nursery will be closed and raise RunFinishedError\n    _core.run(main, False)  # host task will be rescheduled as normal\n    assert record == [\"finished\", \"clean\"]\n\n\nasync def test_trio_token_weak_referenceable() -> None:\n    token = _core.current_trio_token()\n    assert isinstance(token, _core.TrioToken)\n    weak_reference = weakref.ref(token)\n    assert token is weak_reference()\n\n\nasync def test_unsafe_abandon_on_cancel_kwarg() -> None:\n    # This is a stand in for a numpy ndarray or other objects\n    # that (maybe surprisingly) lack a notion of truthiness\n    class BadBool:\n        def __bool__(self) -> bool:\n            raise NotImplementedError\n\n    with pytest.raises(NotImplementedError):\n        await to_thread_run_sync(int, abandon_on_cancel=BadBool())  # type: ignore[arg-type]\n\n\nasync def test_from_thread_reuses_task() -> None:\n    task = _core.current_task()\n\n    async def async_current_task() -> _core.Task:\n        return _core.current_task()\n\n    assert task is await to_thread_run_sync(from_thread_run_sync, _core.current_task)\n    assert task is await to_thread_run_sync(from_thread_run, async_current_task)\n\n\nasync def test_recursive_to_thread() -> None:\n    tid = None\n\n    def get_tid_then_reenter() -> int:\n        nonlocal tid\n        tid = threading.get_ident()\n        return from_thread_run(to_thread_run_sync, threading.get_ident)\n\n    assert tid != await to_thread_run_sync(get_tid_then_reenter)\n\n\nasync def test_from_thread_host_cancelled() -> None:\n    queue: stdlib_queue.Queue[bool] = stdlib_queue.Queue()\n\n    def sync_check() -> None:\n        from_thread_run_sync(cancel_scope.cancel)\n        try:\n            from_thread_run_sync(bool)\n        except _core.Cancelled:  # pragma: no cover\n            queue.put(True)  # sync functions don't raise Cancelled\n        else:\n            queue.put(False)\n\n    with _core.CancelScope() as cancel_scope:\n        await to_thread_run_sync(sync_check)\n\n    assert not cancel_scope.cancelled_caught\n    assert not queue.get_nowait()\n\n    with _core.CancelScope() as cancel_scope:\n        await to_thread_run_sync(sync_check, abandon_on_cancel=True)\n\n    assert cancel_scope.cancelled_caught\n    assert not await to_thread_run_sync(partial(queue.get, timeout=1))\n\n    async def no_checkpoint() -> bool:\n        return True\n\n    def async_check() -> None:\n        from_thread_run_sync(cancel_scope.cancel)\n        try:\n            assert from_thread_run(no_checkpoint)\n        except _core.Cancelled:  # pragma: no cover\n            queue.put(True)  # async functions raise Cancelled at checkpoints\n        else:\n            queue.put(False)\n\n    with _core.CancelScope() as cancel_scope:\n        await to_thread_run_sync(async_check)\n\n    assert not cancel_scope.cancelled_caught\n    assert not queue.get_nowait()\n\n    with _core.CancelScope() as cancel_scope:\n        await to_thread_run_sync(async_check, abandon_on_cancel=True)\n\n    assert cancel_scope.cancelled_caught\n    assert not await to_thread_run_sync(partial(queue.get, timeout=1))\n\n    async def async_time_bomb() -> None:\n        cancel_scope.cancel()\n        with fail_after(10):\n            await sleep_forever()\n\n    with _core.CancelScope() as cancel_scope:\n        await to_thread_run_sync(from_thread_run, async_time_bomb)\n\n    assert cancel_scope.cancelled_caught\n\n\nasync def child(\n    abandon_on_cancel: bool,\n    scope: CancelScope,\n    record: list[str],\n    f: Callable[[], None],\n) -> None:\n    with scope:\n        record.append(\"start\")\n        try:\n            return await to_thread_run_sync(f, abandon_on_cancel=abandon_on_cancel)\n        except _core.Cancelled as e:\n            record.append(str(e))\n            raise\n        finally:\n            record.append(\"exit\")\n\n\n@pytest.mark.parametrize(\"cancel_the_scope\", [False, True])\nasync def test_from_thread_check_cancelled_no_abandon(cancel_the_scope: bool) -> None:\n    q: stdlib_queue.Queue[str | BaseException] = stdlib_queue.Queue()\n\n    def f() -> None:\n        try:\n            from_thread_check_cancelled()\n        except _core.Cancelled as e:  # pragma: no cover, test failure path\n            q.put(str(e))\n        else:\n            q.put(\"Not Cancelled\")\n        ev.wait()\n        return from_thread_check_cancelled()\n\n    record: list[str] = []\n    ev = threading.Event()\n    scope = _core.CancelScope()  # Nursery cancel scope gives false positives\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child, False, scope, record, f)\n        await wait_all_tasks_blocked()\n        assert record[0] == \"start\"\n        assert q.get(timeout=1) == \"Not Cancelled\"\n        if cancel_the_scope:\n            scope.cancel()\n        ev.set()\n    # Base case: nothing cancelled so we shouldn't see cancels anywhere\n    if not cancel_the_scope:\n        # implicit assertion, Cancelled not raised via nursery\n        assert record[1] == \"exit\"\n    else:\n        # abandon_on_cancel=False case: a cancel will pop out but be handled by\n        # the appropriate cancel scope\n\n        assert scope.cancelled_caught\n        assert re.fullmatch(\n            r\"cancelled due to explicit from task \"\n            r\"<Task 'trio._tests.test_threads.test_from_thread_check_cancelled_no_abandon' at 0x\\w*>\",\n            record[1],\n        ), record[1]\n        assert record[2] == \"exit\"\n        assert len(record) == 3\n\n\nasync def test_from_thread_check_cancelled_abandon_on_cancel() -> None:\n    q: stdlib_queue.Queue[str | BaseException] = stdlib_queue.Queue()\n    # abandon_on_cancel=True case: slightly different thread behavior needed\n    # check thread is cancelled \"soon\" after abandonment\n\n    def f() -> None:\n        ev.wait()\n        try:\n            from_thread_check_cancelled()\n        except _core.Cancelled as e:\n            q.put(str(e))\n        except BaseException as e:  # pragma: no cover, test failure path\n            # abandon_on_cancel=True will eat exceptions, so we pass it\n            # through the queue in order to be able to debug any exceptions\n            q.put(e)\n        else:  # pragma: no cover, test failure path\n            q.put(\"Not Cancelled\")\n\n    record: list[str] = []\n    ev = threading.Event()\n    scope = _core.CancelScope()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(child, True, scope, record, f)\n        await wait_all_tasks_blocked()\n        assert record[0] == \"start\"\n        scope.cancel()\n    # In the worst case the nursery fully exits before the threaded function\n    # checks for cancellation.\n    ev.set()\n\n    assert scope.cancelled_caught\n    assert re.fullmatch(\n        r\"cancelled due to explicit from task \"\n        r\"<Task 'trio._tests.test_threads.test_from_thread_check_cancelled_abandon_on_cancel' at 0x\\w*>\",\n        record[1],\n    ), record[1]\n    assert record[-1] == \"exit\"\n    res = q.get(timeout=1)\n    if isinstance(res, BaseException):  # pragma: no cover  # for debugging\n        raise res\n    else:\n        assert re.fullmatch(\n            r\"cancelled due to explicit from task \"\n            r\"<Task 'trio._tests.test_threads.test_from_thread_check_cancelled_abandon_on_cancel' at 0x\\w*>\",\n            res,\n        ), res\n\n\ndef test_from_thread_check_cancelled_raises_in_foreign_threads() -> None:\n    with pytest.raises(RuntimeError):\n        from_thread_check_cancelled()\n    q: stdlib_queue.Queue[Outcome[object]] = stdlib_queue.Queue()\n    _core.start_thread_soon(from_thread_check_cancelled, lambda _: q.put(_))\n    with pytest.raises(RuntimeError):\n        q.get(timeout=1).unwrap()\n\n\n@slow\nasync def test_reentry_doesnt_deadlock() -> None:\n    # Regression test for issue noticed in GH-2827\n    # The failure mode is to hang the whole test suite, unfortunately.\n    # XXX consider running this in a subprocess with a timeout, if it comes up again!\n\n    async def child() -> None:\n        while True:\n            await to_thread_run_sync(from_thread_run, sleep, 0, abandon_on_cancel=False)\n\n    with move_on_after(2):\n        async with _core.open_nursery() as nursery:\n            for _ in range(4):\n                nursery.start_soon(child)\n\n\nasync def test_wait_all_threads_completed() -> None:\n    no_threads_left = False\n    e1 = Event()\n    e2 = Event()\n\n    e1_exited = Event()\n    e2_exited = Event()\n\n    async def wait_event(e: Event, e_exit: Event) -> None:\n        def thread() -> None:\n            from_thread_run(e.wait)\n\n        await to_thread_run_sync(thread)\n        e_exit.set()\n\n    async def wait_no_threads_left() -> None:\n        nonlocal no_threads_left\n        await wait_all_threads_completed()\n        no_threads_left = True\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(wait_event, e1, e1_exited)\n        nursery.start_soon(wait_event, e2, e2_exited)\n        await wait_all_tasks_blocked()\n        nursery.start_soon(wait_no_threads_left)\n        await wait_all_tasks_blocked()\n        assert not no_threads_left\n        assert active_thread_count() == 2\n\n        e1.set()\n        await e1_exited.wait()\n        await wait_all_tasks_blocked()\n        assert not no_threads_left\n        assert active_thread_count() == 1\n\n        e2.set()\n        await e2_exited.wait()\n        await wait_all_tasks_blocked()\n        assert no_threads_left\n        assert active_thread_count() == 0\n\n\nasync def test_wait_all_threads_completed_no_threads() -> None:\n    await wait_all_threads_completed()\n    assert active_thread_count() == 0\n"
  },
  {
    "path": "src/trio/_tests/test_timeouts.py",
    "content": "from __future__ import annotations\n\nimport time\nfrom typing import TYPE_CHECKING, Protocol, TypeVar\n\nimport outcome\nimport pytest\n\nimport trio\n\nfrom .. import _core\nfrom .._core._tests.tutil import slow\nfrom .._timeouts import (\n    TooSlowError,\n    fail_after,\n    fail_at,\n    move_on_after,\n    move_on_at,\n    sleep,\n    sleep_forever,\n    sleep_until,\n)\nfrom ..testing import assert_checkpoints\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\nT = TypeVar(\"T\")\n\n\nasync def check_takes_about(f: Callable[[], Awaitable[T]], expected_dur: float) -> T:\n    start = time.perf_counter()\n    result = await outcome.acapture(f)\n    dur = time.perf_counter() - start\n    print(dur / expected_dur)\n    # 1.5 is an arbitrary fudge factor because there's always some delay\n    # between when we become eligible to wake up and when we actually do. We\n    # used to sleep for 0.05, and regularly observed overruns of 1.6x on\n    # Appveyor, and then started seeing overruns of 2.3x on Travis's macOS, so\n    # now we bumped up the sleep to 1 second, marked the tests as slow, and\n    # hopefully now the proportional error will be less huge.\n    #\n    # We also also for durations that are a hair shorter than expected. For\n    # example, here's a run on Windows where a 1.0 second sleep was measured\n    # to take 0.9999999999999858 seconds:\n    #   https://ci.appveyor.com/project/njsmith/trio/build/1.0.768/job/3lbdyxl63q3h9s21\n    # I believe that what happened here is that Windows's low clock resolution\n    # meant that our calls to time.monotonic() returned exactly the same\n    # values as the calls inside the actual run loop, but the two subtractions\n    # returned slightly different values because the run loop's clock adds a\n    # random floating point offset to both times, which should cancel out, but\n    # lol floating point we got slightly different rounding errors. (That\n    # value above is exactly 128 ULPs below 1.0, which would make sense if it\n    # started as a 1 ULP error at a different dynamic range.)\n    assert (1 - 1e-8) <= (dur / expected_dur) < 1.5\n\n    return result.unwrap()\n\n\n# How long to (attempt to) sleep for when testing. Smaller numbers make the\n# test suite go faster.\nTARGET = 1.0\n\n\n@slow\nasync def test_sleep() -> None:\n    async def sleep_1() -> None:\n        await sleep_until(_core.current_time() + TARGET)\n\n    await check_takes_about(sleep_1, TARGET)\n\n    async def sleep_2() -> None:\n        await sleep(TARGET)\n\n    await check_takes_about(sleep_2, TARGET)\n\n    with assert_checkpoints():\n        await sleep(0)\n    # This also serves as a test of the trivial move_on_at\n    with move_on_at(_core.current_time()):\n        with pytest.raises(_core.Cancelled):\n            await sleep(0)\n\n\n@slow\nasync def test_move_on_after() -> None:\n    async def sleep_3() -> None:\n        with move_on_after(TARGET):\n            await sleep(100)\n\n    await check_takes_about(sleep_3, TARGET)\n\n\nasync def test_cannot_wake_sleep_forever() -> None:\n    # Test an error occurs if you manually wake sleep_forever().\n    task = trio.lowlevel.current_task()\n\n    async def wake_task() -> None:\n        await trio.lowlevel.checkpoint()\n        trio.lowlevel.reschedule(task, outcome.Value(None))\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(wake_task)\n        with pytest.raises(RuntimeError):\n            await trio.sleep_forever()\n\n\nclass TimeoutScope(Protocol):\n    def __call__(self, seconds: float, *, shield: bool) -> trio.CancelScope: ...\n\n\n@pytest.mark.parametrize(\"scope\", [move_on_after, fail_after])\nasync def test_context_shields_from_outer(scope: TimeoutScope) -> None:\n    with _core.CancelScope() as outer, scope(TARGET, shield=True) as inner:\n        outer.cancel()\n        try:\n            await trio.lowlevel.checkpoint()\n        except trio.Cancelled:  # pragma: no cover\n            pytest.fail(\"shield didn't work\")\n        inner.shield = False\n        with pytest.raises(trio.Cancelled):\n            await trio.lowlevel.checkpoint()\n\n\n@slow\nasync def test_move_on_after_moves_on_even_if_shielded() -> None:\n    async def task() -> None:\n        with _core.CancelScope() as outer, move_on_after(TARGET, shield=True):\n            outer.cancel()\n            # The outer scope is cancelled, but this task is protected by the\n            # shield, so it manages to get to sleep until deadline is met\n            await sleep_forever()\n\n    await check_takes_about(task, TARGET)\n\n\n@slow\nasync def test_fail_after_fails_even_if_shielded() -> None:\n    async def task() -> None:\n        with (\n            pytest.raises(TooSlowError),\n            _core.CancelScope() as outer,\n            fail_after(\n                TARGET,\n                shield=True,\n            ),\n        ):\n            outer.cancel()\n            # The outer scope is cancelled, but this task is protected by the\n            # shield, so it manages to get to sleep until deadline is met\n            await sleep_forever()\n\n    await check_takes_about(task, TARGET)\n\n\n@slow\nasync def test_fail() -> None:\n    async def sleep_4() -> None:\n        with fail_at(_core.current_time() + TARGET):\n            await sleep(100)\n\n    with pytest.raises(TooSlowError):\n        await check_takes_about(sleep_4, TARGET)\n\n    with fail_at(_core.current_time() + 100):\n        await sleep(0)\n\n    async def sleep_5() -> None:\n        with fail_after(TARGET):\n            await sleep(100)\n\n    with pytest.raises(TooSlowError):\n        await check_takes_about(sleep_5, TARGET)\n\n    with fail_after(100):\n        await sleep(0)\n\n\nasync def test_timeouts_raise_value_error() -> None:\n    # deadlines are allowed to be negative, but not delays.\n    # neither delays nor deadlines are allowed to be NaN\n\n    nan = float(\"nan\")\n\n    for fun, val in (\n        (sleep, -1),\n        (sleep, nan),\n        (sleep_until, nan),\n    ):\n        with pytest.raises(\n            ValueError,\n            match=r\"^(deadline|`seconds`) must (not )*be (non-negative|NaN)$\",\n        ):\n            await fun(val)\n\n    for cm, val in (\n        (fail_after, -1),\n        (fail_after, nan),\n        (fail_at, nan),\n        (move_on_after, -1),\n        (move_on_after, nan),\n        (move_on_at, nan),\n    ):\n        with pytest.raises(\n            ValueError,\n            match=r\"^(deadline|`seconds`) must (not )*be (non-negative|NaN)$\",\n        ):\n            with cm(val):\n                pass  # pragma: no cover\n\n\nasync def test_timeout_deadline_on_entry(mock_clock: _core.MockClock) -> None:\n    rcs = move_on_after(5)\n    assert rcs.relative_deadline == 5\n\n    mock_clock.jump(3)\n    start = _core.current_time()\n    with rcs as cs:\n        assert cs.is_relative is None\n\n        # This would previously be start+2\n        assert cs.deadline == start + 5\n        assert cs.relative_deadline == 5\n\n        cs.deadline = start + 3\n        assert cs.deadline == start + 3\n        assert cs.relative_deadline == 3\n\n        cs.relative_deadline = 4\n        assert cs.deadline == start + 4\n        assert cs.relative_deadline == 4\n\n    rcs = move_on_after(5)\n    assert rcs.shield is False\n    rcs.shield = True\n    assert rcs.shield is True\n\n    mock_clock.jump(3)\n    start = _core.current_time()\n    with rcs as cs:\n        assert cs.deadline == start + 5\n\n        assert rcs is cs\n\n\nasync def test_invalid_access_unentered(mock_clock: _core.MockClock) -> None:\n    cs = move_on_after(5)\n    mock_clock.jump(3)\n    start = _core.current_time()\n\n    match_str = \"^unentered relative cancel scope does not have an absolute deadline\"\n    with pytest.warns(DeprecationWarning, match=match_str):\n        assert cs.deadline == start + 5\n    mock_clock.jump(1)\n    # this is hella sketchy, but they *have* been warned\n    with pytest.warns(DeprecationWarning, match=match_str):\n        assert cs.deadline == start + 6\n\n    with pytest.warns(DeprecationWarning, match=match_str):\n        cs.deadline = 7\n    # now transformed into absolute\n    assert cs.deadline == 7\n    assert not cs.is_relative\n\n    cs = move_on_at(5)\n\n    match_str = (\n        \"^unentered non-relative cancel scope does not have a relative deadline$\"\n    )\n    with pytest.raises(RuntimeError, match=match_str):\n        assert cs.relative_deadline\n    with pytest.raises(RuntimeError, match=match_str):\n        cs.relative_deadline = 7\n\n\n@pytest.mark.xfail(reason=\"not implemented\")\nasync def test_fail_access_before_entering() -> None:  # pragma: no cover\n    my_fail_at = fail_at(5)\n    assert my_fail_at.deadline  # type: ignore[attr-defined]\n    my_fail_after = fail_after(5)\n    assert my_fail_after.relative_deadline  # type: ignore[attr-defined]\n"
  },
  {
    "path": "src/trio/_tests/test_tracing.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport trio\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator\n\n\nasync def coro1(event: trio.Event) -> None:\n    event.set()\n    await trio.sleep_forever()\n\n\nasync def coro2(event: trio.Event) -> None:\n    await coro1(event)\n\n\nasync def coro3(event: trio.Event) -> None:\n    await coro2(event)\n\n\nasync def coro2_async_gen(event: trio.Event) -> AsyncGenerator[None, None]:\n    # mypy does not like `yield await trio.lowlevel.checkpoint()` - but that\n    # should be equivalent to splitting the statement\n    await trio.lowlevel.checkpoint()\n    yield\n    await coro1(event)\n    yield  # pragma: no cover\n    await trio.lowlevel.checkpoint()  # pragma: no cover\n    yield  # pragma: no cover\n\n\nasync def coro3_async_gen(event: trio.Event) -> None:\n    async for _ in coro2_async_gen(event):\n        pass\n\n\nasync def test_task_iter_await_frames() -> None:\n    async with trio.open_nursery() as nursery:\n        event = trio.Event()\n        nursery.start_soon(coro3, event)\n        await event.wait()\n\n        (task,) = nursery.child_tasks\n\n        assert [frame.f_code.co_name for frame, _ in task.iter_await_frames()][:3] == [\n            \"coro3\",\n            \"coro2\",\n            \"coro1\",\n        ]\n\n        nursery.cancel_scope.cancel()\n\n\nasync def test_task_iter_await_frames_async_gen() -> None:\n    async with trio.open_nursery() as nursery:\n        event = trio.Event()\n        nursery.start_soon(coro3_async_gen, event)\n        await event.wait()\n\n        (task,) = nursery.child_tasks\n\n        assert [frame.f_code.co_name for frame, _ in task.iter_await_frames()][:3] == [\n            \"coro3_async_gen\",\n            \"coro2_async_gen\",\n            \"coro1\",\n        ]\n\n        nursery.cancel_scope.cancel()\n\n\nasync def test_closed_task_iter_await_frames() -> None:\n    async with trio.open_nursery() as nursery:\n        task = object()\n\n        async def capture_task() -> None:\n            nonlocal task\n            task = trio.lowlevel.current_task()\n            await trio.lowlevel.checkpoint()\n\n        nursery.start_soon(capture_task)\n\n    # Task has completed, so coro.cr_frame should be None, thus no frames\n    assert isinstance(task, trio.lowlevel.Task)  # Ran `capture_task`\n    assert task.coro.cr_frame is None  # and the task was over, but\n    assert list(task.iter_await_frames()) == []  # look, no crash!\n"
  },
  {
    "path": "src/trio/_tests/test_trio.py",
    "content": "def test_trio_import() -> None:\r\n    import sys\r\n\r\n    for module in list(sys.modules.keys()):\r\n        if module.startswith(\"trio\"):\r\n            del sys.modules[module]\r\n\r\n    import trio  # noqa: F401\r\n"
  },
  {
    "path": "src/trio/_tests/test_unix_pipes.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport os\nimport select\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom .. import _core\nfrom .._core._tests.tutil import gc_collect_harder, skip_if_fbsd_pipes_broken\nfrom ..testing import check_one_way_stream, wait_all_tasks_blocked\n\nif TYPE_CHECKING:\n    from .._file_io import _HasFileNo\n\nposix = os.name == \"posix\"\npytestmark = pytest.mark.skipif(not posix, reason=\"posix only\")\n\nassert not TYPE_CHECKING or sys.platform == \"unix\"\n\nif posix:\n    from .._unix_pipes import FdStream\n\n\nasync def make_pipe() -> tuple[FdStream, FdStream]:\n    \"\"\"Makes a new pair of pipes.\"\"\"\n    r, w = os.pipe()\n    return FdStream(w), FdStream(r)\n\n\nasync def make_clogged_pipe() -> tuple[FdStream, FdStream]:\n    s, r = await make_pipe()\n    try:\n        while True:\n            # We want to totally fill up the pipe buffer.\n            # This requires working around a weird feature that POSIX pipes\n            # have.\n            # If you do a write of <= PIPE_BUF bytes, then it's guaranteed\n            # to either complete entirely, or not at all. So if we tried to\n            # write PIPE_BUF bytes, and the buffer's free space is only\n            # PIPE_BUF/2, then the write will raise BlockingIOError... even\n            # though a smaller write could still succeed! To avoid this,\n            # make sure to write >PIPE_BUF bytes each time, which disables\n            # the special behavior.\n            # For details, search for PIPE_BUF here:\n            #   http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html\n\n            # for the getattr:\n            # https://bitbucket.org/pypy/pypy/issues/2876/selectpipe_buf-is-missing-on-pypy3\n            buf_size = getattr(select, \"PIPE_BUF\", 8192)\n            os.write(s.fileno(), b\"x\" * buf_size * 2)\n    except BlockingIOError:\n        pass\n    return s, r\n\n\nasync def test_send_pipe() -> None:\n    r, w = os.pipe()\n    async with FdStream(w) as send:\n        assert send.fileno() == w\n        await send.send_all(b\"123\")\n        assert (os.read(r, 8)) == b\"123\"\n\n        os.close(r)\n\n\nasync def test_receive_pipe() -> None:\n    r, w = os.pipe()\n    async with FdStream(r) as recv:\n        assert (recv.fileno()) == r\n        os.write(w, b\"123\")\n        assert (await recv.receive_some(8)) == b\"123\"\n\n        os.close(w)\n\n\nasync def test_pipes_combined() -> None:\n    write, read = await make_pipe()\n    count = 2**20\n\n    async def sender() -> None:\n        big = bytearray(count)\n        await write.send_all(big)\n\n    async def reader() -> None:\n        await wait_all_tasks_blocked()\n        received = 0\n        while received < count:\n            received += len(await read.receive_some(4096))\n\n        assert received == count\n\n    async with _core.open_nursery() as n:\n        n.start_soon(sender)\n        n.start_soon(reader)\n\n    await read.aclose()\n    await write.aclose()\n\n\nasync def test_pipe_errors() -> None:\n    with pytest.raises(TypeError):\n        FdStream(None)\n\n    r, w = os.pipe()\n    os.close(w)\n    async with FdStream(r) as s:\n        with pytest.raises(ValueError, match=r\"^max_bytes must be integer >= 1$\"):\n            await s.receive_some(0)\n\n\nasync def test_del() -> None:\n    w, r = await make_pipe()\n    f1, f2 = w.fileno(), r.fileno()\n    del w, r\n    gc_collect_harder()\n\n    with pytest.raises(OSError, match=r\"Bad file descriptor$\") as excinfo:\n        os.close(f1)\n    assert excinfo.value.errno == errno.EBADF\n\n    with pytest.raises(OSError, match=r\"Bad file descriptor$\") as excinfo:\n        os.close(f2)\n    assert excinfo.value.errno == errno.EBADF\n\n\nasync def test_async_with() -> None:\n    w, r = await make_pipe()\n    async with w, r:\n        pass\n\n    assert w.fileno() == -1\n    assert r.fileno() == -1\n\n    with pytest.raises(OSError, match=r\"Bad file descriptor$\") as excinfo:\n        os.close(w.fileno())\n    assert excinfo.value.errno == errno.EBADF\n\n    with pytest.raises(OSError, match=r\"Bad file descriptor$\") as excinfo:\n        os.close(r.fileno())\n    assert excinfo.value.errno == errno.EBADF\n\n\nasync def test_misdirected_aclose_regression() -> None:\n    # https://github.com/python-trio/trio/issues/661#issuecomment-456582356\n    w, r = await make_pipe()\n    old_r_fd = r.fileno()\n\n    # Close the original objects\n    await w.aclose()\n    await r.aclose()\n\n    # Do a little dance to get a new pipe whose receive handle matches the old\n    # receive handle.\n    r2_fd, w2_fd = os.pipe()\n    if r2_fd != old_r_fd:  # pragma: no cover\n        os.dup2(r2_fd, old_r_fd)\n        os.close(r2_fd)\n    async with FdStream(old_r_fd) as r2:\n        assert r2.fileno() == old_r_fd\n\n        # And now set up a background task that's working on the new receive\n        # handle\n        async def expect_eof() -> None:\n            assert await r2.receive_some(10) == b\"\"\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect_eof)\n            await wait_all_tasks_blocked()\n\n            # Here's the key test: does calling aclose() again on the *old*\n            # handle, cause the task blocked on the *new* handle to raise\n            # ClosedResourceError?\n            await r.aclose()\n            await wait_all_tasks_blocked()\n\n            # Guess we survived! Close the new write handle so that the task\n            # gets an EOF and can exit cleanly.\n            os.close(w2_fd)\n\n\nasync def test_close_at_bad_time_for_receive_some(\n    monkeypatch: pytest.MonkeyPatch,\n) -> None:\n    # We used to have race conditions where if one task was using the pipe,\n    # and another closed it at *just* the wrong moment, it would give an\n    # unexpected error instead of ClosedResourceError:\n    # https://github.com/python-trio/trio/issues/661\n    #\n    # This tests what happens if the pipe gets closed in the moment *between*\n    # when receive_some wakes up, and when it tries to call os.read\n    async def expect_closedresourceerror() -> None:\n        with pytest.raises(_core.ClosedResourceError):\n            await r.receive_some(10)\n\n    orig_wait_readable = _core._run.TheIOManager.wait_readable\n\n    async def patched_wait_readable(\n        self: _core._run.TheIOManager,\n        fd: int | _HasFileNo,\n    ) -> None:\n        await orig_wait_readable(self, fd)\n        await r.aclose()\n\n    monkeypatch.setattr(_core._run.TheIOManager, \"wait_readable\", patched_wait_readable)\n    s, r = await make_pipe()\n    async with s, r:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect_closedresourceerror)\n            await wait_all_tasks_blocked()\n            # Trigger everything by waking up the receiver\n            await s.send_all(b\"x\")\n\n\nasync def test_close_at_bad_time_for_send_all(monkeypatch: pytest.MonkeyPatch) -> None:\n    # We used to have race conditions where if one task was using the pipe,\n    # and another closed it at *just* the wrong moment, it would give an\n    # unexpected error instead of ClosedResourceError:\n    # https://github.com/python-trio/trio/issues/661\n    #\n    # This tests what happens if the pipe gets closed in the moment *between*\n    # when send_all wakes up, and when it tries to call os.write\n    async def expect_closedresourceerror() -> None:\n        with pytest.raises(_core.ClosedResourceError):\n            await s.send_all(b\"x\" * 100)\n\n    orig_wait_writable = _core._run.TheIOManager.wait_writable\n\n    async def patched_wait_writable(\n        self: _core._run.TheIOManager,\n        fd: int | _HasFileNo,\n    ) -> None:\n        await orig_wait_writable(self, fd)\n        await s.aclose()\n\n    monkeypatch.setattr(_core._run.TheIOManager, \"wait_writable\", patched_wait_writable)\n    s, r = await make_clogged_pipe()\n    async with s, r:\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect_closedresourceerror)\n            await wait_all_tasks_blocked()\n            # Trigger everything by waking up the sender. On ppc64el, PIPE_BUF\n            # is 8192 but make_clogged_pipe() ends up writing a total of\n            # 1048576 bytes before the pipe is full, and then a subsequent\n            # receive_some(10000) isn't sufficient for orig_wait_writable() to\n            # return for our subsequent aclose() call. It's necessary to empty\n            # the pipe further before this happens. So we loop here until the\n            # pipe is empty to make sure that the sender wakes up even in this\n            # case. Otherwise patched_wait_writable() never gets to the\n            # aclose(), so expect_closedresourceerror() never returns, the\n            # nursery never finishes all tasks and this test hangs.\n            received_data = await r.receive_some(10000)\n            while received_data:\n                received_data = await r.receive_some(10000)\n\n\n# On FreeBSD, directories are readable, and we haven't found any other trick\n# for making an unreadable fd, so there's no way to run this test. Fortunately\n# the logic this is testing doesn't depend on the platform, so testing on\n# other platforms is probably good enough.\n@pytest.mark.skipif(\n    sys.platform.startswith(\"freebsd\"),\n    reason=\"no way to make read() return a bizarro error on FreeBSD\",\n)\nasync def test_bizarro_OSError_from_receive() -> None:\n    # Make sure that if the read syscall returns some bizarro error, then we\n    # get a BrokenResourceError. This is incredibly unlikely; there's almost\n    # no way to trigger a failure here intentionally (except for EBADF, but we\n    # exploit that to detect file closure, so it takes a different path). So\n    # we set up a strange scenario where the pipe fd somehow transmutes into a\n    # directory fd, causing os.read to raise IsADirectoryError (yes, that's a\n    # real built-in exception type).\n    s, r = await make_pipe()\n    async with s, r:\n        dir_fd = os.open(\"/\", os.O_DIRECTORY, 0)\n        try:\n            os.dup2(dir_fd, r.fileno())\n            with pytest.raises(_core.BrokenResourceError):\n                await r.receive_some(10)\n        finally:\n            os.close(dir_fd)\n\n\n@skip_if_fbsd_pipes_broken\nasync def test_pipe_fully() -> None:\n    await check_one_way_stream(make_pipe, make_clogged_pipe)\n"
  },
  {
    "path": "src/trio/_tests/test_util.py",
    "content": "from __future__ import annotations\n\nimport sys\nimport types\nfrom typing import TYPE_CHECKING, TypeVar\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator, Coroutine, Generator\nimport pytest\n\nimport trio\nfrom trio.testing import _Matcher as Matcher, _RaisesGroup as RaisesGroup\n\nfrom .. import _core\nfrom .._core._tests.tutil import (\n    create_asyncio_future_in_new_loop,\n    ignore_coroutine_never_awaited_warnings,\n)\nfrom .._util import (\n    ConflictDetector,\n    MultipleExceptionError,\n    NoPublicConstructor,\n    coroutine_or_error,\n    final,\n    fixup_module_metadata,\n    is_main_thread,\n    raise_single_exception_from_group,\n)\nfrom ..testing import wait_all_tasks_blocked\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup, ExceptionGroup\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncGenerator\n\nT = TypeVar(\"T\")\n\n\nasync def test_ConflictDetector() -> None:\n    ul1 = ConflictDetector(\"ul1\")\n    ul2 = ConflictDetector(\"ul2\")\n\n    with ul1:\n        with ul2:\n            print(\"ok\")\n\n    with pytest.raises(_core.BusyResourceError, match=\"ul1\"):\n        with ul1:\n            with ul1:\n                pass  # pragma: no cover\n\n    async def wait_with_ul1() -> None:\n        with ul1:\n            await wait_all_tasks_blocked()\n\n    with RaisesGroup(Matcher(_core.BusyResourceError, \"ul1\")):\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(wait_with_ul1)\n            nursery.start_soon(wait_with_ul1)\n\n\ndef test_module_metadata_is_fixed_up() -> None:\n    import trio\n    import trio.testing\n\n    assert trio.Cancelled.__module__ == \"trio\"\n    assert trio.open_nursery.__module__ == \"trio\"\n    assert trio.abc.Stream.__module__ == \"trio.abc\"\n    assert trio.lowlevel.wait_task_rescheduled.__module__ == \"trio.lowlevel\"\n    assert trio.testing.trio_test.__module__ == \"trio.testing\"\n\n    # Also check methods\n    assert trio.lowlevel.ParkingLot.__init__.__module__ == \"trio.lowlevel\"\n    assert trio.abc.Stream.send_all.__module__ == \"trio.abc\"\n\n    # And names\n    assert trio.Cancelled.__name__ == \"Cancelled\"\n    assert trio.Cancelled.__qualname__ == \"Cancelled\"\n    assert trio.abc.SendStream.send_all.__name__ == \"send_all\"\n    assert trio.abc.SendStream.send_all.__qualname__ == \"SendStream.send_all\"\n    assert trio.to_thread.__name__ == \"trio.to_thread\"\n    assert trio.to_thread.run_sync.__name__ == \"run_sync\"\n    assert trio.to_thread.run_sync.__qualname__ == \"run_sync\"\n\n\nasync def test_is_main_thread() -> None:\n    assert is_main_thread()\n\n    def not_main_thread() -> None:\n        assert not is_main_thread()\n\n    await trio.to_thread.run_sync(not_main_thread)\n\n\n# @coroutine is deprecated since python 3.8, which is fine with us.\n@pytest.mark.filterwarnings(\"ignore:.*@coroutine.*:DeprecationWarning\")\ndef test_coroutine_or_error() -> None:\n    class Deferred:\n        \"Just kidding\"\n\n    with ignore_coroutine_never_awaited_warnings():\n\n        async def f() -> None:  # pragma: no cover\n            pass\n\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(f())  # type: ignore[arg-type, unused-coroutine]\n        assert \"expecting an async function\" in str(excinfo.value)\n\n        import asyncio\n\n        if sys.version_info < (3, 11):\n\n            @asyncio.coroutine\n            def generator_based_coro() -> (\n                Generator[Coroutine[None, None, None], None, None]\n            ):  # pragma: no cover\n                yield from asyncio.sleep(1)\n\n            with pytest.raises(TypeError) as excinfo:\n                coroutine_or_error(generator_based_coro())  # type: ignore[arg-type, unused-coroutine]\n            assert \"asyncio\" in str(excinfo.value)\n\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(create_asyncio_future_in_new_loop())  # type: ignore[arg-type, unused-coroutine]\n        assert \"asyncio\" in str(excinfo.value)\n\n        # does not raise arg-type error\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(create_asyncio_future_in_new_loop)  # type: ignore[unused-coroutine]\n        assert \"asyncio\" in str(excinfo.value)\n\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(Deferred())  # type: ignore[arg-type, unused-coroutine]\n        assert \"twisted\" in str(excinfo.value)\n\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(lambda: Deferred())  # type: ignore[arg-type, unused-coroutine, return-value]\n        assert \"twisted\" in str(excinfo.value)\n\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(len, [[1, 2, 3]])  # type: ignore[arg-type, unused-coroutine]\n\n        assert \"appears to be synchronous\" in str(excinfo.value)\n\n        async def async_gen(\n            _: object,\n        ) -> AsyncGenerator[None, None]:  # pragma: no cover\n            yield\n\n        with pytest.raises(TypeError) as excinfo:\n            coroutine_or_error(async_gen, [0])  # type: ignore[arg-type,unused-coroutine]\n        msg = \"expected an async function but got an async generator\"\n        assert msg in str(excinfo.value)\n\n        # Make sure no references are kept around to keep anything alive\n        del excinfo\n\n\ndef test_final_decorator() -> None:\n    \"\"\"Test that subclassing a @final-annotated class is not allowed.\n\n    This checks both runtime results, and verifies that type checkers detect\n    the error statically through the type-ignore comment.\n    \"\"\"\n\n    @final\n    class FinalClass:\n        pass\n\n    with pytest.raises(TypeError):\n\n        class SubClass(FinalClass):  # type: ignore[misc]\n            pass\n\n\ndef test_no_public_constructor_metaclass() -> None:\n    \"\"\"The NoPublicConstructor metaclass prevents calling the constructor directly.\"\"\"\n\n    class SpecialClass(metaclass=NoPublicConstructor):\n        def __init__(self, a: int, b: float) -> None:\n            \"\"\"Check arguments can be passed to __init__.\"\"\"\n            assert a == 8\n            assert b == 3.15\n\n    with pytest.raises(TypeError):\n        SpecialClass(8, 3.15)\n\n    # Private constructor should not raise, and passes args to __init__.\n    assert isinstance(SpecialClass._create(8, b=3.15), SpecialClass)\n\n\ndef test_fixup_module_metadata() -> None:\n    # Ignores modules not in the trio.X tree.\n    non_trio_module = types.ModuleType(\"not_trio\")\n    non_trio_module.some_func = lambda: None  # type: ignore[attr-defined]\n    non_trio_module.some_func.__name__ = \"some_func\"\n    non_trio_module.some_func.__qualname__ = \"some_func\"\n\n    fixup_module_metadata(non_trio_module.__name__, vars(non_trio_module))\n\n    assert non_trio_module.some_func.__name__ == \"some_func\"\n    assert non_trio_module.some_func.__qualname__ == \"some_func\"\n\n    # Bulild up a fake module to test. Just use lambdas since all we care about is the names.\n    mod = types.ModuleType(\"trio._somemodule_impl\")\n    mod.some_func = lambda: None  # type: ignore[attr-defined]\n    mod.some_func.__name__ = \"_something_else\"\n    mod.some_func.__qualname__ = \"_something_else\"\n\n    # No __module__ means it's unchanged.\n    mod.not_funclike = types.SimpleNamespace()  # type: ignore[attr-defined]\n    mod.not_funclike.__name__ = \"not_funclike\"\n\n    # Check __qualname__ being absent works.\n    mod.only_has_name = types.SimpleNamespace()  # type: ignore[attr-defined]\n    mod.only_has_name.__module__ = \"trio._somemodule_impl\"\n    mod.only_has_name.__name__ = \"only_name\"\n\n    # Underscored names are unchanged.\n    mod._private = lambda: None  # type: ignore[attr-defined]\n    mod._private.__module__ = \"trio._somemodule_impl\"\n    mod._private.__name__ = mod._private.__qualname__ = \"_private\"\n\n    # We recurse into classes.\n    mod.SomeClass = type(  # type: ignore[attr-defined]\n        \"SomeClass\",\n        (),\n        {\n            \"__init__\": lambda self: None,\n            \"method\": lambda self: None,\n        },\n    )\n    # Reference loop is fine.\n    mod.SomeClass.recursion = mod.SomeClass\n\n    fixup_module_metadata(\"trio.somemodule\", vars(mod))\n    assert mod.some_func.__name__ == \"some_func\"\n    assert mod.some_func.__module__ == \"trio.somemodule\"\n    assert mod.some_func.__qualname__ == \"some_func\"\n\n    assert mod.not_funclike.__name__ == \"not_funclike\"\n    assert mod._private.__name__ == \"_private\"\n    assert mod._private.__module__ == \"trio._somemodule_impl\"\n    assert mod._private.__qualname__ == \"_private\"\n\n    assert mod.only_has_name.__name__ == \"only_has_name\"\n    assert mod.only_has_name.__module__ == \"trio.somemodule\"\n    assert not hasattr(mod.only_has_name, \"__qualname__\")\n\n    assert mod.SomeClass.method.__name__ == \"method\"\n    assert mod.SomeClass.method.__module__ == \"trio.somemodule\"\n    assert mod.SomeClass.method.__qualname__ == \"SomeClass.method\"\n    # Make coverage happy.\n    non_trio_module.some_func()\n    mod.some_func()\n    mod._private()\n    mod.SomeClass().method()\n\n\nasync def test_raise_single_exception_from_group() -> None:\n    excinfo: pytest.ExceptionInfo[BaseException]\n\n    exc = ValueError(\"foo\")\n    cause = SyntaxError(\"cause\")\n    context = TypeError(\"context\")\n    exc.__cause__ = cause\n    exc.__context__ = context\n    cancelled = trio.Cancelled._create(source=\"deadline\")\n\n    with pytest.raises(ValueError, match=\"foo\") as excinfo:\n        raise_single_exception_from_group(ExceptionGroup(\"\", [exc]))\n    assert excinfo.value.__cause__ == cause\n    assert excinfo.value.__context__ == context\n\n    # only unwraps one layer of exceptiongroup\n    inner_eg = ExceptionGroup(\"inner eg\", [exc])\n    inner_cause = SyntaxError(\"inner eg cause\")\n    inner_context = TypeError(\"inner eg context\")\n    inner_eg.__cause__ = inner_cause\n    inner_eg.__context__ = inner_context\n    with RaisesGroup(Matcher(ValueError, match=\"^foo$\"), match=\"^inner eg$\") as eginfo:\n        raise_single_exception_from_group(ExceptionGroup(\"\", [inner_eg]))\n    assert eginfo.value.__cause__ == inner_cause\n    assert eginfo.value.__context__ == inner_context\n\n    with pytest.raises(ValueError, match=\"foo\") as excinfo:\n        raise_single_exception_from_group(\n            BaseExceptionGroup(\"\", [cancelled, cancelled, exc])\n        )\n    assert excinfo.value.__cause__ == cause\n    assert excinfo.value.__context__ == context\n\n    # multiple non-cancelled\n    eg = ExceptionGroup(\"\", [ValueError(\"foo\"), ValueError(\"bar\")])\n    with pytest.raises(\n        MultipleExceptionError,\n        match=r\"^Attempted to unwrap exceptiongroup with multiple non-cancelled exceptions. This is often caused by a bug in the caller.$\",\n    ) as excinfo:\n        raise_single_exception_from_group(eg)\n    assert excinfo.value.__cause__ is eg\n    assert excinfo.value.__context__ is None\n\n    # keyboardinterrupt overrides everything\n    eg_ki = BaseExceptionGroup(\n        \"\",\n        [\n            ValueError(\"foo\"),\n            ValueError(\"bar\"),\n            KeyboardInterrupt(\"preserve error msg\"),\n        ],\n    )\n    with pytest.raises(\n        KeyboardInterrupt,\n        match=r\"^preserve error msg$\",\n    ) as excinfo:\n        raise_single_exception_from_group(eg_ki)\n\n    assert excinfo.value.__cause__ is eg_ki\n    assert excinfo.value.__context__ is None\n\n    # and same for SystemExit but verify code too\n    systemexit_ki = BaseExceptionGroup(\n        \"\",\n        [\n            ValueError(\"foo\"),\n            ValueError(\"bar\"),\n            SystemExit(2),\n        ],\n    )\n\n    with pytest.raises(SystemExit) as excinfo:\n        raise_single_exception_from_group(systemexit_ki)\n\n    assert excinfo.value.code == 2\n    assert excinfo.value.__cause__ is systemexit_ki\n    assert excinfo.value.__context__ is None\n\n    # if we only got cancelled, first one is reraised\n    with pytest.raises(trio.Cancelled, match=r\"^cancelled due to deadline$\") as excinfo:\n        raise_single_exception_from_group(\n            BaseExceptionGroup(\n                \"\", [cancelled, trio.Cancelled._create(source=\"explicit\")]\n            )\n        )\n    assert excinfo.value is cancelled\n    assert excinfo.value.__cause__ is None\n    assert excinfo.value.__context__ is None\n"
  },
  {
    "path": "src/trio/_tests/test_wait_for_object.py",
    "content": "import os\n\nimport pytest\n\non_windows = os.name == \"nt\"\n# Mark all the tests in this file as being windows-only\npytestmark = pytest.mark.skipif(not on_windows, reason=\"windows only\")\n\nimport trio\n\nfrom .. import _core, _timeouts\nfrom .._core._tests.tutil import slow\n\nif on_windows:\n    from .._core._windows_cffi import Handle, ffi, kernel32\n    from .._wait_for_object import WaitForMultipleObjects_sync, WaitForSingleObject\n\n\ndef test_WaitForMultipleObjects_sync() -> None:\n    # This does a series of tests where we set/close the handle before\n    # initiating the waiting for it.\n    #\n    # Note that closing the handle (not signaling) will cause the\n    # *initiation* of a wait to return immediately. But closing a handle\n    # that is already being waited on will not stop whatever is waiting\n    # for it.\n\n    # One handle\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.SetEvent(handle1)\n    WaitForMultipleObjects_sync(handle1)\n    kernel32.CloseHandle(handle1)\n    print(\"test_WaitForMultipleObjects_sync one OK\")\n\n    # Two handles, signal first\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.SetEvent(handle1)\n    WaitForMultipleObjects_sync(handle1, handle2)\n    kernel32.CloseHandle(handle1)\n    kernel32.CloseHandle(handle2)\n    print(\"test_WaitForMultipleObjects_sync set first OK\")\n\n    # Two handles, signal second\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.SetEvent(handle2)\n    WaitForMultipleObjects_sync(handle1, handle2)\n    kernel32.CloseHandle(handle1)\n    kernel32.CloseHandle(handle2)\n    print(\"test_WaitForMultipleObjects_sync set second OK\")\n\n    # Two handles, close first\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.CloseHandle(handle1)\n    with pytest.raises(OSError, match=r\"^\\[WinError 6\\] The handle is invalid$\"):\n        WaitForMultipleObjects_sync(handle1, handle2)\n    kernel32.CloseHandle(handle2)\n    print(\"test_WaitForMultipleObjects_sync close first OK\")\n\n    # Two handles, close second\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.CloseHandle(handle2)\n    with pytest.raises(OSError, match=r\"^\\[WinError 6\\] The handle is invalid$\"):\n        WaitForMultipleObjects_sync(handle1, handle2)\n    kernel32.CloseHandle(handle1)\n    print(\"test_WaitForMultipleObjects_sync close second OK\")\n\n\n@slow\nasync def test_WaitForMultipleObjects_sync_slow() -> None:\n    # This does a series of test in which the main thread sync-waits for\n    # handles, while we spawn a thread to set the handles after a short while.\n\n    TIMEOUT = 0.3\n\n    # One handle\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    t0 = _core.current_time()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(\n            trio.to_thread.run_sync,\n            WaitForMultipleObjects_sync,\n            handle1,\n        )\n        await _timeouts.sleep(TIMEOUT)\n        # If we would comment the line below, the above thread will be stuck,\n        # and Trio won't exit this scope\n        kernel32.SetEvent(handle1)\n    t1 = _core.current_time()\n    assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT\n    kernel32.CloseHandle(handle1)\n    print(\"test_WaitForMultipleObjects_sync_slow one OK\")\n\n    # Two handles, signal first\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    t0 = _core.current_time()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(\n            trio.to_thread.run_sync,\n            WaitForMultipleObjects_sync,\n            handle1,\n            handle2,\n        )\n        await _timeouts.sleep(TIMEOUT)\n        kernel32.SetEvent(handle1)\n    t1 = _core.current_time()\n    assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT\n    kernel32.CloseHandle(handle1)\n    kernel32.CloseHandle(handle2)\n    print(\"test_WaitForMultipleObjects_sync_slow thread-set first OK\")\n\n    # Two handles, signal second\n    handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    t0 = _core.current_time()\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(\n            trio.to_thread.run_sync,\n            WaitForMultipleObjects_sync,\n            handle1,\n            handle2,\n        )\n        await _timeouts.sleep(TIMEOUT)\n        kernel32.SetEvent(handle2)\n    t1 = _core.current_time()\n    assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT\n    kernel32.CloseHandle(handle1)\n    kernel32.CloseHandle(handle2)\n    print(\"test_WaitForMultipleObjects_sync_slow thread-set second OK\")\n\n\nasync def test_WaitForSingleObject() -> None:\n    # This does a series of test for setting/closing the handle before\n    # initiating the wait.\n\n    # Test already set\n    handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.SetEvent(handle)\n    await WaitForSingleObject(handle)  # should return at once\n    kernel32.CloseHandle(handle)\n    print(\"test_WaitForSingleObject already set OK\")\n\n    # Test already set, as int\n    handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle_int = int(ffi.cast(\"intptr_t\", handle))\n    kernel32.SetEvent(handle)\n    await WaitForSingleObject(handle_int)  # should return at once\n    kernel32.CloseHandle(handle)\n    print(\"test_WaitForSingleObject already set OK\")\n\n    # Test already closed\n    handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    kernel32.CloseHandle(handle)\n    with pytest.raises(OSError, match=r\"^\\[WinError 6\\] The handle is invalid$\"):\n        await WaitForSingleObject(handle)  # should return at once\n    print(\"test_WaitForSingleObject already closed OK\")\n\n    # Not a handle\n    with pytest.raises(TypeError):\n        await WaitForSingleObject(\"not a handle\")  # type: ignore[arg-type] # Wrong type\n    # with pytest.raises(OSError):\n    #     await WaitForSingleObject(99)  # If you're unlucky, it actually IS a handle :(\n    print(\"test_WaitForSingleObject not a handle OK\")\n\n\n@slow\nasync def test_WaitForSingleObject_slow() -> None:\n    # This does a series of test for setting the handle in another task,\n    # and cancelling the wait task.\n\n    # Set the timeout used in the tests. We test the waiting time against\n    # the timeout with a certain margin.\n    TIMEOUT = 0.3\n\n    async def signal_soon_async(handle: Handle) -> None:\n        await _timeouts.sleep(TIMEOUT)\n        kernel32.SetEvent(handle)\n\n    # Test handle is SET after TIMEOUT in separate coroutine\n\n    handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    t0 = _core.current_time()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(WaitForSingleObject, handle)\n        nursery.start_soon(signal_soon_async, handle)\n\n    kernel32.CloseHandle(handle)\n    t1 = _core.current_time()\n    assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT\n    print(\"test_WaitForSingleObject_slow set from task OK\")\n\n    # Test handle is SET after TIMEOUT in separate coroutine, as int\n\n    handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    handle_int = int(ffi.cast(\"intptr_t\", handle))\n    t0 = _core.current_time()\n\n    async with _core.open_nursery() as nursery:\n        nursery.start_soon(WaitForSingleObject, handle_int)\n        nursery.start_soon(signal_soon_async, handle)\n\n    kernel32.CloseHandle(handle)\n    t1 = _core.current_time()\n    assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT\n    print(\"test_WaitForSingleObject_slow set from task as int OK\")\n\n    # Test handle is CLOSED after 1 sec - NOPE see comment above\n\n    # Test cancellation\n\n    handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    t0 = _core.current_time()\n\n    with _timeouts.move_on_after(TIMEOUT):\n        await WaitForSingleObject(handle)\n\n    kernel32.CloseHandle(handle)\n    t1 = _core.current_time()\n    assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT\n    print(\"test_WaitForSingleObject_slow cancellation OK\")\n"
  },
  {
    "path": "src/trio/_tests/test_windows_pipes.py",
    "content": "from __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom .. import _core\nfrom ..testing import check_one_way_stream, wait_all_tasks_blocked\n\n# Mark all the tests in this file as being windows-only\npytestmark = pytest.mark.skipif(sys.platform != \"win32\", reason=\"windows only\")\n\nassert (  # Skip type checking when not on Windows\n    sys.platform == \"win32\" or not TYPE_CHECKING\n)\n\nif sys.platform == \"win32\":\n    from asyncio.windows_utils import pipe\n\n    from .._core._windows_cffi import _handle, kernel32\n    from .._windows_pipes import PipeReceiveStream, PipeSendStream\n\n\nasync def make_pipe() -> tuple[PipeSendStream, PipeReceiveStream]:\n    \"\"\"Makes a new pair of pipes.\"\"\"\n    r, w = pipe()\n    return PipeSendStream(w), PipeReceiveStream(r)\n\n\ndef test_pipe_typecheck() -> None:\n    with pytest.raises(TypeError):\n        PipeSendStream(1.0)  # type: ignore[arg-type]\n    with pytest.raises(TypeError):\n        PipeReceiveStream(None)  # type: ignore[arg-type]\n\n\nasync def test_pipe_error_on_close() -> None:\n    # Make sure we correctly handle a failure from kernel32.CloseHandle\n    r, w = pipe()\n\n    send_stream = PipeSendStream(w)\n    receive_stream = PipeReceiveStream(r)\n\n    assert kernel32.CloseHandle(_handle(r))\n    assert kernel32.CloseHandle(_handle(w))\n\n    with pytest.raises(OSError, match=r\"^\\[WinError 6\\] The handle is invalid$\"):\n        await send_stream.aclose()\n    with pytest.raises(OSError, match=r\"^\\[WinError 6\\] The handle is invalid$\"):\n        await receive_stream.aclose()\n\n\nasync def test_pipes_combined() -> None:\n    write, read = await make_pipe()\n    count = 2**20\n    replicas = 3\n\n    async def sender() -> None:\n        async with write:\n            big = bytearray(count)\n            for _ in range(replicas):\n                await write.send_all(big)\n\n    async def reader() -> None:\n        async with read:\n            await wait_all_tasks_blocked()\n            total_received = 0\n            while True:\n                # 5000 is chosen because it doesn't evenly divide 2**20\n                received = len(await read.receive_some(5000))\n                if not received:\n                    break\n                total_received += received\n\n            assert total_received == count * replicas\n\n    async with _core.open_nursery() as n:\n        n.start_soon(sender)\n        n.start_soon(reader)\n\n\nasync def test_async_with() -> None:\n    w, r = await make_pipe()\n    async with w, r:\n        pass\n\n    with pytest.raises(_core.ClosedResourceError):\n        await w.send_all(b\"\")\n    with pytest.raises(_core.ClosedResourceError):\n        await r.receive_some(10)\n\n\nasync def test_close_during_write() -> None:\n    w, _r = await make_pipe()\n    async with _core.open_nursery() as nursery:\n\n        async def write_forever() -> None:\n            with pytest.raises(_core.ClosedResourceError) as excinfo:\n                while True:\n                    await w.send_all(b\"x\" * 4096)\n            assert \"another task\" in str(excinfo.value)\n\n        nursery.start_soon(write_forever)\n        await wait_all_tasks_blocked(0.1)\n        await w.aclose()\n\n\nasync def test_pipe_fully() -> None:\n    # passing make_clogged_pipe tests wait_send_all_might_not_block, and we\n    # can't implement that on Windows\n    await check_one_way_stream(make_pipe, None)\n"
  },
  {
    "path": "src/trio/_tests/tools/__init__.py",
    "content": ""
  },
  {
    "path": "src/trio/_tests/tools/test_gen_exports.py",
    "content": "import ast\nimport sys\nfrom pathlib import Path\n\nimport pytest\n\nfrom trio._tests.pytest_plugin import skip_if_optional_else_raise\n\n# imports in gen_exports that are not in `install_requires` in setup.py\ntry:\n    import astor  # noqa: F401\n    import isort  # noqa: F401\nexcept ImportError as error:\n    skip_if_optional_else_raise(error)\n\n\nfrom trio._tools.gen_exports import (\n    File,\n    create_passthrough_args,\n    get_public_methods,\n    process,\n    run_linters,\n    run_ruff,\n)\n\nfrom ..._core._tests.tutil import slow\n\nSOURCE = '''from _run import _public\nfrom collections import Counter\n\nclass Test:\n    @_public\n    def public_func(self):\n        \"\"\"With doc string\"\"\"\n\n    @ignore_this\n    @_public\n    @another_decorator\n    async def public_async_func(self) -> Counter:\n        pass  # no doc string\n\n    def not_public(self):\n        pass\n\n    async def not_public_async(self):\n        pass\n'''\n\nIMPORT_1 = \"\"\"\\\nfrom collections import Counter\n\"\"\"\n\nIMPORT_2 = \"\"\"\\\nfrom collections import Counter\nimport os\n\"\"\"\n\nIMPORT_3 = \"\"\"\\\nfrom typing import TYPE_CHECKING\nif TYPE_CHECKING:\n    from collections import Counter\n\"\"\"\n\n\ndef test_get_public_methods() -> None:\n    methods = list(get_public_methods(ast.parse(SOURCE)))\n    assert {m.name for m in methods} == {\"public_func\", \"public_async_func\"}\n\n\ndef test_create_pass_through_args() -> None:\n    testcases = [\n        (\"def f()\", \"()\"),\n        (\"def f(one)\", \"(one)\"),\n        (\"def f(one, two)\", \"(one, two)\"),\n        (\"def f(one, *args)\", \"(one, *args)\"),\n        (\n            \"def f(one, *args, kw1, kw2=None, **kwargs)\",\n            \"(one, *args, kw1=kw1, kw2=kw2, **kwargs)\",\n        ),\n    ]\n\n    for funcdef, expected in testcases:\n        func_node = ast.parse(funcdef + \":\\n  pass\").body[0]\n        assert isinstance(func_node, ast.FunctionDef)\n        assert create_passthrough_args(func_node) == expected\n\n\nskip_lints = pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"gen_exports is internal, black/isort only runs on CPython\",\n)\n\n\n@slow\n@skip_lints\n@pytest.mark.parametrize(\"imports\", [IMPORT_1, IMPORT_2, IMPORT_3])\ndef test_process(\n    tmp_path: Path,\n    imports: str,\n    capsys: pytest.CaptureFixture[str],\n) -> None:\n    try:\n        import black  # noqa: F401\n    # there's no dedicated CI run that has astor+isort, but lacks black.\n    except ImportError as error:  # pragma: no cover\n        skip_if_optional_else_raise(error)\n\n    modpath = tmp_path / \"_module.py\"\n    genpath = tmp_path / \"_generated_module.py\"\n    modpath.write_text(SOURCE, encoding=\"utf-8\")\n    file = File(modpath, \"runner\", platform=\"linux\", imports=imports)\n    assert not genpath.exists()\n    with pytest.raises(SystemExit) as excinfo:\n        process([file], do_test=True)\n    assert excinfo.value.code == 1\n    captured = capsys.readouterr()\n    assert \"Generated sources are outdated. Please regenerate.\" in captured.out\n    with pytest.raises(SystemExit) as excinfo:\n        process([file], do_test=False)\n    assert excinfo.value.code == 1\n    captured = capsys.readouterr()\n    assert \"Regenerated sources successfully.\" in captured.out\n    assert genpath.exists()\n    process([file], do_test=True)\n    # But if we change the lookup path it notices\n    with pytest.raises(SystemExit) as excinfo:\n        process(\n            [File(modpath, \"runner.io_manager\", platform=\"linux\", imports=imports)],\n            do_test=True,\n        )\n    assert excinfo.value.code == 1\n    # Also if the platform is changed.\n    with pytest.raises(SystemExit) as excinfo:\n        process([File(modpath, \"runner\", imports=imports)], do_test=True)\n    assert excinfo.value.code == 1\n\n\n@skip_lints\ndef test_run_ruff(tmp_path: Path) -> None:\n    \"\"\"Test that processing properly fails if ruff does.\"\"\"\n    try:\n        import ruff  # noqa: F401\n    except ImportError as error:  # pragma: no cover\n        skip_if_optional_else_raise(error)\n\n    file = File(tmp_path / \"module.py\", \"module\")\n\n    success, _ = run_ruff(file, \"class not valid code ><\")\n    assert not success\n\n    test_function = '''def combine_and(data: list[str]) -> str:\n    \"\"\"Join values of text, and have 'and' with the last one properly.\"\"\"\n    if len(data) >= 2:\n        data[-1] = 'and ' + data[-1]\n    if len(data) > 2:\n        return ', '.join(data)\n    return ' '.join(data)'''\n\n    success, response = run_ruff(file, test_function)\n    assert success\n    assert response == test_function\n\n\n@skip_lints\ndef test_lint_failure(tmp_path: Path) -> None:\n    \"\"\"Test that processing properly fails if black or ruff does.\"\"\"\n    try:\n        import black  # noqa: F401\n        import ruff  # noqa: F401\n    except ImportError as error:  # pragma: no cover\n        skip_if_optional_else_raise(error)\n\n    file = File(tmp_path / \"module.py\", \"module\")\n\n    with pytest.raises(SystemExit):\n        run_linters(file, \"class not valid code ><\")\n\n    with pytest.raises(SystemExit):\n        run_linters(file, \"import waffle\\n;import trio\")\n"
  },
  {
    "path": "src/trio/_tests/tools/test_mypy_annotate.py",
    "content": "from __future__ import annotations\n\nimport io\nimport sys\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom trio._tools.mypy_annotate import Result, export, main, process_line\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\n@pytest.mark.parametrize(\n    (\"src\", \"expected\"),\n    [\n        (\"\", None),\n        (\"a regular line\\n\", None),\n        (\n            \"package\\\\filename.py:42:8: note: Some info\\n\",\n            Result(\n                kind=\"notice\",\n                filename=\"package\\\\filename.py\",\n                start_line=42,\n                start_col=8,\n                end_line=None,\n                end_col=None,\n                message=\" Some info\",\n            ),\n        ),\n        (\n            \"package/filename.py:42:1:46:3: error: Type error here [code]\\n\",\n            Result(\n                kind=\"error\",\n                filename=\"package/filename.py\",\n                start_line=42,\n                start_col=1,\n                end_line=46,\n                end_col=3,\n                message=\" Type error here [code]\",\n            ),\n        ),\n        (\n            \"package/module.py:87: warn: Bad code\\n\",\n            Result(\n                kind=\"warning\",\n                filename=\"package/module.py\",\n                start_line=87,\n                message=\" Bad code\",\n            ),\n        ),\n    ],\n    ids=[\"blank\", \"normal\", \"note-wcol\", \"error-wend\", \"warn-lineonly\"],\n)\ndef test_processing(src: str, expected: Result | None) -> None:\n    result = process_line(src)\n    assert result == expected\n\n\ndef test_export(capsys: pytest.CaptureFixture[str]) -> None:\n    results = {\n        Result(\n            kind=\"notice\",\n            filename=\"package\\\\filename.py\",\n            start_line=42,\n            start_col=8,\n            end_line=None,\n            end_col=None,\n            message=\" Some info\",\n        ): [\"Windows\", \"Mac\"],\n        Result(\n            kind=\"error\",\n            filename=\"package/filename.py\",\n            start_line=42,\n            start_col=1,\n            end_line=46,\n            end_col=3,\n            message=\" Type error here [code]\",\n        ): [\"Linux\", \"Mac\"],\n        Result(\n            kind=\"warning\",\n            filename=\"package/module.py\",\n            start_line=87,\n            message=\" Bad code\",\n        ): [\"Linux\"],\n    }\n    export(results)\n    std = capsys.readouterr()\n    assert std.err == \"\"\n    assert std.out == (\n        \"::notice file=package\\\\filename.py,line=42,col=8,\"\n        \"title=Mypy-Windows+Mac::package\\\\filename.py:(42:8): Some info\"\n        \"\\n\"\n        \"::error file=package/filename.py,line=42,col=1,endLine=46,endColumn=3,\"\n        \"title=Mypy-Linux+Mac::package/filename.py:(42:1 - 46:3): Type error here [code]\"\n        \"\\n\"\n        \"::warning file=package/module.py,line=87,\"\n        \"title=Mypy-Linux::package/module.py:87: Bad code\\n\"\n    )\n\n\ndef test_endtoend(\n    tmp_path: Path,\n    monkeypatch: pytest.MonkeyPatch,\n    capsys: pytest.CaptureFixture[str],\n) -> None:\n    import trio._tools.mypy_annotate as mypy_annotate\n\n    inp_text = \"\"\"\\\nMypy begun\ntrio/core.py:15: error: Bad types here [misc]\ntrio/package/module.py:48:4:56:18: warn: Missing annotations  [no-untyped-def]\nFound 3 errors in 29 files\n\"\"\"\n    result_file = tmp_path / \"dump.dat\"\n    assert not result_file.exists()\n    with monkeypatch.context():\n        monkeypatch.setattr(sys, \"stdin\", io.StringIO(inp_text))\n\n        mypy_annotate.main(\n            [\"--dumpfile\", str(result_file), \"--platform\", \"SomePlatform\"],\n        )\n\n    std = capsys.readouterr()\n    assert std.err == \"\"\n    assert std.out == inp_text  # Echos the original.\n\n    assert result_file.exists()\n\n    main([\"--dumpfile\", str(result_file)])\n\n    std = capsys.readouterr()\n    assert std.err == \"\"\n    assert std.out == (\n        \"::error file=trio/core.py,line=15,title=Mypy-SomePlatform::trio/core.py:15: Bad types here [misc]\\n\"\n        \"::warning file=trio/package/module.py,line=48,col=4,endLine=56,endColumn=18,\"\n        \"title=Mypy-SomePlatform::trio/package/module.py:(48:4 - 56:18): Missing \"\n        \"annotations  [no-untyped-def]\\n\"\n    )\n"
  },
  {
    "path": "src/trio/_tests/tools/test_sync_requirements.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom trio._tests.pytest_plugin import skip_if_optional_else_raise\n\n# imports in gen_exports that are not in `install_requires` in requirements\ntry:\n    import yaml  # noqa: F401\nexcept ImportError as error:\n    skip_if_optional_else_raise(error)\n\nfrom trio._tools.sync_requirements import (\n    update_requirements,\n    yield_pre_commit_version_data,\n)\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\ndef test_yield_pre_commit_version_data() -> None:\n    text = \"\"\"\nrepos:\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    rev: v0.11.0\n  - repo: https://github.com/psf/black-pre-commit-mirror\n    rev: 25.1.0\n  - bad: data\n\"\"\"\n    results = tuple(yield_pre_commit_version_data(text))\n    assert results == (\n        (\"ruff-pre-commit\", \"0.11.0\"),\n        (\"black-pre-commit-mirror\", \"25.1.0\"),\n    )\n\n\ndef test_update_requirements(\n    tmp_path: Path,\n) -> None:\n    requirements_file = tmp_path / \"requirements.txt\"\n    assert not requirements_file.exists()\n    requirements_file.write_text(\n        \"\"\"# comment\n  # also comment but spaces line start\nwaffles are delicious no equals\nblack==3.1.4 ; specific version thingy\nmypy==1.15.0\nruff==1.2.5\n# required by soupy cat\"\"\",\n        encoding=\"utf-8\",\n    )\n    assert update_requirements(requirements_file, {\"black\": \"3.1.5\", \"ruff\": \"1.2.7\"})\n    assert requirements_file.read_text(encoding=\"utf-8\") == \"\"\"# comment\n  # also comment but spaces line start\nwaffles are delicious no equals\nblack==3.1.5 ; specific version thingy\nmypy==1.15.0\nruff==1.2.7\n# required by soupy cat\"\"\"\n\n\ndef test_update_requirements_no_changes(\n    tmp_path: Path,\n) -> None:\n    requirements_file = tmp_path / \"requirements.txt\"\n    assert not requirements_file.exists()\n    original = \"\"\"# comment\n  # also comment but spaces line start\nwaffles are delicious no equals\nblack==3.1.4 ; specific version thingy\nmypy==1.15.0\nruff==1.2.5\n# required by soupy cat\"\"\"\n    requirements_file.write_text(original, encoding=\"utf-8\")\n    assert not update_requirements(\n        requirements_file, {\"black\": \"3.1.4\", \"ruff\": \"1.2.5\"}\n    )\n    assert requirements_file.read_text(encoding=\"utf-8\") == original\n"
  },
  {
    "path": "src/trio/_tests/type_tests/check_wraps.py",
    "content": "# https://github.com/python-trio/trio/issues/2775#issuecomment-1702267589\n# (except platform independent...)\nimport trio\nfrom typing_extensions import assert_type\n\n\nasync def fn(s: trio.SocketStream) -> None:\n    result = await s.socket.sendto(b\"a\", \"h\")\n    assert_type(result, int)\n"
  },
  {
    "path": "src/trio/_tests/type_tests/open_memory_channel.py",
    "content": "# https://github.com/python-trio/trio/issues/2873\r\nimport trio\r\n\r\ns, r = trio.open_memory_channel[int](0)\r\n"
  },
  {
    "path": "src/trio/_tests/type_tests/path.py",
    "content": "\"\"\"Path wrapping is quite complex, ensure all methods are understood as wrapped correctly.\"\"\"\n\nimport io\nimport os\nimport pathlib\nimport sys\nfrom typing import IO, Any, BinaryIO\n\nimport trio\nfrom trio._file_io import AsyncIOWrapper\nfrom typing_extensions import assert_type\n\n\ndef operator_checks(text: str, tpath: trio.Path, ppath: pathlib.Path) -> None:\n    \"\"\"Verify operators produce the right results.\"\"\"\n    assert_type(tpath / ppath, trio.Path)\n    assert_type(tpath / tpath, trio.Path)\n    assert_type(tpath / text, trio.Path)\n    assert_type(text / tpath, trio.Path)\n\n    assert_type(tpath > tpath, bool)\n    assert_type(tpath >= tpath, bool)\n    assert_type(tpath < tpath, bool)\n    assert_type(tpath <= tpath, bool)\n\n    assert_type(tpath > ppath, bool)\n    assert_type(tpath >= ppath, bool)\n    assert_type(tpath < ppath, bool)\n    assert_type(tpath <= ppath, bool)\n\n    assert_type(ppath > tpath, bool)\n    assert_type(ppath >= tpath, bool)\n    assert_type(ppath < tpath, bool)\n    assert_type(ppath <= tpath, bool)\n\n\ndef sync_attrs(path: trio.Path) -> None:\n    assert_type(path.parts, tuple[str, ...])\n    assert_type(path.drive, str)\n    assert_type(path.root, str)\n    assert_type(path.anchor, str)\n    assert_type(path.parents[3], trio.Path)\n    assert_type(path.parent, trio.Path)\n    assert_type(path.name, str)\n    assert_type(path.suffix, str)\n    assert_type(path.suffixes, list[str])\n    assert_type(path.stem, str)\n    assert_type(path.as_posix(), str)\n    assert_type(path.as_uri(), str)\n    assert_type(path.is_absolute(), bool)\n    assert_type(path.is_relative_to(path), bool)\n    assert_type(path.is_reserved(), bool)\n    assert_type(path.joinpath(path, \"folder\"), trio.Path)\n    assert_type(path.match(\"*.py\"), bool)\n    assert_type(path.relative_to(\"/usr\"), trio.Path)\n    if sys.version_info >= (3, 12):\n        assert_type(path.relative_to(\"/\", walk_up=True), trio.Path)\n    assert_type(path.with_name(\"filename.txt\"), trio.Path)\n    assert_type(path.with_stem(\"readme\"), trio.Path)\n    assert_type(path.with_suffix(\".log\"), trio.Path)\n\n\nasync def async_attrs(path: trio.Path) -> None:\n    assert_type(await trio.Path.cwd(), trio.Path)\n    assert_type(await trio.Path.home(), trio.Path)\n    assert_type(await path.stat(), os.stat_result)\n    assert_type(await path.chmod(0o777), None)\n    assert_type(await path.exists(), bool)\n    assert_type(await path.expanduser(), trio.Path)\n    for result in await path.glob(\"*.py\"):\n        assert_type(result, trio.Path)\n    if sys.platform != \"win32\":\n        assert_type(await path.group(), str)\n    assert_type(await path.is_dir(), bool)\n    assert_type(await path.is_file(), bool)\n    if sys.version_info >= (3, 12):\n        assert_type(await path.is_junction(), bool)\n    if sys.platform != \"win32\":\n        assert_type(await path.is_mount(), bool)\n    assert_type(await path.is_symlink(), bool)\n    assert_type(await path.is_socket(), bool)\n    assert_type(await path.is_fifo(), bool)\n    assert_type(await path.is_block_device(), bool)\n    assert_type(await path.is_char_device(), bool)\n    for child_iter in await path.iterdir():\n        assert_type(child_iter, trio.Path)\n    # TODO: Path.walk() in 3.12\n    assert_type(await path.lchmod(0o111), None)\n    assert_type(await path.lstat(), os.stat_result)\n    assert_type(await path.mkdir(mode=0o777, parents=True, exist_ok=False), None)\n    # Open done separately.\n    if sys.platform != \"win32\":\n        assert_type(await path.owner(), str)\n    assert_type(await path.read_bytes(), bytes)\n    assert_type(await path.read_text(encoding=\"utf16\", errors=\"replace\"), str)\n    assert_type(await path.readlink(), trio.Path)\n    assert_type(await path.rename(\"another\"), trio.Path)\n    assert_type(await path.replace(path), trio.Path)\n    assert_type(await path.resolve(), trio.Path)\n    for child_glob in await path.glob(\"*.py\"):\n        assert_type(child_glob, trio.Path)\n    for child_rglob in await path.rglob(\"*.py\"):\n        assert_type(child_rglob, trio.Path)\n    assert_type(await path.rmdir(), None)\n    assert_type(await path.samefile(\"something_else\"), bool)\n    assert_type(await path.symlink_to(\"somewhere\"), None)\n    assert_type(await path.hardlink_to(\"elsewhere\"), None)\n    assert_type(await path.touch(), None)\n    assert_type(await path.unlink(missing_ok=True), None)\n    assert_type(await path.write_bytes(b\"123\"), int)\n    assert_type(\n        await path.write_text(\"hello\", encoding=\"utf32le\", errors=\"ignore\"),\n        int,\n    )\n\n\nasync def open_results(path: trio.Path, some_int: int, some_str: str) -> None:\n    # Check the overloads.\n    assert_type(await path.open(), AsyncIOWrapper[io.TextIOWrapper])\n    assert_type(await path.open(\"r\"), AsyncIOWrapper[io.TextIOWrapper])\n    assert_type(await path.open(\"r+\"), AsyncIOWrapper[io.TextIOWrapper])\n    assert_type(await path.open(\"w\"), AsyncIOWrapper[io.TextIOWrapper])\n    assert_type(await path.open(\"rb\", buffering=0), AsyncIOWrapper[io.FileIO])\n    assert_type(await path.open(\"rb+\"), AsyncIOWrapper[io.BufferedRandom])\n    assert_type(await path.open(\"wb\"), AsyncIOWrapper[io.BufferedWriter])\n    assert_type(await path.open(\"rb\"), AsyncIOWrapper[io.BufferedReader])\n    assert_type(await path.open(\"rb\", buffering=some_int), AsyncIOWrapper[BinaryIO])\n    assert_type(await path.open(some_str), AsyncIOWrapper[IO[Any]])\n\n    # Check they produce the right types.\n    file_bin = await path.open(\"rb+\")\n    assert_type(await file_bin.read(), bytes)\n    assert_type(await file_bin.write(b\"test\"), int)\n    assert_type(await file_bin.seek(32), int)\n\n    file_text = await path.open(\"r+t\")\n    assert_type(await file_text.read(), str)\n    assert_type(await file_text.write(\"test\"), int)\n    # TODO: report mypy bug: equiv to https://github.com/microsoft/pyright/issues/6833\n    assert_type(await file_text.readlines(), list[str])\n"
  },
  {
    "path": "src/trio/_tests/type_tests/subprocesses.py",
    "content": "import sys\n\nimport trio\n\n\nasync def test() -> None:\n    # this could test more by using platform checks, but currently this\n    # is just regression tests + sanity checks.\n    await trio.run_process(\"python\", executable=\"ls\")\n    await trio.lowlevel.open_process(\"python\", executable=\"ls\")\n\n    # note: there's no error code on the type ignore as it varies\n    # between platforms.\n    await trio.run_process(\"python\", capture_stdout=True)\n    await trio.lowlevel.open_process(\"python\", capture_stdout=True)  # type: ignore\n\n    if sys.platform != \"win32\" and sys.version_info >= (3, 9):\n        await trio.run_process(\"python\", extra_groups=[5])\n        await trio.lowlevel.open_process(\"python\", extra_groups=[5])\n\n        # 3.11+:\n        await trio.run_process(\"python\", process_group=5)  # type: ignore\n        await trio.lowlevel.open_process(\"python\", process_group=5)  # type: ignore\n"
  },
  {
    "path": "src/trio/_tests/type_tests/task_status.py",
    "content": "\"\"\"Check that started() can only be called for TaskStatus[None].\"\"\"\n\nfrom trio import TaskStatus\nfrom typing_extensions import assert_type\n\n\ndef check_status(\n    none_status_explicit: TaskStatus[None],\n    none_status_implicit: TaskStatus,\n    int_status: TaskStatus[int],\n) -> None:\n    assert_type(none_status_explicit, TaskStatus[None])\n    assert_type(none_status_implicit, TaskStatus[None])  # Default typevar\n    assert_type(int_status, TaskStatus[int])\n\n    # Omitting the parameter is only allowed for None.\n    none_status_explicit.started()\n    none_status_implicit.started()\n    int_status.started()  # type: ignore\n\n    # Explicit None is allowed.\n    none_status_explicit.started(None)\n    none_status_implicit.started(None)\n    int_status.started(None)  # type: ignore\n\n    none_status_explicit.started(42)  # type: ignore\n    none_status_implicit.started(42)  # type: ignore\n    int_status.started(42)\n    int_status.started(True)\n"
  },
  {
    "path": "src/trio/_threads.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport contextvars\nimport inspect\nimport queue as stdlib_queue\nimport threading\nfrom itertools import count\nfrom typing import TYPE_CHECKING, Generic, TypeVar\n\nimport attrs\nimport outcome\nfrom attrs import define\nfrom sniffio import current_async_library_cvar\n\nimport trio\n\nfrom ._core import (\n    RunVar,\n    TrioToken,\n    checkpoint,\n    disable_ki_protection,\n    enable_ki_protection,\n    start_thread_soon,\n)\nfrom ._sync import CapacityLimiter, Event\nfrom ._util import coroutine_or_error\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable, Generator\n\n    from typing_extensions import TypeVarTuple, Unpack\n\n    from trio._core._traps import RaiseCancelT\n\n    Ts = TypeVarTuple(\"Ts\")\n\nRetT = TypeVar(\"RetT\")\n\n\nclass _ParentTaskData(threading.local):\n    \"\"\"Global due to Threading API, thread local storage for data related to the\n    parent task of native Trio threads.\"\"\"\n\n    token: TrioToken\n    abandon_on_cancel: bool\n    cancel_register: list[RaiseCancelT | None]\n    task_register: list[trio.lowlevel.Task | None]\n\n\nPARENT_TASK_DATA = _ParentTaskData()\n\n_limiter_local: RunVar[CapacityLimiter] = RunVar(\"limiter\")\n# I pulled this number out of the air; it isn't based on anything. Probably we\n# should make some kind of measurements to pick a good value.\nDEFAULT_LIMIT = 40\n_thread_counter = count()\n\n\n@define\nclass _ActiveThreadCount:\n    count: int\n    event: Event\n\n\n_active_threads_local: RunVar[_ActiveThreadCount] = RunVar(\"active_threads\")\n\n\n@contextlib.contextmanager\ndef _track_active_thread() -> Generator[None, None, None]:\n    try:\n        active_threads_local = _active_threads_local.get()\n    except LookupError:\n        active_threads_local = _ActiveThreadCount(0, Event())\n        _active_threads_local.set(active_threads_local)\n\n    active_threads_local.count += 1\n    try:\n        yield\n    finally:\n        active_threads_local.count -= 1\n        if active_threads_local.count == 0:\n            active_threads_local.event.set()\n            active_threads_local.event = Event()\n\n\nasync def wait_all_threads_completed() -> None:\n    \"\"\"Wait until no threads are still running tasks.\n\n    This is intended to be used when testing code with trio.to_thread to\n    make sure no tasks are still making progress in a thread. See the\n    following code for a usage example::\n\n        async def wait_all_settled():\n            while True:\n                await trio.testing.wait_all_threads_complete()\n                await trio.testing.wait_all_tasks_blocked()\n                if trio.testing.active_thread_count() == 0:\n                    break\n    \"\"\"\n\n    await checkpoint()\n\n    try:\n        active_threads_local = _active_threads_local.get()\n    except LookupError:\n        # If there would have been active threads, the\n        # _active_threads_local would have been set\n        return\n\n    while active_threads_local.count != 0:\n        await active_threads_local.event.wait()\n\n\ndef active_thread_count() -> int:\n    \"\"\"Returns the number of threads that are currently running a task\n\n    See `trio.testing.wait_all_threads_completed`\n    \"\"\"\n    try:\n        return _active_threads_local.get().count\n    except LookupError:\n        return 0\n\n\ndef current_default_thread_limiter() -> CapacityLimiter:\n    \"\"\"Get the default `~trio.CapacityLimiter` used by\n    `trio.to_thread.run_sync`.\n\n    The most common reason to call this would be if you want to modify its\n    :attr:`~trio.CapacityLimiter.total_tokens` attribute.\n\n    \"\"\"\n    try:\n        limiter = _limiter_local.get()\n    except LookupError:\n        limiter = CapacityLimiter(DEFAULT_LIMIT)\n        _limiter_local.set(limiter)\n    return limiter\n\n\n# Eventually we might build this into a full-fledged deadlock-detection\n# system; see https://github.com/python-trio/trio/issues/182\n# But for now we just need an object to stand in for the thread, so we can\n# keep track of who's holding the CapacityLimiter's token.\n@attrs.frozen(eq=False, slots=False)\nclass ThreadPlaceholder:\n    name: str\n\n\n# Types for the to_thread_run_sync message loop\n@attrs.frozen(eq=False, slots=False)\nclass Run(Generic[RetT]):  # type: ignore[explicit-any]\n    afn: Callable[..., Awaitable[RetT]]  # type: ignore[explicit-any]\n    args: tuple[object, ...]\n    context: contextvars.Context = attrs.field(\n        init=False,\n        factory=contextvars.copy_context,\n    )\n    queue: stdlib_queue.SimpleQueue[outcome.Outcome[RetT]] = attrs.field(\n        init=False,\n        factory=stdlib_queue.SimpleQueue,\n    )\n\n    @disable_ki_protection\n    async def unprotected_afn(self) -> RetT:\n        coro = coroutine_or_error(self.afn, *self.args)\n        return await coro\n\n    async def run(self) -> None:\n        # we use extra checkpoints to pick up and reset any context changes\n        task = trio.lowlevel.current_task()\n        old_context = task.context\n        task.context = self.context.copy()\n        await trio.lowlevel.cancel_shielded_checkpoint()\n        result = await outcome.acapture(self.unprotected_afn)\n        task.context = old_context\n        await trio.lowlevel.cancel_shielded_checkpoint()\n        self.queue.put_nowait(result)\n\n    async def run_system(self) -> None:\n        result = await outcome.acapture(self.unprotected_afn)\n        self.queue.put_nowait(result)\n\n    def run_in_host_task(self, token: TrioToken) -> None:\n        task_register = PARENT_TASK_DATA.task_register\n\n        def in_trio_thread() -> None:\n            task = task_register[0]\n            assert task is not None, \"guaranteed by abandon_on_cancel semantics\"\n            trio.lowlevel.reschedule(task, outcome.Value(self))\n\n        token.run_sync_soon(in_trio_thread)\n\n    def run_in_system_nursery(self, token: TrioToken) -> None:\n        def in_trio_thread() -> None:\n            try:\n                trio.lowlevel.spawn_system_task(\n                    self.run_system,\n                    name=self.afn,\n                    context=self.context,\n                )\n            except RuntimeError:  # system nursery is closed\n                self.queue.put_nowait(\n                    outcome.Error(trio.RunFinishedError(\"system nursery is closed\")),\n                )\n\n        token.run_sync_soon(in_trio_thread)\n\n\n@attrs.frozen(eq=False, slots=False)\nclass RunSync(Generic[RetT]):  # type: ignore[explicit-any]\n    fn: Callable[..., RetT]  # type: ignore[explicit-any]\n    args: tuple[object, ...]\n    context: contextvars.Context = attrs.field(\n        init=False,\n        factory=contextvars.copy_context,\n    )\n    queue: stdlib_queue.SimpleQueue[outcome.Outcome[RetT]] = attrs.field(\n        init=False,\n        factory=stdlib_queue.SimpleQueue,\n    )\n\n    @disable_ki_protection\n    def unprotected_fn(self) -> RetT:\n        ret = self.context.run(self.fn, *self.args)\n\n        if inspect.iscoroutine(ret):\n            # Manually close coroutine to avoid RuntimeWarnings\n            ret.close()\n            raise TypeError(\n                \"Trio expected a synchronous function, but {!r} appears to be \"\n                \"asynchronous\".format(getattr(self.fn, \"__qualname__\", self.fn)),\n            )\n\n        return ret\n\n    def run_sync(self) -> None:\n        result = outcome.capture(self.unprotected_fn)\n        self.queue.put_nowait(result)\n\n    def run_in_host_task(self, token: TrioToken) -> None:\n        task_register = PARENT_TASK_DATA.task_register\n\n        def in_trio_thread() -> None:\n            task = task_register[0]\n            assert task is not None, \"guaranteed by abandon_on_cancel semantics\"\n            trio.lowlevel.reschedule(task, outcome.Value(self))\n\n        token.run_sync_soon(in_trio_thread)\n\n    def run_in_system_nursery(self, token: TrioToken) -> None:\n        token.run_sync_soon(self.run_sync)\n\n\n@enable_ki_protection\nasync def to_thread_run_sync(\n    sync_fn: Callable[[Unpack[Ts]], RetT],\n    *args: Unpack[Ts],\n    thread_name: str | None = None,\n    abandon_on_cancel: bool = False,\n    limiter: CapacityLimiter | None = None,\n) -> RetT:\n    \"\"\"Convert a blocking operation into an async operation using a thread.\n\n    These two lines are equivalent::\n\n        sync_fn(*args)\n        await trio.to_thread.run_sync(sync_fn, *args)\n\n    except that if ``sync_fn`` takes a long time, then the first line will\n    block the Trio loop while it runs, while the second line allows other Trio\n    tasks to continue working while ``sync_fn`` runs. This is accomplished by\n    pushing the call to ``sync_fn(*args)`` off into a worker thread.\n\n    From inside the worker thread, you can get back into Trio using the\n    functions in `trio.from_thread`.\n\n    Args:\n      sync_fn: An arbitrary synchronous callable.\n      *args: Positional arguments to pass to sync_fn. If you need keyword\n          arguments, use :func:`functools.partial`.\n      abandon_on_cancel (bool): Whether to abandon this thread upon\n          cancellation of this operation. See discussion below.\n      thread_name (str): Optional string to set the name of the thread.\n          Will always set `threading.Thread.name`, but only set the os name\n          if pthread.h is available (i.e. most POSIX installations).\n          pthread names are limited to 15 characters, and can be read from\n          ``/proc/<PID>/task/<SPID>/comm`` or with ``ps -eT``, among others.\n          Defaults to ``{sync_fn.__name__|None} from {trio.lowlevel.current_task().name}``.\n      limiter (None, or CapacityLimiter-like object):\n          An object used to limit the number of simultaneous threads. Most\n          commonly this will be a `~trio.CapacityLimiter`, but it could be\n          anything providing compatible\n          :meth:`~trio.CapacityLimiter.acquire_on_behalf_of` and\n          :meth:`~trio.CapacityLimiter.release_on_behalf_of` methods. This\n          function will call ``acquire_on_behalf_of`` before starting the\n          thread, and ``release_on_behalf_of`` after the thread has finished.\n\n          If None (the default), uses the default `~trio.CapacityLimiter`, as\n          returned by :func:`current_default_thread_limiter`.\n\n    **Cancellation handling**: Cancellation is a tricky issue here, because\n    neither Python nor the operating systems it runs on provide any general\n    mechanism for cancelling an arbitrary synchronous function running in a\n    thread. This function will always check for cancellation on entry, before\n    starting the thread. But once the thread is running, there are two ways it\n    can handle being cancelled:\n\n    * If ``abandon_on_cancel=False``, the function ignores the cancellation and\n      keeps going, just like if we had called ``sync_fn`` synchronously. This\n      is the default behavior.\n\n    * If ``abandon_on_cancel=True``, then this function immediately raises\n      `~trio.Cancelled`. In this case **the thread keeps running in\n      background** – we just abandon it to do whatever it's going to do, and\n      silently discard any return value or errors that it raises. Only use\n      this if you know that the operation is safe and side-effect free. (For\n      example: :func:`trio.socket.getaddrinfo` uses a thread with\n      ``abandon_on_cancel=True``, because it doesn't really affect anything if a\n      stray hostname lookup keeps running in the background.)\n\n      The ``limiter`` is only released after the thread has *actually*\n      finished – which in the case of cancellation may be some time after this\n      function has returned. If :func:`trio.run` finishes before the thread\n      does, then the limiter release method will never be called at all.\n\n    .. warning::\n\n       You should not use this function to call long-running CPU-bound\n       functions! In addition to the usual GIL-related reasons why using\n       threads for CPU-bound work is not very effective in Python, there is an\n       additional problem: on CPython, `CPU-bound threads tend to \"starve out\"\n       IO-bound threads <https://bugs.python.org/issue7946>`__, so using\n       threads for CPU-bound work is likely to adversely affect the main\n       thread running Trio. If you need to do this, you're better off using a\n       worker process, or perhaps PyPy (which still has a GIL, but may do a\n       better job of fairly allocating CPU time between threads).\n\n    Returns:\n      Whatever ``sync_fn(*args)`` returns.\n\n    Raises:\n      Exception: Whatever ``sync_fn(*args)`` raises.\n\n    \"\"\"\n    await trio.lowlevel.checkpoint_if_cancelled()\n    # raise early if abandon_on_cancel.__bool__ raises\n    # and give a new name to ensure mypy knows it's never None\n    abandon_bool = bool(abandon_on_cancel)\n    if limiter is None:\n        limiter = current_default_thread_limiter()\n\n    # Holds a reference to the task that's blocked in this function waiting\n    # for the result – or None if this function was cancelled and we should\n    # discard the result.\n    task_register: list[trio.lowlevel.Task | None] = [trio.lowlevel.current_task()]\n    # Holds a reference to the raise_cancel function provided if a cancellation\n    # is attempted against this task - or None if no such delivery has happened.\n    cancel_register: list[RaiseCancelT | None] = [None]  # type: ignore[assignment]\n    name = f\"trio.to_thread.run_sync-{next(_thread_counter)}\"\n    placeholder = ThreadPlaceholder(name)\n\n    # This function gets scheduled into the Trio run loop to deliver the\n    # thread's result.\n    def report_back_in_trio_thread_fn(result: outcome.Outcome[RetT]) -> None:\n        def do_release_then_return_result() -> RetT:\n            # release_on_behalf_of is an arbitrary user-defined method, so it\n            # might raise an error. If it does, we want that error to\n            # replace the regular return value, and if the regular return was\n            # already an exception then we want them to chain.\n            try:\n                return result.unwrap()\n            finally:\n                limiter.release_on_behalf_of(placeholder)\n\n        result = outcome.capture(do_release_then_return_result)\n        if task_register[0] is not None:\n            trio.lowlevel.reschedule(task_register[0], outcome.Value(result))\n\n    current_trio_token = trio.lowlevel.current_trio_token()\n\n    if thread_name is None:\n        thread_name = f\"{getattr(sync_fn, '__name__', None)} from {trio.lowlevel.current_task().name}\"\n\n    def worker_fn() -> RetT:\n        PARENT_TASK_DATA.token = current_trio_token\n        PARENT_TASK_DATA.abandon_on_cancel = abandon_bool\n        PARENT_TASK_DATA.cancel_register = cancel_register\n        PARENT_TASK_DATA.task_register = task_register\n        try:\n            ret = context.run(sync_fn, *args)\n\n            if inspect.iscoroutine(ret):\n                # Manually close coroutine to avoid RuntimeWarnings\n                ret.close()\n                raise TypeError(\n                    \"Trio expected a sync function, but {!r} appears to be \"\n                    \"asynchronous\".format(getattr(sync_fn, \"__qualname__\", sync_fn)),\n                )\n\n            return ret\n        finally:\n            del PARENT_TASK_DATA.token\n            del PARENT_TASK_DATA.abandon_on_cancel\n            del PARENT_TASK_DATA.cancel_register\n            del PARENT_TASK_DATA.task_register\n\n    context = contextvars.copy_context()\n    # Trio doesn't use current_async_library_cvar, but if someone\n    # else set it, it would now shine through since\n    # sniffio.thread_local isn't set in the new thread. Make sure\n    # the new thread sees that it's not running in async context.\n    context.run(current_async_library_cvar.set, None)\n\n    def deliver_worker_fn_result(result: outcome.Outcome[RetT]) -> None:\n        # If the entire run finished, the task we're trying to contact is\n        # certainly long gone -- it must have been cancelled and abandoned\n        # us. Just ignore the error in this case.\n        with contextlib.suppress(trio.RunFinishedError):\n            current_trio_token.run_sync_soon(report_back_in_trio_thread_fn, result)\n\n    await limiter.acquire_on_behalf_of(placeholder)\n    with _track_active_thread():\n        try:\n            start_thread_soon(worker_fn, deliver_worker_fn_result, thread_name)\n        except:\n            limiter.release_on_behalf_of(placeholder)\n            raise\n\n        def abort(raise_cancel: RaiseCancelT) -> trio.lowlevel.Abort:\n            # fill so from_thread_check_cancelled can raise\n            # 'raise_cancel' will immediately delete its reason object, so we make\n            # a copy in each thread\n            cancel_register[0] = raise_cancel\n            if abandon_bool:\n                # empty so report_back_in_trio_thread_fn cannot reschedule\n                task_register[0] = None\n                return trio.lowlevel.Abort.SUCCEEDED\n            else:\n                return trio.lowlevel.Abort.FAILED\n\n        while True:\n            # wait_task_rescheduled return value cannot be typed\n            msg_from_thread: outcome.Outcome[RetT] | Run[object] | RunSync[object] = (\n                await trio.lowlevel.wait_task_rescheduled(abort)\n            )\n            if isinstance(msg_from_thread, outcome.Outcome):\n                return msg_from_thread.unwrap()\n            elif isinstance(msg_from_thread, Run):\n                await msg_from_thread.run()\n            elif isinstance(msg_from_thread, RunSync):\n                msg_from_thread.run_sync()\n            else:  # pragma: no cover, internal debugging guard TODO: use assert_never\n                raise TypeError(\n                    f\"trio.to_thread.run_sync received unrecognized thread message {msg_from_thread!r}.\",\n                )\n            del msg_from_thread\n\n\ndef from_thread_check_cancelled() -> None:\n    \"\"\"Raise `trio.Cancelled` if the associated Trio task entered a cancelled status.\n\n     Only applicable to threads spawned by `trio.to_thread.run_sync`. Poll to allow\n     ``abandon_on_cancel=False`` threads to raise :exc:`~trio.Cancelled` at a suitable\n     place, or to end abandoned ``abandon_on_cancel=True`` threads sooner than they may\n     otherwise.\n\n    Raises:\n        Cancelled: If the corresponding call to `trio.to_thread.run_sync` has had a\n            delivery of cancellation attempted against it, regardless of the value of\n            ``abandon_on_cancel`` supplied as an argument to it.\n        RuntimeError: If this thread is not spawned from `trio.to_thread.run_sync`.\n\n    .. note::\n\n       To be precise, :func:`~trio.from_thread.check_cancelled` checks whether the task\n       running :func:`trio.to_thread.run_sync` has ever been cancelled since the last\n       time it was running a :func:`trio.from_thread.run` or :func:`trio.from_thread.run_sync`\n       function. It may raise `trio.Cancelled` even if a cancellation occurred that was\n       later hidden by a modification to `trio.CancelScope.shield` between the cancelled\n       `~trio.CancelScope` and :func:`trio.to_thread.run_sync`. This differs from the\n       behavior of normal Trio checkpoints, which raise `~trio.Cancelled` only if the\n       cancellation is still active when the checkpoint executes. The distinction here is\n       *exceedingly* unlikely to be relevant to your application, but we mention it\n       for completeness.\n    \"\"\"\n    try:\n        raise_cancel = PARENT_TASK_DATA.cancel_register[0]\n    except AttributeError:\n        raise RuntimeError(\n            \"this thread wasn't created by Trio, can't check for cancellation\",\n        ) from None\n    if raise_cancel is not None:\n        raise_cancel()\n\n\ndef _send_message_to_trio(\n    trio_token: TrioToken | None,\n    message_to_trio: Run[RetT] | RunSync[RetT],\n) -> RetT:\n    \"\"\"Shared logic of from_thread functions\"\"\"\n    token_provided = trio_token is not None\n\n    if not token_provided:\n        try:\n            trio_token = PARENT_TASK_DATA.token\n        except AttributeError:\n            raise RuntimeError(\n                \"this thread wasn't created by Trio, pass kwarg trio_token=...\",\n            ) from None\n    elif not isinstance(trio_token, TrioToken):\n        raise RuntimeError(\"Passed kwarg trio_token is not of type TrioToken\")\n\n    # Avoid deadlock by making sure we're not called from Trio thread\n    try:\n        trio.lowlevel.current_task()\n    except RuntimeError:\n        pass\n    else:\n        raise RuntimeError(\"this is a blocking function; call it from a thread\")\n\n    if token_provided or PARENT_TASK_DATA.abandon_on_cancel:\n        message_to_trio.run_in_system_nursery(trio_token)\n    else:\n        message_to_trio.run_in_host_task(trio_token)\n\n    return message_to_trio.queue.get().unwrap()\n\n\ndef from_thread_run(\n    afn: Callable[[Unpack[Ts]], Awaitable[RetT]],\n    *args: Unpack[Ts],\n    trio_token: TrioToken | None = None,\n) -> RetT:\n    \"\"\"Run the given async function in the parent Trio thread, blocking until it\n    is complete.\n\n    Returns:\n      Whatever ``afn(*args)`` returns.\n\n    Returns or raises whatever the given function returns or raises. It\n    can also raise exceptions of its own:\n\n    Raises:\n        RunFinishedError: if the corresponding call to :func:`trio.run` has\n            already completed, or if the run has started its final cleanup phase\n            and can no longer spawn new system tasks.\n        Cancelled: If the original call to :func:`trio.to_thread.run_sync` is cancelled\n            (if *trio_token* is None) or the call to :func:`trio.run` completes\n            (if *trio_token* is not None) while ``afn(*args)`` is running,\n            then *afn* is likely to raise :exc:`trio.Cancelled`.\n        RuntimeError: if you try calling this from inside the Trio thread,\n            which would otherwise cause a deadlock, or if no ``trio_token`` was\n            provided, and we can't infer one from context.\n        TypeError: if ``afn`` is not an asynchronous function.\n\n    **Locating a TrioToken**: There are two ways to specify which\n    `trio.run` loop to reenter:\n\n        - Spawn this thread from `trio.to_thread.run_sync`. Trio will\n          automatically capture the relevant Trio token and use it\n          to re-enter the same Trio task.\n        - Pass a keyword argument, ``trio_token`` specifying a specific\n          `trio.run` loop to re-enter. This is useful in case you have a\n          \"foreign\" thread, spawned using some other framework, and still want\n          to enter Trio, or if you want to use a new system task to call ``afn``,\n          maybe to avoid the cancellation context of a corresponding\n          `trio.to_thread.run_sync` task. You can get this token from\n          :func:`trio.lowlevel.current_trio_token`.\n    \"\"\"\n    return _send_message_to_trio(trio_token, Run(afn, args))\n\n\ndef from_thread_run_sync(\n    fn: Callable[[Unpack[Ts]], RetT],\n    *args: Unpack[Ts],\n    trio_token: TrioToken | None = None,\n) -> RetT:\n    \"\"\"Run the given sync function in the parent Trio thread, blocking until it\n    is complete.\n\n    Returns:\n      Whatever ``fn(*args)`` returns.\n\n    Returns or raises whatever the given function returns or raises. It\n    can also raise exceptions of its own:\n\n    Raises:\n        RunFinishedError: if the corresponding call to `trio.run` has\n            already completed.\n        RuntimeError: if you try calling this from inside the Trio thread,\n            which would otherwise cause a deadlock or if no ``trio_token`` was\n            provided, and we can't infer one from context.\n        TypeError: if ``fn`` is an async function.\n\n    **Locating a TrioToken**: There are two ways to specify which\n    `trio.run` loop to reenter:\n\n        - Spawn this thread from `trio.to_thread.run_sync`. Trio will\n          automatically capture the relevant Trio token and use it when you\n          want to re-enter Trio.\n        - Pass a keyword argument, ``trio_token`` specifying a specific\n          `trio.run` loop to re-enter. This is useful in case you have a\n          \"foreign\" thread, spawned using some other framework, and still want\n          to enter Trio, or if you want to use a new system task to call ``fn``,\n          maybe to avoid the cancellation context of a corresponding\n          `trio.to_thread.run_sync` task.\n    \"\"\"\n    return _send_message_to_trio(trio_token, RunSync(fn, args))\n"
  },
  {
    "path": "src/trio/_timeouts.py",
    "content": "from __future__ import annotations\n\nimport math\nimport sys\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING, NoReturn\n\nimport trio\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n\ndef move_on_at(deadline: float, *, shield: bool = False) -> trio.CancelScope:\n    \"\"\"Use as a context manager to create a cancel scope with the given\n    absolute deadline.\n\n    Args:\n      deadline (float): The deadline.\n      shield (bool): Initial value for the `~trio.CancelScope.shield` attribute\n          of the newly created cancel scope.\n\n    Raises:\n      ValueError: if deadline is NaN.\n\n    \"\"\"\n    # CancelScope validates that deadline isn't math.nan\n    return trio.CancelScope(deadline=deadline, shield=shield)\n\n\ndef move_on_after(\n    seconds: float,\n    *,\n    shield: bool = False,\n) -> trio.CancelScope:\n    \"\"\"Use as a context manager to create a cancel scope whose deadline is\n    set to now + *seconds*.\n\n    The deadline of the cancel scope is calculated upon entering.\n\n    Args:\n      seconds (float): The timeout.\n      shield (bool): Initial value for the `~trio.CancelScope.shield` attribute\n          of the newly created cancel scope.\n\n    Raises:\n      ValueError: if ``seconds`` is less than zero or NaN.\n\n    \"\"\"\n    # duplicate validation logic to have the correct parameter name\n    if seconds < 0:\n        raise ValueError(\"`seconds` must be non-negative\")\n    if math.isnan(seconds):\n        raise ValueError(\"`seconds` must not be NaN\")\n    return trio.CancelScope(\n        shield=shield,\n        relative_deadline=seconds,\n    )\n\n\nasync def sleep_forever() -> NoReturn:\n    \"\"\"Pause execution of the current task forever (or until cancelled).\n\n    Equivalent to calling ``await sleep(math.inf)``, except that if manually\n    rescheduled this will raise a `RuntimeError`.\n\n    Raises:\n      RuntimeError: if rescheduled\n\n    \"\"\"\n    await trio.lowlevel.wait_task_rescheduled(lambda _: trio.lowlevel.Abort.SUCCEEDED)\n    raise RuntimeError(\"Should never have been rescheduled!\")\n\n\nasync def sleep_until(deadline: float) -> None:\n    \"\"\"Pause execution of the current task until the given time.\n\n    The difference between :func:`sleep` and :func:`sleep_until` is that the\n    former takes a relative time and the latter takes an absolute time\n    according to Trio's internal clock (as returned by :func:`current_time`).\n\n    Args:\n        deadline (float): The time at which we should wake up again. May be in\n            the past, in which case this function executes a checkpoint but\n            does not block.\n\n    Raises:\n      ValueError: if deadline is NaN.\n\n    \"\"\"\n    with move_on_at(deadline):\n        await sleep_forever()\n\n\nasync def sleep(seconds: float) -> None:\n    \"\"\"Pause execution of the current task for the given number of seconds.\n\n    Args:\n        seconds (float): The number of seconds to sleep. May be zero to\n            insert a checkpoint without actually blocking.\n\n    Raises:\n        ValueError: if *seconds* is negative or NaN.\n\n    \"\"\"\n    if seconds < 0:\n        raise ValueError(\"`seconds` must be non-negative\")\n    if seconds == 0:\n        await trio.lowlevel.checkpoint()\n    else:\n        await sleep_until(trio.current_time() + seconds)\n\n\nclass TooSlowError(Exception):\n    \"\"\"Raised by :func:`fail_after` and :func:`fail_at` if the timeout\n    expires.\n\n    \"\"\"\n\n\n@contextmanager\ndef fail_at(\n    deadline: float,\n    *,\n    shield: bool = False,\n) -> Generator[trio.CancelScope, None, None]:\n    \"\"\"Creates a cancel scope with the given deadline, and raises an error if it\n    is actually cancelled.\n\n    This function and :func:`move_on_at` are similar in that both create a\n    cancel scope with a given absolute deadline, and if the deadline expires\n    then both will cause :exc:`Cancelled` to be raised within the scope. The\n    difference is that when the :exc:`Cancelled` exception reaches\n    :func:`move_on_at`, it's caught and discarded. When it reaches\n    :func:`fail_at`, then it's caught and :exc:`TooSlowError` is raised in its\n    place.\n\n    Args:\n      deadline (float): The deadline.\n      shield (bool): Initial value for the `~trio.CancelScope.shield` attribute\n          of the newly created cancel scope.\n\n    Raises:\n      TooSlowError: if a :exc:`Cancelled` exception is raised in this scope\n        and caught by the context manager.\n      ValueError: if deadline is NaN.\n\n    \"\"\"\n    with move_on_at(deadline, shield=shield) as scope:\n        yield scope\n    if scope.cancelled_caught:\n        raise TooSlowError\n\n\n@contextmanager\ndef fail_after(\n    seconds: float,\n    *,\n    shield: bool = False,\n) -> Generator[trio.CancelScope, None, None]:\n    \"\"\"Creates a cancel scope with the given timeout, and raises an error if\n    it is actually cancelled.\n\n    This function and :func:`move_on_after` are similar in that both create a\n    cancel scope with a given timeout, and if the timeout expires then both\n    will cause :exc:`Cancelled` to be raised within the scope. The difference\n    is that when the :exc:`Cancelled` exception reaches :func:`move_on_after`,\n    it's caught and discarded. When it reaches :func:`fail_after`, then it's\n    caught and :exc:`TooSlowError` is raised in its place.\n\n    The deadline of the cancel scope is calculated upon entering.\n\n    Args:\n      seconds (float): The timeout.\n      shield (bool): Initial value for the `~trio.CancelScope.shield` attribute\n          of the newly created cancel scope.\n\n    Raises:\n      TooSlowError: if a :exc:`Cancelled` exception is raised in this scope\n        and caught by the context manager.\n      ValueError: if *seconds* is less than zero or NaN.\n\n    \"\"\"\n    with move_on_after(seconds, shield=shield) as scope:\n        yield scope\n    if scope.cancelled_caught:\n        raise TooSlowError\n\n\n# Users don't need to know that fail_at & fail_after wraps move_on_at and move_on_after\n# and there is no functional difference. So we replace the return value when generating\n# documentation.\nif \"sphinx.ext.autodoc\" in sys.modules:\n    import inspect\n\n    for c in (fail_at, fail_after):\n        c.__signature__ = inspect.Signature.from_callable(c).replace(return_annotation=trio.CancelScope)  # type: ignore[union-attr]\n"
  },
  {
    "path": "src/trio/_tools/__init__.py",
    "content": ""
  },
  {
    "path": "src/trio/_tools/gen_exports.py",
    "content": "#! /usr/bin/env python3\n\"\"\"\nCode generation script for class methods\nto be exported as public API\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport ast\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom textwrap import indent\nfrom typing import TYPE_CHECKING, TypeGuard\n\nimport attrs\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable, Iterator\n\n# keep these imports up to date with conditional imports in test_gen_exports\n# isort: split\nimport astor\n\nPREFIX = \"_generated\"\n\nHEADER = \"\"\"# ***********************************************************\n# ******* WARNING: AUTOGENERATED! ALL EDITS WILL BE LOST ******\n# *************************************************************\nfrom __future__ import annotations\n\nimport sys\n\nfrom ._ki import enable_ki_protection\nfrom ._run import GLOBAL_RUN_CONTEXT\n\"\"\"\n\nTEMPLATE = \"\"\"try:\n    return{}GLOBAL_RUN_CONTEXT.{}.{}\nexcept AttributeError:\n    raise RuntimeError(\"must be called from async context\") from None\n\"\"\"\n\n\n@attrs.define\nclass File:\n    path: Path\n    modname: str\n    platform: str = attrs.field(default=\"\", kw_only=True)\n    imports: str = attrs.field(default=\"\", kw_only=True)\n\n\ndef is_function(node: ast.AST) -> TypeGuard[ast.FunctionDef | ast.AsyncFunctionDef]:\n    \"\"\"Check if the AST node is either a function\n    or an async function\n    \"\"\"\n    return isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))\n\n\ndef is_public(node: ast.AST) -> TypeGuard[ast.FunctionDef | ast.AsyncFunctionDef]:\n    \"\"\"Check if the AST node has a _public decorator\"\"\"\n    if is_function(node):\n        for decorator in node.decorator_list:\n            if isinstance(decorator, ast.Name) and decorator.id == \"_public\":\n                return True\n    return False\n\n\ndef get_public_methods(\n    tree: ast.AST,\n) -> Iterator[ast.FunctionDef | ast.AsyncFunctionDef]:\n    \"\"\"Return a list of methods marked as public.\n    The function walks the given tree and extracts\n    all objects that are functions which are marked\n    public.\n    \"\"\"\n    for node in ast.walk(tree):\n        if is_public(node):\n            yield node\n\n\ndef create_passthrough_args(funcdef: ast.FunctionDef | ast.AsyncFunctionDef) -> str:\n    \"\"\"Given a function definition, create a string that represents taking all\n    the arguments from the function, and passing them through to another\n    invocation of the same function.\n\n    Example input: ast.parse(\"def f(a, *, b): ...\")\n    Example output: \"(a, b=b)\"\n    \"\"\"\n    call_args = [arg.arg for arg in funcdef.args.args]\n    if funcdef.args.vararg:\n        call_args.append(\"*\" + funcdef.args.vararg.arg)\n    for arg in funcdef.args.kwonlyargs:\n        call_args.append(arg.arg + \"=\" + arg.arg)  # noqa: PERF401  # clarity\n    if funcdef.args.kwarg:\n        call_args.append(\"**\" + funcdef.args.kwarg.arg)\n    return \"({})\".format(\", \".join(call_args))\n\n\ndef run_black(file: File, source: str) -> tuple[bool, str]:\n    \"\"\"Run black on the specified file.\n\n    Returns:\n      Tuple of success and result string.\n      ex.:\n        (False, \"Failed to run black!\\nerror: cannot format ...\")\n        (True, \"<formatted source>\")\n\n    Raises:\n      ImportError: If black is not installed.\n    \"\"\"\n    # imported to check that `subprocess` calls will succeed\n    import black  # noqa: F401\n\n    # Black has an undocumented API, but it doesn't easily allow reading configuration from\n    # pyproject.toml, and simultaneously pass in / receive the code as a string.\n    # https://github.com/psf/black/issues/779\n    result = subprocess.run(\n        # \"-\" as a filename = use stdin, return on stdout.\n        [sys.executable, \"-m\", \"black\", \"--stdin-filename\", file.path, \"-\"],\n        input=source,\n        capture_output=True,\n        encoding=\"utf8\",\n    )\n\n    if result.returncode != 0:\n        return False, f\"Failed to run black!\\n{result.stderr}\"\n    return True, result.stdout\n\n\ndef run_ruff(file: File, source: str) -> tuple[bool, str]:\n    \"\"\"Run ruff on the specified file.\n\n    Returns:\n      Tuple of success and result string.\n      ex.:\n        (False, \"Failed to run ruff!\\nerror: Failed to parse ...\")\n        (True, \"<formatted source>\")\n\n    Raises:\n      ImportError: If ruff is not installed.\n    \"\"\"\n    # imported to check that `subprocess` calls will succeed\n    import ruff  # noqa: F401\n\n    result = subprocess.run(\n        # \"-\" as a filename = use stdin, return on stdout.\n        [\n            sys.executable,\n            \"-m\",\n            \"ruff\",\n            \"check\",\n            \"--fix\",\n            \"--unsafe-fixes\",\n            \"--stdin-filename\",\n            file.path,\n            \"-\",\n        ],\n        input=source,\n        capture_output=True,\n        encoding=\"utf8\",\n    )\n\n    if result.returncode != 0:\n        return False, f\"Failed to run ruff!\\n{result.stderr}\"\n    return True, result.stdout\n\n\ndef run_linters(file: File, source: str) -> str:\n    \"\"\"Format the specified file using black and ruff.\n\n    Returns:\n      Formatted source code.\n\n    Raises:\n      ImportError: If either is not installed.\n      SystemExit: If either failed.\n    \"\"\"\n\n    for fn in (run_black, run_ruff):\n        success, source = fn(file, source)\n        if not success:\n            print(source)\n            sys.exit(1)\n\n    return source\n\n\ndef gen_public_wrappers_source(file: File) -> str:\n    \"\"\"Scan the given .py file for @_public decorators, and generate wrapper\n    functions.\n\n    \"\"\"\n    header = [HEADER]\n    header.append(file.imports)\n    if file.platform:\n        # Simple checks to avoid repeating imports. If this messes up, type checkers/tests will\n        # just give errors.\n        if \"TYPE_CHECKING\" not in file.imports:\n            header.append(\"from typing import TYPE_CHECKING\\n\")\n        if \"import sys\" not in file.imports:  # pragma: no cover\n            header.append(\"import sys\\n\")\n        header.append(\n            f'\\nassert not TYPE_CHECKING or sys.platform==\"{file.platform}\"\\n',\n        )\n\n    generated = [\"\".join(header)]\n\n    source = astor.code_to_ast.parse_file(file.path)\n    method_names = []\n    for method in get_public_methods(source):\n        # Remove self from arguments\n        assert method.args.args[0].arg == \"self\"\n        del method.args.args[0]\n        method_names.append(method.name)\n\n        for dec in method.decorator_list:  # pragma: no cover\n            if isinstance(dec, ast.Name) and dec.id == \"contextmanager\":\n                is_cm = True\n                break\n        else:\n            is_cm = False\n\n        # Remove decorators\n        method.decorator_list = [ast.Name(\"enable_ki_protection\")]\n\n        # Create pass through arguments\n        new_args = create_passthrough_args(method)\n\n        # Remove method body without the docstring\n        if ast.get_docstring(method) is None:\n            del method.body[:]\n        else:\n            # The first entry is always the docstring\n            del method.body[1:]\n\n        # Create the function definition including the body\n        func = astor.to_source(method, indent_with=\" \" * 4)\n\n        if is_cm:  # pragma: no cover\n            func = func.replace(\"->Iterator\", \"->AbstractContextManager\")\n\n        # Create export function body\n        template = TEMPLATE.format(\n            \" await \" if isinstance(method, ast.AsyncFunctionDef) else \" \",\n            file.modname,\n            method.name + new_args,\n        )\n\n        # Assemble function definition arguments and body\n        snippet = func + indent(template, \" \" * 4)\n\n        # Append the snippet to the corresponding module\n        generated.append(snippet)\n\n    method_names.sort()\n    # Insert after the header, before function definitions\n    generated.insert(1, f\"__all__ = {method_names!r}\")\n    return \"\\n\\n\".join(generated)\n\n\ndef matches_disk_files(new_files: dict[str, str]) -> bool:\n    for new_path, new_source in new_files.items():\n        if not os.path.exists(new_path):\n            return False\n        old_source = Path(new_path).read_text(encoding=\"utf-8\")\n        if old_source != new_source:\n            return False\n    return True\n\n\ndef process(files: Iterable[File], *, do_test: bool) -> None:\n    new_files = {}\n    for file in files:\n        print(\"Scanning:\", file.path)\n        new_source = gen_public_wrappers_source(file)\n        new_source = run_linters(file, new_source)\n        dirname, basename = os.path.split(file.path)\n        new_path = os.path.join(dirname, PREFIX + basename)\n        new_files[new_path] = new_source\n    matches_disk = matches_disk_files(new_files)\n    if do_test:\n        if not matches_disk:\n            print(\"Generated sources are outdated. Please regenerate.\")\n            sys.exit(1)\n        else:\n            print(\"Generated sources are up to date.\")\n    else:\n        for new_path, new_source in new_files.items():\n            Path(new_path).write_text(new_source, encoding=\"utf-8\", newline=\"\\n\")\n        print(\"Regenerated sources successfully.\")\n        if not matches_disk:  # TODO: test this branch\n            # With pre-commit integration, show that we edited files.\n            sys.exit(1)\n\n\n# This is in fact run in CI, but only in the formatting check job, which\n# doesn't collect coverage.\ndef main() -> None:  # pragma: no cover\n    parser = argparse.ArgumentParser(\n        description=\"Generate python code for public api wrappers\",\n    )\n    parser.add_argument(\n        \"--test\",\n        \"-t\",\n        action=\"store_true\",\n        help=\"test if code is still up to date\",\n    )\n    parsed_args = parser.parse_args()\n\n    source_root = Path.cwd()\n    # Double-check we found the right directory\n    assert (source_root / \"LICENSE\").exists()\n    core = source_root / \"src/trio/_core\"\n    to_wrap = [\n        File(core / \"_run.py\", \"runner\", imports=IMPORTS_RUN),\n        File(\n            core / \"_instrumentation.py\",\n            \"runner.instruments\",\n            imports=IMPORTS_INSTRUMENT,\n        ),\n        File(\n            core / \"_io_windows.py\",\n            \"runner.io_manager\",\n            platform=\"win32\",\n            imports=IMPORTS_WINDOWS,\n        ),\n        File(\n            core / \"_io_epoll.py\",\n            \"runner.io_manager\",\n            platform=\"linux\",\n            imports=IMPORTS_EPOLL,\n        ),\n        File(\n            core / \"_io_kqueue.py\",\n            \"runner.io_manager\",\n            platform=\"darwin\",\n            imports=IMPORTS_KQUEUE,\n        ),\n    ]\n\n    process(to_wrap, do_test=parsed_args.test)\n\n\nIMPORTS_RUN = \"\"\"\\\nfrom collections.abc import Awaitable, Callable\nfrom typing import Any, TYPE_CHECKING\n\nimport outcome\nimport contextvars\n\nfrom ._run import _NO_SEND, RunStatistics, Task\nfrom ._entry_queue import TrioToken\nfrom .._abc import Clock\n\nif TYPE_CHECKING:\n    from typing_extensions import Unpack\n    from ._run import PosArgT\n\"\"\"\nIMPORTS_INSTRUMENT = \"\"\"\\\nfrom ._instrumentation import Instrument\n\"\"\"\n\nIMPORTS_EPOLL = \"\"\"\\\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .._file_io import _HasFileNo\n\"\"\"\n\nIMPORTS_KQUEUE = \"\"\"\\\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    import select\n    from collections.abc import Callable\n    from contextlib import AbstractContextManager\n\n    from .. import _core\n    from .._file_io import _HasFileNo\n    from ._traps import Abort, RaiseCancelT\n\"\"\"\n\nIMPORTS_WINDOWS = \"\"\"\\\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from contextlib import AbstractContextManager\n\n    from typing_extensions import Buffer\n\n    from .._file_io import _HasFileNo\n    from ._unbounded_queue import UnboundedQueue\n    from ._windows_cffi import Handle, CData\n\"\"\"\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "src/trio/_tools/mypy_annotate.py",
    "content": "\"\"\"Translates Mypy's output into GitHub's error/warning annotation syntax.\n\nSee: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions\n\nThis first is run with Mypy's output piped in, to collect messages in\nmypy_annotate.dat. After all platforms run, we run this again, which prints the\nmessages in GitHub's format but with cross-platform failures deduplicated.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport pickle\nimport re\nimport sys\n\nimport attrs\n\n# Example: 'package/filename.py:42:1:46:3: error: Type error here [code]'\nreport_re = re.compile(\n    r\"\"\"\n    ([^:]+):  # Filename (anything but \":\")\n    ([0-9]+):  # Line number (start)\n    (?:([0-9]+):  # Optional column number\n      (?:([0-9]+):([0-9]+):)?  # then also optionally, 2 more numbers for end columns\n    )?\n    \\s*(error|warn|note):  # Kind, prefixed with space\n    (.+)  # Message\n    \"\"\",\n    re.VERBOSE,\n)\n\nmypy_to_github = {\n    \"error\": \"error\",\n    \"warn\": \"warning\",\n    \"note\": \"notice\",\n}\n\n\n@attrs.frozen(kw_only=True)\nclass Result:\n    \"\"\"Accumulated results, used as a dict key to deduplicate.\"\"\"\n\n    filename: str\n    start_line: int\n    kind: str\n    message: str\n    start_col: int | None = None\n    end_line: int | None = None\n    end_col: int | None = None\n\n\ndef process_line(line: str) -> Result | None:\n    if match := report_re.fullmatch(line.rstrip()):\n        filename, st_line, st_col, end_line, end_col, kind, message = match.groups()\n        return Result(\n            filename=filename,\n            start_line=int(st_line),\n            start_col=int(st_col) if st_col is not None else None,\n            end_line=int(end_line) if end_line is not None else None,\n            end_col=int(end_col) if end_col is not None else None,\n            kind=mypy_to_github[kind],\n            message=message,\n        )\n    else:\n        return None\n\n\ndef export(results: dict[Result, list[str]]) -> None:\n    \"\"\"Display the collected results.\"\"\"\n    for res, platforms in results.items():\n        print(f\"::{res.kind} file={res.filename},line={res.start_line},\", end=\"\")\n        if res.start_col is not None:\n            print(f\"col={res.start_col},\", end=\"\")\n            if res.end_col is not None and res.end_line is not None:\n                print(f\"endLine={res.end_line},endColumn={res.end_col},\", end=\"\")\n                message = f\"({res.start_line}:{res.start_col} - {res.end_line}:{res.end_col}):{res.message}\"\n            else:\n                message = f\"({res.start_line}:{res.start_col}):{res.message}\"\n        else:\n            message = f\"{res.start_line}:{res.message}\"\n        print(f\"title=Mypy-{'+'.join(platforms)}::{res.filename}:{message}\")\n\n\ndef main(argv: list[str]) -> None:\n    \"\"\"Look for error messages, and convert the format.\"\"\"\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\n        \"--dumpfile\",\n        help=\"File to write pickled messages to.\",\n        required=True,\n    )\n    parser.add_argument(\n        \"--platform\",\n        help=\"OS name, if set Mypy should be piped to stdin.\",\n        default=None,\n    )\n    cmd_line = parser.parse_args(argv)\n\n    results: dict[Result, list[str]]\n    try:\n        with open(cmd_line.dumpfile, \"rb\") as f:\n            results = pickle.load(f)\n    except (FileNotFoundError, pickle.UnpicklingError):\n        # If we fail to load, assume it's an old result.\n        results = {}\n\n    if cmd_line.platform is None:\n        # Write out the results.\n        export(results)\n    else:\n        platform: str = cmd_line.platform\n        for line in sys.stdin:\n            parsed = process_line(line)\n            if parsed is not None:\n                try:\n                    results[parsed].append(platform)\n                except KeyError:\n                    results[parsed] = [platform]\n            sys.stdout.write(line)\n        with open(cmd_line.dumpfile, \"wb\") as f:\n            pickle.dump(results, f)\n\n\nif __name__ == \"__main__\":\n    main(sys.argv[1:])\n"
  },
  {
    "path": "src/trio/_tools/sync_requirements.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Sync Requirements - Automatically upgrade test requirements pinned\nversions from pre-commit config file.\"\"\"\n\nfrom __future__ import annotations\n\nimport sys\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom yaml import load as load_yaml\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n    from yaml import CLoader as _CLoader, Loader as _Loader\n\n    Loader: type[_CLoader | _Loader]\n\ntry:\n    from yaml import CLoader as Loader\nexcept ImportError:\n    from yaml import Loader\n\n\ndef yield_pre_commit_version_data(\n    pre_commit_text: str,\n) -> Generator[tuple[str, str], None, None]:\n    \"\"\"Yield (name, rev) tuples from pre-commit config file.\"\"\"\n    pre_commit_config = load_yaml(pre_commit_text, Loader)\n    for repo in pre_commit_config[\"repos\"]:\n        if \"repo\" not in repo or \"rev\" not in repo:\n            continue\n        url = repo[\"repo\"]\n        name = url.rsplit(\"/\", 1)[-1]\n        rev = repo[\"rev\"].removeprefix(\"v\")\n        yield name, rev\n\n\ndef update_requirements(\n    requirements: Path,\n    version_data: dict[str, str],\n) -> bool:\n    \"\"\"Return if updated requirements file.\n\n    Update requirements file to match versions in version_data.\"\"\"\n    changed = False\n    old_lines = requirements.read_text(encoding=\"utf-8\").splitlines(True)\n\n    with requirements.open(\"w\", encoding=\"utf-8\") as file:\n        for line in old_lines:\n            # If comment or not version mark line, ignore.\n            if line.startswith(\"#\") or \"==\" not in line:\n                file.write(line)\n                continue\n            name, rest = line.split(\"==\", 1)\n            # Maintain extra markers if they exist\n            old_version = rest.strip()\n            extra = \"\\n\"\n            if \";\" in rest:\n                old_version, extra = rest.split(\";\", 1)\n                old_version = old_version.strip()\n                extra = \" ;\" + extra\n            version = version_data.get(name)\n            # If does not exist, skip\n            if version is None:\n                file.write(line)\n                continue\n            # Otherwise might have changed\n            new_line = f\"{name}=={version}{extra}\"\n            if new_line != line:\n                if not changed:\n                    changed = True\n                    print(\"Changed test requirements version to match pre-commit\")\n                print(f\"{name}=={old_version} -> {name}=={version}\")\n            file.write(new_line)\n    return changed\n\n\nif __name__ == \"__main__\":\n    source_root = Path.cwd().absolute()\n\n    # Double-check we found the right directory\n    assert (source_root / \"LICENSE\").exists()\n    pre_commit = source_root / \".pre-commit-config.yaml\"\n    test_requirements = source_root / \"test-requirements.txt\"\n\n    pre_commit_text = pre_commit.read_text(encoding=\"utf-8\")\n\n    # Get tool versions from pre-commit\n    # Get correct names\n    pre_commit_versions = {\n        name.removesuffix(\"-mirror\").removesuffix(\"-pre-commit\"): version\n        for name, version in yield_pre_commit_version_data(pre_commit_text)\n    }\n    changed = update_requirements(test_requirements, pre_commit_versions)\n    sys.exit(int(changed))\n"
  },
  {
    "path": "src/trio/_tools/windows_ffi_build.py",
    "content": "# builder for CFFI out-of-line mode, for reduced import time.\n# run this to generate `trio._core._generated_windows_ffi`.\nimport re\n\nimport cffi\n\nLIB = \"\"\"\n// https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx\ntypedef int BOOL;\ntypedef unsigned char BYTE;\ntypedef unsigned char UCHAR;\ntypedef BYTE BOOLEAN;\ntypedef void* PVOID;\ntypedef PVOID HANDLE;\ntypedef unsigned long DWORD;\ntypedef unsigned long ULONG;\ntypedef unsigned int NTSTATUS;\ntypedef unsigned long u_long;\ntypedef ULONG *PULONG;\ntypedef const void *LPCVOID;\ntypedef void *LPVOID;\ntypedef const wchar_t *LPCWSTR;\ntypedef DWORD* LPDWORD;\n\ntypedef uintptr_t ULONG_PTR;\ntypedef uintptr_t UINT_PTR;\n\ntypedef UINT_PTR SOCKET;\n\ntypedef struct _OVERLAPPED {\n    ULONG_PTR Internal;\n    ULONG_PTR InternalHigh;\n    union {\n        struct {\n            DWORD Offset;\n            DWORD OffsetHigh;\n        } DUMMYSTRUCTNAME;\n        PVOID Pointer;\n    } DUMMYUNIONNAME;\n\n    HANDLE  hEvent;\n} OVERLAPPED, *LPOVERLAPPED;\n\ntypedef OVERLAPPED WSAOVERLAPPED;\ntypedef LPOVERLAPPED LPWSAOVERLAPPED;\ntypedef PVOID LPSECURITY_ATTRIBUTES;\ntypedef PVOID LPCSTR;\n\ntypedef struct _OVERLAPPED_ENTRY {\n    ULONG_PTR lpCompletionKey;\n    LPOVERLAPPED lpOverlapped;\n    ULONG_PTR Internal;\n    DWORD dwNumberOfBytesTransferred;\n} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;\n\n// kernel32.dll\nHANDLE WINAPI CreateIoCompletionPort(\n  _In_     HANDLE    FileHandle,\n  _In_opt_ HANDLE    ExistingCompletionPort,\n  _In_     ULONG_PTR CompletionKey,\n  _In_     DWORD     NumberOfConcurrentThreads\n);\n\nBOOL SetFileCompletionNotificationModes(\n  HANDLE FileHandle,\n  UCHAR  Flags\n);\n\nHANDLE CreateFileW(\n  LPCWSTR               lpFileName,\n  DWORD                 dwDesiredAccess,\n  DWORD                 dwShareMode,\n  LPSECURITY_ATTRIBUTES lpSecurityAttributes,\n  DWORD                 dwCreationDisposition,\n  DWORD                 dwFlagsAndAttributes,\n  HANDLE                hTemplateFile\n);\n\nBOOL WINAPI CloseHandle(\n  _In_ HANDLE hObject\n);\n\nBOOL WINAPI PostQueuedCompletionStatus(\n  _In_     HANDLE       CompletionPort,\n  _In_     DWORD        dwNumberOfBytesTransferred,\n  _In_     ULONG_PTR    dwCompletionKey,\n  _In_opt_ LPOVERLAPPED lpOverlapped\n);\n\nBOOL WINAPI GetQueuedCompletionStatusEx(\n  _In_  HANDLE             CompletionPort,\n  _Out_ LPOVERLAPPED_ENTRY lpCompletionPortEntries,\n  _In_  ULONG              ulCount,\n  _Out_ PULONG             ulNumEntriesRemoved,\n  _In_  DWORD              dwMilliseconds,\n  _In_  BOOL               fAlertable\n);\n\nBOOL WINAPI CancelIoEx(\n  _In_     HANDLE       hFile,\n  _In_opt_ LPOVERLAPPED lpOverlapped\n);\n\nBOOL WriteFile(\n  HANDLE       hFile,\n  LPCVOID      lpBuffer,\n  DWORD        nNumberOfBytesToWrite,\n  LPDWORD      lpNumberOfBytesWritten,\n  LPOVERLAPPED lpOverlapped\n);\n\nBOOL ReadFile(\n  HANDLE       hFile,\n  LPVOID       lpBuffer,\n  DWORD        nNumberOfBytesToRead,\n  LPDWORD      lpNumberOfBytesRead,\n  LPOVERLAPPED lpOverlapped\n);\n\nBOOL WINAPI SetConsoleCtrlHandler(\n  _In_opt_ void*            HandlerRoutine,\n  _In_     BOOL             Add\n);\n\nHANDLE CreateEventA(\n  LPSECURITY_ATTRIBUTES lpEventAttributes,\n  BOOL                  bManualReset,\n  BOOL                  bInitialState,\n  LPCSTR                lpName\n);\n\nBOOL SetEvent(\n  HANDLE hEvent\n);\n\nBOOL ResetEvent(\n  HANDLE hEvent\n);\n\nDWORD WaitForSingleObject(\n  HANDLE hHandle,\n  DWORD  dwMilliseconds\n);\n\nDWORD WaitForMultipleObjects(\n  DWORD        nCount,\n  HANDLE       *lpHandles,\n  BOOL         bWaitAll,\n  DWORD        dwMilliseconds\n);\n\nULONG RtlNtStatusToDosError(\n  NTSTATUS Status\n);\n\nint WSAIoctl(\n  SOCKET                             s,\n  DWORD                              dwIoControlCode,\n  LPVOID                             lpvInBuffer,\n  DWORD                              cbInBuffer,\n  LPVOID                             lpvOutBuffer,\n  DWORD                              cbOutBuffer,\n  LPDWORD                            lpcbBytesReturned,\n  LPWSAOVERLAPPED                    lpOverlapped,\n  // actually LPWSAOVERLAPPED_COMPLETION_ROUTINE\n  void* lpCompletionRoutine\n);\n\nint WSAGetLastError();\n\nBOOL DeviceIoControl(\n  HANDLE       hDevice,\n  DWORD        dwIoControlCode,\n  LPVOID       lpInBuffer,\n  DWORD        nInBufferSize,\n  LPVOID       lpOutBuffer,\n  DWORD        nOutBufferSize,\n  LPDWORD      lpBytesReturned,\n  LPOVERLAPPED lpOverlapped\n);\n\n// From https://github.com/piscisaureus/wepoll/blob/master/src/afd.h\ntypedef struct _AFD_POLL_HANDLE_INFO {\n  HANDLE Handle;\n  ULONG Events;\n  NTSTATUS Status;\n} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO;\n\n// This is really defined as a messy union to allow stuff like\n// i.DUMMYSTRUCTNAME.LowPart, but we don't need those complications.\n// Under all that it's just an int64.\ntypedef int64_t LARGE_INTEGER;\n\ntypedef struct _AFD_POLL_INFO {\n  LARGE_INTEGER Timeout;\n  ULONG NumberOfHandles;\n  ULONG Exclusive;\n  AFD_POLL_HANDLE_INFO Handles[1];\n} AFD_POLL_INFO, *PAFD_POLL_INFO;\n\n\"\"\"\n\n# cribbed from pywincffi\n# programmatically strips out those annotations MSDN likes, like _In_\nLIB = re.sub(r\"\\b(_In_|_Inout_|_Out_|_Outptr_|_Reserved_)(opt_)?\\b\", \" \", LIB)\n\n# Other fixups:\n# - get rid of FAR, cffi doesn't like it\nLIB = re.sub(r\"\\bFAR\\b\", \" \", LIB)\n# - PASCAL is apparently an alias for __stdcall (on modern compilers - modern\n#   being _MSC_VER >= 800)\nLIB = re.sub(r\"\\bPASCAL\\b\", \"__stdcall\", LIB)\n\nffibuilder = cffi.FFI()\n# a bit hacky but, it works\nffibuilder.set_source(\"trio._core._generated_windows_ffi\", None)\nffibuilder.cdef(LIB)\n\nif __name__ == \"__main__\":\n    ffibuilder.compile(\"src\")\n"
  },
  {
    "path": "src/trio/_unix_pipes.py",
    "content": "from __future__ import annotations\n\nimport errno\nimport os\nimport sys\nfrom typing import TYPE_CHECKING, Final as FinalType\n\nimport trio\n\nfrom ._abc import Stream\nfrom ._util import ConflictDetector, final\n\nassert not TYPE_CHECKING or sys.platform != \"win32\"\n\n# XX TODO: is this a good number? who knows... it does match the default Linux\n# pipe capacity though.\nDEFAULT_RECEIVE_SIZE: FinalType = 65536\n\n\nclass _FdHolder:\n    # This class holds onto a raw file descriptor, in non-blocking mode, and\n    # is responsible for managing its lifecycle. In particular, it's\n    # responsible for making sure it gets closed, and also for tracking\n    # whether it's been closed.\n    #\n    # The way we track closure is to set the .fd field to -1, discarding the\n    # original value. You might think that this is a strange idea, since it\n    # overloads the same field to do two different things. Wouldn't it be more\n    # natural to have a dedicated .closed field? But that would be more\n    # error-prone. Fds are represented by small integers, and once an fd is\n    # closed, its integer value may be reused immediately. If we accidentally\n    # used the old fd after being closed, we might end up doing something to\n    # another unrelated fd that happened to get assigned the same integer\n    # value. By throwing away the integer value immediately, it becomes\n    # impossible to make this mistake – we'll just get an EBADF.\n    #\n    # (This trick was copied from the stdlib socket module.)\n    fd: int\n\n    def __init__(self, fd: int) -> None:\n        # make sure self.fd is always initialized to *something*, because even\n        # if we error out here then __del__ will run and access it.\n        self.fd = -1\n        if not isinstance(fd, int):\n            raise TypeError(\"file descriptor must be an int\")\n        self.fd = fd\n        # Store original state, and ensure non-blocking mode is enabled\n        self._original_is_blocking = os.get_blocking(fd)\n        os.set_blocking(fd, False)\n\n    @property\n    def closed(self) -> bool:\n        return self.fd == -1\n\n    def _raw_close(self) -> None:\n        # This doesn't assume it's in a Trio context, so it can be called from\n        # __del__. You should never call it from Trio context, because it\n        # skips calling notify_fd_close. But from __del__, skipping that is\n        # OK, because notify_fd_close just wakes up other tasks that are\n        # waiting on this fd, and those tasks hold a reference to this object.\n        # So if __del__ is being called, we know there aren't any tasks that\n        # need to be woken.\n        if self.closed:\n            return\n        fd = self.fd\n        self.fd = -1\n        os.set_blocking(fd, self._original_is_blocking)\n        os.close(fd)\n\n    def __del__(self) -> None:\n        self._raw_close()\n\n    def close(self) -> None:\n        if not self.closed:\n            trio.lowlevel.notify_closing(self.fd)\n            self._raw_close()\n\n\n@final\nclass FdStream(Stream):\n    \"\"\"Represents a stream given the file descriptor to a pipe, TTY, etc.\n\n    *fd* must refer to a file that is open for reading and/or writing and\n    supports non-blocking I/O (pipes and TTYs will work, on-disk files probably\n    not).  The returned stream takes ownership of the fd, so closing the stream\n    will close the fd too.  As with `os.fdopen`, you should not directly use\n    an fd after you have wrapped it in a stream using this function.\n\n    To be used as a Trio stream, an open file must be placed in non-blocking\n    mode.  Unfortunately, this impacts all I/O that goes through the\n    underlying open file, including I/O that uses a different\n    file descriptor than the one that was passed to Trio. If other threads\n    or processes are using file descriptors that are related through `os.dup`\n    or inheritance across `os.fork` to the one that Trio is using, they are\n    unlikely to be prepared to have non-blocking I/O semantics suddenly\n    thrust upon them.  For example, you can use\n    ``FdStream(os.dup(sys.stdin.fileno()))`` to obtain a stream for reading\n    from standard input, but it is only safe to do so with heavy caveats: your\n    stdin must not be shared by any other processes, and you must not make any\n    calls to synchronous methods of `sys.stdin` until the stream returned by\n    `FdStream` is closed. See `issue #174\n    <https://github.com/python-trio/trio/issues/174>`__ for a discussion of the\n    challenges involved in relaxing this restriction.\n\n    .. warning:: one specific consequence of non-blocking mode\n      applying to the entire open file description is that when\n      your program is run with multiple standard streams connected to\n      a TTY (as in a terminal emulator), all of the streams become\n      non-blocking when you construct an `FdStream` for any of them.\n      For example, if you construct an `FdStream` for standard input,\n      you might observe Python loggers begin to fail with\n      `BlockingIOError`.\n\n    Args:\n      fd (int): The fd to be wrapped.\n\n    Returns:\n      A new `FdStream` object.\n    \"\"\"\n\n    def __init__(self, fd: int) -> None:\n        self._fd_holder = _FdHolder(fd)\n        self._send_conflict_detector = ConflictDetector(\n            \"another task is using this stream for send\",\n        )\n        self._receive_conflict_detector = ConflictDetector(\n            \"another task is using this stream for receive\",\n        )\n\n    async def send_all(self, data: bytes) -> None:\n        with self._send_conflict_detector:\n            # have to check up front, because send_all(b\"\") on a closed pipe\n            # should raise\n            if self._fd_holder.closed:\n                raise trio.ClosedResourceError(\"file was already closed\")\n            await trio.lowlevel.checkpoint()\n            length = len(data)\n            # adapted from the SocketStream code\n            with memoryview(data) as view:\n                sent = 0\n                while sent < length:\n                    with view[sent:] as remaining:\n                        try:\n                            sent += os.write(self._fd_holder.fd, remaining)\n                        except BlockingIOError:\n                            await trio.lowlevel.wait_writable(self._fd_holder.fd)\n                        except OSError as e:\n                            if e.errno == errno.EBADF:\n                                raise trio.ClosedResourceError(\n                                    \"file was already closed\",\n                                ) from None\n                            else:\n                                raise trio.BrokenResourceError from e\n\n    async def wait_send_all_might_not_block(self) -> None:\n        with self._send_conflict_detector:\n            if self._fd_holder.closed:\n                raise trio.ClosedResourceError(\"file was already closed\")\n            await trio.lowlevel.wait_writable(self._fd_holder.fd)\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytes:\n        with self._receive_conflict_detector:\n            if max_bytes is None:\n                max_bytes = DEFAULT_RECEIVE_SIZE\n            else:\n                if not isinstance(max_bytes, int):\n                    raise TypeError(\"max_bytes must be integer >= 1\")\n                if max_bytes < 1:\n                    raise ValueError(\"max_bytes must be integer >= 1\")\n\n            await trio.lowlevel.checkpoint()\n            while True:\n                try:\n                    data = os.read(self._fd_holder.fd, max_bytes)\n                except BlockingIOError:\n                    await trio.lowlevel.wait_readable(self._fd_holder.fd)\n                except OSError as exc:\n                    if exc.errno == errno.EBADF:\n                        raise trio.ClosedResourceError(\n                            \"file was already closed\",\n                        ) from None\n                    else:\n                        raise trio.BrokenResourceError from exc\n                else:\n                    break\n\n            return data\n\n    def close(self) -> None:\n        self._fd_holder.close()\n\n    async def aclose(self) -> None:\n        self.close()\n        await trio.lowlevel.checkpoint()\n\n    def fileno(self) -> int:\n        return self._fd_holder.fd\n"
  },
  {
    "path": "src/trio/_util.py",
    "content": "# Little utilities we use internally\nfrom __future__ import annotations\n\nimport collections.abc\nimport inspect\nimport signal\nfrom abc import ABCMeta\nfrom collections.abc import Awaitable, Callable, Sequence\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    NoReturn,\n    TypeVar,\n    final as std_final,\n)\n\nfrom sniffio import thread_local as sniffio_loop\n\nimport trio\n\n# Explicit \"Any\" is not allowed\nCallT = TypeVar(\"CallT\", bound=Callable[..., Any])  # type: ignore[explicit-any]\nT = TypeVar(\"T\")\nRetT = TypeVar(\"RetT\")\n\nif TYPE_CHECKING:\n    import sys\n    from types import AsyncGeneratorType, TracebackType\n\n    from typing_extensions import TypeVarTuple, Unpack\n\n    if sys.version_info < (3, 11):\n        from exceptiongroup import BaseExceptionGroup\n\n    PosArgsT = TypeVarTuple(\"PosArgsT\")\n\n\n# See: #461 as to why this is needed.\n# The gist is that threading.main_thread() has the capability to lie to us\n# if somebody else edits the threading ident cache to replace the main\n# thread; causing threading.current_thread() to return a _DummyThread,\n# causing the C-c check to fail, and so on.\n# Trying to use signal out of the main thread will fail, so we can then\n# reliably check if this is the main thread without relying on a\n# potentially modified threading.\ndef is_main_thread() -> bool:\n    \"\"\"Attempt to reliably check if we are in the main thread.\"\"\"\n    try:\n        signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT))\n        return True\n    except (TypeError, ValueError):\n        return False\n\n\n######\n# Call the function and get the coroutine object, while giving helpful\n# errors for common mistakes. Returns coroutine object.\n######\ndef coroutine_or_error(\n    async_fn: Callable[[Unpack[PosArgsT]], Awaitable[RetT]],\n    *args: Unpack[PosArgsT],\n) -> collections.abc.Coroutine[object, NoReturn, RetT]:\n    def _return_value_looks_like_wrong_library(value: object) -> bool:\n        # Returned by legacy @asyncio.coroutine functions, which includes\n        # a surprising proportion of asyncio builtins.\n        if isinstance(value, collections.abc.Generator):\n            return True\n        # The protocol for detecting an asyncio Future-like object\n        if getattr(value, \"_asyncio_future_blocking\", None) is not None:\n            return True\n        # This janky check catches tornado Futures and twisted Deferreds.\n        # By the time we're calling this function, we already know\n        # something has gone wrong, so a heuristic is pretty safe.\n        return value.__class__.__name__ in (\"Future\", \"Deferred\")\n\n    # Make sure a sync-fn-that-returns-coroutine still sees itself as being\n    # in trio context\n    prev_loop, sniffio_loop.name = sniffio_loop.name, \"trio\"\n\n    try:\n        coro = async_fn(*args)\n\n    except TypeError:\n        # Give good error for: nursery.start_soon(trio.sleep(1))\n        if isinstance(async_fn, collections.abc.Coroutine):\n            # explicitly close coroutine to avoid RuntimeWarning\n            async_fn.close()\n\n            raise TypeError(\n                \"Trio was expecting an async function, but instead it got \"\n                f\"a coroutine object {async_fn!r}\\n\"\n                \"\\n\"\n                \"Probably you did something like:\\n\"\n                \"\\n\"\n                f\"  trio.run({async_fn.__name__}(...))            # incorrect!\\n\"\n                f\"  nursery.start_soon({async_fn.__name__}(...))  # incorrect!\\n\"\n                \"\\n\"\n                \"Instead, you want (notice the parentheses!):\\n\"\n                \"\\n\"\n                f\"  trio.run({async_fn.__name__}, ...)            # correct!\\n\"\n                f\"  nursery.start_soon({async_fn.__name__}, ...)  # correct!\",\n            ) from None\n\n        # Give good error for: nursery.start_soon(future)\n        if _return_value_looks_like_wrong_library(async_fn):\n            raise TypeError(\n                \"Trio was expecting an async function, but instead it got \"\n                f\"{async_fn!r} – are you trying to use a library written for \"\n                \"asyncio/twisted/tornado or similar? That won't work \"\n                \"without some sort of compatibility shim.\",\n            ) from None\n\n        raise\n\n    finally:\n        sniffio_loop.name = prev_loop\n\n    # We can't check iscoroutinefunction(async_fn), because that will fail\n    # for things like functools.partial objects wrapping an async\n    # function. So we have to just call it and then check whether the\n    # return value is a coroutine object.\n    # Note: will not be necessary on python>=3.8, see https://bugs.python.org/issue34890\n    # TODO: python3.7 support is now dropped, so the above can be addressed.\n    if not isinstance(coro, collections.abc.Coroutine):\n        # Give good error for: nursery.start_soon(func_returning_future)\n        if _return_value_looks_like_wrong_library(coro):\n            raise TypeError(\n                f\"Trio got unexpected {coro!r} – are you trying to use a \"\n                \"library written for asyncio/twisted/tornado or similar? \"\n                \"That won't work without some sort of compatibility shim.\",\n            )\n\n        if inspect.isasyncgen(coro):\n            raise TypeError(\n                \"start_soon expected an async function but got an async \"\n                f\"generator {coro!r}\",\n            )\n\n        # Give good error for: nursery.start_soon(some_sync_fn)\n        raise TypeError(\n            \"Trio expected an async function, but {!r} appears to be \"\n            \"synchronous\".format(getattr(async_fn, \"__qualname__\", async_fn)),\n        )\n\n    return coro\n\n\nclass ConflictDetector:\n    \"\"\"Detect when two tasks are about to perform operations that would\n    conflict.\n\n    Use as a synchronous context manager; if two tasks enter it at the same\n    time then the second one raises an error. You can use it when there are\n    two pieces of code that *would* collide and need a lock if they ever were\n    called at the same time, but that should never happen.\n\n    We use this in particular for things like, making sure that two different\n    tasks don't call sendall simultaneously on the same stream.\n\n    \"\"\"\n\n    def __init__(self, msg: str) -> None:\n        self._msg = msg\n        self._held = False\n\n    def __enter__(self) -> None:\n        if self._held:\n            raise trio.BusyResourceError(self._msg)\n        else:\n            self._held = True\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self._held = False\n\n\ndef async_wraps(  # type: ignore[explicit-any]\n    cls: type[object],\n    wrapped_cls: type[object],\n    attr_name: str,\n) -> Callable[[CallT], CallT]:\n    \"\"\"Similar to wraps, but for async wrappers of non-async functions.\"\"\"\n\n    def decorator(func: CallT) -> CallT:  # type: ignore[explicit-any]\n        func.__name__ = attr_name\n        func.__qualname__ = f\"{cls.__qualname__}.{attr_name}\"\n\n        func.__doc__ = f\"Like :meth:`~{wrapped_cls.__module__}.{wrapped_cls.__qualname__}.{attr_name}`, but async.\"\n\n        return func\n\n    return decorator\n\n\ndef fixup_module_metadata(\n    module_name: str,\n    namespace: collections.abc.Mapping[str, object],\n) -> None:\n    seen_ids: set[int] = set()\n\n    def fix_one(qualname: str, name: str, obj: object) -> None:\n        # avoid infinite recursion (relevant when using\n        # typing.Generic, for example)\n        if id(obj) in seen_ids:\n            return\n        seen_ids.add(id(obj))\n\n        mod = getattr(obj, \"__module__\", None)\n        if mod is not None and mod.startswith(\"trio.\"):\n            obj.__module__ = module_name\n            # Modules, unlike everything else in Python, put fully-qualified\n            # names into their __name__ attribute. We check for \".\" to avoid\n            # rewriting these.\n            if hasattr(obj, \"__name__\") and \".\" not in obj.__name__:\n                obj.__name__ = name\n                if hasattr(obj, \"__qualname__\"):\n                    obj.__qualname__ = qualname\n            if isinstance(obj, type):\n                for attr_name, attr_value in obj.__dict__.items():\n                    fix_one(objname + \".\" + attr_name, attr_name, attr_value)\n\n    for objname, obj in namespace.items():\n        if not objname.startswith(\"_\"):  # ignore private attributes\n            fix_one(objname, objname, obj)\n\n\ndef _init_final_cls(cls: type[object]) -> NoReturn:\n    \"\"\"Raises an exception when a final class is subclassed.\"\"\"\n    raise TypeError(f\"{cls.__module__}.{cls.__qualname__} does not support subclassing\")\n\n\ndef _final_impl(decorated: type[T]) -> type[T]:\n    \"\"\"Decorator that enforces a class to be final (i.e., subclass not allowed).\n\n    If a class uses this metaclass like this::\n\n        @final\n        class SomeClass:\n            pass\n\n    The metaclass will ensure that no subclass can be created.\n\n    Raises\n    ------\n    - TypeError if a subclass is created\n    \"\"\"\n    # Override the method blindly. We're always going to raise, so it doesn't\n    # matter what the original did (if anything).\n    decorated.__init_subclass__ = classmethod(_init_final_cls)  # type: ignore[assignment]\n    # Apply the typing decorator, in 3.11+ it adds a __final__ marker attribute.\n    return std_final(decorated)\n\n\nif TYPE_CHECKING:\n    from typing import final\nelse:\n    final = _final_impl\n\n\n@final  # No subclassing of NoPublicConstructor itself.\nclass NoPublicConstructor(ABCMeta):\n    \"\"\"Metaclass that ensures a private constructor.\n\n    If a class uses this metaclass like this::\n\n        @final\n        class SomeClass(metaclass=NoPublicConstructor):\n            pass\n\n    The metaclass will ensure that no instance can be initialized. This should always be\n    used with @final.\n\n    If you try to instantiate your class (SomeClass()), a TypeError will be thrown. Use\n    _create() instead in the class's implementation.\n\n    Raises\n    ------\n    - TypeError if an instance is created.\n    \"\"\"\n\n    def __call__(cls, *args: object, **kwargs: object) -> None:\n        raise TypeError(\n            f\"{cls.__module__}.{cls.__qualname__} has no public constructor\",\n        )\n\n    def _create(cls: type[T], *args: object, **kwargs: object) -> T:\n        return super().__call__(*args, **kwargs)  # type: ignore\n\n\ndef name_asyncgen(agen: AsyncGeneratorType[object, NoReturn]) -> str:\n    \"\"\"Return the fully-qualified name of the async generator function\n    that produced the async generator iterator *agen*.\n    \"\"\"\n    if not hasattr(agen, \"ag_code\"):  # pragma: no cover\n        return repr(agen)\n    try:\n        module = agen.ag_frame.f_globals[\"__name__\"]\n    except (AttributeError, KeyError):\n        module = f\"<{agen.ag_code.co_filename}>\"\n    try:\n        qualname = agen.__qualname__\n    except AttributeError:\n        qualname = agen.ag_code.co_name\n    return f\"{module}.{qualname}\"\n\n\n# work around a pyright error\nif TYPE_CHECKING:\n    Fn = TypeVar(\"Fn\", bound=Callable[..., object])  # type: ignore[explicit-any]\n\n    def wraps(  # type: ignore[explicit-any]\n        wrapped: Callable[..., object],\n        assigned: Sequence[str] = ...,\n        updated: Sequence[str] = ...,\n    ) -> Callable[[Fn], Fn]: ...\n\nelse:\n    from functools import wraps  # noqa: F401  # this is re-exported\n\n\ndef raise_saving_context(exc: BaseException) -> NoReturn:\n    \"\"\"This helper allows re-raising an exception without __context__ being set.\"\"\"\n    # cause does not need special handling, we simply avoid using `raise .. from ..`\n    # __suppress_context__ also does not need handling, it's only set if modifying cause\n    __tracebackhide__ = True\n    context = exc.__context__\n    try:\n        raise exc\n    finally:\n        exc.__context__ = context\n        del exc, context\n\n\nclass MultipleExceptionError(Exception):\n    \"\"\"Raised by raise_single_exception_from_group if encountering multiple\n    non-cancelled exceptions.\"\"\"\n\n\ndef raise_single_exception_from_group(\n    eg: BaseExceptionGroup[BaseException],\n) -> NoReturn:\n    \"\"\"This function takes an exception group that is assumed to have at most\n    one non-cancelled exception, which it reraises as a standalone exception.\n\n    This exception may be an exceptiongroup itself, in which case it will not be unwrapped.\n\n    If a :exc:`KeyboardInterrupt` is encountered, a new KeyboardInterrupt is immediately\n    raised with the entire group as cause.\n\n    If the group only contains :exc:`Cancelled` it reraises the first one encountered.\n\n    It will retain context and cause of the contained exception, and entirely discard\n    the cause/context of the group(s).\n\n    If multiple non-cancelled exceptions are encountered, it raises\n    :exc:`AssertionError`.\n    \"\"\"\n    # immediately bail out if there's any KI or SystemExit\n    for e in eg.exceptions:\n        if isinstance(e, (KeyboardInterrupt, SystemExit)):\n            raise type(e)(*e.args) from eg\n\n    cancelled_exception: trio.Cancelled | None = None\n    noncancelled_exception: BaseException | None = None\n\n    for e in eg.exceptions:\n        if isinstance(e, trio.Cancelled):\n            if cancelled_exception is None:\n                cancelled_exception = e\n        elif noncancelled_exception is None:\n            noncancelled_exception = e\n        else:\n            raise MultipleExceptionError(\n                \"Attempted to unwrap exceptiongroup with multiple non-cancelled exceptions. This is often caused by a bug in the caller.\"\n            ) from eg\n\n    if noncancelled_exception is not None:\n        raise_saving_context(noncancelled_exception)\n\n    assert cancelled_exception is not None, \"group can't be empty\"\n    raise_saving_context(cancelled_exception)\n"
  },
  {
    "path": "src/trio/_version.py",
    "content": "# This file is imported from __init__.py and parsed by setuptools\n\n__version__ = \"0.33.0+dev\"\n"
  },
  {
    "path": "src/trio/_wait_for_object.py",
    "content": "from __future__ import annotations\n\nimport math\n\nimport trio\n\nfrom ._core._windows_cffi import (\n    CData,\n    ErrorCodes,\n    _handle,\n    ffi,\n    handle_array,\n    kernel32,\n    raise_winerror,\n)\n\n\nasync def WaitForSingleObject(obj: int | CData) -> None:\n    \"\"\"Async and cancellable variant of WaitForSingleObject. Windows only.\n\n    Args:\n      handle: A Win32 handle, as a Python integer.\n\n    Raises:\n      OSError: If the handle is invalid, e.g. when it is already closed.\n\n    \"\"\"\n    # Allow ints or whatever we can convert to a win handle\n    handle = _handle(obj)\n\n    # Quick check; we might not even need to spawn a thread. The zero\n    # means a zero timeout; this call never blocks. We also exit here\n    # if the handle is already closed for some reason.\n    retcode = kernel32.WaitForSingleObject(handle, 0)\n    if retcode == ErrorCodes.WAIT_FAILED:\n        raise_winerror()\n    elif retcode != ErrorCodes.WAIT_TIMEOUT:\n        return\n\n    # Wait for a thread that waits for two handles: the handle plus a handle\n    # that we can use to cancel the thread.\n    cancel_handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)\n    try:\n        await trio.to_thread.run_sync(\n            WaitForMultipleObjects_sync,\n            handle,\n            cancel_handle,\n            abandon_on_cancel=True,\n            limiter=trio.CapacityLimiter(math.inf),\n        )\n    finally:\n        # Clean up our cancel handle. In case we get here because this task was\n        # cancelled, we also want to set the cancel_handle to stop the thread.\n        kernel32.SetEvent(cancel_handle)\n        kernel32.CloseHandle(cancel_handle)\n\n\ndef WaitForMultipleObjects_sync(*handles: int | CData) -> None:\n    \"\"\"Wait for any of the given Windows handles to be signaled.\"\"\"\n    n = len(handles)\n    handle_arr = handle_array(n)\n    for i in range(n):\n        handle_arr[i] = handles[i]\n    timeout = 0xFFFFFFFF  # INFINITE\n    retcode = kernel32.WaitForMultipleObjects(n, handle_arr, False, timeout)  # blocking\n    if retcode == ErrorCodes.WAIT_FAILED:\n        raise_winerror()\n"
  },
  {
    "path": "src/trio/_windows_pipes.py",
    "content": "from __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING\n\nfrom . import _core\nfrom ._abc import ReceiveStream, SendStream\nfrom ._core._windows_cffi import _handle, kernel32, raise_winerror\nfrom ._util import ConflictDetector, final\n\nassert sys.platform == \"win32\" or not TYPE_CHECKING\n\n# XX TODO: don't just make this up based on nothing.\nDEFAULT_RECEIVE_SIZE = 65536\n\n\n# See the comments on _unix_pipes._FdHolder for discussion of why we set the\n# handle to -1 when it's closed.\nclass _HandleHolder:\n    def __init__(self, handle: int) -> None:\n        self.handle = -1\n        if not isinstance(handle, int):\n            raise TypeError(\"handle must be an int\")\n        self.handle = handle\n        _core.register_with_iocp(self.handle)\n\n    @property\n    def closed(self) -> bool:\n        return self.handle == -1\n\n    def close(self) -> None:\n        if self.closed:\n            return\n        handle = self.handle\n        self.handle = -1\n        if not kernel32.CloseHandle(_handle(handle)):\n            raise_winerror()\n\n    def __del__(self) -> None:\n        self.close()\n\n\n@final\nclass PipeSendStream(SendStream):\n    \"\"\"Represents a send stream over a Windows named pipe that has been\n    opened in OVERLAPPED mode.\n    \"\"\"\n\n    def __init__(self, handle: int) -> None:\n        self._handle_holder = _HandleHolder(handle)\n        self._conflict_detector = ConflictDetector(\n            \"another task is currently using this pipe\",\n        )\n\n    async def send_all(self, data: bytes) -> None:\n        with self._conflict_detector:\n            if self._handle_holder.closed:\n                raise _core.ClosedResourceError(\"this pipe is already closed\")\n\n            if not data:\n                await _core.checkpoint()\n                return\n\n            try:\n                written = await _core.write_overlapped(self._handle_holder.handle, data)\n            except BrokenPipeError as ex:\n                raise _core.BrokenResourceError from ex\n            # By my reading of MSDN, this assert is guaranteed to pass so long\n            # as the pipe isn't in nonblocking mode, but... let's just\n            # double-check.\n            assert written == len(data)\n\n    async def wait_send_all_might_not_block(self) -> None:\n        with self._conflict_detector:\n            if self._handle_holder.closed:\n                raise _core.ClosedResourceError(\"This pipe is already closed\")\n\n            # not implemented yet, and probably not needed\n            await _core.checkpoint()\n\n    def close(self) -> None:\n        self._handle_holder.close()\n\n    async def aclose(self) -> None:\n        self.close()\n        await _core.checkpoint()\n\n\n@final\nclass PipeReceiveStream(ReceiveStream):\n    \"\"\"Represents a receive stream over an os.pipe object.\"\"\"\n\n    def __init__(self, handle: int) -> None:\n        self._handle_holder = _HandleHolder(handle)\n        self._conflict_detector = ConflictDetector(\n            \"another task is currently using this pipe\",\n        )\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytes:\n        with self._conflict_detector:\n            if self._handle_holder.closed:\n                raise _core.ClosedResourceError(\"this pipe is already closed\")\n\n            if max_bytes is None:\n                max_bytes = DEFAULT_RECEIVE_SIZE\n            else:\n                if not isinstance(max_bytes, int):\n                    raise TypeError(\"max_bytes must be integer >= 1\")\n                if max_bytes < 1:\n                    raise ValueError(\"max_bytes must be integer >= 1\")\n\n            buffer = bytearray(max_bytes)\n            try:\n                size = await _core.readinto_overlapped(\n                    self._handle_holder.handle,\n                    buffer,\n                )\n            except BrokenPipeError:\n                if self._handle_holder.closed:\n                    raise _core.ClosedResourceError(\n                        \"another task closed this pipe\",\n                    ) from None\n\n                # Windows raises BrokenPipeError on one end of a pipe\n                # whenever the other end closes, regardless of direction.\n                # Convert this to the Unix behavior of returning EOF to the\n                # reader when the writer closes.\n                #\n                # And since we're not raising an exception, we have to\n                # checkpoint. But readinto_overlapped did raise an exception,\n                # so it might not have checkpointed for us. So we have to\n                # checkpoint manually.\n                await _core.checkpoint()\n                return b\"\"\n            else:\n                del buffer[size:]\n                return buffer\n\n    def close(self) -> None:\n        self._handle_holder.close()\n\n    async def aclose(self) -> None:\n        self.close()\n        await _core.checkpoint()\n"
  },
  {
    "path": "src/trio/abc.py",
    "content": "# This is a public namespace, so we don't want to expose any non-underscored\n# attributes that aren't actually part of our public API. But it's very\n# annoying to carefully always use underscored names for module-level\n# temporaries, imports, etc. when implementing the module. So we put the\n# implementation in an underscored module, and then re-export the public parts\n# here.\n\n# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)\nfrom ._abc import (\n    AsyncResource as AsyncResource,\n    Channel as Channel,\n    Clock as Clock,\n    HalfCloseableStream as HalfCloseableStream,\n    HostnameResolver as HostnameResolver,\n    Instrument as Instrument,\n    Listener as Listener,\n    ReceiveChannel as ReceiveChannel,\n    ReceiveStream as ReceiveStream,\n    SendChannel as SendChannel,\n    SendStream as SendStream,\n    SocketFactory as SocketFactory,\n    Stream as Stream,\n)\n"
  },
  {
    "path": "src/trio/from_thread.py",
    "content": "\"\"\"\nThis namespace represents special functions that can call back into Trio from\nan external thread by means of a Trio Token present in Thread Local Storage\n\"\"\"\n\nfrom ._threads import (\n    from_thread_check_cancelled as check_cancelled,\n    from_thread_run as run,\n    from_thread_run_sync as run_sync,\n)\n\n# need to use __all__ for pyright --verifytypes to see re-exports when renaming them\n__all__ = [\"check_cancelled\", \"run\", \"run_sync\"]\n"
  },
  {
    "path": "src/trio/lowlevel.py",
    "content": "\"\"\"\nThis namespace represents low-level functionality not intended for daily use,\nbut useful for extending Trio's functionality.\n\"\"\"\n\n# imports are renamed with leading underscores to indicate they are not part of the public API\nimport select as _select\n\n# static checkers don't understand if importing this as _sys, so it's deleted later\nimport sys\nimport typing as _t\n\n# Generally available symbols\nfrom ._core import (\n    Abort as Abort,\n    ParkingLot as ParkingLot,\n    ParkingLotStatistics as ParkingLotStatistics,\n    RaiseCancelT as RaiseCancelT,\n    RunStatistics as RunStatistics,\n    RunVar as RunVar,\n    RunVarToken as RunVarToken,\n    Task as Task,\n    TrioToken as TrioToken,\n    UnboundedQueue as UnboundedQueue,\n    UnboundedQueueStatistics as UnboundedQueueStatistics,\n    add_instrument as add_instrument,\n    add_parking_lot_breaker as add_parking_lot_breaker,\n    cancel_shielded_checkpoint as cancel_shielded_checkpoint,\n    checkpoint as checkpoint,\n    checkpoint_if_cancelled as checkpoint_if_cancelled,\n    current_clock as current_clock,\n    current_root_task as current_root_task,\n    current_statistics as current_statistics,\n    current_task as current_task,\n    current_trio_token as current_trio_token,\n    currently_ki_protected as currently_ki_protected,\n    disable_ki_protection as disable_ki_protection,\n    enable_ki_protection as enable_ki_protection,\n    in_trio_run as in_trio_run,\n    in_trio_task as in_trio_task,\n    notify_closing as notify_closing,\n    permanently_detach_coroutine_object as permanently_detach_coroutine_object,\n    reattach_detached_coroutine_object as reattach_detached_coroutine_object,\n    remove_instrument as remove_instrument,\n    remove_parking_lot_breaker as remove_parking_lot_breaker,\n    reschedule as reschedule,\n    spawn_system_task as spawn_system_task,\n    start_guest_run as start_guest_run,\n    start_thread_soon as start_thread_soon,\n    temporarily_detach_coroutine_object as temporarily_detach_coroutine_object,\n    wait_readable as wait_readable,\n    wait_task_rescheduled as wait_task_rescheduled,\n    wait_writable as wait_writable,\n)\nfrom ._subprocess import open_process as open_process\n\n# This is the union of a subset of trio/_core/ and some things from trio/*.py.\n# See comments in trio/__init__.py for details.\n\n# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)\n\nif sys.platform == \"win32\" or (\n    not _t.TYPE_CHECKING and \"sphinx.ext.autodoc\" in sys.modules\n):\n    # Windows symbols\n    from ._core import (\n        current_iocp as current_iocp,\n        monitor_completion_key as monitor_completion_key,\n        readinto_overlapped as readinto_overlapped,\n        register_with_iocp as register_with_iocp,\n        wait_overlapped as wait_overlapped,\n        write_overlapped as write_overlapped,\n    )\n\n    # don't let documentation import the actual implementation\n    if sys.platform == \"win32\":  # pragma: no branch\n        from ._wait_for_object import WaitForSingleObject as WaitForSingleObject\n\nif sys.platform != \"win32\" or (\n    not _t.TYPE_CHECKING and \"sphinx.ext.autodoc\" in sys.modules\n):\n    # Unix symbols\n    from ._unix_pipes import FdStream as FdStream\n\n    # Kqueue-specific symbols\n    if (\n        sys.platform != \"linux\" and (_t.TYPE_CHECKING or not hasattr(_select, \"epoll\"))\n    ) or (not _t.TYPE_CHECKING and \"sphinx.ext.autodoc\" in sys.modules):\n        from ._core import (\n            current_kqueue as current_kqueue,\n            monitor_kevent as monitor_kevent,\n            wait_kevent as wait_kevent,\n        )\n\ndel sys\n"
  },
  {
    "path": "src/trio/py.typed",
    "content": ""
  },
  {
    "path": "src/trio/socket.py",
    "content": "from __future__ import annotations\n\n# This is a public namespace, so we don't want to expose any non-underscored\n# attributes that aren't actually part of our public API. But it's very\n# annoying to carefully always use underscored names for module-level\n# temporaries, imports, etc. when implementing the module. So we put the\n# implementation in an underscored module, and then re-export the public parts\n# here.\n# We still have some underscore names though but only a few.\nimport socket as _stdlib_socket\n\n# static checkers don't understand if importing this as _sys, so it's deleted later\nimport sys\nimport typing as _t\n\nfrom . import _socket\n\n_bad_symbols: set[str] = set()\nif sys.platform == \"win32\":\n    # See https://github.com/python-trio/trio/issues/39\n    # Do not import for windows platform\n    # (you can still get it from stdlib socket, of course, if you want it)\n    _bad_symbols.add(\"SO_REUSEADDR\")\n\n# Dynamically re-export whatever constants this particular Python happens to\n# have:\nglobals().update(\n    {\n        name: getattr(_stdlib_socket, name)\n        for name in _stdlib_socket.__all__\n        if name.isupper() and name not in _bad_symbols\n    },\n)\n\n# import the overwrites\nfrom contextlib import suppress as _suppress\n\n# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)\nfrom ._socket import (\n    SocketType as SocketType,\n    from_stdlib_socket as from_stdlib_socket,\n    fromfd as fromfd,\n    getaddrinfo as getaddrinfo,\n    getnameinfo as getnameinfo,\n    getprotobyname as getprotobyname,\n    set_custom_hostname_resolver as set_custom_hostname_resolver,\n    set_custom_socket_factory as set_custom_socket_factory,\n    socket as socket,\n    socketpair as socketpair,\n)\n\n# not always available so expose only if\nif sys.platform == \"win32\" or not _t.TYPE_CHECKING:\n    with _suppress(ImportError):\n        from ._socket import fromshare as fromshare\n\n# expose these functions to trio.socket\nfrom socket import (\n    gaierror as gaierror,\n    gethostname as gethostname,\n    herror as herror,\n    htonl as htonl,\n    htons as htons,\n    inet_aton as inet_aton,\n    inet_ntoa as inet_ntoa,\n    inet_ntop as inet_ntop,\n    inet_pton as inet_pton,\n    ntohs as ntohs,\n)\n\nif sys.implementation.name == \"cpython\":\n    from socket import (\n        if_indextoname as if_indextoname,\n        if_nametoindex as if_nametoindex,\n    )\n\n    # For android devices, if_nameindex support was introduced in API 24,\n    # so it doesn't exist for any version prior.\n    with _suppress(ImportError):\n        from socket import (\n            if_nameindex as if_nameindex,\n        )\n\n\n# not always available so expose only if\nif sys.platform != \"win32\" or not _t.TYPE_CHECKING:\n    with _suppress(ImportError):\n        from socket import (\n            sethostname as sethostname,\n        )\n\nif _t.TYPE_CHECKING:\n    IP_BIND_ADDRESS_NO_PORT: int\nelse:\n    try:\n        IP_BIND_ADDRESS_NO_PORT  # noqa: B018  # \"useless expression\"\n    except NameError:\n        if sys.platform == \"linux\":\n            IP_BIND_ADDRESS_NO_PORT = 24\n\ndel sys\n\n\n# The socket module exports a bunch of platform-specific constants. We want to\n# re-export them. Since the exact set of constants varies depending on Python\n# version, platform, the libc installed on the system where Python was built,\n# etc., we figure out which constants to re-export dynamically at runtime (see\n# above). But that confuses static analysis tools like jedi and mypy. So this\n# import statement statically lists every constant that *could* be\n# exported. There's a test in test_exports.py to make sure that the list is\n# kept up to date.\nif _t.TYPE_CHECKING:\n    from socket import (  # type: ignore[attr-defined]\n        AF_ALG as AF_ALG,\n        AF_APPLETALK as AF_APPLETALK,\n        AF_ASH as AF_ASH,\n        AF_ATMPVC as AF_ATMPVC,\n        AF_ATMSVC as AF_ATMSVC,\n        AF_AX25 as AF_AX25,\n        AF_BLUETOOTH as AF_BLUETOOTH,\n        AF_BRIDGE as AF_BRIDGE,\n        AF_CAN as AF_CAN,\n        AF_ECONET as AF_ECONET,\n        AF_HYPERV as AF_HYPERV,\n        AF_INET as AF_INET,\n        AF_INET6 as AF_INET6,\n        AF_IPX as AF_IPX,\n        AF_IRDA as AF_IRDA,\n        AF_KEY as AF_KEY,\n        AF_LINK as AF_LINK,\n        AF_LLC as AF_LLC,\n        AF_NETBEUI as AF_NETBEUI,\n        AF_NETLINK as AF_NETLINK,\n        AF_NETROM as AF_NETROM,\n        AF_PACKET as AF_PACKET,\n        AF_PPPOX as AF_PPPOX,\n        AF_QIPCRTR as AF_QIPCRTR,\n        AF_RDS as AF_RDS,\n        AF_ROSE as AF_ROSE,\n        AF_ROUTE as AF_ROUTE,\n        AF_SECURITY as AF_SECURITY,\n        AF_SNA as AF_SNA,\n        AF_SYSTEM as AF_SYSTEM,\n        AF_TIPC as AF_TIPC,\n        AF_UNIX as AF_UNIX,\n        AF_UNSPEC as AF_UNSPEC,\n        AF_VSOCK as AF_VSOCK,\n        AF_WANPIPE as AF_WANPIPE,\n        AF_X25 as AF_X25,\n        AI_ADDRCONFIG as AI_ADDRCONFIG,\n        AI_ALL as AI_ALL,\n        AI_CANONNAME as AI_CANONNAME,\n        AI_DEFAULT as AI_DEFAULT,\n        AI_MASK as AI_MASK,\n        AI_NUMERICHOST as AI_NUMERICHOST,\n        AI_NUMERICSERV as AI_NUMERICSERV,\n        AI_PASSIVE as AI_PASSIVE,\n        AI_V4MAPPED as AI_V4MAPPED,\n        AI_V4MAPPED_CFG as AI_V4MAPPED_CFG,\n        ALG_OP_DECRYPT as ALG_OP_DECRYPT,\n        ALG_OP_ENCRYPT as ALG_OP_ENCRYPT,\n        ALG_OP_SIGN as ALG_OP_SIGN,\n        ALG_OP_VERIFY as ALG_OP_VERIFY,\n        ALG_SET_AEAD_ASSOCLEN as ALG_SET_AEAD_ASSOCLEN,\n        ALG_SET_AEAD_AUTHSIZE as ALG_SET_AEAD_AUTHSIZE,\n        ALG_SET_IV as ALG_SET_IV,\n        ALG_SET_KEY as ALG_SET_KEY,\n        ALG_SET_OP as ALG_SET_OP,\n        ALG_SET_PUBKEY as ALG_SET_PUBKEY,\n        BDADDR_ANY as BDADDR_ANY,\n        BDADDR_LOCAL as BDADDR_LOCAL,\n        BTPROTO_HCI as BTPROTO_HCI,\n        BTPROTO_L2CAP as BTPROTO_L2CAP,\n        BTPROTO_RFCOMM as BTPROTO_RFCOMM,\n        BTPROTO_SCO as BTPROTO_SCO,\n        CAN_BCM as CAN_BCM,\n        CAN_BCM_CAN_FD_FRAME as CAN_BCM_CAN_FD_FRAME,\n        CAN_BCM_RX_ANNOUNCE_RESUME as CAN_BCM_RX_ANNOUNCE_RESUME,\n        CAN_BCM_RX_CHANGED as CAN_BCM_RX_CHANGED,\n        CAN_BCM_RX_CHECK_DLC as CAN_BCM_RX_CHECK_DLC,\n        CAN_BCM_RX_DELETE as CAN_BCM_RX_DELETE,\n        CAN_BCM_RX_FILTER_ID as CAN_BCM_RX_FILTER_ID,\n        CAN_BCM_RX_NO_AUTOTIMER as CAN_BCM_RX_NO_AUTOTIMER,\n        CAN_BCM_RX_READ as CAN_BCM_RX_READ,\n        CAN_BCM_RX_RTR_FRAME as CAN_BCM_RX_RTR_FRAME,\n        CAN_BCM_RX_SETUP as CAN_BCM_RX_SETUP,\n        CAN_BCM_RX_STATUS as CAN_BCM_RX_STATUS,\n        CAN_BCM_RX_TIMEOUT as CAN_BCM_RX_TIMEOUT,\n        CAN_BCM_SETTIMER as CAN_BCM_SETTIMER,\n        CAN_BCM_STARTTIMER as CAN_BCM_STARTTIMER,\n        CAN_BCM_TX_ANNOUNCE as CAN_BCM_TX_ANNOUNCE,\n        CAN_BCM_TX_COUNTEVT as CAN_BCM_TX_COUNTEVT,\n        CAN_BCM_TX_CP_CAN_ID as CAN_BCM_TX_CP_CAN_ID,\n        CAN_BCM_TX_DELETE as CAN_BCM_TX_DELETE,\n        CAN_BCM_TX_EXPIRED as CAN_BCM_TX_EXPIRED,\n        CAN_BCM_TX_READ as CAN_BCM_TX_READ,\n        CAN_BCM_TX_RESET_MULTI_IDX as CAN_BCM_TX_RESET_MULTI_IDX,\n        CAN_BCM_TX_SEND as CAN_BCM_TX_SEND,\n        CAN_BCM_TX_SETUP as CAN_BCM_TX_SETUP,\n        CAN_BCM_TX_STATUS as CAN_BCM_TX_STATUS,\n        CAN_EFF_FLAG as CAN_EFF_FLAG,\n        CAN_EFF_MASK as CAN_EFF_MASK,\n        CAN_ERR_FLAG as CAN_ERR_FLAG,\n        CAN_ERR_MASK as CAN_ERR_MASK,\n        CAN_ISOTP as CAN_ISOTP,\n        CAN_J1939 as CAN_J1939,\n        CAN_RAW as CAN_RAW,\n        CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER,\n        CAN_RAW_FD_FRAMES as CAN_RAW_FD_FRAMES,\n        CAN_RAW_FILTER as CAN_RAW_FILTER,\n        CAN_RAW_JOIN_FILTERS as CAN_RAW_JOIN_FILTERS,\n        CAN_RAW_LOOPBACK as CAN_RAW_LOOPBACK,\n        CAN_RAW_RECV_OWN_MSGS as CAN_RAW_RECV_OWN_MSGS,\n        CAN_RTR_FLAG as CAN_RTR_FLAG,\n        CAN_SFF_MASK as CAN_SFF_MASK,\n        CAPI as CAPI,\n        CMSG_LEN as CMSG_LEN,\n        CMSG_SPACE as CMSG_SPACE,\n        EAGAIN as EAGAIN,\n        EAI_ADDRFAMILY as EAI_ADDRFAMILY,\n        EAI_AGAIN as EAI_AGAIN,\n        EAI_BADFLAGS as EAI_BADFLAGS,\n        EAI_BADHINTS as EAI_BADHINTS,\n        EAI_FAIL as EAI_FAIL,\n        EAI_FAMILY as EAI_FAMILY,\n        EAI_MAX as EAI_MAX,\n        EAI_MEMORY as EAI_MEMORY,\n        EAI_NODATA as EAI_NODATA,\n        EAI_NONAME as EAI_NONAME,\n        EAI_OVERFLOW as EAI_OVERFLOW,\n        EAI_PROTOCOL as EAI_PROTOCOL,\n        EAI_SERVICE as EAI_SERVICE,\n        EAI_SOCKTYPE as EAI_SOCKTYPE,\n        EAI_SYSTEM as EAI_SYSTEM,\n        EBADF as EBADF,\n        ETH_P_ALL as ETH_P_ALL,\n        ETHERTYPE_ARP as ETHERTYPE_ARP,\n        ETHERTYPE_IP as ETHERTYPE_IP,\n        ETHERTYPE_IPV6 as ETHERTYPE_IPV6,\n        ETHERTYPE_VLAN as ETHERTYPE_VLAN,\n        EWOULDBLOCK as EWOULDBLOCK,\n        FD_ACCEPT as FD_ACCEPT,\n        FD_CLOSE as FD_CLOSE,\n        FD_CLOSE_BIT as FD_CLOSE_BIT,\n        FD_CONNECT as FD_CONNECT,\n        FD_CONNECT_BIT as FD_CONNECT_BIT,\n        FD_READ as FD_READ,\n        FD_SETSIZE as FD_SETSIZE,\n        FD_WRITE as FD_WRITE,\n        HCI_DATA_DIR as HCI_DATA_DIR,\n        HCI_FILTER as HCI_FILTER,\n        HCI_TIME_STAMP as HCI_TIME_STAMP,\n        HV_GUID_BROADCAST as HV_GUID_BROADCAST,\n        HV_GUID_CHILDREN as HV_GUID_CHILDREN,\n        HV_GUID_LOOPBACK as HV_GUID_LOOPBACK,\n        HV_GUID_PARENT as HV_GUID_PARENT,\n        HV_GUID_WILDCARD as HV_GUID_WILDCARD,\n        HV_GUID_ZERO as HV_GUID_ZERO,\n        HV_PROTOCOL_RAW as HV_PROTOCOL_RAW,\n        HVSOCKET_ADDRESS_FLAG_PASSTHRU as HVSOCKET_ADDRESS_FLAG_PASSTHRU,\n        HVSOCKET_CONNECT_TIMEOUT as HVSOCKET_CONNECT_TIMEOUT,\n        HVSOCKET_CONNECT_TIMEOUT_MAX as HVSOCKET_CONNECT_TIMEOUT_MAX,\n        HVSOCKET_CONNECTED_SUSPEND as HVSOCKET_CONNECTED_SUSPEND,\n        INADDR_ALLHOSTS_GROUP as INADDR_ALLHOSTS_GROUP,\n        INADDR_ANY as INADDR_ANY,\n        INADDR_BROADCAST as INADDR_BROADCAST,\n        INADDR_LOOPBACK as INADDR_LOOPBACK,\n        INADDR_MAX_LOCAL_GROUP as INADDR_MAX_LOCAL_GROUP,\n        INADDR_NONE as INADDR_NONE,\n        INADDR_UNSPEC_GROUP as INADDR_UNSPEC_GROUP,\n        INFINITE as INFINITE,\n        IOCTL_VM_SOCKETS_GET_LOCAL_CID as IOCTL_VM_SOCKETS_GET_LOCAL_CID,\n        IP_ADD_MEMBERSHIP as IP_ADD_MEMBERSHIP,\n        IP_ADD_SOURCE_MEMBERSHIP as IP_ADD_SOURCE_MEMBERSHIP,\n        IP_BLOCK_SOURCE as IP_BLOCK_SOURCE,\n        IP_DEFAULT_MULTICAST_LOOP as IP_DEFAULT_MULTICAST_LOOP,\n        IP_DEFAULT_MULTICAST_TTL as IP_DEFAULT_MULTICAST_TTL,\n        IP_DROP_MEMBERSHIP as IP_DROP_MEMBERSHIP,\n        IP_DROP_SOURCE_MEMBERSHIP as IP_DROP_SOURCE_MEMBERSHIP,\n        IP_FREEBIND as IP_FREEBIND,\n        IP_HDRINCL as IP_HDRINCL,\n        IP_MAX_MEMBERSHIPS as IP_MAX_MEMBERSHIPS,\n        IP_MULTICAST_IF as IP_MULTICAST_IF,\n        IP_MULTICAST_LOOP as IP_MULTICAST_LOOP,\n        IP_MULTICAST_TTL as IP_MULTICAST_TTL,\n        IP_OPTIONS as IP_OPTIONS,\n        IP_PKTINFO as IP_PKTINFO,\n        IP_RECVDSTADDR as IP_RECVDSTADDR,\n        IP_RECVERR as IP_RECVERR,\n        IP_RECVOPTS as IP_RECVOPTS,\n        IP_RECVORIGDSTADDR as IP_RECVORIGDSTADDR,\n        IP_RECVRETOPTS as IP_RECVRETOPTS,\n        IP_RECVTOS as IP_RECVTOS,\n        IP_RECVTTL as IP_RECVTTL,\n        IP_RETOPTS as IP_RETOPTS,\n        IP_TOS as IP_TOS,\n        IP_TRANSPARENT as IP_TRANSPARENT,\n        IP_TTL as IP_TTL,\n        IP_UNBLOCK_SOURCE as IP_UNBLOCK_SOURCE,\n        IPPORT_RESERVED as IPPORT_RESERVED,\n        IPPORT_USERRESERVED as IPPORT_USERRESERVED,\n        IPPROTO_AH as IPPROTO_AH,\n        IPPROTO_CBT as IPPROTO_CBT,\n        IPPROTO_DSTOPTS as IPPROTO_DSTOPTS,\n        IPPROTO_EGP as IPPROTO_EGP,\n        IPPROTO_EON as IPPROTO_EON,\n        IPPROTO_ESP as IPPROTO_ESP,\n        IPPROTO_FRAGMENT as IPPROTO_FRAGMENT,\n        IPPROTO_GGP as IPPROTO_GGP,\n        IPPROTO_GRE as IPPROTO_GRE,\n        IPPROTO_HELLO as IPPROTO_HELLO,\n        IPPROTO_HOPOPTS as IPPROTO_HOPOPTS,\n        IPPROTO_ICLFXBM as IPPROTO_ICLFXBM,\n        IPPROTO_ICMP as IPPROTO_ICMP,\n        IPPROTO_ICMPV6 as IPPROTO_ICMPV6,\n        IPPROTO_IDP as IPPROTO_IDP,\n        IPPROTO_IGMP as IPPROTO_IGMP,\n        IPPROTO_IGP as IPPROTO_IGP,\n        IPPROTO_IP as IPPROTO_IP,\n        IPPROTO_IPCOMP as IPPROTO_IPCOMP,\n        IPPROTO_IPIP as IPPROTO_IPIP,\n        IPPROTO_IPV4 as IPPROTO_IPV4,\n        IPPROTO_IPV6 as IPPROTO_IPV6,\n        IPPROTO_L2TP as IPPROTO_L2TP,\n        IPPROTO_MAX as IPPROTO_MAX,\n        IPPROTO_MOBILE as IPPROTO_MOBILE,\n        IPPROTO_MPTCP as IPPROTO_MPTCP,\n        IPPROTO_ND as IPPROTO_ND,\n        IPPROTO_NONE as IPPROTO_NONE,\n        IPPROTO_PGM as IPPROTO_PGM,\n        IPPROTO_PIM as IPPROTO_PIM,\n        IPPROTO_PUP as IPPROTO_PUP,\n        IPPROTO_RAW as IPPROTO_RAW,\n        IPPROTO_RDP as IPPROTO_RDP,\n        IPPROTO_ROUTING as IPPROTO_ROUTING,\n        IPPROTO_RSVP as IPPROTO_RSVP,\n        IPPROTO_SCTP as IPPROTO_SCTP,\n        IPPROTO_ST as IPPROTO_ST,\n        IPPROTO_TCP as IPPROTO_TCP,\n        IPPROTO_TP as IPPROTO_TP,\n        IPPROTO_UDP as IPPROTO_UDP,\n        IPPROTO_UDPLITE as IPPROTO_UDPLITE,\n        IPPROTO_XTP as IPPROTO_XTP,\n        IPV6_CHECKSUM as IPV6_CHECKSUM,\n        IPV6_DONTFRAG as IPV6_DONTFRAG,\n        IPV6_DSTOPTS as IPV6_DSTOPTS,\n        IPV6_HOPLIMIT as IPV6_HOPLIMIT,\n        IPV6_HOPOPTS as IPV6_HOPOPTS,\n        IPV6_JOIN_GROUP as IPV6_JOIN_GROUP,\n        IPV6_LEAVE_GROUP as IPV6_LEAVE_GROUP,\n        IPV6_MULTICAST_HOPS as IPV6_MULTICAST_HOPS,\n        IPV6_MULTICAST_IF as IPV6_MULTICAST_IF,\n        IPV6_MULTICAST_LOOP as IPV6_MULTICAST_LOOP,\n        IPV6_NEXTHOP as IPV6_NEXTHOP,\n        IPV6_PATHMTU as IPV6_PATHMTU,\n        IPV6_PKTINFO as IPV6_PKTINFO,\n        IPV6_RECVDSTOPTS as IPV6_RECVDSTOPTS,\n        IPV6_RECVERR as IPV6_RECVERR,\n        IPV6_RECVHOPLIMIT as IPV6_RECVHOPLIMIT,\n        IPV6_RECVHOPOPTS as IPV6_RECVHOPOPTS,\n        IPV6_RECVPATHMTU as IPV6_RECVPATHMTU,\n        IPV6_RECVPKTINFO as IPV6_RECVPKTINFO,\n        IPV6_RECVRTHDR as IPV6_RECVRTHDR,\n        IPV6_RECVTCLASS as IPV6_RECVTCLASS,\n        IPV6_RTHDR as IPV6_RTHDR,\n        IPV6_RTHDR_TYPE_0 as IPV6_RTHDR_TYPE_0,\n        IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS,\n        IPV6_TCLASS as IPV6_TCLASS,\n        IPV6_UNICAST_HOPS as IPV6_UNICAST_HOPS,\n        IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU,\n        IPV6_V6ONLY as IPV6_V6ONLY,\n        J1939_EE_INFO_NONE as J1939_EE_INFO_NONE,\n        J1939_EE_INFO_TX_ABORT as J1939_EE_INFO_TX_ABORT,\n        J1939_FILTER_MAX as J1939_FILTER_MAX,\n        J1939_IDLE_ADDR as J1939_IDLE_ADDR,\n        J1939_MAX_UNICAST_ADDR as J1939_MAX_UNICAST_ADDR,\n        J1939_NLA_BYTES_ACKED as J1939_NLA_BYTES_ACKED,\n        J1939_NLA_PAD as J1939_NLA_PAD,\n        J1939_NO_ADDR as J1939_NO_ADDR,\n        J1939_NO_NAME as J1939_NO_NAME,\n        J1939_NO_PGN as J1939_NO_PGN,\n        J1939_PGN_ADDRESS_CLAIMED as J1939_PGN_ADDRESS_CLAIMED,\n        J1939_PGN_ADDRESS_COMMANDED as J1939_PGN_ADDRESS_COMMANDED,\n        J1939_PGN_MAX as J1939_PGN_MAX,\n        J1939_PGN_PDU1_MAX as J1939_PGN_PDU1_MAX,\n        J1939_PGN_REQUEST as J1939_PGN_REQUEST,\n        LOCAL_PEERCRED as LOCAL_PEERCRED,\n        MSG_BCAST as MSG_BCAST,\n        MSG_CMSG_CLOEXEC as MSG_CMSG_CLOEXEC,\n        MSG_CONFIRM as MSG_CONFIRM,\n        MSG_CTRUNC as MSG_CTRUNC,\n        MSG_DONTROUTE as MSG_DONTROUTE,\n        MSG_DONTWAIT as MSG_DONTWAIT,\n        MSG_EOF as MSG_EOF,\n        MSG_EOR as MSG_EOR,\n        MSG_ERRQUEUE as MSG_ERRQUEUE,\n        MSG_FASTOPEN as MSG_FASTOPEN,\n        MSG_MCAST as MSG_MCAST,\n        MSG_MORE as MSG_MORE,\n        MSG_NOSIGNAL as MSG_NOSIGNAL,\n        MSG_NOTIFICATION as MSG_NOTIFICATION,\n        MSG_OOB as MSG_OOB,\n        MSG_PEEK as MSG_PEEK,\n        MSG_TRUNC as MSG_TRUNC,\n        MSG_WAITALL as MSG_WAITALL,\n        NETLINK_CRYPTO as NETLINK_CRYPTO,\n        NETLINK_DNRTMSG as NETLINK_DNRTMSG,\n        NETLINK_FIREWALL as NETLINK_FIREWALL,\n        NETLINK_IP6_FW as NETLINK_IP6_FW,\n        NETLINK_NFLOG as NETLINK_NFLOG,\n        NETLINK_ROUTE as NETLINK_ROUTE,\n        NETLINK_USERSOCK as NETLINK_USERSOCK,\n        NETLINK_XFRM as NETLINK_XFRM,\n        NI_DGRAM as NI_DGRAM,\n        NI_IDN as NI_IDN,\n        NI_MAXHOST as NI_MAXHOST,\n        NI_MAXSERV as NI_MAXSERV,\n        NI_NAMEREQD as NI_NAMEREQD,\n        NI_NOFQDN as NI_NOFQDN,\n        NI_NUMERICHOST as NI_NUMERICHOST,\n        NI_NUMERICSERV as NI_NUMERICSERV,\n        PACKET_BROADCAST as PACKET_BROADCAST,\n        PACKET_FASTROUTE as PACKET_FASTROUTE,\n        PACKET_HOST as PACKET_HOST,\n        PACKET_LOOPBACK as PACKET_LOOPBACK,\n        PACKET_MULTICAST as PACKET_MULTICAST,\n        PACKET_OTHERHOST as PACKET_OTHERHOST,\n        PACKET_OUTGOING as PACKET_OUTGOING,\n        PF_CAN as PF_CAN,\n        PF_PACKET as PF_PACKET,\n        PF_RDS as PF_RDS,\n        PF_SYSTEM as PF_SYSTEM,\n        POLLERR as POLLERR,\n        POLLHUP as POLLHUP,\n        POLLIN as POLLIN,\n        POLLMSG as POLLMSG,\n        POLLNVAL as POLLNVAL,\n        POLLOUT as POLLOUT,\n        POLLPRI as POLLPRI,\n        POLLRDBAND as POLLRDBAND,\n        POLLRDNORM as POLLRDNORM,\n        POLLWRNORM as POLLWRNORM,\n        RCVALL_MAX as RCVALL_MAX,\n        RCVALL_OFF as RCVALL_OFF,\n        RCVALL_ON as RCVALL_ON,\n        RCVALL_SOCKETLEVELONLY as RCVALL_SOCKETLEVELONLY,\n        SCM_CREDENTIALS as SCM_CREDENTIALS,\n        SCM_CREDS as SCM_CREDS,\n        SCM_J1939_DEST_ADDR as SCM_J1939_DEST_ADDR,\n        SCM_J1939_DEST_NAME as SCM_J1939_DEST_NAME,\n        SCM_J1939_ERRQUEUE as SCM_J1939_ERRQUEUE,\n        SCM_J1939_PRIO as SCM_J1939_PRIO,\n        SCM_RIGHTS as SCM_RIGHTS,\n        SHUT_RD as SHUT_RD,\n        SHUT_RDWR as SHUT_RDWR,\n        SHUT_WR as SHUT_WR,\n        SIO_KEEPALIVE_VALS as SIO_KEEPALIVE_VALS,\n        SIO_LOOPBACK_FAST_PATH as SIO_LOOPBACK_FAST_PATH,\n        SIO_RCVALL as SIO_RCVALL,\n        SIOCGIFINDEX as SIOCGIFINDEX,\n        SIOCGIFNAME as SIOCGIFNAME,\n        SO_ACCEPTCONN as SO_ACCEPTCONN,\n        SO_BINDTODEVICE as SO_BINDTODEVICE,\n        SO_BINDTOIFINDEX as SO_BINDTOIFINDEX,\n        SO_BROADCAST as SO_BROADCAST,\n        SO_BTH_ENCRYPT as SO_BTH_ENCRYPT,\n        SO_BTH_MTU as SO_BTH_MTU,\n        SO_BTH_MTU_MAX as SO_BTH_MTU_MAX,\n        SO_BTH_MTU_MIN as SO_BTH_MTU_MIN,\n        SO_DEBUG as SO_DEBUG,\n        SO_DOMAIN as SO_DOMAIN,\n        SO_DONTROUTE as SO_DONTROUTE,\n        SO_ERROR as SO_ERROR,\n        SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE,\n        SO_INCOMING_CPU as SO_INCOMING_CPU,\n        SO_J1939_ERRQUEUE as SO_J1939_ERRQUEUE,\n        SO_J1939_FILTER as SO_J1939_FILTER,\n        SO_J1939_PROMISC as SO_J1939_PROMISC,\n        SO_J1939_SEND_PRIO as SO_J1939_SEND_PRIO,\n        SO_KEEPALIVE as SO_KEEPALIVE,\n        SO_LINGER as SO_LINGER,\n        SO_MARK as SO_MARK,\n        SO_OOBINLINE as SO_OOBINLINE,\n        SO_ORIGINAL_DST as SO_ORIGINAL_DST,\n        SO_PASSCRED as SO_PASSCRED,\n        SO_PASSSEC as SO_PASSSEC,\n        SO_PEERCRED as SO_PEERCRED,\n        SO_PEERSEC as SO_PEERSEC,\n        SO_PRIORITY as SO_PRIORITY,\n        SO_PROTOCOL as SO_PROTOCOL,\n        SO_RCVBUF as SO_RCVBUF,\n        SO_RCVLOWAT as SO_RCVLOWAT,\n        SO_RCVTIMEO as SO_RCVTIMEO,\n        SO_REUSEADDR as SO_REUSEADDR,\n        SO_REUSEPORT as SO_REUSEPORT,\n        SO_SETFIB as SO_SETFIB,\n        SO_SNDBUF as SO_SNDBUF,\n        SO_SNDLOWAT as SO_SNDLOWAT,\n        SO_SNDTIMEO as SO_SNDTIMEO,\n        SO_TYPE as SO_TYPE,\n        SO_USELOOPBACK as SO_USELOOPBACK,\n        SO_VM_SOCKETS_BUFFER_MAX_SIZE as SO_VM_SOCKETS_BUFFER_MAX_SIZE,\n        SO_VM_SOCKETS_BUFFER_MIN_SIZE as SO_VM_SOCKETS_BUFFER_MIN_SIZE,\n        SO_VM_SOCKETS_BUFFER_SIZE as SO_VM_SOCKETS_BUFFER_SIZE,\n        SOCK_CLOEXEC as SOCK_CLOEXEC,\n        SOCK_DGRAM as SOCK_DGRAM,\n        SOCK_NONBLOCK as SOCK_NONBLOCK,\n        SOCK_RAW as SOCK_RAW,\n        SOCK_RDM as SOCK_RDM,\n        SOCK_SEQPACKET as SOCK_SEQPACKET,\n        SOCK_STREAM as SOCK_STREAM,\n        SOL_ALG as SOL_ALG,\n        SOL_CAN_BASE as SOL_CAN_BASE,\n        SOL_CAN_RAW as SOL_CAN_RAW,\n        SOL_HCI as SOL_HCI,\n        SOL_IP as SOL_IP,\n        SOL_RDS as SOL_RDS,\n        SOL_RFCOMM as SOL_RFCOMM,\n        SOL_SOCKET as SOL_SOCKET,\n        SOL_TCP as SOL_TCP,\n        SOL_TIPC as SOL_TIPC,\n        SOL_UDP as SOL_UDP,\n        SOMAXCONN as SOMAXCONN,\n        SYSPROTO_CONTROL as SYSPROTO_CONTROL,\n        TCP_CC_INFO as TCP_CC_INFO,\n        TCP_CONGESTION as TCP_CONGESTION,\n        TCP_CONNECTION_INFO as TCP_CONNECTION_INFO,\n        TCP_CORK as TCP_CORK,\n        TCP_DEFER_ACCEPT as TCP_DEFER_ACCEPT,\n        TCP_FASTOPEN as TCP_FASTOPEN,\n        TCP_FASTOPEN_CONNECT as TCP_FASTOPEN_CONNECT,\n        TCP_FASTOPEN_KEY as TCP_FASTOPEN_KEY,\n        TCP_FASTOPEN_NO_COOKIE as TCP_FASTOPEN_NO_COOKIE,\n        TCP_INFO as TCP_INFO,\n        TCP_INQ as TCP_INQ,\n        TCP_KEEPALIVE as TCP_KEEPALIVE,\n        TCP_KEEPCNT as TCP_KEEPCNT,\n        TCP_KEEPIDLE as TCP_KEEPIDLE,\n        TCP_KEEPINTVL as TCP_KEEPINTVL,\n        TCP_LINGER2 as TCP_LINGER2,\n        TCP_MAXSEG as TCP_MAXSEG,\n        TCP_MD5SIG as TCP_MD5SIG,\n        TCP_MD5SIG_EXT as TCP_MD5SIG_EXT,\n        TCP_NODELAY as TCP_NODELAY,\n        TCP_NOTSENT_LOWAT as TCP_NOTSENT_LOWAT,\n        TCP_QUEUE_SEQ as TCP_QUEUE_SEQ,\n        TCP_QUICKACK as TCP_QUICKACK,\n        TCP_REPAIR as TCP_REPAIR,\n        TCP_REPAIR_OPTIONS as TCP_REPAIR_OPTIONS,\n        TCP_REPAIR_QUEUE as TCP_REPAIR_QUEUE,\n        TCP_REPAIR_WINDOW as TCP_REPAIR_WINDOW,\n        TCP_SAVE_SYN as TCP_SAVE_SYN,\n        TCP_SAVED_SYN as TCP_SAVED_SYN,\n        TCP_SYNCNT as TCP_SYNCNT,\n        TCP_THIN_DUPACK as TCP_THIN_DUPACK,\n        TCP_THIN_LINEAR_TIMEOUTS as TCP_THIN_LINEAR_TIMEOUTS,\n        TCP_TIMESTAMP as TCP_TIMESTAMP,\n        TCP_TX_DELAY as TCP_TX_DELAY,\n        TCP_ULP as TCP_ULP,\n        TCP_USER_TIMEOUT as TCP_USER_TIMEOUT,\n        TCP_WINDOW_CLAMP as TCP_WINDOW_CLAMP,\n        TCP_ZEROCOPY_RECEIVE as TCP_ZEROCOPY_RECEIVE,\n        TIPC_ADDR_ID as TIPC_ADDR_ID,\n        TIPC_ADDR_NAME as TIPC_ADDR_NAME,\n        TIPC_ADDR_NAMESEQ as TIPC_ADDR_NAMESEQ,\n        TIPC_CFG_SRV as TIPC_CFG_SRV,\n        TIPC_CLUSTER_SCOPE as TIPC_CLUSTER_SCOPE,\n        TIPC_CONN_TIMEOUT as TIPC_CONN_TIMEOUT,\n        TIPC_CRITICAL_IMPORTANCE as TIPC_CRITICAL_IMPORTANCE,\n        TIPC_DEST_DROPPABLE as TIPC_DEST_DROPPABLE,\n        TIPC_HIGH_IMPORTANCE as TIPC_HIGH_IMPORTANCE,\n        TIPC_IMPORTANCE as TIPC_IMPORTANCE,\n        TIPC_LOW_IMPORTANCE as TIPC_LOW_IMPORTANCE,\n        TIPC_MEDIUM_IMPORTANCE as TIPC_MEDIUM_IMPORTANCE,\n        TIPC_NODE_SCOPE as TIPC_NODE_SCOPE,\n        TIPC_PUBLISHED as TIPC_PUBLISHED,\n        TIPC_SRC_DROPPABLE as TIPC_SRC_DROPPABLE,\n        TIPC_SUB_CANCEL as TIPC_SUB_CANCEL,\n        TIPC_SUB_PORTS as TIPC_SUB_PORTS,\n        TIPC_SUB_SERVICE as TIPC_SUB_SERVICE,\n        TIPC_SUBSCR_TIMEOUT as TIPC_SUBSCR_TIMEOUT,\n        TIPC_TOP_SRV as TIPC_TOP_SRV,\n        TIPC_WAIT_FOREVER as TIPC_WAIT_FOREVER,\n        TIPC_WITHDRAWN as TIPC_WITHDRAWN,\n        TIPC_ZONE_SCOPE as TIPC_ZONE_SCOPE,\n        UDPLITE_RECV_CSCOV as UDPLITE_RECV_CSCOV,\n        UDPLITE_SEND_CSCOV as UDPLITE_SEND_CSCOV,\n        VM_SOCKETS_INVALID_VERSION as VM_SOCKETS_INVALID_VERSION,\n        VMADDR_CID_ANY as VMADDR_CID_ANY,\n        VMADDR_CID_HOST as VMADDR_CID_HOST,\n        VMADDR_CID_LOCAL as VMADDR_CID_LOCAL,\n        VMADDR_PORT_ANY as VMADDR_PORT_ANY,\n        WSA_FLAG_OVERLAPPED as WSA_FLAG_OVERLAPPED,\n        WSA_INVALID_HANDLE as WSA_INVALID_HANDLE,\n        WSA_INVALID_PARAMETER as WSA_INVALID_PARAMETER,\n        WSA_IO_INCOMPLETE as WSA_IO_INCOMPLETE,\n        WSA_IO_PENDING as WSA_IO_PENDING,\n        WSA_NOT_ENOUGH_MEMORY as WSA_NOT_ENOUGH_MEMORY,\n        WSA_OPERATION_ABORTED as WSA_OPERATION_ABORTED,\n        WSA_WAIT_FAILED as WSA_WAIT_FAILED,\n        WSA_WAIT_TIMEOUT as WSA_WAIT_TIMEOUT,\n    )\n"
  },
  {
    "path": "src/trio/testing/__init__.py",
    "content": "# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)\n\nfrom .. import _deprecate as _deprecate\nfrom .._core import (\n    MockClock as MockClock,\n    wait_all_tasks_blocked as wait_all_tasks_blocked,\n)\nfrom .._threads import (\n    active_thread_count as active_thread_count,\n    wait_all_threads_completed as wait_all_threads_completed,\n)\nfrom .._util import fixup_module_metadata\nfrom ._check_streams import (\n    check_half_closeable_stream as check_half_closeable_stream,\n    check_one_way_stream as check_one_way_stream,\n    check_two_way_stream as check_two_way_stream,\n)\nfrom ._checkpoints import (\n    assert_checkpoints as assert_checkpoints,\n    assert_no_checkpoints as assert_no_checkpoints,\n)\nfrom ._memory_streams import (\n    MemoryReceiveStream as MemoryReceiveStream,\n    MemorySendStream as MemorySendStream,\n    lockstep_stream_one_way_pair as lockstep_stream_one_way_pair,\n    lockstep_stream_pair as lockstep_stream_pair,\n    memory_stream_one_way_pair as memory_stream_one_way_pair,\n    memory_stream_pair as memory_stream_pair,\n    memory_stream_pump as memory_stream_pump,\n)\nfrom ._network import open_stream_to_socket_listener as open_stream_to_socket_listener\nfrom ._raises_group import Matcher as _Matcher, RaisesGroup as _RaisesGroup\nfrom ._sequencer import Sequencer as Sequencer\nfrom ._trio_test import trio_test as trio_test\n\n################################################################\n\n\n_deprecate.deprecate_attributes(\n    __name__,\n    {\n        \"RaisesGroup\": _deprecate.DeprecatedAttribute(\n            _RaisesGroup,\n            version=\"0.33.0\",\n            issue=3326,\n            instead=\"See https://docs.pytest.org/en/stable/reference/reference.html#pytest.RaisesGroup\",\n        ),\n        \"Matcher\": _deprecate.DeprecatedAttribute(\n            _Matcher,\n            version=\"0.33.0\",\n            issue=3326,\n            instead=\"See https://docs.pytest.org/en/stable/reference/reference.html#pytest.RaisesExc\",\n        ),\n    },\n)\n\nfixup_module_metadata(__name__, globals())\ndel fixup_module_metadata\n"
  },
  {
    "path": "src/trio/testing/_check_streams.py",
    "content": "# Generic stream tests\nfrom __future__ import annotations\n\nimport random\nimport sys\nfrom collections.abc import Awaitable, Callable, Generator\nfrom contextlib import contextmanager, suppress\nfrom typing import (\n    TYPE_CHECKING,\n    Generic,\n    TypeAlias,\n    TypeVar,\n)\n\nfrom .. import CancelScope, _core\nfrom .._abc import AsyncResource, HalfCloseableStream, ReceiveStream, SendStream, Stream\nfrom .._highlevel_generic import aclose_forcefully\nfrom ._checkpoints import assert_checkpoints\n\nif TYPE_CHECKING:\n    from types import TracebackType\n\n    from typing_extensions import ParamSpec\n\n    ArgsT = ParamSpec(\"ArgsT\")\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup\n\nRes1 = TypeVar(\"Res1\", bound=AsyncResource)\nRes2 = TypeVar(\"Res2\", bound=AsyncResource)\nStreamMaker: TypeAlias = Callable[[], Awaitable[tuple[Res1, Res2]]]\n\n\nclass _ForceCloseBoth(Generic[Res1, Res2]):\n    def __init__(self, both: tuple[Res1, Res2]) -> None:\n        self._first, self._second = both\n\n    async def __aenter__(self) -> tuple[Res1, Res2]:\n        return self._first, self._second\n\n    async def __aexit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        try:\n            await aclose_forcefully(self._first)\n        finally:\n            await aclose_forcefully(self._second)\n\n\n# This is used in this file instead of pytest.raises in order to avoid a dependency\n# on pytest, as the check_* functions are publicly exported.\n@contextmanager\ndef _assert_raises(\n    expected_exc: type[BaseException],\n    wrapped: bool = False,\n) -> Generator[None, None, None]:\n    __tracebackhide__ = True\n    try:\n        yield\n    except BaseExceptionGroup as exc:\n        assert wrapped, \"caught exceptiongroup, but expected an unwrapped exception\"\n        # assert in except block ignored below\n        assert len(exc.exceptions) == 1  # noqa: PT017\n        assert isinstance(exc.exceptions[0], expected_exc)  # noqa: PT017\n    except expected_exc:\n        assert not wrapped, \"caught exception, but expected an exceptiongroup\"\n    else:\n        raise AssertionError(f\"expected exception: {expected_exc}\")\n\n\nasync def check_one_way_stream(\n    stream_maker: StreamMaker[SendStream, ReceiveStream],\n    clogged_stream_maker: StreamMaker[SendStream, ReceiveStream] | None,\n) -> None:\n    \"\"\"Perform a number of generic tests on a custom one-way stream\n    implementation.\n\n    Args:\n      stream_maker: An async (!) function which returns a connected\n          (:class:`~trio.abc.SendStream`, :class:`~trio.abc.ReceiveStream`)\n          pair.\n      clogged_stream_maker: Either None, or an async function similar to\n          stream_maker, but with the extra property that the returned stream\n          is in a state where ``send_all`` and\n          ``wait_send_all_might_not_block`` will block until ``receive_some``\n          has been called. This allows for more thorough testing of some edge\n          cases, especially around ``wait_send_all_might_not_block``.\n\n    Raises:\n      AssertionError: if a test fails.\n\n    \"\"\"\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n        assert isinstance(s, SendStream)\n        assert isinstance(r, ReceiveStream)\n\n        async def do_send_all(data: bytes | bytearray | memoryview) -> None:\n            with assert_checkpoints():  # We're testing that it doesn't return anything.\n                assert await s.send_all(data) is None  # type: ignore[func-returns-value]\n\n        async def do_receive_some(max_bytes: int | None = None) -> bytes | bytearray:\n            with assert_checkpoints():\n                return await r.receive_some(max_bytes)\n\n        async def checked_receive_1(expected: bytes) -> None:\n            assert await do_receive_some(1) == expected\n\n        async def do_aclose(resource: AsyncResource) -> None:\n            with assert_checkpoints():\n                await resource.aclose()\n\n        # Simple sending/receiving\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_send_all, b\"x\")\n            nursery.start_soon(checked_receive_1, b\"x\")\n\n        async def send_empty_then_y() -> None:\n            # Streams should tolerate sending b\"\" without giving it any\n            # special meaning.\n            await do_send_all(b\"\")\n            await do_send_all(b\"y\")\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(send_empty_then_y)\n            nursery.start_soon(checked_receive_1, b\"y\")\n\n        # ---- Checking various argument types ----\n\n        # send_all accepts bytearray and memoryview\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_send_all, bytearray(b\"1\"))\n            nursery.start_soon(checked_receive_1, b\"1\")\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_send_all, memoryview(b\"2\"))\n            nursery.start_soon(checked_receive_1, b\"2\")\n\n        # max_bytes must be a positive integer\n        with _assert_raises(ValueError):\n            await r.receive_some(-1)\n        with _assert_raises(ValueError):\n            await r.receive_some(0)\n        with _assert_raises(TypeError):\n            await r.receive_some(1.5)  # type: ignore[arg-type]\n        # it can also be missing or None\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_send_all, b\"x\")\n            assert await do_receive_some() == b\"x\"\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_send_all, b\"x\")\n            assert await do_receive_some(None) == b\"x\"\n\n        with _assert_raises(_core.BusyResourceError, wrapped=True):\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(do_receive_some, 1)\n                nursery.start_soon(do_receive_some, 1)\n\n        # Method always has to exist, and an empty stream with a blocked\n        # receive_some should *always* allow send_all. (Technically it's legal\n        # for send_all to wait until receive_some is called to run, though; a\n        # stream doesn't *have* to have any internal buffering. That's why we\n        # start a concurrent receive_some call, then cancel it.)\n        async def simple_check_wait_send_all_might_not_block(\n            scope: CancelScope,\n        ) -> None:\n            with assert_checkpoints():\n                await s.wait_send_all_might_not_block()\n            scope.cancel()\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(\n                simple_check_wait_send_all_might_not_block,\n                nursery.cancel_scope,\n            )\n            nursery.start_soon(do_receive_some, 1)\n\n        # closing the r side leads to BrokenResourceError on the s side\n        # (eventually)\n        async def expect_broken_stream_on_send() -> None:\n            with _assert_raises(_core.BrokenResourceError):\n                while True:\n                    await do_send_all(b\"x\" * 100)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect_broken_stream_on_send)\n            nursery.start_soon(do_aclose, r)\n\n        # once detected, the stream stays broken\n        with _assert_raises(_core.BrokenResourceError):\n            await do_send_all(b\"x\" * 100)\n\n        # r closed -> ClosedResourceError on the receive side\n        with _assert_raises(_core.ClosedResourceError):\n            await do_receive_some(4096)\n\n        # we can close the same stream repeatedly, it's fine\n        await do_aclose(r)\n        await do_aclose(r)\n\n        # closing the sender side\n        await do_aclose(s)\n\n        # now trying to send raises ClosedResourceError\n        with _assert_raises(_core.ClosedResourceError):\n            await do_send_all(b\"x\" * 100)\n\n        # even if it's an empty send\n        with _assert_raises(_core.ClosedResourceError):\n            await do_send_all(b\"\")\n\n        # ditto for wait_send_all_might_not_block\n        with _assert_raises(_core.ClosedResourceError):\n            with assert_checkpoints():\n                await s.wait_send_all_might_not_block()\n\n        # and again, repeated closing is fine\n        await do_aclose(s)\n        await do_aclose(s)\n\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n        # if send-then-graceful-close, receiver gets data then b\"\"\n        async def send_then_close() -> None:\n            await do_send_all(b\"y\")\n            await do_aclose(s)\n\n        async def receive_send_then_close() -> None:\n            # We want to make sure that if the sender closes the stream before\n            # we read anything, then we still get all the data. But some\n            # streams might block on the do_send_all call. So we let the\n            # sender get as far as it can, then we receive.\n            await _core.wait_all_tasks_blocked()\n            await checked_receive_1(b\"y\")\n            await checked_receive_1(b\"\")\n            await do_aclose(r)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(send_then_close)\n            nursery.start_soon(receive_send_then_close)\n\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n        await aclose_forcefully(r)\n\n        with _assert_raises(_core.BrokenResourceError):\n            while True:\n                await do_send_all(b\"x\" * 100)\n\n        with _assert_raises(_core.ClosedResourceError):\n            await do_receive_some(4096)\n\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n        await aclose_forcefully(s)\n\n        with _assert_raises(_core.ClosedResourceError):\n            await do_send_all(b\"123\")\n\n        # after the sender does a forceful close, the receiver might either\n        # get BrokenResourceError or a clean b\"\"; either is OK. Not OK would be\n        # if it freezes, or returns data.\n        with suppress(_core.BrokenResourceError):\n            await checked_receive_1(b\"\")\n\n    # cancelled aclose still closes\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            await r.aclose()\n\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            await s.aclose()\n\n        with _assert_raises(_core.ClosedResourceError):\n            await do_send_all(b\"123\")\n\n        with _assert_raises(_core.ClosedResourceError):\n            await do_receive_some(4096)\n\n    # Check that we can still gracefully close a stream after an operation has\n    # been cancelled. This can be challenging if cancellation can leave the\n    # stream internals in an inconsistent state, e.g. for\n    # SSLStream. Unfortunately this test isn't very thorough; the really\n    # challenging case for something like SSLStream is it gets cancelled\n    # *while* it's sending data on the underlying, not before. But testing\n    # that requires some special-case handling of the particular stream setup;\n    # we can't do it here. Maybe we could do a bit better with\n    #     https://github.com/python-trio/trio/issues/77\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n\n        async def expect_cancelled(\n            afn: Callable[ArgsT, Awaitable[object]],\n            *args: ArgsT.args,\n            **kwargs: ArgsT.kwargs,\n        ) -> None:\n            with _assert_raises(_core.Cancelled):\n                await afn(*args, **kwargs)\n\n        with _core.CancelScope() as scope:\n            scope.cancel()\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(expect_cancelled, do_send_all, b\"x\")\n                nursery.start_soon(expect_cancelled, do_receive_some, 1)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(do_aclose, s)\n            nursery.start_soon(do_aclose, r)\n\n    # Check that if a task is blocked in receive_some, then closing the\n    # receive stream causes it to wake up.\n    async with _ForceCloseBoth(await stream_maker()) as (s, r):\n\n        async def receive_expecting_closed() -> None:\n            with _assert_raises(_core.ClosedResourceError):\n                await r.receive_some(10)\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(receive_expecting_closed)\n            await _core.wait_all_tasks_blocked()\n            await aclose_forcefully(r)\n\n    # check wait_send_all_might_not_block, if we can\n    if clogged_stream_maker is not None:\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n            record: list[str] = []\n\n            async def waiter(cancel_scope: CancelScope) -> None:\n                record.append(\"waiter sleeping\")\n                with assert_checkpoints():\n                    await s.wait_send_all_might_not_block()\n                record.append(\"waiter wokeup\")\n                cancel_scope.cancel()\n\n            async def receiver() -> None:\n                # give wait_send_all_might_not_block a chance to block\n                await _core.wait_all_tasks_blocked()\n                record.append(\"receiver starting\")\n                while True:\n                    await r.receive_some(16834)\n\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(waiter, nursery.cancel_scope)\n                await _core.wait_all_tasks_blocked()\n                nursery.start_soon(receiver)\n\n            assert record == [\n                \"waiter sleeping\",\n                \"receiver starting\",\n                \"waiter wokeup\",\n            ]\n\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n            # simultaneous wait_send_all_might_not_block fails\n            with _assert_raises(_core.BusyResourceError, wrapped=True):\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(s.wait_send_all_might_not_block)\n                    nursery.start_soon(s.wait_send_all_might_not_block)\n\n            # and simultaneous send_all and wait_send_all_might_not_block (NB\n            # this test might destroy the stream b/c we end up cancelling\n            # send_all and e.g. SSLStream can't handle that, so we have to\n            # recreate afterwards)\n            with _assert_raises(_core.BusyResourceError, wrapped=True):\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(s.wait_send_all_might_not_block)\n                    nursery.start_soon(s.send_all, b\"123\")\n\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n            # send_all and send_all blocked simultaneously should also raise\n            # (but again this might destroy the stream)\n            with _assert_raises(_core.BusyResourceError, wrapped=True):\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(s.send_all, b\"123\")\n                    nursery.start_soon(s.send_all, b\"123\")\n\n        # closing the receiver causes wait_send_all_might_not_block to return,\n        # with or without an exception\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n\n            async def sender() -> None:\n                try:\n                    with assert_checkpoints():\n                        await s.wait_send_all_might_not_block()\n                except _core.BrokenResourceError:  # pragma: no cover\n                    pass\n\n            async def receiver() -> None:\n                await _core.wait_all_tasks_blocked()\n                await aclose_forcefully(r)\n\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(sender)\n                nursery.start_soon(receiver)\n\n        # and again with the call starting after the close\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n            await aclose_forcefully(r)\n            try:\n                with assert_checkpoints():\n                    await s.wait_send_all_might_not_block()\n            except _core.BrokenResourceError:  # pragma: no cover\n                pass\n\n        # Check that if a task is blocked in a send-side method, then closing\n        # the send stream causes it to wake up.\n        async def close_soon(s: SendStream) -> None:\n            await _core.wait_all_tasks_blocked()\n            await aclose_forcefully(s)\n\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(close_soon, s)\n                with _assert_raises(_core.ClosedResourceError):\n                    await s.send_all(b\"xyzzy\")\n\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s, r):\n            async with _core.open_nursery() as nursery:\n                nursery.start_soon(close_soon, s)\n                with _assert_raises(_core.ClosedResourceError):\n                    await s.wait_send_all_might_not_block()\n\n\nasync def check_two_way_stream(\n    stream_maker: StreamMaker[Stream, Stream],\n    clogged_stream_maker: StreamMaker[Stream, Stream] | None,\n) -> None:\n    \"\"\"Perform a number of generic tests on a custom two-way stream\n    implementation.\n\n    This is similar to :func:`check_one_way_stream`, except that the maker\n    functions are expected to return objects implementing the\n    :class:`~trio.abc.Stream` interface.\n\n    This function tests a *superset* of what :func:`check_one_way_stream`\n    checks – if you call this, then you don't need to also call\n    :func:`check_one_way_stream`.\n\n    \"\"\"\n    await check_one_way_stream(stream_maker, clogged_stream_maker)\n\n    async def flipped_stream_maker() -> tuple[Stream, Stream]:\n        return (await stream_maker())[::-1]\n\n    flipped_clogged_stream_maker: Callable[[], Awaitable[tuple[Stream, Stream]]] | None\n\n    if clogged_stream_maker is not None:\n\n        async def flipped_clogged_stream_maker() -> tuple[Stream, Stream]:\n            return (await clogged_stream_maker())[::-1]\n\n    else:\n        flipped_clogged_stream_maker = None\n    await check_one_way_stream(flipped_stream_maker, flipped_clogged_stream_maker)\n\n    async with _ForceCloseBoth(await stream_maker()) as (s1, s2):\n        assert isinstance(s1, Stream)\n        assert isinstance(s2, Stream)\n\n        # Duplex can be a bit tricky, might as well check it as well\n        DUPLEX_TEST_SIZE = 2**20\n        CHUNK_SIZE_MAX = 2**14\n\n        r = random.Random(0)\n        i = r.getrandbits(8 * DUPLEX_TEST_SIZE)\n        test_data = i.to_bytes(DUPLEX_TEST_SIZE, \"little\")\n\n        async def sender(\n            s: Stream,\n            data: bytes | bytearray | memoryview,\n            seed: int,\n        ) -> None:\n            r = random.Random(seed)\n            m = memoryview(data)\n            while m:\n                chunk_size = r.randint(1, CHUNK_SIZE_MAX)\n                await s.send_all(m[:chunk_size])\n                m = m[chunk_size:]\n\n        async def receiver(s: Stream, data: bytes | bytearray, seed: int) -> None:\n            r = random.Random(seed)\n            got = bytearray()\n            while len(got) < len(data):\n                chunk = await s.receive_some(r.randint(1, CHUNK_SIZE_MAX))\n                assert chunk\n                got += chunk\n            assert got == data\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(sender, s1, test_data, 0)\n            nursery.start_soon(sender, s2, test_data[::-1], 1)\n            nursery.start_soon(receiver, s1, test_data[::-1], 2)\n            nursery.start_soon(receiver, s2, test_data, 3)\n\n        async def expect_receive_some_empty() -> None:\n            assert await s2.receive_some(10) == b\"\"\n            await s2.aclose()\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(expect_receive_some_empty)\n            nursery.start_soon(s1.aclose)\n\n\nasync def check_half_closeable_stream(\n    stream_maker: StreamMaker[HalfCloseableStream, HalfCloseableStream],\n    clogged_stream_maker: StreamMaker[HalfCloseableStream, HalfCloseableStream] | None,\n) -> None:\n    \"\"\"Perform a number of generic tests on a custom half-closeable stream\n    implementation.\n\n    This is similar to :func:`check_two_way_stream`, except that the maker\n    functions are expected to return objects that implement the\n    :class:`~trio.abc.HalfCloseableStream` interface.\n\n    This function tests a *superset* of what :func:`check_two_way_stream`\n    checks – if you call this, then you don't need to also call\n    :func:`check_two_way_stream`.\n\n    \"\"\"\n    await check_two_way_stream(stream_maker, clogged_stream_maker)\n\n    async with _ForceCloseBoth(await stream_maker()) as (s1, s2):\n        assert isinstance(s1, HalfCloseableStream)\n        assert isinstance(s2, HalfCloseableStream)\n\n        async def send_x_then_eof(s: HalfCloseableStream) -> None:\n            await s.send_all(b\"x\")\n            with assert_checkpoints():\n                await s.send_eof()\n\n        async def expect_x_then_eof(r: HalfCloseableStream) -> None:\n            await _core.wait_all_tasks_blocked()\n            assert await r.receive_some(10) == b\"x\"\n            assert await r.receive_some(10) == b\"\"\n\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(send_x_then_eof, s1)\n            nursery.start_soon(expect_x_then_eof, s2)\n\n        # now sending is disallowed\n        with _assert_raises(_core.ClosedResourceError):\n            await s1.send_all(b\"y\")\n\n        # but we can do send_eof again\n        with assert_checkpoints():\n            await s1.send_eof()\n\n        # and we can still send stuff back the other way\n        async with _core.open_nursery() as nursery:\n            nursery.start_soon(send_x_then_eof, s2)\n            nursery.start_soon(expect_x_then_eof, s1)\n\n    if clogged_stream_maker is not None:\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s1, s2):\n            # send_all and send_eof simultaneously is not ok\n            with _assert_raises(_core.BusyResourceError, wrapped=True):\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(s1.send_all, b\"x\")\n                    await _core.wait_all_tasks_blocked()\n                    nursery.start_soon(s1.send_eof)\n\n        async with _ForceCloseBoth(await clogged_stream_maker()) as (s1, s2):\n            # wait_send_all_might_not_block and send_eof simultaneously is not\n            # ok either\n            with _assert_raises(_core.BusyResourceError, wrapped=True):\n                async with _core.open_nursery() as nursery:\n                    nursery.start_soon(s1.wait_send_all_might_not_block)\n                    await _core.wait_all_tasks_blocked()\n                    nursery.start_soon(s1.send_eof)\n"
  },
  {
    "path": "src/trio/testing/_checkpoints.py",
    "content": "from __future__ import annotations\n\nfrom contextlib import AbstractContextManager, contextmanager\nfrom typing import TYPE_CHECKING\n\nfrom .. import _core\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n\n@contextmanager\ndef _assert_yields_or_not(expected: bool) -> Generator[None, None, None]:\n    \"\"\"Check if checkpoints are executed in a block of code.\"\"\"\n    __tracebackhide__ = True\n    task = _core.current_task()\n    orig_cancel = task._cancel_points\n    orig_schedule = task._schedule_points\n    try:\n        yield\n        if expected and (\n            task._cancel_points == orig_cancel or task._schedule_points == orig_schedule\n        ):\n            raise AssertionError(\"assert_checkpoints block did not yield!\")\n    finally:\n        if not expected and (\n            task._cancel_points != orig_cancel or task._schedule_points != orig_schedule\n        ):\n            raise AssertionError(\"assert_no_checkpoints block yielded!\")\n\n\ndef assert_checkpoints() -> AbstractContextManager[None]:\n    \"\"\"Use as a context manager to check that the code inside the ``with``\n    block either exits with an exception or executes at least one\n    :ref:`checkpoint <checkpoints>`.\n\n    Raises:\n      AssertionError: if no checkpoint was executed.\n\n    Example:\n      Check that :func:`trio.sleep` is a checkpoint, even if it doesn't\n      block::\n\n         with trio.testing.assert_checkpoints():\n             await trio.sleep(0)\n\n    \"\"\"\n    __tracebackhide__ = True\n    return _assert_yields_or_not(True)\n\n\ndef assert_no_checkpoints() -> AbstractContextManager[None]:\n    \"\"\"Use as a context manager to check that the code inside the ``with``\n    block does not execute any :ref:`checkpoints <checkpoints>`.\n\n    Raises:\n      AssertionError: if a checkpoint was executed.\n\n    Example:\n      Synchronous code never contains any checkpoints, but we can double-check\n      that::\n\n         send_channel, receive_channel = trio.open_memory_channel(10)\n         with trio.testing.assert_no_checkpoints():\n             send_channel.send_nowait(None)\n\n    \"\"\"\n    __tracebackhide__ = True\n    return _assert_yields_or_not(False)\n"
  },
  {
    "path": "src/trio/testing/_fake_net.py",
    "content": "# This should eventually be cleaned up and become public, but for right now I'm just\n# implementing enough to test DTLS.\n\n# TODO:\n# - user-defined routers\n# - TCP\n# - UDP broadcast\n\nfrom __future__ import annotations\n\nimport contextlib\nimport errno\nimport ipaddress\nimport os\nimport socket\nimport sys\nfrom typing import TYPE_CHECKING, Any, NoReturn, TypeAlias, overload\n\nimport attrs\n\nimport trio\nfrom trio._util import NoPublicConstructor, final\n\nif TYPE_CHECKING:\n    import builtins\n    from collections.abc import Iterable\n    from socket import AddressFamily, SocketKind\n    from types import TracebackType\n\n    from typing_extensions import Buffer, Self\n\n    from trio._socket import AddressFormat\n\nIPAddress: TypeAlias = ipaddress.IPv4Address | ipaddress.IPv6Address\n\n\ndef _family_for(ip: IPAddress) -> int:\n    if isinstance(ip, ipaddress.IPv4Address):\n        return trio.socket.AF_INET\n    elif isinstance(ip, ipaddress.IPv6Address):\n        return trio.socket.AF_INET6\n    raise NotImplementedError(\"Unhandled IPAddress instance type\")  # pragma: no cover\n\n\ndef _wildcard_ip_for(family: int) -> IPAddress:\n    if family == trio.socket.AF_INET:\n        return ipaddress.ip_address(\"0.0.0.0\")\n    elif family == trio.socket.AF_INET6:\n        return ipaddress.ip_address(\"::\")\n    raise NotImplementedError(\"Unhandled ip address family\")  # pragma: no cover\n\n\n# not used anywhere\ndef _localhost_ip_for(family: int) -> IPAddress:  # pragma: no cover\n    if family == trio.socket.AF_INET:\n        return ipaddress.ip_address(\"127.0.0.1\")\n    elif family == trio.socket.AF_INET6:\n        return ipaddress.ip_address(\"::1\")\n    raise NotImplementedError(\"Unhandled ip address family\")\n\n\ndef _fake_err(code: int) -> NoReturn:\n    raise OSError(code, os.strerror(code))\n\n\ndef _scatter(data: bytes, buffers: Iterable[Buffer]) -> int:\n    written = 0\n    for buf in buffers:  # pragma: no branch\n        next_piece = data[written : written + memoryview(buf).nbytes]\n        with memoryview(buf) as mbuf:\n            mbuf[: len(next_piece)] = next_piece\n        written += len(next_piece)\n        if written == len(data):  # pragma: no branch\n            break\n    return written\n\n\n@attrs.frozen\nclass UDPEndpoint:\n    ip: IPAddress\n    port: int\n\n    def as_python_sockaddr(self) -> tuple[str, int] | tuple[str, int, int, int]:\n        sockaddr: tuple[str, int] | tuple[str, int, int, int] = (\n            self.ip.compressed,\n            self.port,\n        )\n        if isinstance(self.ip, ipaddress.IPv6Address):\n            sockaddr += (0, 0)  # type: ignore[assignment]\n        return sockaddr\n\n    @classmethod\n    def from_python_sockaddr(\n        cls,\n        sockaddr: tuple[str, int] | tuple[str, int, int, int],\n    ) -> UDPEndpoint:\n        ip, port = sockaddr[:2]\n        return cls(ip=ipaddress.ip_address(ip), port=port)\n\n\n@attrs.frozen\nclass UDPBinding:\n    local: UDPEndpoint\n    # remote: UDPEndpoint # ??\n\n\n@attrs.frozen\nclass UDPPacket:\n    source: UDPEndpoint\n    destination: UDPEndpoint\n    payload: bytes = attrs.field(repr=lambda p: p.hex())\n\n    # not used/tested anywhere\n    def reply(self, payload: bytes) -> UDPPacket:  # pragma: no cover\n        return UDPPacket(\n            source=self.destination,\n            destination=self.source,\n            payload=payload,\n        )\n\n\n@attrs.frozen\nclass FakeSocketFactory(trio.abc.SocketFactory):\n    fake_net: FakeNet\n\n    def socket(self, family: int, type_: int, proto: int) -> FakeSocket:  # type: ignore[override]\n        return FakeSocket._create(self.fake_net, family, type_, proto)\n\n\n@attrs.frozen\nclass FakeHostnameResolver(trio.abc.HostnameResolver):\n    fake_net: FakeNet\n\n    async def getaddrinfo(\n        self,\n        host: bytes | None,\n        port: bytes | str | int | None,\n        family: int = 0,\n        type: int = 0,\n        proto: int = 0,\n        flags: int = 0,\n    ) -> list[\n        tuple[\n            AddressFamily,\n            SocketKind,\n            int,\n            str,\n            tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],\n        ]\n    ]:\n        raise NotImplementedError(\"FakeNet doesn't do fake DNS yet\")\n\n    async def getnameinfo(\n        self,\n        sockaddr: tuple[str, int] | tuple[str, int, int, int],\n        flags: int,\n    ) -> tuple[str, str]:\n        raise NotImplementedError(\"FakeNet doesn't do fake DNS yet\")\n\n\n@final\nclass FakeNet:\n    def __init__(self) -> None:\n        # When we need to pick an arbitrary unique ip address/port, use these:\n        self._auto_ipv4_iter = ipaddress.IPv4Network(\"1.0.0.0/8\").hosts()  # untested\n        self._auto_ipv6_iter = ipaddress.IPv6Network(\"1::/16\").hosts()  # untested\n        self._auto_port_iter = iter(range(50000, 65535))\n\n        self._bound: dict[UDPBinding, FakeSocket] = {}\n\n        self.route_packet = None\n\n    def _bind(self, binding: UDPBinding, socket: FakeSocket) -> None:\n        if binding in self._bound:\n            _fake_err(errno.EADDRINUSE)\n        self._bound[binding] = socket\n\n    def enable(self) -> None:\n        trio.socket.set_custom_socket_factory(FakeSocketFactory(self))\n        trio.socket.set_custom_hostname_resolver(FakeHostnameResolver(self))\n\n    def send_packet(self, packet: UDPPacket) -> None:\n        if self.route_packet is None:\n            self.deliver_packet(packet)\n        else:\n            self.route_packet(packet)\n\n    def deliver_packet(self, packet: UDPPacket) -> None:\n        binding = UDPBinding(local=packet.destination)\n        if binding in self._bound:\n            self._bound[binding]._deliver_packet(packet)\n        else:\n            # No valid destination, so drop it\n            pass\n\n\n@final\nclass FakeSocket(trio.socket.SocketType, metaclass=NoPublicConstructor):\n    def __init__(\n        self,\n        fake_net: FakeNet,\n        family: AddressFamily,\n        type: SocketKind,\n        proto: int,\n    ) -> None:\n        self._fake_net = fake_net\n\n        if not family:  # pragma: no cover\n            family = trio.socket.AF_INET\n        if not type:  # pragma: no cover\n            type = trio.socket.SOCK_STREAM  # noqa: A001  # name shadowing builtin\n\n        if family not in (trio.socket.AF_INET, trio.socket.AF_INET6):\n            raise NotImplementedError(f\"FakeNet doesn't (yet) support family={family}\")\n        if type != trio.socket.SOCK_DGRAM:\n            raise NotImplementedError(f\"FakeNet doesn't (yet) support type={type}\")\n\n        self._family = family\n        self._type = type\n        self._proto = proto\n\n        self._closed = False\n\n        self._packet_sender, self._packet_receiver = trio.open_memory_channel[\n            UDPPacket\n        ](float(\"inf\"))\n\n        # This is the source-of-truth for what port etc. this socket is bound to\n        self._binding: UDPBinding | None = None\n\n    @property\n    def type(self) -> SocketKind:\n        return self._type\n\n    @property\n    def family(self) -> AddressFamily:\n        return self._family\n\n    @property\n    def proto(self) -> int:\n        return self._proto\n\n    def _check_closed(self) -> None:\n        if self._closed:\n            _fake_err(errno.EBADF)\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        self._closed = True\n        if self._binding is not None:\n            del self._fake_net._bound[self._binding]\n        self._packet_receiver.close()\n\n    async def _resolve_address_nocp(\n        self,\n        address: object,\n        *,\n        local: bool,\n    ) -> tuple[str, int]:\n        return await trio._socket._resolve_address_nocp(  # type: ignore[no-any-return]\n            self.type,\n            self.family,\n            self.proto,\n            address=address,\n            ipv6_v6only=False,\n            local=local,\n        )\n\n    def _deliver_packet(self, packet: UDPPacket) -> None:\n        # sending to a closed socket -- UDP packets get dropped\n        with contextlib.suppress(trio.BrokenResourceError):\n            self._packet_sender.send_nowait(packet)\n\n    ################################################################\n    # Actual IO operation implementations\n    ################################################################\n\n    async def bind(self, addr: object) -> None:\n        self._check_closed()\n        if self._binding is not None:\n            _fake_err(errno.EINVAL)\n        await trio.lowlevel.checkpoint()\n        ip_str, port, *_ = await self._resolve_address_nocp(addr, local=True)\n        assert _ == [], \"TODO: handle other values?\"\n\n        ip = ipaddress.ip_address(ip_str)\n        assert _family_for(ip) == self.family\n        # We convert binds to INET_ANY into binds to localhost\n        if ip == ipaddress.ip_address(\"0.0.0.0\"):\n            ip = ipaddress.ip_address(\"127.0.0.1\")\n        elif ip == ipaddress.ip_address(\"::\"):\n            ip = ipaddress.ip_address(\"::1\")\n        if port == 0:\n            port = next(self._fake_net._auto_port_iter)\n        binding = UDPBinding(local=UDPEndpoint(ip, port))\n        self._fake_net._bind(binding, self)\n        self._binding = binding\n\n    async def connect(self, peer: object) -> NoReturn:\n        raise NotImplementedError(\"FakeNet does not (yet) support connected sockets\")\n\n    async def _sendmsg(\n        self,\n        buffers: Iterable[Buffer],\n        ancdata: Iterable[tuple[int, int, Buffer]] = (),\n        flags: int = 0,\n        address: AddressFormat | None = None,\n    ) -> int:\n        self._check_closed()\n\n        await trio.lowlevel.checkpoint()\n\n        if address is not None:\n            address = await self._resolve_address_nocp(address, local=False)\n        if ancdata:\n            raise NotImplementedError(\"FakeNet doesn't support ancillary data\")\n        if flags:\n            raise NotImplementedError(f\"FakeNet send flags must be 0, not {flags}\")\n\n        if address is None:\n            _fake_err(errno.ENOTCONN)\n\n        destination = UDPEndpoint.from_python_sockaddr(address)\n\n        if self._binding is None:\n            await self.bind((_wildcard_ip_for(self.family).compressed, 0))\n\n        payload = b\"\".join(buffers)\n\n        assert self._binding is not None\n        packet = UDPPacket(\n            source=self._binding.local,\n            destination=destination,\n            payload=payload,\n        )\n\n        self._fake_net.send_packet(packet)\n\n        return len(payload)\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(socket.socket, \"sendmsg\")\n    ):\n        sendmsg = _sendmsg\n\n    async def _recvmsg_into(\n        self,\n        buffers: Iterable[Buffer],\n        ancbufsize: int = 0,\n        flags: int = 0,\n    ) -> tuple[\n        int,\n        list[tuple[int, int, bytes]],\n        int,\n        tuple[str, int] | tuple[str, int, int, int],\n    ]:\n        if ancbufsize != 0:\n            raise NotImplementedError(\"FakeNet doesn't support ancillary data\")\n        if flags != 0:\n            raise NotImplementedError(\"FakeNet doesn't support any recv flags\")\n        if self._binding is None:\n            # I messed this up a few times when writing tests ... but it also never happens\n            # in any of the existing tests, so maybe it could be intentional...\n            raise NotImplementedError(\n                \"The code will most likely hang if you try to receive on a fakesocket \"\n                \"without a binding. If that is not the case, or you explicitly want to \"\n                \"test that, remove this warning.\",\n            )\n\n        self._check_closed()\n\n        ancdata: list[tuple[int, int, bytes]] = []\n        msg_flags = 0\n\n        packet = await self._packet_receiver.receive()\n        address = packet.source.as_python_sockaddr()\n        written = _scatter(packet.payload, buffers)\n        if written < len(packet.payload):\n            msg_flags |= trio.socket.MSG_TRUNC\n        return written, ancdata, msg_flags, address\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(socket.socket, \"sendmsg\")\n    ):\n        recvmsg_into = _recvmsg_into\n\n    ################################################################\n    # Simple state query stuff\n    ################################################################\n\n    def getsockname(self) -> tuple[str, int] | tuple[str, int, int, int]:\n        self._check_closed()\n        if self._binding is not None:\n            return self._binding.local.as_python_sockaddr()\n        elif self.family == trio.socket.AF_INET:\n            return (\"0.0.0.0\", 0)\n        else:\n            assert self.family == trio.socket.AF_INET6\n            return (\"::\", 0)\n\n    # TODO: This method is not tested, and seems to make incorrect assumptions. It should maybe raise NotImplementedError.\n    def getpeername(self) -> tuple[str, int] | tuple[str, int, int, int]:\n        self._check_closed()\n        if self._binding is not None:\n            assert hasattr(\n                self._binding,\n                \"remote\",\n            ), \"This method seems to assume that self._binding has a remote UDPEndpoint\"\n            if self._binding.remote is not None:  # pragma: no cover\n                assert isinstance(\n                    self._binding.remote,\n                    UDPEndpoint,\n                ), \"Self._binding.remote should be a UDPEndpoint\"\n                return self._binding.remote.as_python_sockaddr()\n        _fake_err(errno.ENOTCONN)\n\n    @overload\n    def getsockopt(self, /, level: int, optname: int) -> int: ...\n\n    @overload\n    def getsockopt(self, /, level: int, optname: int, buflen: int) -> bytes: ...\n\n    def getsockopt(\n        self,\n        /,\n        level: int,\n        optname: int,\n        buflen: int | None = None,\n    ) -> int | bytes:\n        self._check_closed()\n        raise OSError(f\"FakeNet doesn't implement getsockopt({level}, {optname})\")\n\n    @overload\n    def setsockopt(self, /, level: int, optname: int, value: int | Buffer) -> None: ...\n\n    @overload\n    def setsockopt(\n        self,\n        /,\n        level: int,\n        optname: int,\n        value: None,\n        optlen: int,\n    ) -> None: ...\n\n    def setsockopt(\n        self,\n        /,\n        level: int,\n        optname: int,\n        value: int | Buffer | None,\n        optlen: int | None = None,\n    ) -> None:\n        self._check_closed()\n\n        if (level, optname) == (\n            trio.socket.IPPROTO_IPV6,\n            trio.socket.IPV6_V6ONLY,\n        ) and not value:\n            raise NotImplementedError(\"FakeNet always has IPV6_V6ONLY=True\")\n\n        raise OSError(f\"FakeNet doesn't implement setsockopt({level}, {optname}, ...)\")\n\n    ################################################################\n    # Various boilerplate and trivial stubs\n    ################################################################\n\n    def __enter__(self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: builtins.type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self.close()\n\n    async def send(self, data: Buffer, flags: int = 0) -> int:\n        return await self.sendto(data, flags, None)\n\n    # __ prefixed arguments because typeshed uses that and typechecker issues\n    @overload\n    async def sendto(\n        self,\n        __data: Buffer,  # noqa: PYI063\n        __address: tuple[object, ...] | str | Buffer,\n    ) -> int: ...\n\n    # __ prefixed arguments because typeshed uses that and typechecker issues\n    @overload\n    async def sendto(\n        self,\n        __data: Buffer,  # noqa: PYI063\n        __flags: int,\n        __address: tuple[object, ...] | str | Buffer | None,\n    ) -> int: ...\n\n    async def sendto(  # type: ignore[explicit-any]\n        self,\n        *args: Any,\n    ) -> int:\n        data: Buffer\n        flags: int\n        address: tuple[object, ...] | str | Buffer\n        if len(args) == 2:\n            data, address = args\n            flags = 0\n        elif len(args) == 3:\n            data, flags, address = args\n        else:\n            raise TypeError(\"wrong number of arguments\")\n        return await self._sendmsg([data], [], flags, address)\n\n    async def recv(self, bufsize: int, flags: int = 0) -> bytes:\n        data, _address = await self.recvfrom(bufsize, flags)\n        return data\n\n    async def recv_into(self, buf: Buffer, nbytes: int = 0, flags: int = 0) -> int:\n        got_bytes, _address = await self.recvfrom_into(buf, nbytes, flags)\n        return got_bytes\n\n    async def recvfrom(\n        self,\n        bufsize: int,\n        flags: int = 0,\n    ) -> tuple[bytes, AddressFormat]:\n        data, _ancdata, _msg_flags, address = await self._recvmsg(bufsize, flags)\n        return data, address\n\n    async def recvfrom_into(\n        self,\n        buf: Buffer,\n        nbytes: int = 0,\n        flags: int = 0,\n    ) -> tuple[int, AddressFormat]:\n        if nbytes != 0 and nbytes != memoryview(buf).nbytes:\n            raise NotImplementedError(\"partial recvfrom_into\")\n        got_nbytes, _ancdata, _msg_flags, address = await self._recvmsg_into(\n            [buf],\n            0,\n            flags,\n        )\n        return got_nbytes, address\n\n    async def _recvmsg(\n        self,\n        bufsize: int,\n        ancbufsize: int = 0,\n        flags: int = 0,\n    ) -> tuple[bytes, list[tuple[int, int, bytes]], int, AddressFormat]:\n        buf = bytearray(bufsize)\n        got_nbytes, ancdata, msg_flags, address = await self._recvmsg_into(\n            [buf],\n            ancbufsize,\n            flags,\n        )\n        return (bytes(buf[:got_nbytes]), ancdata, msg_flags, address)\n\n    if sys.platform != \"win32\" or (\n        not TYPE_CHECKING and hasattr(socket.socket, \"sendmsg\")\n    ):\n        recvmsg = _recvmsg\n\n    def fileno(self) -> int:\n        raise NotImplementedError(\"can't get fileno() for FakeNet sockets\")\n\n    def detach(self) -> int:\n        raise NotImplementedError(\"can't detach() a FakeNet socket\")\n\n    def get_inheritable(self) -> bool:\n        return False\n\n    def set_inheritable(self, inheritable: bool) -> None:\n        if inheritable:\n            raise NotImplementedError(\"FakeNet can't make inheritable sockets\")\n\n    if sys.platform == \"win32\" or (\n        not TYPE_CHECKING and hasattr(socket.socket, \"share\")\n    ):\n\n        def share(self, process_id: int) -> bytes:\n            raise NotImplementedError(\"FakeNet can't share sockets\")\n"
  },
  {
    "path": "src/trio/testing/_memory_streams.py",
    "content": "from __future__ import annotations\n\nimport operator\nfrom collections.abc import Awaitable, Callable\nfrom typing import TypeAlias, TypeVar\n\nfrom .. import _core, _util\nfrom .._highlevel_generic import StapledStream\nfrom ..abc import ReceiveStream, SendStream\n\nAsyncHook: TypeAlias = Callable[[], Awaitable[object]]\n# Would be nice to exclude awaitable here, but currently not possible.\nSyncHook: TypeAlias = Callable[[], object]\nSendStreamT = TypeVar(\"SendStreamT\", bound=SendStream)\nReceiveStreamT = TypeVar(\"ReceiveStreamT\", bound=ReceiveStream)\n\n\n################################################################\n# In-memory streams - Unbounded buffer version\n################################################################\n\n\nclass _UnboundedByteQueue:\n    def __init__(self) -> None:\n        self._data = bytearray()\n        self._closed = False\n        self._lot = _core.ParkingLot()\n        self._fetch_lock = _util.ConflictDetector(\n            \"another task is already fetching data\",\n        )\n\n    # This object treats \"close\" as being like closing the send side of a\n    # channel: so after close(), calling put() raises ClosedResourceError, and\n    # calling the get() variants drains the buffer and then returns an empty\n    # bytearray.\n    def close(self) -> None:\n        self._closed = True\n        self._lot.unpark_all()\n\n    def close_and_wipe(self) -> None:\n        self._data = bytearray()\n        self.close()\n\n    def put(self, data: bytes | bytearray | memoryview) -> None:\n        if self._closed:\n            raise _core.ClosedResourceError(\"virtual connection closed\")\n        self._data += data\n        self._lot.unpark_all()\n\n    def _check_max_bytes(self, max_bytes: int | None) -> None:\n        if max_bytes is None:\n            return\n        max_bytes = operator.index(max_bytes)\n        if max_bytes < 1:\n            raise ValueError(\"max_bytes must be >= 1\")\n\n    def _get_impl(self, max_bytes: int | None) -> bytearray:\n        assert self._closed or self._data\n        if max_bytes is None:\n            max_bytes = len(self._data)\n        if self._data:\n            chunk = self._data[:max_bytes]\n            del self._data[:max_bytes]\n            assert chunk\n            return chunk\n        else:\n            return bytearray()\n\n    def get_nowait(self, max_bytes: int | None = None) -> bytearray:\n        with self._fetch_lock:\n            self._check_max_bytes(max_bytes)\n            if not self._closed and not self._data:\n                raise _core.WouldBlock\n            return self._get_impl(max_bytes)\n\n    async def get(self, max_bytes: int | None = None) -> bytearray:\n        with self._fetch_lock:\n            self._check_max_bytes(max_bytes)\n            if not self._closed and not self._data:\n                await self._lot.park()\n            else:\n                await _core.checkpoint()\n            return self._get_impl(max_bytes)\n\n\n@_util.final\nclass MemorySendStream(SendStream):\n    \"\"\"An in-memory :class:`~trio.abc.SendStream`.\n\n    Args:\n      send_all_hook: An async function, or None. Called from\n          :meth:`send_all`. Can do whatever you like.\n      wait_send_all_might_not_block_hook: An async function, or None. Called\n          from :meth:`wait_send_all_might_not_block`. Can do whatever you\n          like.\n      close_hook: A synchronous function, or None. Called from :meth:`close`\n          and :meth:`aclose`. Can do whatever you like.\n\n    .. attribute:: send_all_hook\n                   wait_send_all_might_not_block_hook\n                   close_hook\n\n       All of these hooks are also exposed as attributes on the object, and\n       you can change them at any time.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        send_all_hook: AsyncHook | None = None,\n        wait_send_all_might_not_block_hook: AsyncHook | None = None,\n        close_hook: SyncHook | None = None,\n    ) -> None:\n        self._conflict_detector = _util.ConflictDetector(\n            \"another task is using this stream\",\n        )\n        self._outgoing = _UnboundedByteQueue()\n        self.send_all_hook = send_all_hook\n        self.wait_send_all_might_not_block_hook = wait_send_all_might_not_block_hook\n        self.close_hook = close_hook\n\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        \"\"\"Places the given data into the object's internal buffer, and then\n        calls the :attr:`send_all_hook` (if any).\n\n        \"\"\"\n        # Execute two checkpoints so we have more of a chance to detect\n        # buggy user code that calls this twice at the same time.\n        with self._conflict_detector:\n            await _core.checkpoint()\n            await _core.checkpoint()\n            self._outgoing.put(data)\n            if self.send_all_hook is not None:\n                await self.send_all_hook()\n\n    async def wait_send_all_might_not_block(self) -> None:\n        \"\"\"Calls the :attr:`wait_send_all_might_not_block_hook` (if any), and\n        then returns immediately.\n\n        \"\"\"\n        # Execute two checkpoints so that we have more of a chance to detect\n        # buggy user code that calls this twice at the same time.\n        with self._conflict_detector:\n            await _core.checkpoint()\n            await _core.checkpoint()\n            # check for being closed:\n            self._outgoing.put(b\"\")\n            if self.wait_send_all_might_not_block_hook is not None:\n                await self.wait_send_all_might_not_block_hook()\n\n    def close(self) -> None:\n        \"\"\"Marks this stream as closed, and then calls the :attr:`close_hook`\n        (if any).\n\n        \"\"\"\n        # XXX should this cancel any pending calls to the send_all_hook and\n        # wait_send_all_might_not_block_hook? Those are the only places where\n        # send_all and wait_send_all_might_not_block can be blocked.\n        #\n        # The way we set things up, send_all_hook is memory_stream_pump, and\n        # wait_send_all_might_not_block_hook is unset. memory_stream_pump is\n        # synchronous. So normally, send_all and wait_send_all_might_not_block\n        # cannot block at all.\n        self._outgoing.close()\n        if self.close_hook is not None:\n            self.close_hook()\n\n    async def aclose(self) -> None:\n        \"\"\"Same as :meth:`close`, but async.\"\"\"\n        self.close()\n        await _core.checkpoint()\n\n    async def get_data(self, max_bytes: int | None = None) -> bytearray:\n        \"\"\"Retrieves data from the internal buffer, blocking if necessary.\n\n        Args:\n          max_bytes (int or None): The maximum amount of data to\n              retrieve. None (the default) means to retrieve all the data\n              that's present (but still blocks until at least one byte is\n              available).\n\n        Returns:\n          If this stream has been closed, an empty bytearray. Otherwise, the\n          requested data.\n\n        \"\"\"\n        return await self._outgoing.get(max_bytes)\n\n    def get_data_nowait(self, max_bytes: int | None = None) -> bytearray:\n        \"\"\"Retrieves data from the internal buffer, but doesn't block.\n\n        See :meth:`get_data` for details.\n\n        Raises:\n          trio.WouldBlock: if no data is available to retrieve.\n\n        \"\"\"\n        return self._outgoing.get_nowait(max_bytes)\n\n\n@_util.final\nclass MemoryReceiveStream(ReceiveStream):\n    \"\"\"An in-memory :class:`~trio.abc.ReceiveStream`.\n\n    Args:\n      receive_some_hook: An async function, or None. Called from\n          :meth:`receive_some`. Can do whatever you like.\n      close_hook: A synchronous function, or None. Called from :meth:`close`\n          and :meth:`aclose`. Can do whatever you like.\n\n    .. attribute:: receive_some_hook\n                   close_hook\n\n       Both hooks are also exposed as attributes on the object, and you can\n       change them at any time.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        receive_some_hook: AsyncHook | None = None,\n        close_hook: SyncHook | None = None,\n    ) -> None:\n        self._conflict_detector = _util.ConflictDetector(\n            \"another task is using this stream\",\n        )\n        self._incoming = _UnboundedByteQueue()\n        self._closed = False\n        self.receive_some_hook = receive_some_hook\n        self.close_hook = close_hook\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytearray:\n        \"\"\"Calls the :attr:`receive_some_hook` (if any), and then retrieves\n        data from the internal buffer, blocking if necessary.\n\n        \"\"\"\n        # Execute two checkpoints so we have more of a chance to detect\n        # buggy user code that calls this twice at the same time.\n        with self._conflict_detector:\n            await _core.checkpoint()\n            await _core.checkpoint()\n            if self._closed:\n                raise _core.ClosedResourceError\n            if self.receive_some_hook is not None:\n                await self.receive_some_hook()\n            # self._incoming's closure state tracks whether we got an EOF.\n            # self._closed tracks whether we, ourselves, are closed.\n            # self.close() sends an EOF to wake us up and sets self._closed,\n            # so after we wake up we have to check self._closed again.\n            data = await self._incoming.get(max_bytes)\n            if self._closed:\n                raise _core.ClosedResourceError\n            return data\n\n    def close(self) -> None:\n        \"\"\"Discards any pending data from the internal buffer, and marks this\n        stream as closed.\n\n        \"\"\"\n        self._closed = True\n        self._incoming.close_and_wipe()\n        if self.close_hook is not None:\n            self.close_hook()\n\n    async def aclose(self) -> None:\n        \"\"\"Same as :meth:`close`, but async.\"\"\"\n        self.close()\n        await _core.checkpoint()\n\n    def put_data(self, data: bytes | bytearray | memoryview) -> None:\n        \"\"\"Appends the given data to the internal buffer.\"\"\"\n        self._incoming.put(data)\n\n    def put_eof(self) -> None:\n        \"\"\"Adds an end-of-file marker to the internal buffer.\"\"\"\n        self._incoming.close()\n\n\n# TODO: investigate why this is necessary for the docs\nMemorySendStream.__module__ = MemorySendStream.__module__.replace(\n    \"._memory_streams\", \"\"\n)\nMemoryReceiveStream.__module__ = MemoryReceiveStream.__module__.replace(\n    \"._memory_streams\", \"\"\n)\n\n\ndef memory_stream_pump(\n    memory_send_stream: MemorySendStream,\n    memory_receive_stream: MemoryReceiveStream,\n    *,\n    max_bytes: int | None = None,\n) -> bool:\n    \"\"\"Take data out of the given :class:`MemorySendStream`'s internal buffer,\n    and put it into the given :class:`MemoryReceiveStream`'s internal buffer.\n\n    Args:\n      memory_send_stream (MemorySendStream): The stream to get data from.\n      memory_receive_stream (MemoryReceiveStream): The stream to put data into.\n      max_bytes (int or None): The maximum amount of data to transfer in this\n          call, or None to transfer all available data.\n\n    Returns:\n      True if it successfully transferred some data, or False if there was no\n      data to transfer.\n\n    This is used to implement :func:`memory_stream_one_way_pair` and\n    :func:`memory_stream_pair`; see the latter's docstring for an example\n    of how you might use it yourself.\n\n    \"\"\"\n    try:\n        data = memory_send_stream.get_data_nowait(max_bytes)\n    except _core.WouldBlock:\n        return False\n    try:\n        if not data:\n            memory_receive_stream.put_eof()\n        else:\n            memory_receive_stream.put_data(data)\n    except _core.ClosedResourceError:\n        raise _core.BrokenResourceError(\"MemoryReceiveStream was closed\") from None\n    return True\n\n\ndef memory_stream_one_way_pair() -> tuple[MemorySendStream, MemoryReceiveStream]:\n    \"\"\"Create a connected, pure-Python, unidirectional stream with infinite\n    buffering and flexible configuration options.\n\n    You can think of this as being a no-operating-system-involved\n    Trio-streamsified version of :func:`os.pipe` (except that :func:`os.pipe`\n    returns the streams in the wrong order – we follow the superior convention\n    that data flows from left to right).\n\n    Returns:\n      A tuple (:class:`MemorySendStream`, :class:`MemoryReceiveStream`), where\n      the :class:`MemorySendStream` has its hooks set up so that it calls\n      :func:`memory_stream_pump` from its\n      :attr:`~MemorySendStream.send_all_hook` and\n      :attr:`~MemorySendStream.close_hook`.\n\n    The end result is that data automatically flows from the\n    :class:`MemorySendStream` to the :class:`MemoryReceiveStream`. But you're\n    also free to rearrange things however you like. For example, you can\n    temporarily set the :attr:`~MemorySendStream.send_all_hook` to None if you\n    want to simulate a stall in data transmission. Or see\n    :func:`memory_stream_pair` for a more elaborate example.\n\n    \"\"\"\n    send_stream = MemorySendStream()\n    recv_stream = MemoryReceiveStream()\n\n    def pump_from_send_stream_to_recv_stream() -> None:\n        memory_stream_pump(send_stream, recv_stream)\n\n    # await not used\n    async def async_pump_from_send_stream_to_recv_stream() -> None:  # noqa: RUF029\n        pump_from_send_stream_to_recv_stream()\n\n    send_stream.send_all_hook = async_pump_from_send_stream_to_recv_stream\n    send_stream.close_hook = pump_from_send_stream_to_recv_stream\n    return send_stream, recv_stream\n\n\ndef _make_stapled_pair(\n    one_way_pair: Callable[[], tuple[SendStreamT, ReceiveStreamT]],\n) -> tuple[\n    StapledStream[SendStreamT, ReceiveStreamT],\n    StapledStream[SendStreamT, ReceiveStreamT],\n]:\n    pipe1_send, pipe1_recv = one_way_pair()\n    pipe2_send, pipe2_recv = one_way_pair()\n    stream1 = StapledStream(pipe1_send, pipe2_recv)\n    stream2 = StapledStream(pipe2_send, pipe1_recv)\n    return stream1, stream2\n\n\ndef memory_stream_pair() -> tuple[\n    StapledStream[MemorySendStream, MemoryReceiveStream],\n    StapledStream[MemorySendStream, MemoryReceiveStream],\n]:\n    \"\"\"Create a connected, pure-Python, bidirectional stream with infinite\n    buffering and flexible configuration options.\n\n    This is a convenience function that creates two one-way streams using\n    :func:`memory_stream_one_way_pair`, and then uses\n    :class:`~trio.StapledStream` to combine them into a single bidirectional\n    stream.\n\n    This is like a no-operating-system-involved, Trio-streamsified version of\n    :func:`socket.socketpair`.\n\n    Returns:\n      A pair of :class:`~trio.StapledStream` objects that are connected so\n      that data automatically flows from one to the other in both directions.\n\n    After creating a stream pair, you can send data back and forth, which is\n    enough for simple tests::\n\n       left, right = memory_stream_pair()\n       await left.send_all(b\"123\")\n       assert await right.receive_some() == b\"123\"\n       await right.send_all(b\"456\")\n       assert await left.receive_some() == b\"456\"\n\n    But if you read the docs for :class:`~trio.StapledStream` and\n    :func:`memory_stream_one_way_pair`, you'll see that all the pieces\n    involved in wiring this up are public APIs, so you can adjust to suit the\n    requirements of your tests. For example, here's how to tweak a stream so\n    that data flowing from left to right trickles in one byte at a time (but\n    data flowing from right to left proceeds at full speed)::\n\n        left, right = memory_stream_pair()\n        async def trickle():\n            # left is a StapledStream, and left.send_stream is a MemorySendStream\n            # right is a StapledStream, and right.recv_stream is a MemoryReceiveStream\n            while memory_stream_pump(left.send_stream, right.recv_stream, max_bytes=1):\n                # Pause between each byte\n                await trio.sleep(1)\n        # Normally this send_all_hook calls memory_stream_pump directly without\n        # passing in a max_bytes. We replace it with our custom version:\n        left.send_stream.send_all_hook = trickle\n\n    And here's a simple test using our modified stream objects::\n\n        async def sender():\n            await left.send_all(b\"12345\")\n            await left.send_eof()\n\n        async def receiver():\n            async for data in right:\n                print(data)\n\n        async with trio.open_nursery() as nursery:\n            nursery.start_soon(sender)\n            nursery.start_soon(receiver)\n\n    By default, this will print ``b\"12345\"`` and then immediately exit; with\n    our trickle stream it instead sleeps 1 second, then prints ``b\"1\"``, then\n    sleeps 1 second, then prints ``b\"2\"``, etc.\n\n    Pro-tip: you can insert sleep calls (like in our example above) to\n    manipulate the flow of data across tasks... and then use\n    :class:`MockClock` and its :attr:`~MockClock.autojump_threshold`\n    functionality to keep your test suite running quickly.\n\n    If you want to stress test a protocol implementation, one nice trick is to\n    use the :mod:`random` module (preferably with a fixed seed) to move random\n    numbers of bytes at a time, and insert random sleeps in between them. You\n    can also set up a custom :attr:`~MemoryReceiveStream.receive_some_hook` if\n    you want to manipulate things on the receiving side, and not just the\n    sending side.\n\n    \"\"\"\n    return _make_stapled_pair(memory_stream_one_way_pair)\n\n\n################################################################\n# In-memory streams - Lockstep version\n################################################################\n\n\nclass _LockstepByteQueue:\n    def __init__(self) -> None:\n        self._data = bytearray()\n        self._sender_closed = False\n        self._receiver_closed = False\n        self._receiver_waiting = False\n        self._waiters = _core.ParkingLot()\n        self._send_conflict_detector = _util.ConflictDetector(\n            \"another task is already sending\",\n        )\n        self._receive_conflict_detector = _util.ConflictDetector(\n            \"another task is already receiving\",\n        )\n\n    def _something_happened(self) -> None:\n        self._waiters.unpark_all()\n\n    # Always wakes up when one side is closed, because everyone always reacts\n    # to that.\n    async def _wait_for(self, fn: Callable[[], bool]) -> None:\n        while True:\n            if fn():\n                break\n            if self._sender_closed or self._receiver_closed:\n                break\n            await self._waiters.park()\n        await _core.checkpoint()\n\n    def close_sender(self) -> None:\n        self._sender_closed = True\n        self._something_happened()\n\n    def close_receiver(self) -> None:\n        self._receiver_closed = True\n        self._something_happened()\n\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        with self._send_conflict_detector:\n            if self._sender_closed:\n                raise _core.ClosedResourceError\n            if self._receiver_closed:\n                raise _core.BrokenResourceError\n            assert not self._data\n            self._data += data\n            self._something_happened()\n            await self._wait_for(lambda: self._data == b\"\")\n            if self._sender_closed:\n                raise _core.ClosedResourceError\n            if self._data and self._receiver_closed:\n                raise _core.BrokenResourceError\n\n    async def wait_send_all_might_not_block(self) -> None:\n        with self._send_conflict_detector:\n            if self._sender_closed:\n                raise _core.ClosedResourceError\n            if self._receiver_closed:\n                await _core.checkpoint()\n                return\n            await self._wait_for(lambda: self._receiver_waiting)\n            if self._sender_closed:\n                raise _core.ClosedResourceError\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:\n        with self._receive_conflict_detector:\n            # Argument validation\n            if max_bytes is not None:\n                max_bytes = operator.index(max_bytes)\n                if max_bytes < 1:\n                    raise ValueError(\"max_bytes must be >= 1\")\n            # State validation\n            if self._receiver_closed:\n                raise _core.ClosedResourceError\n            # Wake wait_send_all_might_not_block and wait for data\n            self._receiver_waiting = True\n            self._something_happened()\n            try:\n                await self._wait_for(lambda: self._data != b\"\")\n            finally:\n                self._receiver_waiting = False\n            if self._receiver_closed:\n                raise _core.ClosedResourceError\n            # Get data, possibly waking send_all\n            if self._data:\n                # Neat trick: if max_bytes is None, then obj[:max_bytes] is\n                # the same as obj[:].\n                got = self._data[:max_bytes]\n                del self._data[:max_bytes]\n                self._something_happened()\n                return got\n            else:\n                assert self._sender_closed\n                return b\"\"\n\n\nclass _LockstepSendStream(SendStream):\n    def __init__(self, lbq: _LockstepByteQueue) -> None:\n        self._lbq = lbq\n\n    def close(self) -> None:\n        self._lbq.close_sender()\n\n    async def aclose(self) -> None:\n        self.close()\n        await _core.checkpoint()\n\n    async def send_all(self, data: bytes | bytearray | memoryview) -> None:\n        await self._lbq.send_all(data)\n\n    async def wait_send_all_might_not_block(self) -> None:\n        await self._lbq.wait_send_all_might_not_block()\n\n\nclass _LockstepReceiveStream(ReceiveStream):\n    def __init__(self, lbq: _LockstepByteQueue) -> None:\n        self._lbq = lbq\n\n    def close(self) -> None:\n        self._lbq.close_receiver()\n\n    async def aclose(self) -> None:\n        self.close()\n        await _core.checkpoint()\n\n    async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:\n        return await self._lbq.receive_some(max_bytes)\n\n\ndef lockstep_stream_one_way_pair() -> tuple[SendStream, ReceiveStream]:\n    \"\"\"Create a connected, pure Python, unidirectional stream where data flows\n    in lockstep.\n\n    Returns:\n      A tuple\n      (:class:`~trio.abc.SendStream`, :class:`~trio.abc.ReceiveStream`).\n\n    This stream has *absolutely no* buffering. Each call to\n    :meth:`~trio.abc.SendStream.send_all` will block until all the given data\n    has been returned by a call to\n    :meth:`~trio.abc.ReceiveStream.receive_some`.\n\n    This can be useful for testing flow control mechanisms in an extreme case,\n    or for setting up \"clogged\" streams to use with\n    :func:`check_one_way_stream` and friends.\n\n    In addition to fulfilling the :class:`~trio.abc.SendStream` and\n    :class:`~trio.abc.ReceiveStream` interfaces, the return objects\n    also have a synchronous ``close`` method.\n\n    \"\"\"\n\n    lbq = _LockstepByteQueue()\n    return _LockstepSendStream(lbq), _LockstepReceiveStream(lbq)\n\n\ndef lockstep_stream_pair() -> tuple[\n    StapledStream[SendStream, ReceiveStream],\n    StapledStream[SendStream, ReceiveStream],\n]:\n    \"\"\"Create a connected, pure-Python, bidirectional stream where data flows\n    in lockstep.\n\n    Returns:\n      A tuple (:class:`~trio.StapledStream`, :class:`~trio.StapledStream`).\n\n    This is a convenience function that creates two one-way streams using\n    :func:`lockstep_stream_one_way_pair`, and then uses\n    :class:`~trio.StapledStream` to combine them into a single bidirectional\n    stream.\n\n    \"\"\"\n    return _make_stapled_pair(lockstep_stream_one_way_pair)\n"
  },
  {
    "path": "src/trio/testing/_network.py",
    "content": "from .. import socket as tsocket\nfrom .._highlevel_socket import SocketListener, SocketStream\n\n\nasync def open_stream_to_socket_listener(\n    socket_listener: SocketListener,\n) -> SocketStream:\n    \"\"\"Connect to the given :class:`~trio.SocketListener`.\n\n    This is particularly useful in tests when you want to let a server pick\n    its own port, and then connect to it::\n\n        listeners = await trio.open_tcp_listeners(0)\n        client = await trio.testing.open_stream_to_socket_listener(listeners[0])\n\n    Args:\n      socket_listener (~trio.SocketListener): The\n          :class:`~trio.SocketListener` to connect to.\n\n    Returns:\n      SocketStream: a stream connected to the given listener.\n\n    \"\"\"\n    family = socket_listener.socket.family\n    sockaddr = socket_listener.socket.getsockname()\n    if family in (tsocket.AF_INET, tsocket.AF_INET6):\n        sockaddr = list(sockaddr)\n        if sockaddr[0] == \"0.0.0.0\":\n            sockaddr[0] = \"127.0.0.1\"\n        if sockaddr[0] == \"::\":\n            sockaddr[0] = \"::1\"\n        sockaddr = tuple(sockaddr)\n\n    sock = tsocket.socket(family=family)\n    await sock.connect(sockaddr)\n    return SocketStream(sock)\n"
  },
  {
    "path": "src/trio/testing/_raises_group.py",
    "content": "from __future__ import annotations\n\nimport re\nimport sys\nfrom abc import ABC, abstractmethod\nfrom re import Pattern\nfrom textwrap import indent\nfrom typing import TYPE_CHECKING, Generic, Literal, TypeGuard, cast, overload\n\nfrom trio._util import final\n\nif TYPE_CHECKING:\n    import builtins\n\n    # sphinx will *only* work if we use types.TracebackType, and import\n    # *inside* TYPE_CHECKING. No other combination works.....\n    import types\n    from collections.abc import Callable, Sequence\n\n    from _pytest._code.code import ExceptionChainRepr, ReprExceptionInfo, Traceback\n    from typing_extensions import TypeVar\n\n    # this conditional definition is because we want to allow a TypeVar default\n    MatchE = TypeVar(\n        \"MatchE\",\n        bound=BaseException,\n        default=BaseException,\n        covariant=True,\n    )\nelse:\n    from typing import TypeVar\n\n    MatchE = TypeVar(\"MatchE\", bound=BaseException, covariant=True)\n\n# RaisesGroup doesn't work with a default.\nBaseExcT_co = TypeVar(\"BaseExcT_co\", bound=BaseException, covariant=True)\nBaseExcT_1 = TypeVar(\"BaseExcT_1\", bound=BaseException)\nBaseExcT_2 = TypeVar(\"BaseExcT_2\", bound=BaseException)\nExcT_1 = TypeVar(\"ExcT_1\", bound=Exception)\nExcT_2 = TypeVar(\"ExcT_2\", bound=Exception)\n\nif sys.version_info < (3, 11):\n    from exceptiongroup import BaseExceptionGroup, ExceptionGroup\n\n\n@final\nclass _ExceptionInfo(Generic[MatchE]):\n    \"\"\"Minimal re-implementation of pytest.ExceptionInfo, only used if pytest is not available. Supports a subset of its features necessary for functionality of :class:`trio.testing.RaisesGroup` and :class:`trio.testing.Matcher`.\"\"\"\n\n    _excinfo: tuple[type[MatchE], MatchE, types.TracebackType] | None\n\n    def __init__(\n        self,\n        excinfo: tuple[type[MatchE], MatchE, types.TracebackType] | None,\n    ) -> None:\n        self._excinfo = excinfo\n\n    def fill_unfilled(\n        self,\n        exc_info: tuple[type[MatchE], MatchE, types.TracebackType],\n    ) -> None:\n        \"\"\"Fill an unfilled ExceptionInfo created with ``for_later()``.\"\"\"\n        assert self._excinfo is None, \"ExceptionInfo was already filled\"\n        self._excinfo = exc_info\n\n    @classmethod\n    def for_later(cls) -> _ExceptionInfo[MatchE]:\n        \"\"\"Return an unfilled ExceptionInfo.\"\"\"\n        return cls(None)\n\n    # Note, special cased in sphinx config, since \"type\" conflicts.\n    @property\n    def type(self) -> type[MatchE]:\n        \"\"\"The exception class.\"\"\"\n        assert (\n            self._excinfo is not None\n        ), \".type can only be used after the context manager exits\"\n        return self._excinfo[0]\n\n    @property\n    def value(self) -> MatchE:\n        \"\"\"The exception value.\"\"\"\n        assert (\n            self._excinfo is not None\n        ), \".value can only be used after the context manager exits\"\n        return self._excinfo[1]\n\n    @property\n    def tb(self) -> types.TracebackType:\n        \"\"\"The exception raw traceback.\"\"\"\n        assert (\n            self._excinfo is not None\n        ), \".tb can only be used after the context manager exits\"\n        return self._excinfo[2]\n\n    def exconly(self, tryshort: bool = False) -> str:\n        raise NotImplementedError(\n            \"This is a helper method only available if you use RaisesGroup with the pytest package installed\",\n        )\n\n    def errisinstance(\n        self,\n        exc: builtins.type[BaseException] | tuple[builtins.type[BaseException], ...],\n    ) -> bool:\n        raise NotImplementedError(\n            \"This is a helper method only available if you use RaisesGroup with the pytest package installed\",\n        )\n\n    def getrepr(\n        self,\n        showlocals: bool = False,\n        style: str = \"long\",\n        abspath: bool = False,\n        tbfilter: bool | Callable[[_ExceptionInfo], Traceback] = True,\n        funcargs: bool = False,\n        truncate_locals: bool = True,\n        chain: bool = True,\n    ) -> ReprExceptionInfo | ExceptionChainRepr:\n        raise NotImplementedError(\n            \"This is a helper method only available if you use RaisesGroup with the pytest package installed\",\n        )\n\n\n# Type checkers are not able to do conditional types depending on installed packages, so\n# we've added signatures for all helpers to _ExceptionInfo, and then always use that.\n# If this ends up leading to problems, we can resort to always using _ExceptionInfo and\n# users that want to use getrepr/errisinstance/exconly can write helpers on their own, or\n# we reimplement them ourselves...or get this merged in upstream pytest.\nif TYPE_CHECKING:\n    ExceptionInfo = _ExceptionInfo\n\nelse:\n    try:\n        from pytest import ExceptionInfo  # noqa: PT013\n    except ImportError:  # pragma: no cover\n        ExceptionInfo = _ExceptionInfo\n\n\n# copied from pytest.ExceptionInfo\ndef _stringify_exception(exc: BaseException) -> str:\n    return \"\\n\".join(\n        [\n            getattr(exc, \"message\", str(exc)),\n            *getattr(exc, \"__notes__\", []),\n        ],\n    )\n\n\n# String patterns default to including the unicode flag.\n_REGEX_NO_FLAGS = re.compile(r\"\").flags\n\n\ndef _match_pattern(match: Pattern[str]) -> str | Pattern[str]:\n    \"\"\"helper function to remove redundant `re.compile` calls when printing regex\"\"\"\n    return match.pattern if match.flags == _REGEX_NO_FLAGS else match\n\n\ndef repr_callable(fun: Callable[[BaseExcT_1], bool]) -> str:\n    \"\"\"Get the repr of a ``check`` parameter.\n\n    Split out so it can be monkeypatched (e.g. by our hypothesis plugin)\n    \"\"\"\n    return repr(fun)\n\n\ndef _exception_type_name(e: type[BaseException]) -> str:\n    return repr(e.__name__)\n\n\ndef _check_raw_type(\n    expected_type: type[BaseException] | None,\n    exception: BaseException,\n) -> str | None:\n    if expected_type is None:\n        return None\n\n    if not isinstance(\n        exception,\n        expected_type,\n    ):\n        actual_type_str = _exception_type_name(type(exception))\n        expected_type_str = _exception_type_name(expected_type)\n        if isinstance(exception, BaseExceptionGroup) and not issubclass(\n            expected_type, BaseExceptionGroup\n        ):\n            return f\"Unexpected nested {actual_type_str}, expected {expected_type_str}\"\n        return f\"{actual_type_str} is not of type {expected_type_str}\"\n    return None\n\n\nclass AbstractMatcher(ABC, Generic[BaseExcT_co]):\n    \"\"\"ABC with common functionality shared between Matcher and RaisesGroup\"\"\"\n\n    def __init__(\n        self,\n        match: str | Pattern[str] | None,\n        check: Callable[[BaseExcT_co], bool] | None,\n    ) -> None:\n        if isinstance(match, str):\n            self.match: Pattern[str] | None = re.compile(match)\n        else:\n            self.match = match\n        self.check = check\n        self._fail_reason: str | None = None\n\n        # used to suppress repeated printing of `repr(self.check)`\n        self._nested: bool = False\n\n    @property\n    def fail_reason(self) -> str | None:\n        \"\"\"Set after a call to `matches` to give a human-readable\n        reason for why the match failed.\n        When used as a context manager the string will be given as the text of an\n        `AssertionError`\"\"\"\n        return self._fail_reason\n\n    def _check_check(\n        self: AbstractMatcher[BaseExcT_1],\n        exception: BaseExcT_1,\n    ) -> bool:\n        if self.check is None:\n            return True\n\n        if self.check(exception):\n            return True\n\n        check_repr = \"\" if self._nested else \" \" + repr_callable(self.check)\n        self._fail_reason = f\"check{check_repr} did not return True\"\n        return False\n\n    def _check_match(self, e: BaseException) -> bool:\n        if self.match is None or re.search(\n            self.match,\n            stringified_exception := _stringify_exception(e),\n        ):\n            return True\n\n        maybe_specify_type = (\n            f\" of {_exception_type_name(type(e))}\"\n            if isinstance(e, BaseExceptionGroup)\n            else \"\"\n        )\n        self._fail_reason = f\"Regex pattern {_match_pattern(self.match)!r} did not match {stringified_exception!r}{maybe_specify_type}\"\n        if _match_pattern(self.match) == stringified_exception:\n            self._fail_reason += \"\\n  Did you mean to `re.escape()` the regex?\"\n        return False\n\n    # TODO: when transitioning to pytest, harmonize Matcher and RaisesGroup\n    # signatures. One names the parameter `exc_val` and the other `exception`\n    @abstractmethod\n    def matches(\n        self: AbstractMatcher[BaseExcT_1], exc_val: BaseException\n    ) -> TypeGuard[BaseExcT_1]:\n        \"\"\"Check if an exception matches the requirements of this AbstractMatcher.\n        If it fails, `AbstractMatcher.fail_reason` should be set.\n        \"\"\"\n\n\n@final\nclass Matcher(AbstractMatcher[MatchE]):\n    \"\"\"Helper class to be used together with RaisesGroups when you want to specify requirements on sub-exceptions. Only specifying the type is redundant, and it's also unnecessary when the type is a nested `RaisesGroup` since it supports the same arguments.\n    The type is checked with `isinstance`, and does not need to be an exact match. If that is wanted you can use the ``check`` parameter.\n    :meth:`Matcher.matches` can also be used standalone to check individual exceptions.\n\n    Examples::\n\n        with RaisesGroups(Matcher(ValueError, match=\"string\")):\n            ...\n        with RaisesGroups(Matcher(check=lambda x: x.args == (3, \"hello\"))):\n            ...\n        with RaisesGroups(Matcher(check=lambda x: type(x) is ValueError)):\n            ...\n\n    Tip: if you install ``hypothesis`` and import it in ``conftest.py`` you will get\n    readable ``repr``s of ``check`` callables in the output.\n    \"\"\"\n\n    # At least one of the three parameters must be passed.\n    @overload\n    def __init__(\n        self,\n        exception_type: type[MatchE],\n        match: str | Pattern[str] = ...,\n        check: Callable[[MatchE], bool] = ...,\n    ) -> None: ...\n\n    @overload\n    def __init__(\n        self: Matcher[BaseException],  # Give E a value.\n        *,\n        match: str | Pattern[str],\n        # If exception_type is not provided, check() must do any typechecks itself.\n        check: Callable[[BaseException], bool] = ...,\n    ) -> None: ...\n\n    @overload\n    def __init__(self, *, check: Callable[[BaseException], bool]) -> None: ...\n\n    def __init__(\n        self,\n        exception_type: type[MatchE] | None = None,\n        match: str | Pattern[str] | None = None,\n        check: Callable[[MatchE], bool] | None = None,\n    ):\n        super().__init__(match, check)\n        if exception_type is None and match is None and check is None:\n            raise ValueError(\"You must specify at least one parameter to match on.\")\n        if exception_type is not None and not issubclass(exception_type, BaseException):\n            raise ValueError(\n                f\"exception_type {exception_type} must be a subclass of BaseException\",\n            )\n        self.exception_type = exception_type\n\n    def matches(\n        self,\n        exception: BaseException,\n    ) -> TypeGuard[MatchE]:\n        \"\"\"Check if an exception matches the requirements of this Matcher.\n        If it fails, `Matcher.fail_reason` will be set.\n\n        Examples::\n\n            assert Matcher(ValueError).matches(my_exception)\n            # is equivalent to\n            assert isinstance(my_exception, ValueError)\n\n            # this can be useful when checking e.g. the ``__cause__`` of an exception.\n            with pytest.raises(ValueError) as excinfo:\n                ...\n            assert Matcher(SyntaxError, match=\"foo\").matches(excinfo.value.__cause__)\n            # above line is equivalent to\n            assert isinstance(excinfo.value.__cause__, SyntaxError)\n            assert re.search(\"foo\", str(excinfo.value.__cause__))\n\n        \"\"\"\n        if not self._check_type(exception):\n            return False\n\n        if not self._check_match(exception):\n            return False\n\n        return self._check_check(exception)\n\n    def __repr__(self) -> str:\n        parameters = []\n        if self.exception_type is not None:\n            parameters.append(self.exception_type.__name__)\n        if self.match is not None:\n            # If no flags were specified, discard the redundant re.compile() here.\n            parameters.append(\n                f\"match={_match_pattern(self.match)!r}\",\n            )\n        if self.check is not None:\n            parameters.append(f\"check={repr_callable(self.check)}\")\n        return f'Matcher({\", \".join(parameters)})'\n\n    def _check_type(self, exception: BaseException) -> TypeGuard[MatchE]:\n        self._fail_reason = _check_raw_type(self.exception_type, exception)\n        return self._fail_reason is None\n\n\n@final\nclass RaisesGroup(AbstractMatcher[BaseExceptionGroup[BaseExcT_co]]):\n    \"\"\"Contextmanager for checking for an expected `ExceptionGroup`.\n    This works similar to ``pytest.raises``, and a version of it will hopefully be added upstream, after which this can be deprecated and removed. See https://github.com/pytest-dev/pytest/issues/11538\n\n\n    The catching behaviour differs from :ref:`except* <except_star>` in multiple different ways, being much stricter by default. By using ``allow_unwrapped=True`` and ``flatten_subgroups=True`` you can match ``except*`` fully when expecting a single exception.\n\n    #. All specified exceptions must be present, *and no others*.\n\n       * If you expect a variable number of exceptions you need to use ``pytest.raises(ExceptionGroup)`` and manually check the contained exceptions. Consider making use of :func:`Matcher.matches`.\n\n    #. It will only catch exceptions wrapped in an exceptiongroup by default.\n\n       * With ``allow_unwrapped=True`` you can specify a single expected exception or `Matcher` and it will match the exception even if it is not inside an `ExceptionGroup`. If you expect one of several different exception types you need to use a `Matcher` object.\n\n    #. By default it cares about the full structure with nested `ExceptionGroup`'s. You can specify nested `ExceptionGroup`'s by passing `RaisesGroup` objects as expected exceptions.\n\n       * With ``flatten_subgroups=True`` it will \"flatten\" the raised `ExceptionGroup`, extracting all exceptions inside any nested :class:`ExceptionGroup`, before matching.\n\n    It does not care about the order of the exceptions, so ``RaisesGroups(ValueError, TypeError)`` is equivalent to ``RaisesGroups(TypeError, ValueError)``.\n\n    Examples::\n\n        with RaisesGroups(ValueError):\n            raise ExceptionGroup(\"\", (ValueError(),))\n        with RaisesGroups(ValueError, ValueError, Matcher(TypeError, match=\"expected int\")):\n            ...\n        with RaisesGroups(KeyboardInterrupt, match=\"hello\", check=lambda x: type(x) is BaseExceptionGroup):\n            ...\n        with RaisesGroups(RaisesGroups(ValueError)):\n            raise ExceptionGroup(\"\", (ExceptionGroup(\"\", (ValueError(),)),))\n\n        # flatten_subgroups\n        with RaisesGroups(ValueError, flatten_subgroups=True):\n            raise ExceptionGroup(\"\", (ExceptionGroup(\"\", (ValueError(),)),))\n\n        # allow_unwrapped\n        with RaisesGroups(ValueError, allow_unwrapped=True):\n            raise ValueError\n\n\n    `RaisesGroup.matches` can also be used directly to check a standalone exception group.\n\n\n    The matching algorithm is greedy, which means cases such as this may fail::\n\n        with RaisesGroups(ValueError, Matcher(ValueError, match=\"hello\")):\n            raise ExceptionGroup(\"\", (ValueError(\"hello\"), ValueError(\"goodbye\")))\n\n    even though it generally does not care about the order of the exceptions in the group.\n    To avoid the above you should specify the first ValueError with a Matcher as well.\n\n    Tip: if you install ``hypothesis`` and import it in ``conftest.py`` you will get\n    readable ``repr``s of ``check`` callables in the output.\n    \"\"\"\n\n    # allow_unwrapped=True requires: singular exception, exception not being\n    # RaisesGroup instance, match is None, check is None\n    @overload\n    def __init__(\n        self,\n        exception: type[BaseExcT_co] | Matcher[BaseExcT_co],\n        *,\n        allow_unwrapped: Literal[True],\n        flatten_subgroups: bool = False,\n    ) -> None: ...\n\n    # flatten_subgroups = True also requires no nested RaisesGroup\n    @overload\n    def __init__(\n        self,\n        exception: type[BaseExcT_co] | Matcher[BaseExcT_co],\n        *other_exceptions: type[BaseExcT_co] | Matcher[BaseExcT_co],\n        flatten_subgroups: Literal[True],\n        match: str | Pattern[str] | None = None,\n        check: Callable[[BaseExceptionGroup[BaseExcT_co]], bool] | None = None,\n    ) -> None: ...\n\n    # simplify the typevars if possible (the following 3 are equivalent but go simpler->complicated)\n    # ... the first handles RaisesGroup[ValueError], the second RaisesGroup[ExceptionGroup[ValueError]],\n    #     the third RaisesGroup[ValueError | ExceptionGroup[ValueError]].\n    # ... otherwise, we will get results like RaisesGroup[ValueError | ExceptionGroup[Never]] (I think)\n    #     (technically correct but misleading)\n    @overload\n    def __init__(\n        self: RaisesGroup[ExcT_1],\n        exception: type[ExcT_1] | Matcher[ExcT_1],\n        *other_exceptions: type[ExcT_1] | Matcher[ExcT_1],\n        match: str | Pattern[str] | None = None,\n        check: Callable[[ExceptionGroup[ExcT_1]], bool] | None = None,\n    ) -> None: ...\n\n    @overload\n    def __init__(\n        self: RaisesGroup[ExceptionGroup[ExcT_2]],\n        exception: RaisesGroup[ExcT_2],\n        *other_exceptions: RaisesGroup[ExcT_2],\n        match: str | Pattern[str] | None = None,\n        check: Callable[[ExceptionGroup[ExceptionGroup[ExcT_2]]], bool] | None = None,\n    ) -> None: ...\n\n    @overload\n    def __init__(\n        self: RaisesGroup[ExcT_1 | ExceptionGroup[ExcT_2]],\n        exception: type[ExcT_1] | Matcher[ExcT_1] | RaisesGroup[ExcT_2],\n        *other_exceptions: type[ExcT_1] | Matcher[ExcT_1] | RaisesGroup[ExcT_2],\n        match: str | Pattern[str] | None = None,\n        check: (\n            Callable[[ExceptionGroup[ExcT_1 | ExceptionGroup[ExcT_2]]], bool] | None\n        ) = None,\n    ) -> None: ...\n\n    # same as the above 3 but handling BaseException\n    @overload\n    def __init__(\n        self: RaisesGroup[BaseExcT_1],\n        exception: type[BaseExcT_1] | Matcher[BaseExcT_1],\n        *other_exceptions: type[BaseExcT_1] | Matcher[BaseExcT_1],\n        match: str | Pattern[str] | None = None,\n        check: Callable[[BaseExceptionGroup[BaseExcT_1]], bool] | None = None,\n    ) -> None: ...\n\n    @overload\n    def __init__(\n        self: RaisesGroup[BaseExceptionGroup[BaseExcT_2]],\n        exception: RaisesGroup[BaseExcT_2],\n        *other_exceptions: RaisesGroup[BaseExcT_2],\n        match: str | Pattern[str] | None = None,\n        check: (\n            Callable[[BaseExceptionGroup[BaseExceptionGroup[BaseExcT_2]]], bool] | None\n        ) = None,\n    ) -> None: ...\n\n    @overload\n    def __init__(\n        self: RaisesGroup[BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]],\n        exception: type[BaseExcT_1] | Matcher[BaseExcT_1] | RaisesGroup[BaseExcT_2],\n        *other_exceptions: type[BaseExcT_1]\n        | Matcher[BaseExcT_1]\n        | RaisesGroup[BaseExcT_2],\n        match: str | Pattern[str] | None = None,\n        check: (\n            Callable[\n                [BaseExceptionGroup[BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]]],\n                bool,\n            ]\n            | None\n        ) = None,\n    ) -> None: ...\n\n    def __init__(\n        self: RaisesGroup[ExcT_1 | BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]],\n        exception: type[BaseExcT_1] | Matcher[BaseExcT_1] | RaisesGroup[BaseExcT_2],\n        *other_exceptions: type[BaseExcT_1]\n        | Matcher[BaseExcT_1]\n        | RaisesGroup[BaseExcT_2],\n        allow_unwrapped: bool = False,\n        flatten_subgroups: bool = False,\n        match: str | Pattern[str] | None = None,\n        check: (\n            Callable[[BaseExceptionGroup[BaseExcT_1]], bool]\n            | Callable[[ExceptionGroup[ExcT_1]], bool]\n            | None\n        ) = None,\n    ):\n        # The type hint on the `self` and `check` parameters uses different formats\n        # that are *very* hard to reconcile while adhering to the overloads, so we cast\n        # it to avoid an error when passing it to super().__init__\n        check = cast(\n            \"Callable[[\"\n            \"BaseExceptionGroup[ExcT_1|BaseExcT_1|BaseExceptionGroup[BaseExcT_2]]\"\n            \"], bool]\",\n            check,\n        )\n        super().__init__(match, check)\n        self.expected_exceptions: tuple[\n            type[BaseExcT_co] | Matcher[BaseExcT_co] | RaisesGroup[BaseException], ...\n        ] = (\n            exception,\n            *other_exceptions,\n        )\n        self.allow_unwrapped = allow_unwrapped\n        self.flatten_subgroups: bool = flatten_subgroups\n        self.is_baseexceptiongroup: bool = False\n\n        if allow_unwrapped and other_exceptions:\n            raise ValueError(\n                \"You cannot specify multiple exceptions with `allow_unwrapped=True.`\"\n                \" If you want to match one of multiple possible exceptions you should\"\n                \" use a `Matcher`.\"\n                \" E.g. `Matcher(check=lambda e: isinstance(e, (...)))`\",\n            )\n        if allow_unwrapped and isinstance(exception, RaisesGroup):\n            raise ValueError(\n                \"`allow_unwrapped=True` has no effect when expecting a `RaisesGroup`.\"\n                \" You might want it in the expected `RaisesGroup`, or\"\n                \" `flatten_subgroups=True` if you don't care about the structure.\",\n            )\n        if allow_unwrapped and (match is not None or check is not None):\n            raise ValueError(\n                \"`allow_unwrapped=True` bypasses the `match` and `check` parameters\"\n                \" if the exception is unwrapped. If you intended to match/check the\"\n                \" exception you should use a `Matcher` object. If you want to match/check\"\n                \" the exceptiongroup when the exception *is* wrapped you need to\"\n                \" do e.g. `if isinstance(exc.value, ExceptionGroup):\"\n                \" assert RaisesGroup(...).matches(exc.value)` afterwards.\",\n            )\n\n        # verify `expected_exceptions` and set `self.is_baseexceptiongroup`\n        for exc in self.expected_exceptions:\n            if isinstance(exc, RaisesGroup):\n                if self.flatten_subgroups:\n                    raise ValueError(\n                        \"You cannot specify a nested structure inside a RaisesGroup with\"\n                        \" `flatten_subgroups=True`. The parameter will flatten subgroups\"\n                        \" in the raised exceptiongroup before matching, which would never\"\n                        \" match a nested structure.\",\n                    )\n                self.is_baseexceptiongroup |= exc.is_baseexceptiongroup\n                exc._nested = True\n            elif isinstance(exc, Matcher):\n                if exc.exception_type is not None:\n                    # Matcher __init__ assures it's a subclass of BaseException\n                    self.is_baseexceptiongroup |= not issubclass(\n                        exc.exception_type,\n                        Exception,\n                    )\n                exc._nested = True\n            elif isinstance(exc, type) and issubclass(exc, BaseException):\n                self.is_baseexceptiongroup |= not issubclass(exc, Exception)\n            else:\n                raise ValueError(\n                    f'Invalid argument \"{exc!r}\" must be exception type, Matcher, or'\n                    \" RaisesGroup.\",\n                )\n\n    @overload\n    def __enter__(\n        self: RaisesGroup[ExcT_1],\n    ) -> ExceptionInfo[ExceptionGroup[ExcT_1]]: ...\n    @overload\n    def __enter__(\n        self: RaisesGroup[BaseExcT_1],\n    ) -> ExceptionInfo[BaseExceptionGroup[BaseExcT_1]]: ...\n\n    def __enter__(self) -> ExceptionInfo[BaseExceptionGroup[BaseException]]:\n        self.excinfo: ExceptionInfo[BaseExceptionGroup[BaseExcT_co]] = (\n            ExceptionInfo.for_later()\n        )\n        return self.excinfo\n\n    def __repr__(self) -> str:\n        parameters = [\n            e.__name__ if isinstance(e, type) else repr(e)\n            for e in self.expected_exceptions\n        ]\n        if self.allow_unwrapped:\n            parameters.append(f\"allow_unwrapped={self.allow_unwrapped}\")\n        if self.flatten_subgroups:\n            parameters.append(f\"flatten_subgroups={self.flatten_subgroups}\")\n        if self.match is not None:\n            # If no flags were specified, discard the redundant re.compile() here.\n            parameters.append(f\"match={_match_pattern(self.match)!r}\")\n        if self.check is not None:\n            parameters.append(f\"check={repr_callable(self.check)}\")\n        return f\"RaisesGroup({', '.join(parameters)})\"\n\n    def _unroll_exceptions(\n        self,\n        exceptions: Sequence[BaseException],\n    ) -> Sequence[BaseException]:\n        \"\"\"Used if `flatten_subgroups=True`.\"\"\"\n        res: list[BaseException] = []\n        for exc in exceptions:\n            if isinstance(exc, BaseExceptionGroup):\n                res.extend(self._unroll_exceptions(exc.exceptions))\n\n            else:\n                res.append(exc)\n        return res\n\n    @overload\n    def matches(\n        self: RaisesGroup[ExcT_1],\n        exc_val: BaseException | None,\n    ) -> TypeGuard[ExceptionGroup[ExcT_1]]: ...\n    @overload\n    def matches(\n        self: RaisesGroup[BaseExcT_1],\n        exc_val: BaseException | None,\n    ) -> TypeGuard[BaseExceptionGroup[BaseExcT_1]]: ...\n\n    def matches(\n        self,\n        exc_val: BaseException | None,\n    ) -> TypeGuard[BaseExceptionGroup[BaseExcT_co]]:\n        \"\"\"Check if an exception matches the requirements of this RaisesGroup.\n        If it fails, `RaisesGroup.fail_reason` will be set.\n\n        Example::\n\n            with pytest.raises(TypeError) as excinfo:\n                ...\n            assert RaisesGroups(ValueError).matches(excinfo.value.__cause__)\n            # the above line is equivalent to\n            myexc = excinfo.value.__cause\n            assert isinstance(myexc, BaseExceptionGroup)\n            assert len(myexc.exceptions) == 1\n            assert isinstance(myexc.exceptions[0], ValueError)\n        \"\"\"\n        self._fail_reason = None\n        if exc_val is None:\n            self._fail_reason = \"exception is None\"\n            return False\n        if not isinstance(exc_val, BaseExceptionGroup):\n            # we opt to only print type of the exception here, as the repr would\n            # likely be quite long\n            not_group_msg = f\"{type(exc_val).__name__!r} is not an exception group\"\n            if len(self.expected_exceptions) > 1:\n                self._fail_reason = not_group_msg\n                return False\n            # if we have 1 expected exception, check if it would work even if\n            # allow_unwrapped is not set\n            res = self._check_expected(self.expected_exceptions[0], exc_val)\n            if res is None and self.allow_unwrapped:\n                return True\n\n            if res is None:\n                self._fail_reason = (\n                    f\"{not_group_msg}, but would match with `allow_unwrapped=True`\"\n                )\n            elif self.allow_unwrapped:\n                self._fail_reason = res\n            else:\n                self._fail_reason = not_group_msg\n            return False\n\n        actual_exceptions: Sequence[BaseException] = exc_val.exceptions\n        if self.flatten_subgroups:\n            actual_exceptions = self._unroll_exceptions(actual_exceptions)\n\n        if not self._check_match(exc_val):\n            old_reason = self._fail_reason\n            if (\n                len(actual_exceptions) == len(self.expected_exceptions) == 1\n                and isinstance(expected := self.expected_exceptions[0], type)\n                and isinstance(actual := actual_exceptions[0], expected)\n                and self._check_match(actual)\n            ):\n                assert self.match is not None, \"can't be None if _check_match failed\"\n                assert self._fail_reason is old_reason is not None\n                self._fail_reason += f\", but matched the expected {self._repr_expected(expected)}. You might want RaisesGroup(Matcher({expected.__name__}, match={_match_pattern(self.match)!r}))\"\n            else:\n                self._fail_reason = old_reason\n            return False\n\n        # do the full check on expected exceptions\n        if not self._check_exceptions(\n            exc_val,\n            actual_exceptions,\n        ):\n            assert self._fail_reason is not None\n            old_reason = self._fail_reason\n            # if we're not expecting a nested structure, and there is one, do a second\n            # pass where we try flattening it\n            if (\n                not self.flatten_subgroups\n                and not any(\n                    isinstance(e, RaisesGroup) for e in self.expected_exceptions\n                )\n                and any(isinstance(e, BaseExceptionGroup) for e in actual_exceptions)\n                and self._check_exceptions(\n                    exc_val,\n                    self._unroll_exceptions(exc_val.exceptions),\n                )\n            ):\n                # only indent if it's a single-line reason. In a multi-line there's already\n                # indented lines that this does not belong to.\n                indent = \"  \" if \"\\n\" not in self._fail_reason else \"\"\n                self._fail_reason = (\n                    old_reason\n                    + f\"\\n{indent}Did you mean to use `flatten_subgroups=True`?\"\n                )\n            else:\n                self._fail_reason = old_reason\n            return False\n\n        # Only run `self.check` once we know `exc_val` is of the correct type.\n        # TODO: if this fails, we should say the *group* did not match\n        return self._check_check(exc_val)\n\n    @staticmethod\n    def _check_expected(\n        expected_type: (\n            type[BaseException] | Matcher[BaseException] | RaisesGroup[BaseException]\n        ),\n        exception: BaseException,\n    ) -> str | None:\n        \"\"\"Helper method for `RaisesGroup.matches` and `RaisesGroup._check_exceptions`\n        to check one of potentially several expected exceptions.\"\"\"\n        if isinstance(expected_type, type):\n            return _check_raw_type(expected_type, exception)\n        res = expected_type.matches(exception)\n        if res:\n            return None\n        assert expected_type.fail_reason is not None\n        if expected_type.fail_reason.startswith(\"\\n\"):\n            return f\"\\n{expected_type!r}: {indent(expected_type.fail_reason, '  ')}\"\n        return f\"{expected_type!r}: {expected_type.fail_reason}\"\n\n    @staticmethod\n    def _repr_expected(e: type[BaseException] | AbstractMatcher[BaseException]) -> str:\n        \"\"\"Get the repr of an expected type/Matcher/RaisesGroup, but we only want\n        the name if it's a type\"\"\"\n        if isinstance(e, type):\n            return _exception_type_name(e)\n        return repr(e)\n\n    @overload\n    def _check_exceptions(\n        self: RaisesGroup[ExcT_1],\n        _exc_val: Exception,\n        actual_exceptions: Sequence[Exception],\n    ) -> TypeGuard[ExceptionGroup[ExcT_1]]: ...\n    @overload\n    def _check_exceptions(\n        self: RaisesGroup[BaseExcT_1],\n        _exc_val: BaseException,\n        actual_exceptions: Sequence[BaseException],\n    ) -> TypeGuard[BaseExceptionGroup[BaseExcT_1]]: ...\n\n    def _check_exceptions(\n        self,\n        _exc_val: BaseException,\n        actual_exceptions: Sequence[BaseException],\n    ) -> TypeGuard[BaseExceptionGroup[BaseExcT_co]]:\n        \"\"\"helper method for RaisesGroup.matches that attempts to pair up expected and actual exceptions\"\"\"\n        # full table with all results\n        results = ResultHolder(self.expected_exceptions, actual_exceptions)\n\n        # (indexes of) raised exceptions that haven't (yet) found an expected\n        remaining_actual = list(range(len(actual_exceptions)))\n        # (indexes of) expected exceptions that haven't found a matching raised\n        failed_expected: list[int] = []\n        # successful greedy matches\n        matches: dict[int, int] = {}\n\n        # loop over expected exceptions first to get a more predictable result\n        for i_exp, expected in enumerate(self.expected_exceptions):\n            for i_rem in remaining_actual:\n                res = self._check_expected(expected, actual_exceptions[i_rem])\n                results.set_result(i_exp, i_rem, res)\n                if res is None:\n                    remaining_actual.remove(i_rem)\n                    matches[i_exp] = i_rem\n                    break\n            else:\n                failed_expected.append(i_exp)\n\n        # All exceptions matched up successfully\n        if not remaining_actual and not failed_expected:\n            return True\n\n        # in case of a single expected and single raised we simplify the output\n        if 1 == len(actual_exceptions) == len(self.expected_exceptions):\n            assert not matches\n            self._fail_reason = res\n            return False\n\n        # The test case is failing, so we can do a slow and exhaustive check to find\n        # duplicate matches etc that will be helpful in debugging\n        for i_exp, expected in enumerate(self.expected_exceptions):\n            for i_actual, actual in enumerate(actual_exceptions):\n                if results.has_result(i_exp, i_actual):\n                    continue\n                results.set_result(\n                    i_exp, i_actual, self._check_expected(expected, actual)\n                )\n\n        successful_str = (\n            f\"{len(matches)} matched exception{'s' if len(matches) > 1 else ''}. \"\n            if matches\n            else \"\"\n        )\n\n        # all expected were found\n        if not failed_expected and results.no_match_for_actual(remaining_actual):\n            self._fail_reason = f\"{successful_str}Unexpected exception(s): {[actual_exceptions[i] for i in remaining_actual]!r}\"\n            return False\n        # all raised exceptions were expected\n        if not remaining_actual and results.no_match_for_expected(failed_expected):\n            self._fail_reason = f\"{successful_str}Too few exceptions raised, found no match for: [{', '.join(self._repr_expected(self.expected_exceptions[i]) for i in failed_expected)}]\"\n            return False\n\n        # if there's only one remaining and one failed, and the unmatched didn't match anything else,\n        # we elect to only print why the remaining and the failed didn't match.\n        if (\n            1 == len(remaining_actual) == len(failed_expected)\n            and results.no_match_for_actual(remaining_actual)\n            and results.no_match_for_expected(failed_expected)\n        ):\n            self._fail_reason = f\"{successful_str}{results.get_result(failed_expected[0], remaining_actual[0])}\"\n            return False\n\n        # there's both expected and raised exceptions without matches\n        s = \"\"\n        if matches:\n            s += f\"\\n{successful_str}\"\n        indent_1 = \" \" * 2\n        indent_2 = \" \" * 4\n\n        if not remaining_actual:\n            s += \"\\nToo few exceptions raised!\"\n        elif not failed_expected:\n            s += \"\\nUnexpected exception(s)!\"\n\n        if failed_expected:\n            s += \"\\nThe following expected exceptions did not find a match:\"\n            rev_matches = {v: k for k, v in matches.items()}\n        for i_failed in failed_expected:\n            s += (\n                f\"\\n{indent_1}{self._repr_expected(self.expected_exceptions[i_failed])}\"\n            )\n            for i_actual, actual in enumerate(actual_exceptions):\n                if results.get_result(i_exp, i_actual) is None:\n                    # we print full repr of match target\n                    s += f\"\\n{indent_2}It matches {actual!r} which was paired with {self._repr_expected(self.expected_exceptions[rev_matches[i_actual]])}\"\n\n        if remaining_actual:\n            s += \"\\nThe following raised exceptions did not find a match\"\n        for i_actual in remaining_actual:\n            s += f\"\\n{indent_1}{actual_exceptions[i_actual]!r}:\"\n            for i_exp, expected in enumerate(self.expected_exceptions):\n                res = results.get_result(i_exp, i_actual)\n                if i_exp in failed_expected:\n                    assert res is not None\n                    if res[0] != \"\\n\":\n                        s += \"\\n\"\n                    s += indent(res, indent_2)\n                if res is None:\n                    # we print full repr of match target\n                    s += f\"\\n{indent_2}It matches {self._repr_expected(expected)} which was paired with {actual_exceptions[matches[i_exp]]!r}\"\n\n        if len(self.expected_exceptions) == len(actual_exceptions) and possible_match(\n            results\n        ):\n            s += \"\\nThere exist a possible match when attempting an exhaustive check, but RaisesGroup uses a greedy algorithm. Please make your expected exceptions more stringent with `Matcher` etc so the greedy algorithm can function.\"\n        self._fail_reason = s\n        return False\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_val: BaseException | None,\n        exc_tb: types.TracebackType | None,\n    ) -> bool:\n        __tracebackhide__ = True\n        assert (\n            exc_type is not None\n        ), f\"DID NOT RAISE any exception, expected {self.expected_type()}\"\n        assert (\n            self.excinfo is not None\n        ), \"Internal error - should have been constructed in __enter__\"\n\n        group_str = (\n            \"(group)\"\n            if self.allow_unwrapped and not issubclass(exc_type, BaseExceptionGroup)\n            else \"group\"\n        )\n\n        assert self.matches(\n            exc_val,\n        ), f\"Raised exception {group_str} did not match: {self._fail_reason}\"\n\n        # Cast to narrow the exception type now that it's verified.\n        exc_info = cast(\n            \"tuple[type[BaseExceptionGroup[BaseExcT_co]], BaseExceptionGroup[BaseExcT_co], types.TracebackType]\",\n            (exc_type, exc_val, exc_tb),\n        )\n        self.excinfo.fill_unfilled(exc_info)\n        return True\n\n    def expected_type(self) -> str:\n        subexcs = []\n        for e in self.expected_exceptions:\n            if isinstance(e, Matcher):\n                subexcs.append(str(e))\n            elif isinstance(e, RaisesGroup):\n                subexcs.append(e.expected_type())\n            elif isinstance(e, type):\n                subexcs.append(e.__name__)\n            else:  # pragma: no cover\n                raise AssertionError(\"unknown type\")\n        group_type = \"Base\" if self.is_baseexceptiongroup else \"\"\n        return f\"{group_type}ExceptionGroup({', '.join(subexcs)})\"\n\n\n@final\nclass NotChecked: ...\n\n\nclass ResultHolder:\n    def __init__(\n        self,\n        expected_exceptions: tuple[\n            type[BaseException] | AbstractMatcher[BaseException], ...\n        ],\n        actual_exceptions: Sequence[BaseException],\n    ) -> None:\n        self.results: list[list[str | type[NotChecked] | None]] = [\n            [NotChecked for _ in expected_exceptions] for _ in actual_exceptions\n        ]\n\n    def set_result(self, expected: int, actual: int, result: str | None) -> None:\n        self.results[actual][expected] = result\n\n    def get_result(self, expected: int, actual: int) -> str | None:\n        res = self.results[actual][expected]\n        # mypy doesn't support `assert res is not NotChecked`\n        assert not isinstance(res, type)\n        return res\n\n    def has_result(self, expected: int, actual: int) -> bool:\n        return self.results[actual][expected] is not NotChecked\n\n    def no_match_for_expected(self, expected: list[int]) -> bool:\n        for i in expected:\n            for actual_results in self.results:\n                assert actual_results[i] is not NotChecked\n                if actual_results[i] is None:\n                    return False\n        return True\n\n    def no_match_for_actual(self, actual: list[int]) -> bool:\n        for i in actual:\n            for res in self.results[i]:\n                assert res is not NotChecked\n                if res is None:\n                    return False\n        return True\n\n\ndef possible_match(results: ResultHolder, used: set[int] | None = None) -> bool:\n    if used is None:\n        used = set()\n    curr_row = len(used)\n    if curr_row == len(results.results):\n        return True\n\n    for i, val in enumerate(results.results[curr_row]):\n        if val is None and i not in used and possible_match(results, used | {i}):\n            return True\n    return False\n"
  },
  {
    "path": "src/trio/testing/_sequencer.py",
    "content": "from __future__ import annotations\n\nfrom collections import defaultdict\nfrom contextlib import asynccontextmanager\nfrom typing import TYPE_CHECKING\n\nimport attrs\n\nfrom .. import Event, _core, _util\n\nif TYPE_CHECKING:\n    from collections.abc import AsyncIterator\n\n\n@_util.final\n@attrs.define(eq=False, slots=False)\nclass Sequencer:\n    \"\"\"A convenience class for forcing code in different tasks to run in an\n    explicit linear order.\n\n    Instances of this class implement a ``__call__`` method which returns an\n    async context manager. The idea is that you pass a sequence number to\n    ``__call__`` to say where this block of code should go in the linear\n    sequence. Block 0 starts immediately, and then block N doesn't start until\n    block N-1 has finished.\n\n    Example:\n      An extremely elaborate way to print the numbers 0-5, in order::\n\n         async def worker1(seq):\n             async with seq(0):\n                 print(0)\n             async with seq(4):\n                 print(4)\n\n         async def worker2(seq):\n             async with seq(2):\n                 print(2)\n             async with seq(5):\n                 print(5)\n\n         async def worker3(seq):\n             async with seq(1):\n                 print(1)\n             async with seq(3):\n                 print(3)\n\n         async def main():\n            seq = trio.testing.Sequencer()\n            async with trio.open_nursery() as nursery:\n                nursery.start_soon(worker1, seq)\n                nursery.start_soon(worker2, seq)\n                nursery.start_soon(worker3, seq)\n\n    \"\"\"\n\n    _sequence_points: defaultdict[int, Event] = attrs.field(\n        factory=lambda: defaultdict(Event),\n        init=False,\n    )\n    _claimed: set[int] = attrs.field(factory=set, init=False)\n    _broken: bool = attrs.field(default=False, init=False)\n\n    @asynccontextmanager\n    async def __call__(self, position: int) -> AsyncIterator[None]:\n        if position in self._claimed:\n            raise RuntimeError(f\"Attempted to reuse sequence point {position}\")\n        if self._broken:\n            raise RuntimeError(\"sequence broken!\")\n        self._claimed.add(position)\n        if position != 0:\n            try:\n                await self._sequence_points[position].wait()\n            except _core.Cancelled:\n                self._broken = True\n                for event in self._sequence_points.values():\n                    event.set()\n                raise RuntimeError(\n                    \"Sequencer wait cancelled -- sequence broken\",\n                ) from None\n            else:\n                if self._broken:\n                    raise RuntimeError(\"sequence broken!\")\n        try:\n            yield\n        finally:\n            self._sequence_points[position + 1].set()\n"
  },
  {
    "path": "src/trio/testing/_trio_test.py",
    "content": "from __future__ import annotations\n\nfrom functools import partial, wraps\nfrom typing import TYPE_CHECKING, TypeVar\n\nfrom .. import _core\nfrom ..abc import Clock, Instrument\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n    from typing_extensions import ParamSpec\n\n    ArgsT = ParamSpec(\"ArgsT\")\n\n\nRetT = TypeVar(\"RetT\")\n\n\ndef trio_test(fn: Callable[ArgsT, Awaitable[RetT]]) -> Callable[ArgsT, RetT]:\n    \"\"\"Converts an async test function to be synchronous, running via Trio.\n\n    Usage::\n\n        @trio_test\n        async def test_whatever():\n            await ...\n\n    If a pytest fixture is passed in that subclasses the :class:`~trio.abc.Clock` or\n    :class:`~trio.abc.Instrument` ABCs, then those are passed to :meth:`trio.run()`.\n    \"\"\"\n\n    @wraps(fn)\n    def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT:\n        __tracebackhide__ = True\n        clocks = [c for c in kwargs.values() if isinstance(c, Clock)]\n        if not clocks:\n            clock = None\n        elif len(clocks) == 1:\n            clock = clocks[0]\n        else:\n            raise ValueError(\"too many clocks spoil the broth!\")\n        instruments = [i for i in kwargs.values() if isinstance(i, Instrument)]\n        return _core.run(\n            partial(fn, *args, **kwargs),\n            clock=clock,\n            instruments=instruments,\n        )\n\n    return wrapper\n"
  },
  {
    "path": "src/trio/to_thread.py",
    "content": "from ._threads import current_default_thread_limiter, to_thread_run_sync as run_sync\n\n# need to use __all__ for pyright --verifytypes to see re-exports when renaming them\n__all__ = [\"current_default_thread_limiter\", \"run_sync\"]\n"
  },
  {
    "path": "test-requirements.in",
    "content": "# For tests\npytest >= 8.4         # for pytest.RaisesGroup\ncoverage >= 7.2.5\nasync_generator >= 1.9\npyright\npyOpenSSL >= 22.0.0   # for the ssl + DTLS tests\ntrustme               # for the ssl + DTLS tests\npylint                # for pylint finding all symbols tests\njedi; implementation_name == \"cpython\"                  # for jedi code completion tests\ncryptography>=41.0.0  # cryptography<41 segfaults on pypy3.10\n\n# Tools\nblack; implementation_name == \"cpython\"\nmypy; implementation_name == \"cpython\"\nruff >= 0.8.0\nastor          # code generation\nuv >= 0.2.24\ncodespell\npre-commit\n\n# https://github.com/python-trio/trio/pull/654#issuecomment-420518745\nmypy-extensions\ntyping-extensions\ntypes-cffi\ntypes-pyOpenSSL\n# annotations in doc files\ntypes-docutils\nsphinx\n# sync-requirements\ntypes-PyYAML\n\n# Trio's own dependencies\ncffi; os_name == \"nt\"\nattrs >= 23.2.0\nsortedcontainers\nidna\noutcome\nsniffio\n# 1.2.1 fixes types\nexceptiongroup >= 1.2.1; python_version < \"3.11\"\n"
  },
  {
    "path": "test-requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv pip compile --universal --python-version=3.10 test-requirements.in -o test-requirements.txt\nalabaster==1.0.0\n    # via sphinx\nastor==0.8.1\n    # via -r test-requirements.in\nastroid==4.0.4\n    # via pylint\nasync-generator==1.10\n    # via -r test-requirements.in\nattrs==25.4.0\n    # via\n    #   -r test-requirements.in\n    #   outcome\nbabel==2.18.0\n    # via sphinx\nblack==26.3.1 ; implementation_name == 'cpython'\n    # via -r test-requirements.in\ncertifi==2026.2.25\n    # via requests\ncffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy'\n    # via\n    #   -r test-requirements.in\n    #   cryptography\ncfgv==3.5.0\n    # via pre-commit\ncharset-normalizer==3.4.6\n    # via requests\nclick==8.3.1 ; implementation_name == 'cpython'\n    # via black\ncodespell==2.4.2\n    # via -r test-requirements.in\ncolorama==0.4.6 ; sys_platform == 'win32'\n    # via\n    #   click\n    #   pylint\n    #   pytest\n    #   sphinx\ncoverage==7.13.5\n    # via -r test-requirements.in\ncryptography==46.0.5\n    # via\n    #   -r test-requirements.in\n    #   pyopenssl\n    #   trustme\n    #   types-pyopenssl\ndill==0.4.1\n    # via pylint\ndistlib==0.4.0\n    # via virtualenv\ndocutils==0.21.2 ; python_full_version < '3.11'\n    # via sphinx\ndocutils==0.22.4 ; python_full_version >= '3.11'\n    # via sphinx\nexceptiongroup==1.3.1 ; python_full_version < '3.11'\n    # via\n    #   -r test-requirements.in\n    #   pytest\nfilelock==3.25.2\n    # via\n    #   python-discovery\n    #   virtualenv\nidentify==2.6.18\n    # via pre-commit\nidna==3.11\n    # via\n    #   -r test-requirements.in\n    #   requests\n    #   trustme\nimagesize==2.0.0\n    # via sphinx\niniconfig==2.3.0\n    # via pytest\nisort==8.0.1\n    # via pylint\njedi==0.19.2 ; implementation_name == 'cpython'\n    # via -r test-requirements.in\njinja2==3.1.6\n    # via sphinx\nlibrt==0.8.1 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy'\n    # via mypy\nmarkupsafe==3.0.3\n    # via jinja2\nmccabe==0.7.0\n    # via pylint\nmypy==1.19.1 ; implementation_name == 'cpython'\n    # via -r test-requirements.in\nmypy-extensions==1.1.0\n    # via\n    #   -r test-requirements.in\n    #   black\n    #   mypy\nnodeenv==1.10.0\n    # via\n    #   pre-commit\n    #   pyright\noutcome==1.3.0.post0\n    # via -r test-requirements.in\npackaging==26.0\n    # via\n    #   black\n    #   pytest\n    #   sphinx\nparso==0.8.6 ; implementation_name == 'cpython'\n    # via jedi\npathspec==1.0.4 ; implementation_name == 'cpython'\n    # via\n    #   black\n    #   mypy\nplatformdirs==4.9.4\n    # via\n    #   black\n    #   pylint\n    #   python-discovery\n    #   virtualenv\npluggy==1.6.0\n    # via pytest\npre-commit==4.5.1\n    # via -r test-requirements.in\npycparser==3.0 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy')\n    # via cffi\npygments==2.19.2\n    # via\n    #   pytest\n    #   sphinx\npylint==4.0.5\n    # via -r test-requirements.in\npyopenssl==26.0.0\n    # via -r test-requirements.in\npyright==1.1.408\n    # via -r test-requirements.in\npytest==9.0.2\n    # via -r test-requirements.in\npython-discovery==1.2.0\n    # via virtualenv\npytokens==0.4.1 ; implementation_name == 'cpython'\n    # via black\npyyaml==6.0.3\n    # via pre-commit\nrequests==2.32.5\n    # via sphinx\nroman-numerals==4.1.0 ; python_full_version >= '3.11'\n    # via sphinx\nruff==0.15.6\n    # via -r test-requirements.in\nsniffio==1.3.1\n    # via -r test-requirements.in\nsnowballstemmer==3.0.1\n    # via sphinx\nsortedcontainers==2.4.0\n    # via -r test-requirements.in\nsphinx==8.1.3 ; python_full_version < '3.11'\n    # via -r test-requirements.in\nsphinx==9.0.4 ; python_full_version == '3.11.*'\n    # via -r test-requirements.in\nsphinx==9.1.0 ; python_full_version >= '3.12'\n    # via -r test-requirements.in\nsphinxcontrib-applehelp==2.0.0\n    # via sphinx\nsphinxcontrib-devhelp==2.0.0\n    # via sphinx\nsphinxcontrib-htmlhelp==2.1.0\n    # via sphinx\nsphinxcontrib-jsmath==1.0.1\n    # via sphinx\nsphinxcontrib-qthelp==2.0.0\n    # via sphinx\nsphinxcontrib-serializinghtml==2.0.0\n    # via sphinx\ntomli==2.4.0 ; python_full_version < '3.11'\n    # via\n    #   black\n    #   mypy\n    #   pylint\n    #   pytest\n    #   sphinx\ntomlkit==0.14.0\n    # via pylint\ntrustme==1.2.1\n    # via -r test-requirements.in\ntypes-cffi==2.0.0.20260316\n    # via\n    #   -r test-requirements.in\n    #   types-pyopenssl\ntypes-docutils==0.22.3.20260316\n    # via -r test-requirements.in\ntypes-pyopenssl==24.1.0.20240722\n    # via -r test-requirements.in\ntypes-pyyaml==6.0.12.20250915\n    # via -r test-requirements.in\ntypes-setuptools==82.0.0.20260210\n    # via types-cffi\ntyping-extensions==4.15.0\n    # via\n    #   -r test-requirements.in\n    #   astroid\n    #   black\n    #   cryptography\n    #   exceptiongroup\n    #   mypy\n    #   pyopenssl\n    #   pyright\n    #   virtualenv\nurllib3==2.6.3\n    # via requests\nuv==0.10.11\n    # via -r test-requirements.in\nvirtualenv==21.2.0\n    # via pre-commit\n"
  },
  {
    "path": "tests/_trio_check_attrs_aliases.py",
    "content": "\"\"\"Plugins are executed by Pytest before test modules.\n\nWe use this to monkeypatch attrs.field(), so that we can detect if aliases are used for test_exports.\n\"\"\"\n\nfrom typing import Any\n\nimport attrs\n\norig_field = attrs.field\n\n\ndef field(**kwargs: Any) -> Any:\n    original_args = kwargs.copy()\n    metadata = kwargs.setdefault(\"metadata\", {})\n    metadata[\"trio_original_args\"] = original_args\n    return orig_field(**kwargs)\n\n\n# Mark it as being ours, so the test knows it can actually run.\nfield.trio_modded = True  # type: ignore\nattrs.field = field\n"
  },
  {
    "path": "tests/cython/run_test_cython.py",
    "content": "from .test_cython import invoke_main_entry_point\n\ninvoke_main_entry_point()\n"
  },
  {
    "path": "tests/cython/test_cython.pyx",
    "content": "# cython: language_level=3\nimport trio\n\n# the output of the prints are not currently checked, we only check\n# if the program can be compiled and doesn't crash when run.\n\n# The content of the program can easily be extended if there's other behaviour\n# that might be likely to be problematic for cython.\nasync def foo() -> None:\n    print('.')\n\nasync def trio_main() -> None:\n    print('hello...')\n    await trio.sleep(1)\n    print(' world !')\n\n    async with trio.open_nursery() as nursery:\n        nursery.start_soon(foo)\n        nursery.start_soon(foo)\n        nursery.start_soon(foo)\n\ndef invoke_main_entry_point():\n    trio.run(trio_main)\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py{39,310,311,312,313,314,py310,py311}\nlabels =\n    check = typing, gen_exports, type_completeness, pip_compile\n    cython = py39-cython2,py39-cython,py311-cython2,py313-cython\n\n# TODO:\n# * environment to check coverage\n# * replace ci.sh\n#   * --verbose --durations=10\n#   * -p _trio_check_attrs_aliases\n#   * mypy cache\n#   * LSP\n#   * apport\n# * use tox in CI\n# * switch to nox?\n# * move to pyproject.toml?\n#   * this means conditional deps need to be replaced\n\n# protip: install tox-uv for faster venv generation\n\n[testenv]\ndescription = \"Base environment for running tests depending on python version.\"\n# use wheels instead of sdist, significantly faster install\npackage = wheel\nwheel_build_env = .pkg\ndeps =\n    hypothesis: hypothesis\n    -r test-requirements.txt\nset_env =\n    slow: TOX_RUN_SLOW = '--run-slow'\ncommands =\n    pytest {env:TOX_RUN_SLOW:} {posargs}\n\n[testenv:no_test_requirements]\ndescription = \"Run tests without optional test-requirements, to see we don't accidentally depend on a library not specified in depends.\"\ndeps =\n    pytest\ncommands =\n    pytest --skip-optional-imports {posargs}\n\n[testenv:docs]\ndescription = \"Build documentation into docs/build.\"\ndeps =\n    -r docs-requirements.txt\n# base_python synced with .readthedocs.yml\n# To avoid syncing we can make RTD call the tox environment\nbase_python = 3.11\ncommands =\n    sphinx-build {posargs:--fresh-env} docs/source docs/build\n\n[testenv:py39-cython2,py39-cython,py311-cython2,py313-cython]\ndescription = \"Run cython tests.\"\ndeps =\n    # cython 3.1.0 broke stuff https://github.com/cython/cython/issues/6865\n    cython: cython\n    cython2: cython<3\n    setuptools ; python_version >= '3.12'\ncommands_pre =\n    python --version\n    cython --version\n    cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx\ncommands =\n    python -m tests.cython.run_test_cython\n\n[testenv:cov-cython]\ndeps =\n    setuptools\n    cython\nset_env =\n    CFLAGS= -DCYTHON_TRACE_NOGIL=1\nallowlist_externals =\n    sed\n    cp\ncommands_pre =\n    python --version\n    cython --version\n    cp pyproject.toml {temp_dir}/\n    sed -i \"s/plugins\\ =\\ \\\\[\\\\]/plugins = [\\\"Cython.Coverage\\\"]/\" {temp_dir}/pyproject.toml\n    cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx\ncommands =\n    coverage run -m tests.cython.run_test_cython --rcfile={temp_dir}/pyproject.toml\n    coverage combine\n    coverage report\n\n[testenv:gen_exports]\ndescription = \"Run gen_exports.py, regenerating code for public API wrappers.\"\ndeps =\n    -r test-requirements.txt\nbase_python = 3.13\ncommands =\n    python ./src/trio/_tools/gen_exports.py --test\n\n[testenv:pip_compile]\ndescription = \"Run pre-commit job pip-compile\"\nbase_python = 3.13\ncommands =\n    pre-commit run pip-compile --all-files\n\n[testenv:typing]\ndescription = \"Run type checks: mypy on all platforms, and pyright on `src/trio[/_core]/_tests/type_tests/`.\"\ndeps =\n    -r test-requirements.txt\n    exceptiongroup\nbase_python = 3.10\nset_env =\n    PYRIGHT_PYTHON_IGNORE_WARNINGS=1\ncommands =\n    # use mypy_annotate if running in CI? if not, should remove it\n    mypy --platform linux\n    mypy --platform darwin\n    mypy --platform win32\n\n    pyright src/trio/_tests/type_tests\n    pyright src/trio/_core/_tests/type_tests\n\n[testenv:type_completeness]\ndescription = \"Check type completeness, using our wrapper around pyright --verifytypes.\"\ndeps =\n    -r test-requirements.txt\n    exceptiongroup\nbase_python = 3.13\nset_env =\n    PYRIGHT_PYTHON_IGNORE_WARNINGS=1\ncommands =\n    python src/trio/_tests/check_type_completeness.py\n"
  },
  {
    "path": "zizmor.yml",
    "content": "rules:\r\n  unpinned-uses:\r\n    config:\r\n      policies:\r\n        # TODO: use the default policies\r\n        \"*\": any\r\n"
  }
]